treehouse/crates/treehouse/src/generate/tree.rs
liquidev fbb9f39353 generate subpage listings automatically
right now very barebones!

- doesn't sort pages quite correctly
- no search function
- still not sure about the UI design aspects

includes refactor of tree generation code
2025-01-18 20:33:55 +01:00

111 lines
2.8 KiB
Rust

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},
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::root_to_html").entered();
let renderer = tree::Renderer {
sources,
dirs,
file_id,
};
let mut tree = String::new();
renderer.root(&mut tree);
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:?}"),
}
}