refactor: asset pipeline
This commit is contained in:
parent
3a82e64901
commit
426edc3a03
100
src/gen/load.rs
100
src/gen/load.rs
|
@ -2,53 +2,69 @@ use std::collections::HashSet ;
|
||||||
|
|
||||||
use camino::Utf8PathBuf;
|
use camino::Utf8PathBuf;
|
||||||
use glob::glob;
|
use glob::glob;
|
||||||
|
use hayagriva::Library;
|
||||||
|
|
||||||
|
use crate::html::Linkable;
|
||||||
|
|
||||||
|
use super::Sack;
|
||||||
|
|
||||||
|
|
||||||
|
/// Whether the item should be treated as a content page, converted into a standalone HTML page, or
|
||||||
|
/// as a bundled asset.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SourceKind {
|
pub enum StaticItemKind {
|
||||||
|
/// Convert to `index.html`
|
||||||
Index,
|
Index,
|
||||||
Asset,
|
/// Convert to a bundled asset
|
||||||
|
Bundle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Metadata for a single item consumed by SSG.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Source {
|
pub struct StaticItem {
|
||||||
pub kind: SourceKind,
|
/// Kind of an item
|
||||||
|
pub kind: StaticItemKind,
|
||||||
|
/// Original extension for the source file
|
||||||
pub ext: String,
|
pub ext: String,
|
||||||
pub dirs: Utf8PathBuf,
|
pub dir: Utf8PathBuf,
|
||||||
pub path: Utf8PathBuf,
|
pub src: Utf8PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum AssetKind {
|
||||||
|
Html(Box<dyn Fn(&Sack) -> String>),
|
||||||
|
Image,
|
||||||
|
Other,
|
||||||
|
Bib(Library),
|
||||||
|
}
|
||||||
|
|
||||||
fn to_source(path: Utf8PathBuf, exts: &HashSet<&'static str>) -> Source {
|
pub struct Asset {
|
||||||
let dir = path.parent().unwrap();
|
pub kind: AssetKind,
|
||||||
let ext = path.extension().unwrap();
|
pub out: Utf8PathBuf,
|
||||||
|
pub meta: StaticItem,
|
||||||
|
pub link: Option<Linkable>,
|
||||||
|
}
|
||||||
|
|
||||||
if !exts.contains(ext) {
|
pub enum PipelineItem {
|
||||||
return Source {
|
Skip(StaticItem),
|
||||||
kind: SourceKind::Asset,
|
Take(Asset),
|
||||||
ext: ext.to_owned(),
|
}
|
||||||
dirs: dir.to_owned(),
|
|
||||||
path,
|
impl From<StaticItem> for PipelineItem {
|
||||||
};
|
fn from(value: StaticItem) -> Self {
|
||||||
|
Self::Skip(value)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let dirs = match path.file_stem().unwrap() {
|
impl From<Asset> for PipelineItem {
|
||||||
"index" => dir.to_owned(),
|
fn from(value: Asset) -> Self {
|
||||||
name => dir.join(name),
|
Self::Take(value)
|
||||||
};
|
|
||||||
|
|
||||||
Source {
|
|
||||||
kind: SourceKind::Index,
|
|
||||||
ext: ext.to_owned(),
|
|
||||||
dirs,
|
|
||||||
path,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn gather(pattern: &str, exts: &HashSet<&'static str>) -> Vec<Source> {
|
pub fn gather(pattern: &str, exts: &HashSet<&'static str>) -> Vec<PipelineItem> {
|
||||||
glob(pattern)
|
glob(pattern)
|
||||||
.unwrap()
|
.expect("Invalid glob pattern")
|
||||||
.filter_map(|path| {
|
.filter_map(|path| {
|
||||||
let path = path.unwrap();
|
let path = path.unwrap();
|
||||||
let path = Utf8PathBuf::from_path_buf(path).expect("Filename is not valid UTF8");
|
let path = Utf8PathBuf::from_path_buf(path).expect("Filename is not valid UTF8");
|
||||||
|
@ -58,5 +74,33 @@ pub fn gather(pattern: &str, exts: &HashSet<&'static str>) -> Vec<Source> {
|
||||||
false => Some(to_source(path, exts))
|
false => Some(to_source(path, exts))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.map(Into::into)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn to_source(path: Utf8PathBuf, exts: &HashSet<&'static str>) -> StaticItem {
|
||||||
|
let dir = path.parent().unwrap();
|
||||||
|
let ext = path.extension().unwrap();
|
||||||
|
|
||||||
|
if !exts.contains(ext) {
|
||||||
|
return StaticItem {
|
||||||
|
kind: StaticItemKind::Bundle,
|
||||||
|
ext: ext.to_owned(),
|
||||||
|
dir: dir.to_owned(),
|
||||||
|
src: path,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let dirs = match path.file_stem().unwrap() {
|
||||||
|
"index" => dir.to_owned(),
|
||||||
|
name => dir.join(name),
|
||||||
|
};
|
||||||
|
|
||||||
|
StaticItem {
|
||||||
|
kind: StaticItemKind::Index,
|
||||||
|
ext: ext.to_owned(),
|
||||||
|
dir: dirs,
|
||||||
|
src: path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ use camino::Utf8PathBuf;
|
||||||
use hayagriva::Library;
|
use hayagriva::Library;
|
||||||
use hypertext::Renderable;
|
use hypertext::Renderable;
|
||||||
|
|
||||||
pub use load::{gather, Source, SourceKind};
|
pub use load::{gather, StaticItem, StaticItemKind, Asset, AssetKind, PipelineItem};
|
||||||
pub use render::{render, Asset, AssetKind, Virtual, Item};
|
pub use render::{render, Virtual, Item};
|
||||||
pub use sack::{TreePage, Sack};
|
pub use sack::{TreePage, Sack};
|
||||||
|
|
||||||
use crate::{html::Linkable, text::md::Outline};
|
use crate::{html::Linkable, text::md::Outline};
|
||||||
|
|
|
@ -1,38 +1,12 @@
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use camino::{Utf8Path, Utf8PathBuf};
|
use camino::{Utf8Path, Utf8PathBuf};
|
||||||
|
|
||||||
use crate::html::Linkable;
|
|
||||||
use crate::Sack;
|
use crate::Sack;
|
||||||
|
|
||||||
|
use super::{Asset, AssetKind};
|
||||||
|
|
||||||
pub enum AssetKind {
|
|
||||||
Html(Box<dyn Fn(&Sack) -> String>),
|
|
||||||
Image,
|
|
||||||
Unknown,
|
|
||||||
Bib(hayagriva::Library),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for AssetKind {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Html(ptr) => f.debug_tuple("Html").field(&format!("{:p}", *ptr)).finish(),
|
|
||||||
Self::Image => write!(f, "Image"),
|
|
||||||
Self::Unknown => write!(f, "Unknown"),
|
|
||||||
Self::Bib(bib) => f.debug_tuple("Bib").field(bib).finish(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Asset {
|
|
||||||
pub kind: AssetKind,
|
|
||||||
pub out: Utf8PathBuf,
|
|
||||||
pub link: Option<Linkable>,
|
|
||||||
pub meta: super::Source,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Virtual(pub Utf8PathBuf, pub Box<dyn Fn(&Sack) -> String>);
|
pub struct Virtual(pub Utf8PathBuf, pub Box<dyn Fn(&Sack) -> String>);
|
||||||
|
|
||||||
|
@ -85,7 +59,7 @@ pub fn render(items: &[Item]) {
|
||||||
fn render_real(item: &Asset, sack: &Sack) {
|
fn render_real(item: &Asset, sack: &Sack) {
|
||||||
match &item.kind {
|
match &item.kind {
|
||||||
AssetKind::Html(render) => {
|
AssetKind::Html(render) => {
|
||||||
let i = &item.meta.path;
|
let i = &item.meta.src;
|
||||||
let o = Utf8Path::new("dist").join(&item.out);
|
let o = Utf8Path::new("dist").join(&item.out);
|
||||||
|
|
||||||
fs::create_dir_all(o.parent().unwrap()).unwrap();
|
fs::create_dir_all(o.parent().unwrap()).unwrap();
|
||||||
|
@ -96,15 +70,15 @@ fn render_real(item: &Asset, sack: &Sack) {
|
||||||
println!("HTML: {} -> {}", i, o);
|
println!("HTML: {} -> {}", i, o);
|
||||||
},
|
},
|
||||||
AssetKind::Image => {
|
AssetKind::Image => {
|
||||||
let i = &item.meta.path;
|
let i = &item.meta.src;
|
||||||
let o = Utf8Path::new("dist").join(&item.out);
|
let o = Utf8Path::new("dist").join(&item.out);
|
||||||
fs::create_dir_all(o.parent().unwrap()).unwrap();
|
fs::create_dir_all(o.parent().unwrap()).unwrap();
|
||||||
fs::copy(i, &o).unwrap();
|
fs::copy(i, &o).unwrap();
|
||||||
println!("Image: {} -> {}", i, o);
|
println!("Image: {} -> {}", i, o);
|
||||||
},
|
},
|
||||||
AssetKind::Bib(_) => (),
|
AssetKind::Bib(_) => (),
|
||||||
AssetKind::Unknown => {
|
AssetKind::Other => {
|
||||||
let i = &item.meta.path;
|
let i = &item.meta.src;
|
||||||
let o = Utf8Path::new("dist").join(&item.out);
|
let o = Utf8Path::new("dist").join(&item.out);
|
||||||
fs::create_dir_all(o.parent().unwrap()).unwrap();
|
fs::create_dir_all(o.parent().unwrap()).unwrap();
|
||||||
fs::copy(i, &o).unwrap();
|
fs::copy(i, &o).unwrap();
|
||||||
|
|
|
@ -35,7 +35,6 @@ impl TreePage {
|
||||||
|
|
||||||
|
|
||||||
/// This struct allows for querying the website hierarchy.
|
/// This struct allows for querying the website hierarchy.
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Sack<'a> {
|
pub struct Sack<'a> {
|
||||||
assets: &'a [&'a Asset],
|
assets: &'a [&'a Asset],
|
||||||
path: &'a Utf8PathBuf,
|
path: &'a Utf8PathBuf,
|
||||||
|
|
173
src/main.rs
173
src/main.rs
|
@ -4,11 +4,12 @@ use std::fs;
|
||||||
|
|
||||||
use camino::{Utf8Path, Utf8PathBuf};
|
use camino::{Utf8Path, Utf8PathBuf};
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
use gen::{Asset, Sack, Content};
|
use gen::{Asset, AssetKind, Content, PipelineItem, Sack, StaticItemKind};
|
||||||
use hayagriva::Library;
|
use hayagriva::Library;
|
||||||
use html::{Link, LinkDate, Linkable};
|
use html::{Link, LinkDate, Linkable};
|
||||||
use hypertext::{Raw, Renderable};
|
use hypertext::{Raw, Renderable};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use serde::Deserialize;
|
||||||
use text::md::Outline;
|
use text::md::Outline;
|
||||||
|
|
||||||
mod md;
|
mod md;
|
||||||
|
@ -171,74 +172,73 @@ fn to_list(list: Vec<LinkDate>) -> String {
|
||||||
html::list("", &groups).render().into()
|
html::list("", &groups).render().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_index<T>(item: PipelineItem) -> PipelineItem
|
||||||
fn transform<T>(meta: gen::Source) -> Asset
|
|
||||||
where
|
where
|
||||||
T: for<'de> serde::Deserialize<'de>,
|
T: for<'de> Deserialize<'de> + Content + 'static,
|
||||||
T: Content + 'static,
|
|
||||||
{
|
{
|
||||||
let dir = meta.dirs.strip_prefix("content").unwrap();
|
let meta = match item {
|
||||||
|
PipelineItem::Skip(meta) if matches!(meta.kind, StaticItemKind::Index) => meta,
|
||||||
|
_ => return item,
|
||||||
|
};
|
||||||
|
|
||||||
match meta.kind {
|
let dir = meta.dir.strip_prefix("content").unwrap();
|
||||||
gen::SourceKind::Index => match meta.ext.as_str() {
|
match meta.ext.as_str() {
|
||||||
"md" | "mdx" | "lhs" => {
|
"md" | "mdx" | "lhs" => {
|
||||||
let path = dir.join("index.html");
|
let path = dir.join("index.html");
|
||||||
|
|
||||||
let data = fs::read_to_string(&meta.path).unwrap();
|
let data = fs::read_to_string(&meta.src).unwrap();
|
||||||
let (fm, md) = md::preflight::<T>(&data);
|
let (fm, md) = md::preflight::<T>(&data);
|
||||||
let link = T::as_link(&fm, Utf8Path::new("/").join(dir));
|
let link = T::as_link(&fm, Utf8Path::new("/").join(dir));
|
||||||
|
|
||||||
let call = move |sack: &Sack| {
|
let call = move |sack: &Sack| {
|
||||||
let lib = sack.get_library();
|
let lib = sack.get_library();
|
||||||
let (outline, html, bib) = T::render(&md, lib);
|
let (outline, html, bib) = T::render(&md, lib);
|
||||||
T::transform(&fm, Raw(html), outline, sack, bib).render().into()
|
T::transform(&fm, Raw(html), outline, sack, bib).render().into()
|
||||||
};
|
};
|
||||||
|
|
||||||
gen::Asset {
|
gen::Asset {
|
||||||
kind: gen::AssetKind::Html(Box::new(call)),
|
kind: gen::AssetKind::Html(Box::new(call)),
|
||||||
out: path,
|
out: path,
|
||||||
link,
|
link,
|
||||||
meta,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => gen::Asset {
|
|
||||||
kind: gen::AssetKind::Unknown,
|
|
||||||
out: dir.join(meta.path.file_name().unwrap()).to_owned(),
|
|
||||||
link: None,
|
|
||||||
meta,
|
meta,
|
||||||
}
|
}.into()
|
||||||
},
|
},
|
||||||
gen::SourceKind::Asset => {
|
_ => meta.into(),
|
||||||
let loc = dir.join(meta.path.file_name().unwrap()).to_owned();
|
|
||||||
match meta.ext.as_str() {
|
|
||||||
"jpg" | "png" | "gif" => gen::Asset {
|
|
||||||
kind: gen::AssetKind::Image,
|
|
||||||
out: loc,
|
|
||||||
link: None,
|
|
||||||
meta,
|
|
||||||
},
|
|
||||||
"bib" => {
|
|
||||||
let data = fs::read_to_string(&meta.path).unwrap();
|
|
||||||
let data = hayagriva::io::from_biblatex_str(&data).unwrap();
|
|
||||||
|
|
||||||
gen::Asset {
|
|
||||||
kind: gen::AssetKind::Bib(data),
|
|
||||||
out: loc,
|
|
||||||
link: None,
|
|
||||||
meta,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => gen::Asset {
|
|
||||||
kind: gen::AssetKind::Unknown,
|
|
||||||
out: loc,
|
|
||||||
link: None,
|
|
||||||
meta,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_bundle(item: PipelineItem) -> PipelineItem {
|
||||||
|
let meta = match item {
|
||||||
|
PipelineItem::Skip(meta) if matches!(meta.kind, StaticItemKind::Bundle) => meta,
|
||||||
|
_ => return item,
|
||||||
|
};
|
||||||
|
|
||||||
|
let dir = meta.dir.strip_prefix("content").unwrap();
|
||||||
|
let out = dir.join(meta.src.file_name().unwrap()).to_owned();
|
||||||
|
|
||||||
|
match meta.ext.as_str() {
|
||||||
|
"jpg" | "png" | "gif" => gen::Asset {
|
||||||
|
kind: gen::AssetKind::Image,
|
||||||
|
out,
|
||||||
|
link: None,
|
||||||
|
meta,
|
||||||
|
}.into(),
|
||||||
|
"bib" => {
|
||||||
|
let data = fs::read_to_string(&meta.src).unwrap();
|
||||||
|
let data = hayagriva::io::from_biblatex_str(&data).unwrap();
|
||||||
|
|
||||||
|
Asset {
|
||||||
|
kind: AssetKind::Bib(data),
|
||||||
|
out,
|
||||||
|
link: None,
|
||||||
|
meta,
|
||||||
|
}.into()
|
||||||
|
},
|
||||||
|
_ => meta.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if fs::metadata("dist").is_ok() {
|
if fs::metadata("dist").is_ok() {
|
||||||
println!("Cleaning dist");
|
println!("Cleaning dist");
|
||||||
|
@ -247,7 +247,36 @@ fn main() {
|
||||||
|
|
||||||
fs::create_dir("dist").unwrap();
|
fs::create_dir("dist").unwrap();
|
||||||
|
|
||||||
|
let assets: Vec<Asset> = vec![
|
||||||
|
gen::gather("content/about.md", &["md"].into())
|
||||||
|
.into_iter()
|
||||||
|
.map(to_index::<md::Post> as fn(PipelineItem) -> PipelineItem),
|
||||||
|
gen::gather("content/posts/**/*", &["md", "mdx"].into())
|
||||||
|
.into_iter()
|
||||||
|
.map(to_index::<md::Post>),
|
||||||
|
gen::gather("content/slides/**/*", &["md", "lhs"].into())
|
||||||
|
.into_iter()
|
||||||
|
.map(to_index::<md::Slide>),
|
||||||
|
gen::gather("content/wiki/**/*", &["md"].into())
|
||||||
|
.into_iter()
|
||||||
|
.map(to_index::<md::Wiki>),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.map(to_bundle)
|
||||||
|
.filter_map(|item| match item {
|
||||||
|
PipelineItem::Skip(skip) => {
|
||||||
|
println!("Skipping {}", skip.src);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
PipelineItem::Take(take) => Some(take),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let assets: Vec<Vec<gen::Item>> = vec![
|
let assets: Vec<Vec<gen::Item>> = vec![
|
||||||
|
assets.into_iter()
|
||||||
|
.map(Into::into)
|
||||||
|
.collect(),
|
||||||
vec![
|
vec![
|
||||||
gen::Virtual::new("map/index.html", |_| html::map().render().to_owned().into()).into(),
|
gen::Virtual::new("map/index.html", |_| html::map().render().to_owned().into()).into(),
|
||||||
gen::Virtual::new("search/index.html", |_| html::search().render().to_owned().into()).into(),
|
gen::Virtual::new("search/index.html", |_| html::search().render().to_owned().into()).into(),
|
||||||
|
@ -259,11 +288,11 @@ fn main() {
|
||||||
})),
|
})),
|
||||||
out: "index.html".into(),
|
out: "index.html".into(),
|
||||||
link: None,
|
link: None,
|
||||||
meta: gen::Source {
|
meta: gen::StaticItem {
|
||||||
kind: gen::SourceKind::Index,
|
kind: gen::StaticItemKind::Index,
|
||||||
ext: "md".into(),
|
ext: "md".into(),
|
||||||
dirs: "".into(),
|
dir: "".into(),
|
||||||
path: "content/index.md".into()
|
src: "content/index.md".into()
|
||||||
}
|
}
|
||||||
}.into(),
|
}.into(),
|
||||||
gen::Virtual("posts/index.html".into(), Box::new(|all|
|
gen::Virtual("posts/index.html".into(), Box::new(|all|
|
||||||
|
@ -273,26 +302,6 @@ fn main() {
|
||||||
to_list(all.get_links("slides/**/*.html"))
|
to_list(all.get_links("slides/**/*.html"))
|
||||||
)).into(),
|
)).into(),
|
||||||
],
|
],
|
||||||
gen::gather("content/about.md", &["md"].into())
|
|
||||||
.into_iter()
|
|
||||||
.map(transform::<md::Post>)
|
|
||||||
.map(Into::into)
|
|
||||||
.collect(),
|
|
||||||
gen::gather("content/posts/**/*", &["md", "mdx"].into())
|
|
||||||
.into_iter()
|
|
||||||
.map(transform::<md::Post>)
|
|
||||||
.map(Into::into)
|
|
||||||
.collect(),
|
|
||||||
gen::gather("content/slides/**/*", &["md", "lhs"].into())
|
|
||||||
.into_iter()
|
|
||||||
.map(transform::<md::Slide>)
|
|
||||||
.map(Into::into)
|
|
||||||
.collect(),
|
|
||||||
gen::gather("content/wiki/**/*", &["md"].into())
|
|
||||||
.into_iter()
|
|
||||||
.map(transform::<md::Wiki>)
|
|
||||||
.map(Into::into)
|
|
||||||
.collect(),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let all: Vec<gen::Item> = assets
|
let all: Vec<gen::Item> = assets
|
||||||
|
|
Loading…
Reference in a new issue