further breaking up treehouse::generate into smaller submodules
This commit is contained in:
parent
fd40f99810
commit
2e14197fd1
|
@ -1,9 +1,10 @@
|
|||
mod dir_helper;
|
||||
mod include_static_helper;
|
||||
mod simple_template;
|
||||
mod tree;
|
||||
|
||||
use std::{collections::HashMap, fmt, ops::ControlFlow, sync::Arc};
|
||||
|
||||
use anyhow::{ensure, Context};
|
||||
use dir_helper::DirHelper;
|
||||
use handlebars::{handlebars_helper, Handlebars};
|
||||
use include_static_helper::IncludeStaticHelper;
|
||||
|
@ -14,32 +15,13 @@ use crate::{
|
|||
config::Config,
|
||||
dirs::Dirs,
|
||||
fun::seasons::Season,
|
||||
html::{breadcrumbs::breadcrumbs_to_html, tree::branches_to_html},
|
||||
sources::Sources,
|
||||
state::FileId,
|
||||
vfs::{
|
||||
self, Cd, ContentCache, Dir, DirEntry, DynDir, HtmlCanonicalize, MemDir, Overlay, ToDynDir,
|
||||
VPath, VPathBuf,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Page {
|
||||
title: String,
|
||||
thumbnail: Option<Thumbnail>,
|
||||
scripts: Vec<String>,
|
||||
styles: Vec<String>,
|
||||
breadcrumbs: String,
|
||||
tree_path: Option<String>,
|
||||
tree: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Thumbnail {
|
||||
url: String,
|
||||
alt: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct BaseTemplateData<'a> {
|
||||
config: &'a Config,
|
||||
|
@ -48,11 +30,37 @@ struct BaseTemplateData<'a> {
|
|||
dev: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct PageTemplateData<'a> {
|
||||
#[serde(flatten)]
|
||||
base: &'a BaseTemplateData<'a>,
|
||||
page: Page,
|
||||
impl<'a> BaseTemplateData<'a> {
|
||||
fn new(sources: &'a Sources) -> Self {
|
||||
Self {
|
||||
config: &sources.config,
|
||||
import_map: serde_json::to_string_pretty(&sources.import_map)
|
||||
.expect("import map should be serializable to JSON"),
|
||||
season: Season::current(),
|
||||
dev: cfg!(debug_assertions),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TreehouseDir {
|
||||
dirs: Arc<Dirs>,
|
||||
sources: Arc<Sources>,
|
||||
dir_index: DirIndex,
|
||||
handlebars: Handlebars<'static>,
|
||||
}
|
||||
|
||||
impl TreehouseDir {
|
||||
fn new(dirs: Arc<Dirs>, sources: Arc<Sources>, dir_index: DirIndex) -> Self {
|
||||
let mut handlebars = create_handlebars(&sources.config.site, dirs.static_.clone());
|
||||
load_templates(&mut handlebars, &dirs.template);
|
||||
|
||||
Self {
|
||||
dirs,
|
||||
sources,
|
||||
dir_index,
|
||||
handlebars,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_handlebars(site: &str, static_: DynDir) -> Handlebars<'static> {
|
||||
|
@ -85,173 +93,6 @@ fn load_templates(handlebars: &mut Handlebars, dir: &dyn Dir) {
|
|||
});
|
||||
}
|
||||
|
||||
#[instrument(skip(sources, handlebars))]
|
||||
fn generate_simple_template(
|
||||
sources: &Sources,
|
||||
handlebars: &Handlebars,
|
||||
template_name: &str,
|
||||
) -> anyhow::Result<String> {
|
||||
let base_template_data = BaseTemplateData {
|
||||
config: &sources.config,
|
||||
import_map: serde_json::to_string_pretty(&sources.import_map)
|
||||
.expect("import map should be serializable to JSON"),
|
||||
season: Season::current(),
|
||||
dev: cfg!(debug_assertions),
|
||||
};
|
||||
handlebars
|
||||
.render(template_name, &base_template_data)
|
||||
.context("failed to render template")
|
||||
}
|
||||
|
||||
fn generate_simple_template_or_error(
|
||||
sources: &Sources,
|
||||
handlebars: &Handlebars,
|
||||
template_name: &str,
|
||||
) -> String {
|
||||
match generate_simple_template(sources, handlebars, template_name) {
|
||||
Ok(html) => html,
|
||||
Err(error) => format!("error: {error:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(sources, dirs, handlebars))]
|
||||
fn generate_tree(
|
||||
sources: &Sources,
|
||||
dirs: &Dirs,
|
||||
handlebars: &Handlebars,
|
||||
file_id: FileId,
|
||||
) -> anyhow::Result<String> {
|
||||
let breadcrumbs = breadcrumbs_to_html(&sources.config, &sources.navigation_map, file_id);
|
||||
|
||||
let roots = sources
|
||||
.treehouse
|
||||
.roots
|
||||
.get(&file_id)
|
||||
.expect("tree should have been added to the treehouse");
|
||||
|
||||
let tree = {
|
||||
let _span = info_span!("generate_tree::branches_to_html").entered();
|
||||
let mut tree = String::new();
|
||||
branches_to_html(
|
||||
&mut tree,
|
||||
&sources.treehouse,
|
||||
&sources.config,
|
||||
dirs,
|
||||
file_id,
|
||||
&roots.branches,
|
||||
);
|
||||
tree
|
||||
};
|
||||
|
||||
let base_template_data = BaseTemplateData {
|
||||
config: &sources.config,
|
||||
import_map: serde_json::to_string_pretty(&sources.import_map)
|
||||
.expect("import map should be serializable to JSON"),
|
||||
season: Season::current(),
|
||||
dev: cfg!(debug_assertions),
|
||||
};
|
||||
|
||||
let template_data = PageTemplateData {
|
||||
base: &base_template_data,
|
||||
page: Page {
|
||||
title: roots.attributes.title.clone(),
|
||||
thumbnail: roots
|
||||
.attributes
|
||||
.thumbnail
|
||||
.as_ref()
|
||||
.map(|thumbnail| Thumbnail {
|
||||
url: sources.config.pic_url(&*dirs.pic, &thumbnail.id),
|
||||
alt: thumbnail.alt.clone(),
|
||||
}),
|
||||
scripts: roots.attributes.scripts.clone(),
|
||||
styles: roots.attributes.styles.clone(),
|
||||
breadcrumbs,
|
||||
tree_path: sources.treehouse.tree_path(file_id).map(|s| s.to_string()),
|
||||
tree,
|
||||
},
|
||||
};
|
||||
let template_name = roots
|
||||
.attributes
|
||||
.template
|
||||
.clone()
|
||||
.unwrap_or_else(|| "_tree.hbs".into());
|
||||
|
||||
ensure!(
|
||||
handlebars.has_template(&template_name),
|
||||
"template {template_name} does not exist"
|
||||
);
|
||||
|
||||
let _span = info_span!("handlebars::render").entered();
|
||||
handlebars
|
||||
.render(&template_name, &template_data)
|
||||
.context("template rendering failed")
|
||||
}
|
||||
|
||||
fn generate_tree_or_error(
|
||||
sources: &Sources,
|
||||
dirs: &Dirs,
|
||||
handlebars: &Handlebars,
|
||||
file_id: FileId,
|
||||
) -> String {
|
||||
match generate_tree(sources, dirs, handlebars, file_id) {
|
||||
Ok(html) => html,
|
||||
Err(error) => format!("error: {error:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Acceleration structure for `dir` operations on [`TreehouseDir`]s.
|
||||
#[derive(Debug, Default)]
|
||||
struct DirIndex {
|
||||
full_path: VPathBuf,
|
||||
children: HashMap<VPathBuf, DirIndex>,
|
||||
}
|
||||
|
||||
impl DirIndex {
|
||||
#[instrument(name = "DirIndex::new", skip(paths))]
|
||||
pub fn new<'a>(paths: impl Iterator<Item = &'a VPath>) -> Self {
|
||||
let mut root = DirIndex::default();
|
||||
|
||||
for path in paths {
|
||||
let mut parent = &mut root;
|
||||
let mut full_path = VPath::ROOT.to_owned();
|
||||
for segment in path.segments() {
|
||||
full_path.push(segment);
|
||||
let child = parent
|
||||
.children
|
||||
.entry(segment.to_owned())
|
||||
.or_insert_with(|| DirIndex {
|
||||
full_path: full_path.clone(),
|
||||
children: HashMap::new(),
|
||||
});
|
||||
parent = child;
|
||||
}
|
||||
}
|
||||
|
||||
root
|
||||
}
|
||||
}
|
||||
|
||||
struct TreehouseDir {
|
||||
dirs: Arc<Dirs>,
|
||||
sources: Arc<Sources>,
|
||||
dir_index: DirIndex,
|
||||
handlebars: Handlebars<'static>,
|
||||
}
|
||||
|
||||
impl TreehouseDir {
|
||||
fn new(dirs: Arc<Dirs>, sources: Arc<Sources>, dir_index: DirIndex) -> Self {
|
||||
let mut handlebars = create_handlebars(&sources.config.site, dirs.static_.clone());
|
||||
load_templates(&mut handlebars, &dirs.template);
|
||||
|
||||
Self {
|
||||
dirs,
|
||||
sources,
|
||||
dir_index,
|
||||
handlebars,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dir for TreehouseDir {
|
||||
#[instrument("TreehouseDir::dir", skip(self))]
|
||||
fn dir(&self, path: &VPath) -> Vec<DirEntry> {
|
||||
|
@ -290,14 +131,14 @@ impl Dir for TreehouseDir {
|
|||
.files_by_tree_path
|
||||
.get(path)
|
||||
.map(|&file_id| {
|
||||
generate_tree_or_error(&self.sources, &self.dirs, &self.handlebars, file_id).into()
|
||||
tree::generate_or_error(&self.sources, &self.dirs, &self.handlebars, file_id).into()
|
||||
})
|
||||
.or_else(|| {
|
||||
if path.file_name().is_some_and(|s| !s.starts_with('_')) {
|
||||
let template_name = path.with_extension("hbs");
|
||||
if self.handlebars.has_template(template_name.as_str()) {
|
||||
return Some(
|
||||
generate_simple_template_or_error(
|
||||
simple_template::generate_or_error(
|
||||
&self.sources,
|
||||
&self.handlebars,
|
||||
template_name.as_str(),
|
||||
|
@ -321,6 +162,38 @@ impl fmt::Debug for TreehouseDir {
|
|||
}
|
||||
}
|
||||
|
||||
/// Acceleration structure for `dir` operations on [`TreehouseDir`]s.
|
||||
#[derive(Debug, Default)]
|
||||
struct DirIndex {
|
||||
full_path: VPathBuf,
|
||||
children: HashMap<VPathBuf, DirIndex>,
|
||||
}
|
||||
|
||||
impl DirIndex {
|
||||
#[instrument(name = "DirIndex::new", skip(paths))]
|
||||
pub fn new<'a>(paths: impl Iterator<Item = &'a VPath>) -> Self {
|
||||
let mut root = DirIndex::default();
|
||||
|
||||
for path in paths {
|
||||
let mut parent = &mut root;
|
||||
let mut full_path = VPath::ROOT.to_owned();
|
||||
for segment in path.segments() {
|
||||
full_path.push(segment);
|
||||
let child = parent
|
||||
.children
|
||||
.entry(segment.to_owned())
|
||||
.or_insert_with(|| DirIndex {
|
||||
full_path: full_path.clone(),
|
||||
children: HashMap::new(),
|
||||
});
|
||||
parent = child;
|
||||
}
|
||||
}
|
||||
|
||||
root
|
||||
}
|
||||
}
|
||||
|
||||
pub fn target(dirs: Arc<Dirs>, sources: Arc<Sources>) -> DynDir {
|
||||
let mut root = MemDir::new();
|
||||
root.add(VPath::new("static"), dirs.static_.clone());
|
||||
|
|
30
crates/treehouse/src/generate/simple_template.rs
Normal file
30
crates/treehouse/src/generate/simple_template.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use anyhow::Context;
|
||||
use handlebars::Handlebars;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::sources::Sources;
|
||||
|
||||
use super::BaseTemplateData;
|
||||
|
||||
#[instrument(name = "simple_template::generate", skip(sources, handlebars))]
|
||||
pub fn generate(
|
||||
sources: &Sources,
|
||||
handlebars: &Handlebars,
|
||||
template_name: &str,
|
||||
) -> anyhow::Result<String> {
|
||||
let base_template_data = BaseTemplateData::new(sources);
|
||||
handlebars
|
||||
.render(template_name, &base_template_data)
|
||||
.context("failed to render template")
|
||||
}
|
||||
|
||||
pub fn generate_or_error(
|
||||
sources: &Sources,
|
||||
handlebars: &Handlebars,
|
||||
template_name: &str,
|
||||
) -> String {
|
||||
match generate(sources, handlebars, template_name) {
|
||||
Ok(html) => html,
|
||||
Err(error) => format!("error: {error:?}"),
|
||||
}
|
||||
}
|
113
crates/treehouse/src/generate/tree.rs
Normal file
113
crates/treehouse/src/generate/tree.rs
Normal file
|
@ -0,0 +1,113 @@
|
|||
use anyhow::{ensure, Context};
|
||||
use handlebars::Handlebars;
|
||||
use serde::Serialize;
|
||||
use tracing::{info_span, instrument};
|
||||
|
||||
use crate::{
|
||||
dirs::Dirs,
|
||||
generate::BaseTemplateData,
|
||||
html::{breadcrumbs::breadcrumbs_to_html, tree::branches_to_html},
|
||||
sources::Sources,
|
||||
state::FileId,
|
||||
};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Page {
|
||||
title: String,
|
||||
thumbnail: Option<Thumbnail>,
|
||||
scripts: Vec<String>,
|
||||
styles: Vec<String>,
|
||||
breadcrumbs: String,
|
||||
tree_path: Option<String>,
|
||||
tree: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Thumbnail {
|
||||
url: String,
|
||||
alt: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct PageTemplateData<'a> {
|
||||
#[serde(flatten)]
|
||||
base: &'a BaseTemplateData<'a>,
|
||||
page: Page,
|
||||
}
|
||||
|
||||
#[instrument(skip(sources, dirs, handlebars))]
|
||||
pub fn generate(
|
||||
sources: &Sources,
|
||||
dirs: &Dirs,
|
||||
handlebars: &Handlebars,
|
||||
file_id: FileId,
|
||||
) -> anyhow::Result<String> {
|
||||
let breadcrumbs = breadcrumbs_to_html(&sources.config, &sources.navigation_map, file_id);
|
||||
|
||||
let roots = sources
|
||||
.treehouse
|
||||
.roots
|
||||
.get(&file_id)
|
||||
.expect("tree should have been added to the treehouse");
|
||||
|
||||
let tree = {
|
||||
let _span = info_span!("generate_tree::branches_to_html").entered();
|
||||
let mut tree = String::new();
|
||||
branches_to_html(
|
||||
&mut tree,
|
||||
&sources.treehouse,
|
||||
&sources.config,
|
||||
dirs,
|
||||
file_id,
|
||||
&roots.branches,
|
||||
);
|
||||
tree
|
||||
};
|
||||
|
||||
let template_data = PageTemplateData {
|
||||
base: &BaseTemplateData::new(sources),
|
||||
page: Page {
|
||||
title: roots.attributes.title.clone(),
|
||||
thumbnail: roots
|
||||
.attributes
|
||||
.thumbnail
|
||||
.as_ref()
|
||||
.map(|thumbnail| Thumbnail {
|
||||
url: sources.config.pic_url(&*dirs.pic, &thumbnail.id),
|
||||
alt: thumbnail.alt.clone(),
|
||||
}),
|
||||
scripts: roots.attributes.scripts.clone(),
|
||||
styles: roots.attributes.styles.clone(),
|
||||
breadcrumbs,
|
||||
tree_path: sources.treehouse.tree_path(file_id).map(|s| s.to_string()),
|
||||
tree,
|
||||
},
|
||||
};
|
||||
let template_name = roots
|
||||
.attributes
|
||||
.template
|
||||
.clone()
|
||||
.unwrap_or_else(|| "_tree.hbs".into());
|
||||
|
||||
ensure!(
|
||||
handlebars.has_template(&template_name),
|
||||
"template {template_name} does not exist"
|
||||
);
|
||||
|
||||
let _span = info_span!("handlebars::render").entered();
|
||||
handlebars
|
||||
.render(&template_name, &template_data)
|
||||
.context("template rendering failed")
|
||||
}
|
||||
|
||||
pub fn generate_or_error(
|
||||
sources: &Sources,
|
||||
dirs: &Dirs,
|
||||
handlebars: &Handlebars,
|
||||
file_id: FileId,
|
||||
) -> String {
|
||||
match generate(sources, dirs, handlebars, file_id) {
|
||||
Ok(html) => html,
|
||||
Err(error) => format!("error: {error:?}"),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue