use std::ffi::OsStr; use std::path::PathBuf; use include_dir::include_dir; use include_dir::Dir; use crate::blog_post::convert_blog_post_to_render_context; use crate::blog_post::BlogPost; use crate::error::CustomError; use crate::render::DusterRenderer; use crate::render::RendererIntegration; static MAIN_TEMPLATES: Dir = include_dir!("$CARGO_MANIFEST_DIR/default_environment/templates/html"); pub(crate) struct SiteRenderer { output_directory: PathBuf, blog_posts: Vec, } impl SiteRenderer { pub(crate) fn new>( output_directory: P, blog_posts: Vec, ) -> SiteRenderer { SiteRenderer { output_directory: output_directory.into(), blog_posts, } } pub(crate) async fn render_blog_posts(&self) -> Result<(), CustomError> { let mut renderer_integration = DusterRenderer::new(); let (main_template, other_templates): (Vec<_>, Vec<_>) = MAIN_TEMPLATES .files() .filter(|f| f.path().extension() == Some(OsStr::new("dust"))) .partition(|f| f.path().file_stem() == Some(OsStr::new("main"))); if main_template.len() != 1 { return Err("Expect exactly 1 main.dust template file.".into()); } for entry in main_template { load_template_from_include_dir(&mut renderer_integration, entry)?; } for entry in other_templates { load_template_from_include_dir(&mut renderer_integration, entry)?; } for blog_post in &self.blog_posts { let render_context = convert_blog_post_to_render_context(blog_post); renderer_integration.render(render_context)?; } Ok(()) } } fn load_template_from_include_dir( renderer_integration: &mut RI, entry: &include_dir::File<'_>, ) -> Result<(), CustomError> { let path = entry.path(); let name = path .file_stem() .ok_or("All templates should have a stem.")? .to_str() .ok_or("All template filenames should be valid utf-8.")?; let contents = std::str::from_utf8(entry.contents())?; renderer_integration.load_template(name, contents)?; Ok(()) }