diff --git a/default_environment/templates/html/main.dust b/default_environment/templates/html/main.dust new file mode 100644 index 0000000..2d07f56 --- /dev/null +++ b/default_environment/templates/html/main.dust @@ -0,0 +1,9 @@ + + + + + + +

Hello world!

+ + diff --git a/src/command/build/render.rs b/src/command/build/render.rs index 5b4ce4c..cb2224d 100644 --- a/src/command/build/render.rs +++ b/src/command/build/render.rs @@ -1,19 +1,51 @@ +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 { - pub(crate) output_directory: PathBuf, - pub(crate) blog_posts: Vec, + 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 {}; + 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)?; @@ -22,3 +54,18 @@ impl SiteRenderer { 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(()) +} diff --git a/src/command/build/runner.rs b/src/command/build/runner.rs index 8b859e5..7f970bd 100644 --- a/src/command/build/runner.rs +++ b/src/command/build/runner.rs @@ -9,10 +9,7 @@ use crate::error::CustomError; pub(crate) async fn build_site(args: BuildArgs) -> Result<(), CustomError> { let config = Config::load_from_file(args.config).await?; let blog_posts = load_blog_posts(&config).await?; - let renderer = SiteRenderer { - output_directory: get_output_directory(&config).await?, - blog_posts, - }; + let renderer = SiteRenderer::new(get_output_directory(&config).await?, blog_posts); renderer.render_blog_posts().await?; Ok(()) diff --git a/src/error/error.rs b/src/error/error.rs index 78f109f..5daa7d6 100644 --- a/src/error/error.rs +++ b/src/error/error.rs @@ -1,3 +1,6 @@ +use std::str::Utf8Error; +use std::string::FromUtf8Error; + #[derive(Debug)] pub(crate) enum CustomError { Static(&'static str), @@ -7,6 +10,8 @@ pub(crate) enum CustomError { WalkDir(walkdir::Error), Tokio(tokio::task::JoinError), Serde(serde_json::Error), + Utf8(Utf8Error), + FromUtf8(FromUtf8Error), } impl From for CustomError { @@ -50,3 +55,15 @@ impl From for CustomError { CustomError::Serde(value) } } + +impl From for CustomError { + fn from(value: Utf8Error) -> Self { + CustomError::Utf8(value) + } +} + +impl From for CustomError { + fn from(value: FromUtf8Error) -> Self { + CustomError::FromUtf8(value) + } +} diff --git a/src/render/duster_renderer.rs b/src/render/duster_renderer.rs index 435aab0..ec19b6a 100644 --- a/src/render/duster_renderer.rs +++ b/src/render/duster_renderer.rs @@ -4,13 +4,18 @@ use serde::Serialize; pub(crate) struct DusterRenderer {} +impl DusterRenderer { + pub(crate) fn new() -> DusterRenderer { + DusterRenderer {} + } +} + impl RendererIntegration for DusterRenderer { - fn load_templates(&mut self, dust_templates: I) -> Result<(), crate::error::CustomError> + fn load_template(&mut self, name: N, contents: C) -> Result<(), crate::error::CustomError> where - I: Iterator, - P: Into, + N: Into, + C: Into, { - // TODO Ok(()) } diff --git a/src/render/renderer_integration.rs b/src/render/renderer_integration.rs index 4fbbba1..3087739 100644 --- a/src/render/renderer_integration.rs +++ b/src/render/renderer_integration.rs @@ -1,14 +1,12 @@ -use std::path::PathBuf; - use serde::Serialize; use crate::error::CustomError; pub(crate) trait RendererIntegration { - fn load_templates(&mut self, dust_templates: I) -> Result<(), CustomError> + fn load_template(&mut self, name: N, contents: C) -> Result<(), CustomError> where - I: Iterator, - P: Into; + N: Into, + C: Into; fn render(&self, context: C) -> Result where