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, scripts: Vec, styles: Vec, breadcrumbs: String, tree_path: Option, tree: String, } #[derive(Serialize)] struct Thumbnail { url: String, alt: Option, } #[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 { 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:?}"), } }