diff --git a/default_environment/templates/html/blog_post_page.dust b/default_environment/templates/html/blog_post_page.dust new file mode 100644 index 0000000..eac6770 --- /dev/null +++ b/default_environment/templates/html/blog_post_page.dust @@ -0,0 +1,5 @@ +
+
+ +
+
diff --git a/default_environment/templates/html/main.dust b/default_environment/templates/html/main.dust index 2d07f56..eba851f 100644 --- a/default_environment/templates/html/main.dust +++ b/default_environment/templates/html/main.dust @@ -2,8 +2,16 @@ + {#global_settings.css_files}{/global_settings.css_files} + {#global_settings.js_files}{/global_settings.js_files} + {?global_settings.page_title}{global_settings.page_title}{/global_settings.page_title} -

Hello world!

+
+ {@select key=.type} + {@eq value="blog_post_page"}{>blog_post_page/}{/eq} + {@none}{!TODO: make this panic!}ERROR: Unrecognized page content type{/none} + {/select} +
diff --git a/src/blog_post/convert.rs b/src/blog_post/convert.rs index 8c17621..7d2dcaa 100644 --- a/src/blog_post/convert.rs +++ b/src/blog_post/convert.rs @@ -1,3 +1,4 @@ +use super::render_context::GlobalSettings; use super::render_context::RenderBlogPostPage; use super::BlogPost; use super::BlogPostPage; @@ -5,6 +6,7 @@ use super::BlogPostPage; pub(crate) fn convert_blog_post_page_to_render_context( _post: &BlogPost, page: &BlogPostPage, + global_settings: GlobalSettings, ) -> RenderBlogPostPage { - RenderBlogPostPage::new(page.title.clone()) + RenderBlogPostPage::new(global_settings, page.title.clone()) } diff --git a/src/blog_post/mod.rs b/src/blog_post/mod.rs index bd4e010..2b6790c 100644 --- a/src/blog_post/mod.rs +++ b/src/blog_post/mod.rs @@ -5,3 +5,4 @@ mod render_context; pub(crate) use convert::convert_blog_post_page_to_render_context; pub(crate) use definition::BlogPost; pub(crate) use page::BlogPostPage; +pub(crate) use render_context::GlobalSettings; diff --git a/src/blog_post/render_context.rs b/src/blog_post/render_context.rs index fb56b61..8015ece 100644 --- a/src/blog_post/render_context.rs +++ b/src/blog_post/render_context.rs @@ -1,14 +1,46 @@ use serde::Serialize; +/// The settings that a "global" to a single dustjs render. +#[derive(Debug, Serialize)] +pub(crate) struct GlobalSettings { + /// The title that goes in the html tag in the <head>. + page_title: Option<String>, + css_files: Vec<String>, + js_files: Vec<String>, +} + +impl GlobalSettings { + pub(crate) fn new( + page_title: Option<String>, + css_files: Vec<String>, + js_files: Vec<String>, + ) -> GlobalSettings { + GlobalSettings { + page_title, + css_files, + js_files, + } + } +} + #[derive(Debug, Serialize)] #[serde(tag = "type")] #[serde(rename = "blog_post_page")] pub(crate) struct RenderBlogPostPage { + global_settings: GlobalSettings, + + /// The title that will be shown visibly on the page. title: Option<String>, } impl RenderBlogPostPage { - pub(crate) fn new(title: Option<String>) -> RenderBlogPostPage { - RenderBlogPostPage { title } + pub(crate) fn new( + global_settings: GlobalSettings, + title: Option<String>, + ) -> RenderBlogPostPage { + RenderBlogPostPage { + global_settings, + title, + } } } diff --git a/src/command/build/render.rs b/src/command/build/render.rs index 1ce1d82..1acd6af 100644 --- a/src/command/build/render.rs +++ b/src/command/build/render.rs @@ -1,4 +1,5 @@ use std::ffi::OsStr; +use std::path::Path; use std::path::PathBuf; use include_dir::include_dir; @@ -6,6 +7,8 @@ use include_dir::Dir; use crate::blog_post::convert_blog_post_page_to_render_context; use crate::blog_post::BlogPost; +use crate::blog_post::GlobalSettings; +use crate::config::Config; use crate::error::CustomError; use crate::render::DusterRenderer; use crate::render::RendererIntegration; @@ -27,7 +30,7 @@ impl SiteRenderer { blog_posts, } } - pub(crate) async fn render_blog_posts(&self) -> Result<(), CustomError> { + pub(crate) async fn render_blog_posts(&self, config: &Config) -> Result<(), CustomError> { let mut renderer_integration = DusterRenderer::new(); let sources: Vec<_> = MAIN_TEMPLATES @@ -62,9 +65,25 @@ impl SiteRenderer { .join("posts") .join(&blog_post.id) .join(blog_post_page.get_output_path()); - println!("Output path: {:?}", output_path); - let render_context = - convert_blog_post_page_to_render_context(blog_post, blog_post_page); + let css_files = vec![get_web_path( + config, + &self.output_directory, + &output_path, + "main.css", + )?]; + let js_files = vec![get_web_path( + config, + &self.output_directory, + &output_path, + "blog_post.js", + )?]; + let global_settings = + GlobalSettings::new(blog_post_page.title.clone(), css_files, js_files); + let render_context = convert_blog_post_page_to_render_context( + blog_post, + blog_post_page, + global_settings, + ); let rendered_output = renderer_integration.render(render_context)?; println!("Rendered: {}", rendered_output); let parent_directory = output_path @@ -91,3 +110,41 @@ fn build_name_contents_pairs<'a>( let contents = std::str::from_utf8(entry.contents())?; Ok((name, contents)) } + +fn get_web_path<D: AsRef<Path>, F: AsRef<Path>, P: AsRef<Path>>( + config: &Config, + output_directory: D, + containing_file: F, + path_from_web_root: P, +) -> Result<String, CustomError> { + let path_from_web_root = path_from_web_root.as_ref(); + if config.use_relative_paths() { + let output_directory = output_directory.as_ref(); + let containing_file = containing_file.as_ref(); + // Subtracting 1 from the depth to "remove" the file name. + let depth_from_web_root = containing_file + .strip_prefix(output_directory)? + .components() + .count() + - 1; + let prefix = "../".repeat(depth_from_web_root); + let final_path = PathBuf::from(prefix).join(path_from_web_root); + let final_string = final_path + .as_path() + .to_str() + .map(str::to_string) + .ok_or("Path should be valid utf-8.")?; + Ok(final_string) + } else { + let web_root = config + .get_web_root() + .ok_or("Must either use_relative_paths or set the web_root in the config.")?; + let final_path = PathBuf::from(web_root).join(path_from_web_root); + let final_string = final_path + .as_path() + .to_str() + .map(str::to_string) + .ok_or("Path should be valid utf-8.")?; + Ok(final_string) + } +} diff --git a/src/command/build/runner.rs b/src/command/build/runner.rs index 7f970bd..bb267f6 100644 --- a/src/command/build/runner.rs +++ b/src/command/build/runner.rs @@ -10,7 +10,7 @@ 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::new(get_output_directory(&config).await?, blog_posts); - renderer.render_blog_posts().await?; + renderer.render_blog_posts(&config).await?; Ok(()) } diff --git a/src/config/full.rs b/src/config/full.rs index 0e499f7..0f962aa 100644 --- a/src/config/full.rs +++ b/src/config/full.rs @@ -59,4 +59,12 @@ impl Config { pub(crate) fn get_output_directory(&self) -> PathBuf { self.get_root_directory().join("output") } + + pub(crate) fn use_relative_paths(&self) -> bool { + self.raw.use_relative_paths.unwrap_or(true) + } + + pub(crate) fn get_web_root(&self) -> Option<&str> { + self.raw.web_root.as_deref() + } } diff --git a/src/config/raw.rs b/src/config/raw.rs index 3d43bd5..b0d95cf 100644 --- a/src/config/raw.rs +++ b/src/config/raw.rs @@ -7,6 +7,8 @@ pub(crate) struct RawConfig { site_title: String, author: Option<String>, email: Option<String>, + pub(super) use_relative_paths: Option<bool>, + pub(super) web_root: Option<String>, } impl Default for RawConfig { @@ -15,6 +17,8 @@ impl Default for RawConfig { site_title: "My super awesome website".to_owned(), author: None, email: None, + use_relative_paths: None, + web_root: None, } } }