diff --git a/default_environment/templates/html/blog_post_page.dust b/default_environment/templates/html/blog_post_page.dust index 067b223..d846e3e 100644 --- a/default_environment/templates/html/blog_post_page.dust +++ b/default_environment/templates/html/blog_post_page.dust @@ -1,6 +1,6 @@
- {?.title}{?.self_link}{.title}{:else}
{.title}
{/.self_link}{/.title} + {?.title}{?.self_link}{.title}{:else}
{.title}
{/.self_link}{/.title} {! TODO: date? !}
diff --git a/default_environment/templates/html/blog_stream.dust b/default_environment/templates/html/blog_stream.dust new file mode 100644 index 0000000..97fe63c --- /dev/null +++ b/default_environment/templates/html/blog_stream.dust @@ -0,0 +1,31 @@ +
+ {#.children} +
+
+ {?.title}{?.self_link}{.title}{:else}
{.title}
{/.self_link}{/.title} + {! TODO: date? !} +
+ + {! TODO: Table of contents? !} + +
+ {#.children} + {>document_element/} + {/.children} + + {?.footnotes} +

Footnotes:

+ {#.footnotes} + {>real_footnote_definition/} + {/.footnotes} + {/.footnotes} +
+
+ {/.children} + {#.stream_pagination} +
+ {?.older_link}Older{/.older_link} + {?.newer_link}Newer{/.newer_link} +
+ {/.stream_pagination} +
diff --git a/default_environment/templates/html/main.dust b/default_environment/templates/html/main.dust index 375d21b..9f0f5ed 100644 --- a/default_environment/templates/html/main.dust +++ b/default_environment/templates/html/main.dust @@ -11,6 +11,7 @@
{@select key=.type} {@eq value="blog_post_page"}{>blog_post_page/}{/eq} + {@eq value="blog_stream"}{>blog_stream/}{/eq} {@none}{!TODO: make this panic!}ERROR: Unrecognized page content type{/none} {/select}
diff --git a/src/command/build/render.rs b/src/command/build/render.rs index 4420ddc..642a386 100644 --- a/src/command/build/render.rs +++ b/src/command/build/render.rs @@ -5,8 +5,13 @@ use include_dir::include_dir; use include_dir::Dir; use crate::config::Config; +use crate::context::RenderBlogPostPage; +use crate::context::RenderBlogPostPageInput; +use crate::context::RenderBlogStream; +use crate::context::RenderBlogStreamInput; +use crate::context::RenderContext; use crate::error::CustomError; -use crate::intermediate::convert_blog_post_page_to_render_context; +use crate::intermediate::get_web_path; use crate::intermediate::BlogPost; use crate::render::DusterRenderer; use crate::render::RendererIntegration; @@ -34,7 +39,7 @@ impl SiteRenderer { } } - pub(crate) async fn render_blog_posts(&self, config: &Config) -> Result<(), CustomError> { + fn init_renderer_integration(&self) -> Result, CustomError> { let mut renderer_integration = DusterRenderer::new(); let sources: Vec<_> = MAIN_TEMPLATES @@ -62,21 +67,27 @@ impl SiteRenderer { renderer_integration.load_template(name, contents)?; } + Ok(renderer_integration) + } + + pub(crate) async fn render_blog_posts(&self, config: &Config) -> Result<(), CustomError> { + let renderer_integration = self.init_renderer_integration()?; + for blog_post in &self.blog_posts { for blog_post_page in &blog_post.pages { let output_path = self .output_directory - .join("posts") - .join(&blog_post.id) + .join(config.get_relative_path_to_post(&blog_post.id)) .join(blog_post_page.get_output_path()); - let render_context = convert_blog_post_page_to_render_context( + let convert_input = RenderBlogPostPageInput::new(blog_post, blog_post_page); + let render_context = RenderContext::new( config, - &self.output_directory, - &output_path, - blog_post, - blog_post_page, + self.output_directory.as_path(), + output_path.as_path(), + None, )?; + let render_context = RenderBlogPostPage::new(render_context, &convert_input)?; let rendered_output = renderer_integration.render(render_context)?; let parent_directory = output_path .parent() @@ -89,6 +100,87 @@ impl SiteRenderer { Ok(()) } + pub(crate) async fn render_blog_stream(&self, config: &Config) -> Result<(), CustomError> { + let renderer_integration = self.init_renderer_integration()?; + + // Sort blog posts by date, newest first. + let sorted_blog_posts = { + let mut sorted_blog_posts: Vec<_> = self.blog_posts.iter().collect(); + sorted_blog_posts + .sort_by_key(|blog_post| (blog_post.get_date(), blog_post.id.as_str())); + sorted_blog_posts.reverse(); + sorted_blog_posts + }; + + for blog_post in &sorted_blog_posts { + if blog_post.get_date().is_none() { + return Err(format!("Blog post {} does not have a date.", blog_post.id).into()); + } + } + + // Group blog posts based on # of posts per page. + let stream_chunks: Vec<_> = sorted_blog_posts + .chunks(config.get_stream_entries_per_page()) + .collect(); + + // For each group, create a RenderBlogStream. + let num_stream_pages = stream_chunks.len(); + for (page_num, chunk) in stream_chunks.into_iter().enumerate() { + let output_file = if page_num == 0 { + self.output_directory.join("index.html") + } else { + self.output_directory + .join("stream") + .join(format!("{}.html", page_num)) + }; + let newer_link = if page_num == 0 { + None + } else if page_num == 1 { + Some(get_web_path( + config, + &self.output_directory, + &output_file, + "index.html", + )?) + } else { + Some(get_web_path( + config, + &self.output_directory, + &output_file, + format!("stream/{}.html", page_num - 1), + )?) + }; + let older_link = if page_num == (num_stream_pages - 1) { + None + } else { + Some(get_web_path( + config, + &self.output_directory, + &output_file, + format!("stream/{}.html", page_num + 1), + )?) + }; + + let convert_input = RenderBlogStreamInput::new(chunk, older_link, newer_link); + let render_context = RenderContext::new( + config, + self.output_directory.as_path(), + output_file.as_path(), + None, + )?; + let blog_stream = RenderBlogStream::new(render_context, &convert_input)?; + + // Pass each RenderBlogStream to dust as the context to render index.html and any additional stream pages. + let rendered_output = renderer_integration.render(blog_stream)?; + let parent_directory = output_file + .parent() + .ok_or("Output file should have a containing directory.")?; + tokio::fs::create_dir_all(parent_directory).await?; + tokio::fs::write(output_file, rendered_output).await?; + } + Ok(()) + } + pub(crate) async fn render_stylesheets(&self) -> Result<(), CustomError> { let stylesheet_output_directory = self.output_directory.join("stylesheet"); if !stylesheet_output_directory.exists() { diff --git a/src/command/build/runner.rs b/src/command/build/runner.rs index f41cf74..984dd9b 100644 --- a/src/command/build/runner.rs +++ b/src/command/build/runner.rs @@ -23,6 +23,7 @@ pub(crate) async fn build_site(args: BuildArgs) -> Result<(), CustomError> { stylesheets, ); renderer.render_blog_posts(&config).await?; + renderer.render_blog_stream(&config).await?; renderer.render_stylesheets().await?; Ok(()) diff --git a/src/config/full.rs b/src/config/full.rs index 4978e8d..5fdfd1f 100644 --- a/src/config/full.rs +++ b/src/config/full.rs @@ -8,6 +8,7 @@ use crate::error::CustomError; use super::raw::RawConfig; /// This is the config struct used by most of the code, which is an interpreted version of the RawConfig struct which is the raw disk-representation of the config. +#[derive(Debug)] pub(crate) struct Config { raw: RawConfig, config_path: PathBuf, @@ -56,6 +57,15 @@ impl Config { self.get_root_directory().join("posts") } + /// Get the relative path to the folder containing a blog post. + /// + /// This could be appended to the output root directory to get the + /// blog post output folder or it could be used to generate a link + /// to the blog post. + pub(crate) fn get_relative_path_to_post>(&self, post_id: P) -> PathBuf { + Path::new("posts").join(post_id) + } + pub(crate) fn get_output_directory(&self) -> PathBuf { self.get_root_directory().join("output") } @@ -71,4 +81,13 @@ impl Config { pub(crate) fn get_site_title(&self) -> Option<&str> { self.raw.site_title.as_deref() } + + pub(crate) fn get_stream_entries_per_page(&self) -> usize { + self.raw + .stream + .as_ref() + .map(|stream| stream.entries_per_page) + .flatten() + .unwrap_or(5) + } } diff --git a/src/config/raw.rs b/src/config/raw.rs index ce34997..29cb4b9 100644 --- a/src/config/raw.rs +++ b/src/config/raw.rs @@ -2,13 +2,14 @@ use serde::Deserialize; use serde::Serialize; /// This is the struct for the writer.toml config file that ends up in each site's root directory. -#[derive(Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize)] pub(crate) struct RawConfig { pub(super) site_title: Option, author: Option, email: Option, pub(super) use_relative_paths: Option, pub(super) web_root: Option, + pub(super) stream: Option, } impl Default for RawConfig { @@ -19,6 +20,20 @@ impl Default for RawConfig { email: None, use_relative_paths: None, web_root: None, + stream: None, + } + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub(crate) struct RawConfigStream { + pub(super) entries_per_page: Option, +} + +impl Default for RawConfigStream { + fn default() -> Self { + RawConfigStream { + entries_per_page: None, } } } diff --git a/src/context/angle_link.rs b/src/context/angle_link.rs index bb7fcb2..f456e60 100644 --- a/src/context/angle_link.rs +++ b/src/context/angle_link.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IAngleLink; diff --git a/src/context/ast_node.rs b/src/context/ast_node.rs index 7a2ffcd..c466297 100644 --- a/src/context/ast_node.rs +++ b/src/context/ast_node.rs @@ -1,8 +1,5 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; use crate::error::CustomError; use crate::intermediate::IAstNode; @@ -45,6 +42,7 @@ use super::quote_block::RenderQuoteBlock; use super::radio_link::RenderRadioLink; use super::radio_target::RenderRadioTarget; use super::regular_link::RenderRegularLink; +use super::render_context::RenderContext; use super::special_block::RenderSpecialBlock; use super::src_block::RenderSrcBlock; use super::statistics_cookie::RenderStatisticsCookie; @@ -121,285 +119,207 @@ pub(crate) enum RenderAstNode { pub(crate) trait IntoRenderAstNode { fn into_render_ast_node( &self, - config: &Config, - output_directory: &Path, - output_file: &Path, + render_context: RenderContext<'_>, ) -> Result; } impl IntoRenderAstNode for IAstNode { fn into_render_ast_node( &self, - config: &Config, - output_directory: &Path, - output_file: &Path, + render_context: RenderContext<'_>, ) -> Result { match self { IAstNode::Heading(inner) => Ok(RenderAstNode::Heading(RenderHeading::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::Section(inner) => Ok(RenderAstNode::Section(RenderSection::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::Paragraph(inner) => Ok(RenderAstNode::Paragraph(RenderParagraph::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::PlainList(inner) => Ok(RenderAstNode::PlainList(RenderPlainList::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::CenterBlock(inner) => Ok(RenderAstNode::CenterBlock(RenderCenterBlock::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::QuoteBlock(inner) => Ok(RenderAstNode::QuoteBlock(RenderQuoteBlock::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::SpecialBlock(inner) => Ok(RenderAstNode::SpecialBlock( - RenderSpecialBlock::new(config, output_directory, output_file, inner)?, + RenderSpecialBlock::new(render_context, inner)?, )), IAstNode::DynamicBlock(inner) => Ok(RenderAstNode::DynamicBlock( - RenderDynamicBlock::new(config, output_directory, output_file, inner)?, + RenderDynamicBlock::new(render_context, inner)?, )), IAstNode::FootnoteDefinition(inner) => Ok(RenderAstNode::FootnoteDefinition( - RenderFootnoteDefinition::new(config, output_directory, output_file, inner)?, + RenderFootnoteDefinition::new(render_context, inner)?, )), IAstNode::Comment(inner) => Ok(RenderAstNode::Comment(RenderComment::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::Drawer(inner) => Ok(RenderAstNode::Drawer(RenderDrawer::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::PropertyDrawer(inner) => Ok(RenderAstNode::PropertyDrawer( - RenderPropertyDrawer::new(config, output_directory, output_file, inner)?, + RenderPropertyDrawer::new(render_context, inner)?, )), IAstNode::Table(inner) => Ok(RenderAstNode::Table(RenderTable::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::VerseBlock(inner) => Ok(RenderAstNode::VerseBlock(RenderVerseBlock::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::CommentBlock(inner) => Ok(RenderAstNode::CommentBlock( - RenderCommentBlock::new(config, output_directory, output_file, inner)?, + RenderCommentBlock::new(render_context, inner)?, )), IAstNode::ExampleBlock(inner) => Ok(RenderAstNode::ExampleBlock( - RenderExampleBlock::new(config, output_directory, output_file, inner)?, + RenderExampleBlock::new(render_context, inner)?, )), IAstNode::ExportBlock(inner) => Ok(RenderAstNode::ExportBlock(RenderExportBlock::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::SrcBlock(inner) => Ok(RenderAstNode::SrcBlock(RenderSrcBlock::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::Clock(inner) => Ok(RenderAstNode::Clock(RenderClock::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::DiarySexp(inner) => Ok(RenderAstNode::DiarySexp(RenderDiarySexp::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::Planning(inner) => Ok(RenderAstNode::Planning(RenderPlanning::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::FixedWidthArea(inner) => Ok(RenderAstNode::FixedWidthArea( - RenderFixedWidthArea::new(config, output_directory, output_file, inner)?, + RenderFixedWidthArea::new(render_context, inner)?, )), IAstNode::HorizontalRule(inner) => Ok(RenderAstNode::HorizontalRule( - RenderHorizontalRule::new(config, output_directory, output_file, inner)?, + RenderHorizontalRule::new(render_context, inner)?, )), IAstNode::Keyword(inner) => Ok(RenderAstNode::Keyword(RenderKeyword::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::BabelCall(inner) => Ok(RenderAstNode::BabelCall(RenderBabelCall::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::LatexEnvironment(inner) => Ok(RenderAstNode::LatexEnvironment( - RenderLatexEnvironment::new(config, output_directory, output_file, inner)?, + RenderLatexEnvironment::new(render_context, inner)?, )), - IAstNode::Bold(inner) => Ok(RenderAstNode::Bold(RenderBold::new( - config, - output_directory, - output_file, - inner, - )?)), + IAstNode::Bold(inner) => { + Ok(RenderAstNode::Bold(RenderBold::new(render_context, inner)?)) + } IAstNode::Italic(inner) => Ok(RenderAstNode::Italic(RenderItalic::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::Underline(inner) => Ok(RenderAstNode::Underline(RenderUnderline::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::StrikeThrough(inner) => Ok(RenderAstNode::StrikeThrough( - RenderStrikeThrough::new(config, output_directory, output_file, inner)?, + RenderStrikeThrough::new(render_context, inner)?, )), - IAstNode::Code(inner) => Ok(RenderAstNode::Code(RenderCode::new( - config, - output_directory, - output_file, - inner, - )?)), + IAstNode::Code(inner) => { + Ok(RenderAstNode::Code(RenderCode::new(render_context, inner)?)) + } IAstNode::Verbatim(inner) => Ok(RenderAstNode::Verbatim(RenderVerbatim::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::PlainText(inner) => Ok(RenderAstNode::PlainText(RenderPlainText::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::RegularLink(inner) => Ok(RenderAstNode::RegularLink(RenderRegularLink::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::RadioLink(inner) => Ok(RenderAstNode::RadioLink(RenderRadioLink::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::RadioTarget(inner) => Ok(RenderAstNode::RadioTarget(RenderRadioTarget::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::PlainLink(inner) => Ok(RenderAstNode::PlainLink(RenderPlainLink::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::AngleLink(inner) => Ok(RenderAstNode::AngleLink(RenderAngleLink::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::OrgMacro(inner) => Ok(RenderAstNode::OrgMacro(RenderOrgMacro::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::Entity(inner) => Ok(RenderAstNode::Entity(RenderEntity::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::LatexFragment(inner) => Ok(RenderAstNode::LatexFragment( - RenderLatexFragment::new(config, output_directory, output_file, inner)?, + RenderLatexFragment::new(render_context, inner)?, )), IAstNode::ExportSnippet(inner) => Ok(RenderAstNode::ExportSnippet( - RenderExportSnippet::new(config, output_directory, output_file, inner)?, + RenderExportSnippet::new(render_context, inner)?, )), IAstNode::FootnoteReference(inner) => Ok(RenderAstNode::FootnoteReference( - RenderFootnoteReference::new(config, output_directory, output_file, inner)?, + RenderFootnoteReference::new(render_context, inner)?, )), IAstNode::Citation(inner) => Ok(RenderAstNode::Citation(RenderCitation::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::CitationReference(inner) => Ok(RenderAstNode::CitationReference( - RenderCitationReference::new(config, output_directory, output_file, inner)?, + RenderCitationReference::new(render_context, inner)?, )), IAstNode::InlineBabelCall(inner) => Ok(RenderAstNode::InlineBabelCall( - RenderInlineBabelCall::new(config, output_directory, output_file, inner)?, + RenderInlineBabelCall::new(render_context, inner)?, )), IAstNode::InlineSourceBlock(inner) => Ok(RenderAstNode::InlineSourceBlock( - RenderInlineSourceBlock::new(config, output_directory, output_file, inner)?, + RenderInlineSourceBlock::new(render_context, inner)?, )), IAstNode::LineBreak(inner) => Ok(RenderAstNode::LineBreak(RenderLineBreak::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::Target(inner) => Ok(RenderAstNode::Target(RenderTarget::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::StatisticsCookie(inner) => Ok(RenderAstNode::StatisticsCookie( - RenderStatisticsCookie::new(config, output_directory, output_file, inner)?, + RenderStatisticsCookie::new(render_context, inner)?, )), IAstNode::Subscript(inner) => Ok(RenderAstNode::Subscript(RenderSubscript::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::Superscript(inner) => Ok(RenderAstNode::Superscript(RenderSuperscript::new( - config, - output_directory, - output_file, + render_context, inner, )?)), IAstNode::Timestamp(inner) => Ok(RenderAstNode::Timestamp(RenderTimestamp::new( - config, - output_directory, - output_file, + render_context, inner, )?)), } diff --git a/src/context/babel_call.rs b/src/context/babel_call.rs index f376b36..9c1fa0d 100644 --- a/src/context/babel_call.rs +++ b/src/context/babel_call.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IBabelCall; diff --git a/src/context/blog_post_page.rs b/src/context/blog_post_page.rs index b42df31..ed5b8d9 100644 --- a/src/context/blog_post_page.rs +++ b/src/context/blog_post_page.rs @@ -1,10 +1,29 @@ use serde::Serialize; +use super::render_context::RenderContext; +use crate::error::CustomError; +use crate::intermediate::get_web_path; +use crate::intermediate::BlogPost; +use crate::intermediate::BlogPostPage; + use super::footnote_definition::RenderRealFootnoteDefinition; +use super::macros::render; use super::GlobalSettings; use super::PageHeader; use super::RenderDocumentElement; +#[derive(Debug)] +pub(crate) struct RenderBlogPostPageInput<'a> { + post: &'a BlogPost, + page: &'a BlogPostPage, +} + +impl<'a> RenderBlogPostPageInput<'a> { + pub(crate) fn new(post: &'a BlogPost, page: &'a BlogPostPage) -> RenderBlogPostPageInput<'a> { + RenderBlogPostPageInput { post, page } + } +} + #[derive(Debug, Serialize)] #[serde(tag = "type")] #[serde(rename = "blog_post_page")] @@ -23,23 +42,82 @@ pub(crate) struct RenderBlogPostPage { footnotes: Vec, } -impl RenderBlogPostPage { - // TODO: Maybe these settings should be moved into a common struct so this can have the same type signature as the others. - pub(crate) fn new( - global_settings: GlobalSettings, - page_header: Option, - title: Option, - self_link: Option, - children: Vec, - footnotes: Vec, - ) -> RenderBlogPostPage { - RenderBlogPostPage { +render!( + RenderBlogPostPage, + RenderBlogPostPageInput, + original, + render_context, + { + let css_files = vec![ + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/reset.css", + )?, + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/main.css", + )?, + ]; + let js_files = vec![get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "blog_post.js", + )?]; + let global_settings = GlobalSettings::new(original.page.title.clone(), css_files, js_files); + let page_header = PageHeader::new( + render_context.config.get_site_title().map(str::to_string), + Some(get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "", + )?), + ); + let link_to_blog_post = get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + render_context + .output_file + .strip_prefix(render_context.output_root_directory)?, + )?; + + let children = { + let mut children = Vec::new(); + + for child in original.page.children.iter() { + children.push(RenderDocumentElement::new(render_context.clone(), child)?); + } + + children + }; + + let footnotes = { + let mut ret = Vec::new(); + + for footnote in original.page.footnotes.iter() { + ret.push(RenderRealFootnoteDefinition::new( + render_context.clone(), + footnote, + )?); + } + + ret + }; + + let ret = RenderBlogPostPage { global_settings, - page_header, - title, - self_link, + page_header: Some(page_header), + title: original.page.title.clone(), + self_link: Some(link_to_blog_post), children, footnotes, - } + }; + Ok(ret) } -} +); diff --git a/src/context/blog_stream.rs b/src/context/blog_stream.rs new file mode 100644 index 0000000..04fac51 --- /dev/null +++ b/src/context/blog_stream.rs @@ -0,0 +1,213 @@ +use serde::Serialize; + +use super::macros::render; +use super::render_context::RenderContext; +use crate::context::RenderDocumentElement; +use crate::context::RenderRealFootnoteDefinition; +use crate::error::CustomError; +use crate::intermediate::get_web_path; +use crate::intermediate::BlogPost; + +use super::GlobalSettings; +use super::PageHeader; + +#[derive(Debug)] +pub(crate) struct RenderBlogStreamInput<'a, 'b> { + original: &'a [&'b BlogPost], + older_link: Option, + newer_link: Option, +} + +impl<'a, 'b> RenderBlogStreamInput<'a, 'b> { + pub(crate) fn new( + original: &'a [&'b BlogPost], + older_link: Option, + newer_link: Option, + ) -> RenderBlogStreamInput<'a, 'b> { + RenderBlogStreamInput { + original, + older_link, + newer_link, + } + } +} + +#[derive(Debug, Serialize)] +#[serde(tag = "type")] +#[serde(rename = "blog_stream")] +pub(crate) struct RenderBlogStream { + global_settings: GlobalSettings, + page_header: Option, + children: Vec, + stream_pagination: Option, +} + +render!( + RenderBlogStream, + RenderBlogStreamInput, + original, + render_context, + { + let css_files = vec![ + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/reset.css", + )?, + get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "stylesheet/main.css", + )?, + ]; + let js_files = vec![get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "blog_post.js", + )?]; + let global_settings = GlobalSettings::new( + render_context.config.get_site_title().map(str::to_string), + css_files, + js_files, + ); + let page_header = PageHeader::new( + render_context.config.get_site_title().map(str::to_string), + Some(get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + "", + )?), + ); + + let children = original + .original + .into_iter() + .enumerate() + .map(|(i, blog_post)| { + RenderBlogStreamEntry::new( + render_context.clone(), + &RenderBlogStreamEntryInput::new(blog_post, i), + ) + }) + .collect::, _>>()?; + + let stream_pagination = if original.older_link.is_some() || original.newer_link.is_some() { + Some(RenderBlogStreamPagination::new( + original.older_link.clone(), + original.newer_link.clone(), + )?) + } else { + None + }; + + Ok(RenderBlogStream { + global_settings, + page_header: Some(page_header), + children, + stream_pagination, + }) + } +); + +#[derive(Debug)] +pub(crate) struct RenderBlogStreamEntryInput<'a> { + original: &'a BlogPost, + offset: usize, +} + +impl<'a> RenderBlogStreamEntryInput<'a> { + fn new(original: &'a BlogPost, offset: usize) -> RenderBlogStreamEntryInput<'a> { + RenderBlogStreamEntryInput { original, offset } + } +} + +#[derive(Debug, Serialize)] +pub(crate) struct RenderBlogStreamEntry { + /// The title that will be shown visibly on the page. + title: Option, + + self_link: Option, + + children: Vec, + + footnotes: Vec, +} + +render!( + RenderBlogStreamEntry, + RenderBlogStreamEntryInput, + original, + render_context, + { + let offset_string = original.offset.to_string(); + let render_context = { + let mut render_context = render_context.clone(); + render_context.id_addition = Some(offset_string.as_str()); + render_context + }; + let link_to_blog_post = get_web_path( + render_context.config, + render_context.output_root_directory, + render_context.output_file, + render_context + .config + .get_relative_path_to_post(&original.original.id), + )?; + + // TODO: Should I guess an index page instead of erroring out? + let index_page = original + .original + .get_index_page() + .ok_or_else(|| format!("Blog post {} needs an index page.", original.original.id))?; + + let title = index_page.title.clone(); + + let children = index_page + .children + .iter() + .map(|child| RenderDocumentElement::new(render_context.clone(), child)) + .collect::, _>>()?; + + let footnotes = { + let mut ret = Vec::new(); + + for footnote in index_page.footnotes.iter() { + ret.push(RenderRealFootnoteDefinition::new( + render_context.clone(), + footnote, + )?); + } + + ret + }; + + Ok(RenderBlogStreamEntry { + title, + self_link: Some(link_to_blog_post), + children, + footnotes, + }) + } +); + +#[derive(Debug, Serialize)] +pub(crate) struct RenderBlogStreamPagination { + older_link: Option, + newer_link: Option, +} + +impl RenderBlogStreamPagination { + fn new( + older_link: Option, + newer_link: Option, + ) -> Result { + Ok(RenderBlogStreamPagination { + older_link, + newer_link, + }) + } +} diff --git a/src/context/bold.rs b/src/context/bold.rs index 87c6fb0..7049a23 100644 --- a/src/context/bold.rs +++ b/src/context/bold.rs @@ -1,12 +1,10 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; use crate::error::CustomError; use crate::intermediate::IBold; use super::macros::render; +use super::render_context::RenderContext; use super::RenderObject; #[derive(Debug, Serialize)] @@ -16,27 +14,14 @@ pub(crate) struct RenderBold { children: Vec, } -render!( - RenderBold, - IBold, - original, - config, - output_directory, - output_file, - { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderObject::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderBold, IBold, original, render_context, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderObject::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderBold { children }) - } -); + Ok(RenderBold { children }) +}); diff --git a/src/context/center_block.rs b/src/context/center_block.rs index e0da129..04db135 100644 --- a/src/context/center_block.rs +++ b/src/context/center_block.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ICenterBlock; diff --git a/src/context/citation.rs b/src/context/citation.rs index 0ca5fbb..c9d44fa 100644 --- a/src/context/citation.rs +++ b/src/context/citation.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ICitation; diff --git a/src/context/citation_reference.rs b/src/context/citation_reference.rs index 7d1143f..8c188d4 100644 --- a/src/context/citation_reference.rs +++ b/src/context/citation_reference.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ICitationReference; diff --git a/src/context/clock.rs b/src/context/clock.rs index 8165502..a8fe617 100644 --- a/src/context/clock.rs +++ b/src/context/clock.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IClock; diff --git a/src/context/code.rs b/src/context/code.rs index b7ff530..bdce2ac 100644 --- a/src/context/code.rs +++ b/src/context/code.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ICode; @@ -15,16 +13,8 @@ pub(crate) struct RenderCode { contents: String, } -render!( - RenderCode, - ICode, - original, - _config, - _output_directory, - _output_file, - { - Ok(RenderCode { - contents: original.contents.clone(), - }) - } -); +render!(RenderCode, ICode, original, _render_context, { + Ok(RenderCode { + contents: original.contents.clone(), + }) +}); diff --git a/src/context/comment.rs b/src/context/comment.rs index 37c81ef..fbab8e1 100644 --- a/src/context/comment.rs +++ b/src/context/comment.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IComment; diff --git a/src/context/comment_block.rs b/src/context/comment_block.rs index c2801f6..34702e2 100644 --- a/src/context/comment_block.rs +++ b/src/context/comment_block.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ICommentBlock; diff --git a/src/context/diary_sexp.rs b/src/context/diary_sexp.rs index 889fc9f..e6235e5 100644 --- a/src/context/diary_sexp.rs +++ b/src/context/diary_sexp.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IDiarySexp; diff --git a/src/context/document_element.rs b/src/context/document_element.rs index aa10d75..b11239c 100644 --- a/src/context/document_element.rs +++ b/src/context/document_element.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IDocumentElement; @@ -21,16 +19,14 @@ render!( RenderDocumentElement, IDocumentElement, original, - config, - output_directory, - output_file, + render_context, { match original { IDocumentElement::Heading(inner) => Ok(RenderDocumentElement::Heading( - RenderHeading::new(config, output_directory, output_file, inner)?, + RenderHeading::new(render_context.clone(), inner)?, )), IDocumentElement::Section(inner) => Ok(RenderDocumentElement::Section( - RenderSection::new(config, output_directory, output_file, inner)?, + RenderSection::new(render_context.clone(), inner)?, )), } } diff --git a/src/context/drawer.rs b/src/context/drawer.rs index f6989b7..a6b7f5e 100644 --- a/src/context/drawer.rs +++ b/src/context/drawer.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IDrawer; diff --git a/src/context/dynamic_block.rs b/src/context/dynamic_block.rs index bd562e1..bf7840f 100644 --- a/src/context/dynamic_block.rs +++ b/src/context/dynamic_block.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IDynamicBlock; diff --git a/src/context/element.rs b/src/context/element.rs index 781ca28..f493919 100644 --- a/src/context/element.rs +++ b/src/context/element.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IElement; @@ -61,132 +59,98 @@ pub(crate) enum RenderElement { LatexEnvironment(RenderLatexEnvironment), } -render!( - RenderElement, - IElement, - original, - config, - output_directory, - output_file, - { - match original { - IElement::Paragraph(inner) => Ok(RenderElement::Paragraph(RenderParagraph::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::PlainList(inner) => Ok(RenderElement::PlainList(RenderPlainList::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::CenterBlock(inner) => Ok(RenderElement::CenterBlock(RenderCenterBlock::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::QuoteBlock(inner) => Ok(RenderElement::QuoteBlock(RenderQuoteBlock::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::SpecialBlock(inner) => Ok(RenderElement::SpecialBlock( - RenderSpecialBlock::new(config, output_directory, output_file, inner)?, - )), - IElement::DynamicBlock(inner) => Ok(RenderElement::DynamicBlock( - RenderDynamicBlock::new(config, output_directory, output_file, inner)?, - )), - IElement::FootnoteDefinition(inner) => Ok(RenderElement::FootnoteDefinition( - RenderFootnoteDefinition::new(config, output_directory, output_file, inner)?, - )), - IElement::Comment(inner) => Ok(RenderElement::Comment(RenderComment::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::Drawer(inner) => Ok(RenderElement::Drawer(RenderDrawer::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::PropertyDrawer(inner) => Ok(RenderElement::PropertyDrawer( - RenderPropertyDrawer::new(config, output_directory, output_file, inner)?, - )), - IElement::Table(inner) => Ok(RenderElement::Table(RenderTable::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::VerseBlock(inner) => Ok(RenderElement::VerseBlock(RenderVerseBlock::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::CommentBlock(inner) => Ok(RenderElement::CommentBlock( - RenderCommentBlock::new(config, output_directory, output_file, inner)?, - )), - IElement::ExampleBlock(inner) => Ok(RenderElement::ExampleBlock( - RenderExampleBlock::new(config, output_directory, output_file, inner)?, - )), - IElement::ExportBlock(inner) => Ok(RenderElement::ExportBlock(RenderExportBlock::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::SrcBlock(inner) => Ok(RenderElement::SrcBlock(RenderSrcBlock::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::Clock(inner) => Ok(RenderElement::Clock(RenderClock::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::DiarySexp(inner) => Ok(RenderElement::DiarySexp(RenderDiarySexp::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::Planning(inner) => Ok(RenderElement::Planning(RenderPlanning::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::FixedWidthArea(inner) => Ok(RenderElement::FixedWidthArea( - RenderFixedWidthArea::new(config, output_directory, output_file, inner)?, - )), - IElement::HorizontalRule(inner) => Ok(RenderElement::HorizontalRule( - RenderHorizontalRule::new(config, output_directory, output_file, inner)?, - )), - IElement::Keyword(inner) => Ok(RenderElement::Keyword(RenderKeyword::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::BabelCall(inner) => Ok(RenderElement::BabelCall(RenderBabelCall::new( - config, - output_directory, - output_file, - inner, - )?)), - IElement::LatexEnvironment(inner) => Ok(RenderElement::LatexEnvironment( - RenderLatexEnvironment::new(config, output_directory, output_file, inner)?, - )), - } +render!(RenderElement, IElement, original, render_context, { + match original { + IElement::Paragraph(inner) => Ok(RenderElement::Paragraph(RenderParagraph::new( + render_context.clone(), + inner, + )?)), + IElement::PlainList(inner) => Ok(RenderElement::PlainList(RenderPlainList::new( + render_context.clone(), + inner, + )?)), + IElement::CenterBlock(inner) => Ok(RenderElement::CenterBlock(RenderCenterBlock::new( + render_context.clone(), + inner, + )?)), + IElement::QuoteBlock(inner) => Ok(RenderElement::QuoteBlock(RenderQuoteBlock::new( + render_context.clone(), + inner, + )?)), + IElement::SpecialBlock(inner) => Ok(RenderElement::SpecialBlock(RenderSpecialBlock::new( + render_context.clone(), + inner, + )?)), + IElement::DynamicBlock(inner) => Ok(RenderElement::DynamicBlock(RenderDynamicBlock::new( + render_context.clone(), + inner, + )?)), + IElement::FootnoteDefinition(inner) => Ok(RenderElement::FootnoteDefinition( + RenderFootnoteDefinition::new(render_context.clone(), inner)?, + )), + IElement::Comment(inner) => Ok(RenderElement::Comment(RenderComment::new( + render_context.clone(), + inner, + )?)), + IElement::Drawer(inner) => Ok(RenderElement::Drawer(RenderDrawer::new( + render_context.clone(), + inner, + )?)), + IElement::PropertyDrawer(inner) => Ok(RenderElement::PropertyDrawer( + RenderPropertyDrawer::new(render_context.clone(), inner)?, + )), + IElement::Table(inner) => Ok(RenderElement::Table(RenderTable::new( + render_context.clone(), + inner, + )?)), + IElement::VerseBlock(inner) => Ok(RenderElement::VerseBlock(RenderVerseBlock::new( + render_context.clone(), + inner, + )?)), + IElement::CommentBlock(inner) => Ok(RenderElement::CommentBlock(RenderCommentBlock::new( + render_context.clone(), + inner, + )?)), + IElement::ExampleBlock(inner) => Ok(RenderElement::ExampleBlock(RenderExampleBlock::new( + render_context.clone(), + inner, + )?)), + IElement::ExportBlock(inner) => Ok(RenderElement::ExportBlock(RenderExportBlock::new( + render_context.clone(), + inner, + )?)), + IElement::SrcBlock(inner) => Ok(RenderElement::SrcBlock(RenderSrcBlock::new( + render_context.clone(), + inner, + )?)), + IElement::Clock(inner) => Ok(RenderElement::Clock(RenderClock::new( + render_context.clone(), + inner, + )?)), + IElement::DiarySexp(inner) => Ok(RenderElement::DiarySexp(RenderDiarySexp::new( + render_context.clone(), + inner, + )?)), + IElement::Planning(inner) => Ok(RenderElement::Planning(RenderPlanning::new( + render_context.clone(), + inner, + )?)), + IElement::FixedWidthArea(inner) => Ok(RenderElement::FixedWidthArea( + RenderFixedWidthArea::new(render_context.clone(), inner)?, + )), + IElement::HorizontalRule(inner) => Ok(RenderElement::HorizontalRule( + RenderHorizontalRule::new(render_context.clone(), inner)?, + )), + IElement::Keyword(inner) => Ok(RenderElement::Keyword(RenderKeyword::new( + render_context.clone(), + inner, + )?)), + IElement::BabelCall(inner) => Ok(RenderElement::BabelCall(RenderBabelCall::new( + render_context.clone(), + inner, + )?)), + IElement::LatexEnvironment(inner) => Ok(RenderElement::LatexEnvironment( + RenderLatexEnvironment::new(render_context.clone(), inner)?, + )), } -); +}); diff --git a/src/context/entity.rs b/src/context/entity.rs index f4c2ec9..a69f24f 100644 --- a/src/context/entity.rs +++ b/src/context/entity.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IEntity; @@ -15,16 +13,8 @@ pub(crate) struct RenderEntity { html: String, } -render!( - RenderEntity, - IEntity, - original, - _config, - _output_directory, - _output_file, - { - Ok(RenderEntity { - html: original.html.clone(), - }) - } -); +render!(RenderEntity, IEntity, original, _render_context, { + Ok(RenderEntity { + html: original.html.clone(), + }) +}); diff --git a/src/context/example_block.rs b/src/context/example_block.rs index a15868a..464d435 100644 --- a/src/context/example_block.rs +++ b/src/context/example_block.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IExampleBlock; diff --git a/src/context/export_block.rs b/src/context/export_block.rs index 837df0f..36e5a06 100644 --- a/src/context/export_block.rs +++ b/src/context/export_block.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IExportBlock; diff --git a/src/context/export_snippet.rs b/src/context/export_snippet.rs index 4a422c0..246f25a 100644 --- a/src/context/export_snippet.rs +++ b/src/context/export_snippet.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IExportSnippet; diff --git a/src/context/fixed_width_area.rs b/src/context/fixed_width_area.rs index c31fbc8..ca9bb7f 100644 --- a/src/context/fixed_width_area.rs +++ b/src/context/fixed_width_area.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IFixedWidthArea; diff --git a/src/context/footnote_definition.rs b/src/context/footnote_definition.rs index e443fc1..8077f33 100644 --- a/src/context/footnote_definition.rs +++ b/src/context/footnote_definition.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IFootnoteDefinition; use crate::intermediate::IRealFootnoteDefinition; @@ -33,21 +31,19 @@ render!( RenderRealFootnoteDefinition, IRealFootnoteDefinition, original, - config, - output_directory, - output_file, + render_context, { let contents = { let mut ret = Vec::new(); for obj in original.contents.iter() { - ret.push(obj.into_render_ast_node(config, output_directory, output_file)?); + ret.push(obj.into_render_ast_node(render_context.clone())?); } ret }; Ok(RenderRealFootnoteDefinition { - definition_id: original.get_definition_id(), - reference_link: format!("#{}", original.get_reference_id()), + definition_id: original.get_definition_id(render_context.id_addition), + reference_link: format!("#{}", original.get_reference_id(render_context.id_addition)), label: original.get_display_label(), contents, }) diff --git a/src/context/footnote_reference.rs b/src/context/footnote_reference.rs index 0b188cc..28e319d 100644 --- a/src/context/footnote_reference.rs +++ b/src/context/footnote_reference.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IFootnoteReference; @@ -21,13 +19,14 @@ render!( RenderFootnoteReference, IFootnoteReference, original, - _config, - _output_directory, - _output_file, + render_context, { Ok(RenderFootnoteReference { - reference_id: original.get_reference_id(), - definition_link: format!("#{}", original.get_definition_id()), + reference_id: original.get_reference_id(render_context.id_addition), + definition_link: format!( + "#{}", + original.get_definition_id(render_context.id_addition) + ), label: original.get_display_label(), }) } diff --git a/src/context/heading.rs b/src/context/heading.rs index f9f9fa8..9b0064f 100644 --- a/src/context/heading.rs +++ b/src/context/heading.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IHeading; @@ -19,44 +17,26 @@ pub(crate) struct RenderHeading { children: Vec, } -render!( - RenderHeading, - IHeading, - original, - config, - output_directory, - output_file, - { - let title = { - let mut ret = Vec::new(); - for obj in original.title.iter() { - ret.push(RenderObject::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderHeading, IHeading, original, render_context, { + let title = { + let mut ret = Vec::new(); + for obj in original.title.iter() { + ret.push(RenderObject::new(render_context.clone(), obj)?); + } + ret + }; - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderDocumentElement::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderDocumentElement::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderHeading { - level: original.level + 1, // Adding 1 because the page title is going to be h1. - title, - children, - }) - } -); + Ok(RenderHeading { + level: original.level + 1, // Adding 1 because the page title is going to be h1. + title, + children, + }) +}); diff --git a/src/context/horizontal_rule.rs b/src/context/horizontal_rule.rs index 92ecb34..e941eb7 100644 --- a/src/context/horizontal_rule.rs +++ b/src/context/horizontal_rule.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IHorizontalRule; diff --git a/src/context/inline_babel_call.rs b/src/context/inline_babel_call.rs index 6ff79db..cdbb1ac 100644 --- a/src/context/inline_babel_call.rs +++ b/src/context/inline_babel_call.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IInlineBabelCall; diff --git a/src/context/inline_source_block.rs b/src/context/inline_source_block.rs index 637678c..79c9710 100644 --- a/src/context/inline_source_block.rs +++ b/src/context/inline_source_block.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IInlineSourceBlock; @@ -19,9 +17,7 @@ render!( RenderInlineSourceBlock, IInlineSourceBlock, original, - _config, - _output_directory, - _output_file, + _render_context, { Ok(RenderInlineSourceBlock { value: original.value.clone(), diff --git a/src/context/italic.rs b/src/context/italic.rs index d5240b4..9c4e995 100644 --- a/src/context/italic.rs +++ b/src/context/italic.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IItalic; @@ -16,27 +14,14 @@ pub(crate) struct RenderItalic { children: Vec, } -render!( - RenderItalic, - IItalic, - original, - config, - output_directory, - output_file, - { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderObject::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderItalic, IItalic, original, render_context, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderObject::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderItalic { children }) - } -); + Ok(RenderItalic { children }) +}); diff --git a/src/context/keyword.rs b/src/context/keyword.rs index c1f7268..17e08e7 100644 --- a/src/context/keyword.rs +++ b/src/context/keyword.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IKeyword; diff --git a/src/context/latex_environment.rs b/src/context/latex_environment.rs index a43f5ce..72a59e4 100644 --- a/src/context/latex_environment.rs +++ b/src/context/latex_environment.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ILatexEnvironment; diff --git a/src/context/latex_fragment.rs b/src/context/latex_fragment.rs index 6226ad5..fa695a0 100644 --- a/src/context/latex_fragment.rs +++ b/src/context/latex_fragment.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ILatexFragment; @@ -19,9 +17,7 @@ render!( RenderLatexFragment, ILatexFragment, original, - _config, - _output_directory, - _output_file, + _render_context, { Ok(RenderLatexFragment { value: original.value.clone(), diff --git a/src/context/line_break.rs b/src/context/line_break.rs index aa6b7a9..95f8554 100644 --- a/src/context/line_break.rs +++ b/src/context/line_break.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ILineBreak; diff --git a/src/context/macros.rs b/src/context/macros.rs index aa6bf2f..1ed3338 100644 --- a/src/context/macros.rs +++ b/src/context/macros.rs @@ -2,18 +2,12 @@ /// /// This exists to make changing the type signature easier. macro_rules! render { - ($rstruct:ident, $istruct:ident, $original:ident, $config:ident, $output_directory:ident, $output_file:ident, $fnbody:tt) => { + ($rstruct:ident, $istruct:ident, $original:ident, $render_context:ident, $fnbody:tt) => { impl $rstruct { pub(crate) fn new( - config: &Config, - output_directory: &Path, - output_file: &Path, - original: &$istruct, + $render_context: RenderContext<'_>, + $original: &$istruct, ) -> Result<$rstruct, CustomError> { - let $original = original; - let $config = config; - let $output_directory = output_directory; - let $output_file = output_file; $fnbody } } @@ -29,9 +23,7 @@ macro_rules! rnoop { ($rstruct:ident, $istruct:ident) => { impl $rstruct { pub(crate) fn new( - _config: &Config, - _output_directory: &Path, - _output_file: &Path, + _render_context: RenderContext<'_>, _original: &$istruct, ) -> Result<$rstruct, CustomError> { Ok($rstruct {}) diff --git a/src/context/mod.rs b/src/context/mod.rs index f3a8311..99ff45c 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -2,6 +2,7 @@ mod angle_link; mod ast_node; mod babel_call; mod blog_post_page; +mod blog_stream; mod bold; mod center_block; mod citation; @@ -47,6 +48,7 @@ mod quote_block; mod radio_link; mod radio_target; mod regular_link; +mod render_context; mod section; mod special_block; mod src_block; @@ -64,6 +66,9 @@ mod verbatim; mod verse_block; pub(crate) use blog_post_page::RenderBlogPostPage; +pub(crate) use blog_post_page::RenderBlogPostPageInput; +pub(crate) use blog_stream::RenderBlogStream; +pub(crate) use blog_stream::RenderBlogStreamInput; pub(crate) use document_element::RenderDocumentElement; pub(crate) use element::RenderElement; pub(crate) use footnote_definition::RenderRealFootnoteDefinition; @@ -71,4 +76,5 @@ pub(crate) use global_settings::GlobalSettings; pub(crate) use heading::RenderHeading; pub(crate) use object::RenderObject; pub(crate) use page_header::PageHeader; +pub(crate) use render_context::RenderContext; pub(crate) use section::RenderSection; diff --git a/src/context/object.rs b/src/context/object.rs index 44fa771..c684e7e 100644 --- a/src/context/object.rs +++ b/src/context/object.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IObject; @@ -67,153 +65,110 @@ pub(crate) enum RenderObject { Timestamp(RenderTimestamp), } -render!( - RenderObject, - IObject, - original, - config, - output_directory, - output_file, - { - match original { - IObject::Bold(inner) => Ok(RenderObject::Bold(RenderBold::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::Italic(inner) => Ok(RenderObject::Italic(RenderItalic::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::Underline(inner) => Ok(RenderObject::Underline(RenderUnderline::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::StrikeThrough(inner) => Ok(RenderObject::StrikeThrough( - RenderStrikeThrough::new(config, output_directory, output_file, inner)?, - )), - IObject::Code(inner) => Ok(RenderObject::Code(RenderCode::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::Verbatim(inner) => Ok(RenderObject::Verbatim(RenderVerbatim::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::PlainText(inner) => Ok(RenderObject::PlainText(RenderPlainText::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::RegularLink(inner) => Ok(RenderObject::RegularLink(RenderRegularLink::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::RadioLink(inner) => Ok(RenderObject::RadioLink(RenderRadioLink::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::RadioTarget(inner) => Ok(RenderObject::RadioTarget(RenderRadioTarget::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::PlainLink(inner) => Ok(RenderObject::PlainLink(RenderPlainLink::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::AngleLink(inner) => Ok(RenderObject::AngleLink(RenderAngleLink::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::OrgMacro(inner) => Ok(RenderObject::OrgMacro(RenderOrgMacro::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::Entity(inner) => Ok(RenderObject::Entity(RenderEntity::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::LatexFragment(inner) => Ok(RenderObject::LatexFragment( - RenderLatexFragment::new(config, output_directory, output_file, inner)?, - )), - IObject::ExportSnippet(inner) => Ok(RenderObject::ExportSnippet( - RenderExportSnippet::new(config, output_directory, output_file, inner)?, - )), - IObject::FootnoteReference(inner) => Ok(RenderObject::FootnoteReference( - RenderFootnoteReference::new(config, output_directory, output_file, inner)?, - )), - IObject::Citation(inner) => Ok(RenderObject::Citation(RenderCitation::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::CitationReference(inner) => Ok(RenderObject::CitationReference( - RenderCitationReference::new(config, output_directory, output_file, inner)?, - )), - IObject::InlineBabelCall(inner) => Ok(RenderObject::InlineBabelCall( - RenderInlineBabelCall::new(config, output_directory, output_file, inner)?, - )), - IObject::InlineSourceBlock(inner) => Ok(RenderObject::InlineSourceBlock( - RenderInlineSourceBlock::new(config, output_directory, output_file, inner)?, - )), - IObject::LineBreak(inner) => Ok(RenderObject::LineBreak(RenderLineBreak::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::Target(inner) => Ok(RenderObject::Target(RenderTarget::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::StatisticsCookie(inner) => Ok(RenderObject::StatisticsCookie( - RenderStatisticsCookie::new(config, output_directory, output_file, inner)?, - )), - IObject::Subscript(inner) => Ok(RenderObject::Subscript(RenderSubscript::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::Superscript(inner) => Ok(RenderObject::Superscript(RenderSuperscript::new( - config, - output_directory, - output_file, - inner, - )?)), - IObject::Timestamp(inner) => Ok(RenderObject::Timestamp(RenderTimestamp::new( - config, - output_directory, - output_file, - inner, - )?)), - } +render!(RenderObject, IObject, original, render_context, { + match original { + IObject::Bold(inner) => Ok(RenderObject::Bold(RenderBold::new( + render_context.clone(), + inner, + )?)), + IObject::Italic(inner) => Ok(RenderObject::Italic(RenderItalic::new( + render_context.clone(), + inner, + )?)), + IObject::Underline(inner) => Ok(RenderObject::Underline(RenderUnderline::new( + render_context.clone(), + inner, + )?)), + IObject::StrikeThrough(inner) => Ok(RenderObject::StrikeThrough(RenderStrikeThrough::new( + render_context.clone(), + inner, + )?)), + IObject::Code(inner) => Ok(RenderObject::Code(RenderCode::new( + render_context.clone(), + inner, + )?)), + IObject::Verbatim(inner) => Ok(RenderObject::Verbatim(RenderVerbatim::new( + render_context.clone(), + inner, + )?)), + IObject::PlainText(inner) => Ok(RenderObject::PlainText(RenderPlainText::new( + render_context.clone(), + inner, + )?)), + IObject::RegularLink(inner) => Ok(RenderObject::RegularLink(RenderRegularLink::new( + render_context.clone(), + inner, + )?)), + IObject::RadioLink(inner) => Ok(RenderObject::RadioLink(RenderRadioLink::new( + render_context.clone(), + inner, + )?)), + IObject::RadioTarget(inner) => Ok(RenderObject::RadioTarget(RenderRadioTarget::new( + render_context.clone(), + inner, + )?)), + IObject::PlainLink(inner) => Ok(RenderObject::PlainLink(RenderPlainLink::new( + render_context.clone(), + inner, + )?)), + IObject::AngleLink(inner) => Ok(RenderObject::AngleLink(RenderAngleLink::new( + render_context.clone(), + inner, + )?)), + IObject::OrgMacro(inner) => Ok(RenderObject::OrgMacro(RenderOrgMacro::new( + render_context.clone(), + inner, + )?)), + IObject::Entity(inner) => Ok(RenderObject::Entity(RenderEntity::new( + render_context.clone(), + inner, + )?)), + IObject::LatexFragment(inner) => Ok(RenderObject::LatexFragment(RenderLatexFragment::new( + render_context.clone(), + inner, + )?)), + IObject::ExportSnippet(inner) => Ok(RenderObject::ExportSnippet(RenderExportSnippet::new( + render_context.clone(), + inner, + )?)), + IObject::FootnoteReference(inner) => Ok(RenderObject::FootnoteReference( + RenderFootnoteReference::new(render_context.clone(), inner)?, + )), + IObject::Citation(inner) => Ok(RenderObject::Citation(RenderCitation::new( + render_context.clone(), + inner, + )?)), + IObject::CitationReference(inner) => Ok(RenderObject::CitationReference( + RenderCitationReference::new(render_context.clone(), inner)?, + )), + IObject::InlineBabelCall(inner) => Ok(RenderObject::InlineBabelCall( + RenderInlineBabelCall::new(render_context.clone(), inner)?, + )), + IObject::InlineSourceBlock(inner) => Ok(RenderObject::InlineSourceBlock( + RenderInlineSourceBlock::new(render_context.clone(), inner)?, + )), + IObject::LineBreak(inner) => Ok(RenderObject::LineBreak(RenderLineBreak::new( + render_context.clone(), + inner, + )?)), + IObject::Target(inner) => Ok(RenderObject::Target(RenderTarget::new( + render_context.clone(), + inner, + )?)), + IObject::StatisticsCookie(inner) => Ok(RenderObject::StatisticsCookie( + RenderStatisticsCookie::new(render_context.clone(), inner)?, + )), + IObject::Subscript(inner) => Ok(RenderObject::Subscript(RenderSubscript::new( + render_context.clone(), + inner, + )?)), + IObject::Superscript(inner) => Ok(RenderObject::Superscript(RenderSuperscript::new( + render_context.clone(), + inner, + )?)), + IObject::Timestamp(inner) => Ok(RenderObject::Timestamp(RenderTimestamp::new( + render_context.clone(), + inner, + )?)), } -); +}); diff --git a/src/context/org_macro.rs b/src/context/org_macro.rs index cdf6659..8c03ff2 100644 --- a/src/context/org_macro.rs +++ b/src/context/org_macro.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IOrgMacro; diff --git a/src/context/paragraph.rs b/src/context/paragraph.rs index 4063e5e..c7bd6e3 100644 --- a/src/context/paragraph.rs +++ b/src/context/paragraph.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IParagraph; @@ -16,27 +14,14 @@ pub(crate) struct RenderParagraph { children: Vec, } -render!( - RenderParagraph, - IParagraph, - original, - config, - output_directory, - output_file, - { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderObject::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderParagraph, IParagraph, original, render_context, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderObject::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderParagraph { children }) - } -); + Ok(RenderParagraph { children }) +}); diff --git a/src/context/plain_link.rs b/src/context/plain_link.rs index ffb5481..a30c77e 100644 --- a/src/context/plain_link.rs +++ b/src/context/plain_link.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IPlainLink; diff --git a/src/context/plain_list.rs b/src/context/plain_list.rs index d7be696..7b6fe69 100644 --- a/src/context/plain_list.rs +++ b/src/context/plain_list.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IPlainList; @@ -17,35 +15,22 @@ pub(crate) struct RenderPlainList { children: Vec, } -render!( - RenderPlainList, - IPlainList, - original, - config, - output_directory, - output_file, - { - let list_type = match original.list_type { - organic::types::PlainListType::Unordered => "unordered".to_owned(), - organic::types::PlainListType::Ordered => "ordered".to_owned(), - organic::types::PlainListType::Descriptive => "descriptive".to_owned(), - }; - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderPlainListItem::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderPlainList, IPlainList, original, render_context, { + let list_type = match original.list_type { + organic::types::PlainListType::Unordered => "unordered".to_owned(), + organic::types::PlainListType::Ordered => "ordered".to_owned(), + organic::types::PlainListType::Descriptive => "descriptive".to_owned(), + }; + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderPlainListItem::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderPlainList { - list_type, - children, - }) - } -); + Ok(RenderPlainList { + list_type, + children, + }) +}); diff --git a/src/context/plain_list_item.rs b/src/context/plain_list_item.rs index 4fa2edc..e40484c 100644 --- a/src/context/plain_list_item.rs +++ b/src/context/plain_list_item.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IPlainListItem; @@ -22,19 +20,12 @@ render!( RenderPlainListItem, IPlainListItem, original, - config, - output_directory, - output_file, + render_context, { let tag = { let mut ret = Vec::new(); for obj in original.tag.iter() { - ret.push(RenderObject::new( - config, - output_directory, - output_file, - obj, - )?); + ret.push(RenderObject::new(render_context.clone(), obj)?); } ret }; @@ -42,12 +33,7 @@ render!( let children = { let mut ret = Vec::new(); for obj in original.children.iter() { - ret.push(RenderElement::new( - config, - output_directory, - output_file, - obj, - )?); + ret.push(RenderElement::new(render_context.clone(), obj)?); } ret }; diff --git a/src/context/plain_text.rs b/src/context/plain_text.rs index 00dfda8..134d685 100644 --- a/src/context/plain_text.rs +++ b/src/context/plain_text.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IPlainText; @@ -15,16 +13,8 @@ pub(crate) struct RenderPlainText { source: String, } -render!( - RenderPlainText, - IPlainText, - original, - _config, - _output_directory, - _output_file, - { - Ok(RenderPlainText { - source: original.source.clone(), - }) - } -); +render!(RenderPlainText, IPlainText, original, _render_context, { + Ok(RenderPlainText { + source: original.source.clone(), + }) +}); diff --git a/src/context/planning.rs b/src/context/planning.rs index 6a13895..33f8b2c 100644 --- a/src/context/planning.rs +++ b/src/context/planning.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IPlanning; diff --git a/src/context/property_drawer.rs b/src/context/property_drawer.rs index d19ba3b..69ee47e 100644 --- a/src/context/property_drawer.rs +++ b/src/context/property_drawer.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IPropertyDrawer; diff --git a/src/context/quote_block.rs b/src/context/quote_block.rs index cae413d..581cbe2 100644 --- a/src/context/quote_block.rs +++ b/src/context/quote_block.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IQuoteBlock; @@ -16,27 +14,14 @@ pub(crate) struct RenderQuoteBlock { children: Vec, } -render!( - RenderQuoteBlock, - IQuoteBlock, - original, - config, - output_directory, - output_file, - { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderElement::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderQuoteBlock, IQuoteBlock, original, render_context, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderElement::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderQuoteBlock { children }) - } -); + Ok(RenderQuoteBlock { children }) +}); diff --git a/src/context/radio_link.rs b/src/context/radio_link.rs index 06c727f..a7eabdb 100644 --- a/src/context/radio_link.rs +++ b/src/context/radio_link.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IRadioLink; diff --git a/src/context/radio_target.rs b/src/context/radio_target.rs index 5bbfd56..5f91d13 100644 --- a/src/context/radio_target.rs +++ b/src/context/radio_target.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IRadioTarget; diff --git a/src/context/regular_link.rs b/src/context/regular_link.rs index 91efe9e..9dfceae 100644 --- a/src/context/regular_link.rs +++ b/src/context/regular_link.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IRegularLink; @@ -17,30 +15,17 @@ pub(crate) struct RenderRegularLink { children: Vec, } -render!( - RenderRegularLink, - IRegularLink, - original, - config, - output_directory, - output_file, - { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderObject::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderRegularLink, IRegularLink, original, render_context, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderObject::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderRegularLink { - raw_link: original.raw_link.clone(), - children, - }) - } -); + Ok(RenderRegularLink { + raw_link: original.raw_link.clone(), + children, + }) +}); diff --git a/src/context/render_context.rs b/src/context/render_context.rs new file mode 100644 index 0000000..18ad7d0 --- /dev/null +++ b/src/context/render_context.rs @@ -0,0 +1,36 @@ +use std::path::Path; + +use crate::config::Config; +use crate::error::CustomError; + +/// The supporting information used for converting the intermediate representation into the dust context for rendering. +#[derive(Debug, Clone)] +pub(crate) struct RenderContext<'intermediate> { + pub(crate) config: &'intermediate Config, + // TODO: Perhaps rename to output_root_directory. + pub(crate) output_root_directory: &'intermediate Path, + pub(crate) output_file: &'intermediate Path, + + /// An optional string that gets added to IDs in HTML. + /// + /// This is useful for cases where you may have conflicting HTML + /// IDs, for example, multiple blog posts with footnotes in a blog + /// stream. + pub(crate) id_addition: Option<&'intermediate str>, +} + +impl<'intermediate> RenderContext<'intermediate> { + pub(crate) fn new( + config: &'intermediate Config, + output_directory: &'intermediate Path, + output_file: &'intermediate Path, + id_addition: Option<&'intermediate str>, + ) -> Result, CustomError> { + Ok(RenderContext { + config, + output_root_directory: output_directory, + output_file, + id_addition, + }) + } +} diff --git a/src/context/section.rs b/src/context/section.rs index 3f92fe9..aa297c3 100644 --- a/src/context/section.rs +++ b/src/context/section.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ISection; @@ -16,27 +14,14 @@ pub(crate) struct RenderSection { children: Vec, } -render!( - RenderSection, - ISection, - original, - config, - output_directory, - output_file, - { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderElement::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderSection, ISection, original, render_context, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderElement::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderSection { children }) - } -); + Ok(RenderSection { children }) +}); diff --git a/src/context/special_block.rs b/src/context/special_block.rs index cb42967..21f96fc 100644 --- a/src/context/special_block.rs +++ b/src/context/special_block.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ISpecialBlock; diff --git a/src/context/src_block.rs b/src/context/src_block.rs index f6161f8..d5346d7 100644 --- a/src/context/src_block.rs +++ b/src/context/src_block.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ISrcBlock; @@ -15,16 +13,8 @@ pub(crate) struct RenderSrcBlock { lines: Vec, } -render!( - RenderSrcBlock, - ISrcBlock, - original, - _config, - _output_directory, - _output_file, - { - Ok(RenderSrcBlock { - lines: original.lines.clone(), - }) - } -); +render!(RenderSrcBlock, ISrcBlock, original, _render_context, { + Ok(RenderSrcBlock { + lines: original.lines.clone(), + }) +}); diff --git a/src/context/statistics_cookie.rs b/src/context/statistics_cookie.rs index 35dc122..ca71b95 100644 --- a/src/context/statistics_cookie.rs +++ b/src/context/statistics_cookie.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IStatisticsCookie; diff --git a/src/context/strike_through.rs b/src/context/strike_through.rs index c84fa37..1d53f9c 100644 --- a/src/context/strike_through.rs +++ b/src/context/strike_through.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IStrikeThrough; @@ -20,19 +18,12 @@ render!( RenderStrikeThrough, IStrikeThrough, original, - config, - output_directory, - output_file, + render_context, { let children = { let mut ret = Vec::new(); for obj in original.children.iter() { - ret.push(RenderObject::new( - config, - output_directory, - output_file, - obj, - )?); + ret.push(RenderObject::new(render_context.clone(), obj)?); } ret }; diff --git a/src/context/subscript.rs b/src/context/subscript.rs index b54c5e1..359492c 100644 --- a/src/context/subscript.rs +++ b/src/context/subscript.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ISubscript; diff --git a/src/context/superscript.rs b/src/context/superscript.rs index bb030c5..3af3340 100644 --- a/src/context/superscript.rs +++ b/src/context/superscript.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ISuperscript; diff --git a/src/context/table.rs b/src/context/table.rs index f460650..d1af62f 100644 --- a/src/context/table.rs +++ b/src/context/table.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ITable; @@ -16,27 +14,14 @@ pub(crate) struct RenderTable { children: Vec, } -render!( - RenderTable, - ITable, - original, - config, - output_directory, - output_file, - { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderTableRow::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderTable, ITable, original, render_context, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderTableRow::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderTable { children }) - } -); + Ok(RenderTable { children }) +}); diff --git a/src/context/table_cell.rs b/src/context/table_cell.rs index c2cef3c..85ce972 100644 --- a/src/context/table_cell.rs +++ b/src/context/table_cell.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ITableCell; @@ -16,27 +14,14 @@ pub(crate) struct RenderTableCell { children: Vec, } -render!( - RenderTableCell, - ITableCell, - original, - config, - output_directory, - output_file, - { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderObject::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderTableCell, ITableCell, original, render_context, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderObject::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderTableCell { children }) - } -); + Ok(RenderTableCell { children }) +}); diff --git a/src/context/table_row.rs b/src/context/table_row.rs index c4da3a8..95cae10 100644 --- a/src/context/table_row.rs +++ b/src/context/table_row.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ITableRow; @@ -16,27 +14,14 @@ pub(crate) struct RenderTableRow { children: Vec, } -render!( - RenderTableRow, - ITableRow, - original, - config, - output_directory, - output_file, - { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderTableCell::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderTableRow, ITableRow, original, render_context, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderTableCell::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderTableRow { children }) - } -); + Ok(RenderTableRow { children }) +}); diff --git a/src/context/target.rs b/src/context/target.rs index 9a04061..458bc69 100644 --- a/src/context/target.rs +++ b/src/context/target.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ITarget; @@ -15,16 +13,8 @@ pub(crate) struct RenderTarget { id: String, } -render!( - RenderTarget, - ITarget, - original, - _config, - _output_directory, - _output_file, - { - Ok(RenderTarget { - id: original.id.clone(), - }) - } -); +render!(RenderTarget, ITarget, original, _render_context, { + Ok(RenderTarget { + id: original.id.clone(), + }) +}); diff --git a/src/context/timestamp.rs b/src/context/timestamp.rs index 73b6031..cf5ce54 100644 --- a/src/context/timestamp.rs +++ b/src/context/timestamp.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::ITimestamp; diff --git a/src/context/underline.rs b/src/context/underline.rs index 41cb21c..ab5fa5a 100644 --- a/src/context/underline.rs +++ b/src/context/underline.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IUnderline; @@ -16,27 +14,14 @@ pub(crate) struct RenderUnderline { children: Vec, } -render!( - RenderUnderline, - IUnderline, - original, - config, - output_directory, - output_file, - { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(RenderObject::new( - config, - output_directory, - output_file, - obj, - )?); - } - ret - }; +render!(RenderUnderline, IUnderline, original, render_context, { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(RenderObject::new(render_context.clone(), obj)?); + } + ret + }; - Ok(RenderUnderline { children }) - } -); + Ok(RenderUnderline { children }) +}); diff --git a/src/context/verbatim.rs b/src/context/verbatim.rs index 0314b63..68d2d0d 100644 --- a/src/context/verbatim.rs +++ b/src/context/verbatim.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IVerbatim; @@ -15,16 +13,8 @@ pub(crate) struct RenderVerbatim { contents: String, } -render!( - RenderVerbatim, - IVerbatim, - original, - _config, - _output_directory, - _output_file, - { - Ok(RenderVerbatim { - contents: original.contents.clone(), - }) - } -); +render!(RenderVerbatim, IVerbatim, original, _render_context, { + Ok(RenderVerbatim { + contents: original.contents.clone(), + }) +}); diff --git a/src/context/verse_block.rs b/src/context/verse_block.rs index 9e4cef2..cea5eb5 100644 --- a/src/context/verse_block.rs +++ b/src/context/verse_block.rs @@ -1,8 +1,6 @@ -use std::path::Path; - use serde::Serialize; -use crate::config::Config; +use super::render_context::RenderContext; use crate::error::CustomError; use crate::intermediate::IVerseBlock; diff --git a/src/error/error.rs b/src/error/error.rs index 9707bdb..a6816ee 100644 --- a/src/error/error.rs +++ b/src/error/error.rs @@ -4,6 +4,7 @@ use std::string::FromUtf8Error; #[derive(Debug)] pub(crate) enum CustomError { Static(&'static str), + String(String), IO(std::io::Error), TomlSerialize(toml::ser::Error), TomlDeserialize(toml::de::Error), @@ -29,6 +30,12 @@ impl From<&'static str> for CustomError { } } +impl From for CustomError { + fn from(value: String) -> Self { + CustomError::String(value) + } +} + impl From for CustomError { fn from(value: toml::ser::Error) -> Self { CustomError::TomlSerialize(value) diff --git a/src/intermediate/definition.rs b/src/intermediate/blog_post.rs similarity index 71% rename from src/intermediate/definition.rs rename to src/intermediate/blog_post.rs index 4b58d8e..6116653 100644 --- a/src/intermediate/definition.rs +++ b/src/intermediate/blog_post.rs @@ -7,6 +7,7 @@ use tokio::task::JoinHandle; use walkdir::WalkDir; use crate::error::CustomError; +use crate::intermediate::page::BlogPostPageInput; use crate::intermediate::registry::Registry; use super::BlogPostPage; @@ -63,8 +64,11 @@ impl BlogPost { let registry = Arc::new(Mutex::new(registry)); let relative_to_post_dir_path = real_path.strip_prefix(post_dir)?; ret.push( - BlogPostPage::new(relative_to_post_dir_path, registry, parsed_document) - .await?, + BlogPostPage::new( + registry, + BlogPostPageInput::new(relative_to_post_dir_path, parsed_document), + ) + .await?, ); } ret @@ -77,6 +81,37 @@ impl BlogPost { } inner(root_dir.as_ref(), post_dir.as_ref()).await } + + /// Get the date for a blog post. + /// + /// The date is set by the "#+date" export setting. This will + /// first attempt to read the date from an index.org if such a + /// file exists. If that file does not exist or that file does not + /// contain a date export setting, then this will iterate through + /// all the pages under the blog post looking for any page that + /// contains a date export setting. It will return the first date + /// found. + pub(crate) fn get_date(&self) -> Option<&str> { + let index_page_date = self + .get_index_page() + .map(|index_page| index_page.date.as_ref().map(String::as_str)) + .flatten(); + if index_page_date.is_some() { + return index_page_date; + } + + self.pages + .iter() + .filter_map(|page| page.date.as_ref().map(String::as_str)) + .next() + } + + /// Get the blog post page for index.org + pub(crate) fn get_index_page(&self) -> Option<&BlogPostPage> { + self.pages + .iter() + .find(|page| page.path == Path::new("index.org")) + } } async fn read_file(path: PathBuf) -> std::io::Result<(PathBuf, String)> { diff --git a/src/intermediate/bold.rs b/src/intermediate/bold.rs index dbca21d..6774fb0 100644 --- a/src/intermediate/bold.rs +++ b/src/intermediate/bold.rs @@ -8,14 +8,20 @@ pub(crate) struct IBold { pub(crate) children: Vec, } -intermediate!(IBold, Bold, original, registry, { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IObject::new(registry.clone(), obj).await?); - } - ret - }; +intermediate!( + IBold, + &'orig organic::types::Bold<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; - Ok(IBold { children }) -}); + Ok(IBold { children }) + } +); diff --git a/src/intermediate/code.rs b/src/intermediate/code.rs index b121666..6f32ac6 100644 --- a/src/intermediate/code.rs +++ b/src/intermediate/code.rs @@ -7,9 +7,15 @@ pub(crate) struct ICode { pub(crate) contents: String, } -intermediate!(ICode, Code, original, _registry, { - Ok(ICode { - // TODO: Should this coalesce whitespace like PlainText? - contents: original.contents.to_owned(), - }) -}); +intermediate!( + ICode, + &'orig organic::types::Code<'parse>, + original, + _registry, + { + Ok(ICode { + // TODO: Should this coalesce whitespace like PlainText? + contents: original.contents.to_owned(), + }) + } +); diff --git a/src/intermediate/convert.rs b/src/intermediate/convert.rs index 596a55c..ca462ef 100644 --- a/src/intermediate/convert.rs +++ b/src/intermediate/convert.rs @@ -3,94 +3,9 @@ use std::path::Path; use std::path::PathBuf; use crate::config::Config; -use crate::context::GlobalSettings; -use crate::context::PageHeader; -use crate::context::RenderBlogPostPage; -use crate::context::RenderDocumentElement; -use crate::context::RenderRealFootnoteDefinition; use crate::error::CustomError; -use super::BlogPost; -use super::BlogPostPage; - -pub(crate) fn convert_blog_post_page_to_render_context, F: AsRef>( - config: &Config, - output_directory: D, - output_file: F, - _post: &BlogPost, - page: &BlogPostPage, -) -> Result { - let output_directory = output_directory.as_ref(); - let output_file = output_file.as_ref(); - let css_files = vec![ - get_web_path( - config, - output_directory, - output_file, - "stylesheet/reset.css", - )?, - get_web_path(config, output_directory, output_file, "stylesheet/main.css")?, - ]; - let js_files = vec![get_web_path( - config, - output_directory, - output_file, - "blog_post.js", - )?]; - let global_settings = GlobalSettings::new(page.title.clone(), css_files, js_files); - let page_header = PageHeader::new( - config.get_site_title().map(str::to_string), - Some(get_web_path(config, output_directory, output_file, "")?), - ); - let link_to_blog_post = get_web_path( - config, - output_directory, - output_file, - output_file.strip_prefix(output_directory)?, - )?; - - let children = { - let mut children = Vec::new(); - - for child in page.children.iter() { - children.push(RenderDocumentElement::new( - config, - output_directory, - output_file, - child, - )?); - } - - children - }; - - let footnotes = { - let mut ret = Vec::new(); - - for footnote in page.footnotes.iter() { - ret.push(RenderRealFootnoteDefinition::new( - config, - output_directory, - output_file, - footnote, - )?); - } - - ret - }; - - let ret = RenderBlogPostPage::new( - global_settings, - Some(page_header), - page.title.clone(), - Some(link_to_blog_post), - children, - footnotes, - ); - Ok(ret) -} - -fn get_web_path, F: AsRef, P: AsRef>( +pub(crate) fn get_web_path, F: AsRef, P: AsRef>( config: &Config, output_directory: D, containing_file: F, diff --git a/src/intermediate/entity.rs b/src/intermediate/entity.rs index b2043ed..071daa0 100644 --- a/src/intermediate/entity.rs +++ b/src/intermediate/entity.rs @@ -7,8 +7,14 @@ pub(crate) struct IEntity { pub(crate) html: String, } -intermediate!(IEntity, Entity, original, _registry, { - Ok(IEntity { - html: original.html.to_owned(), - }) -}); +intermediate!( + IEntity, + &'orig organic::types::Entity<'parse>, + original, + _registry, + { + Ok(IEntity { + html: original.html.to_owned(), + }) + } +); diff --git a/src/intermediate/footnote_definition.rs b/src/intermediate/footnote_definition.rs index 424c56f..265c215 100644 --- a/src/intermediate/footnote_definition.rs +++ b/src/intermediate/footnote_definition.rs @@ -10,7 +10,7 @@ pub(crate) struct IFootnoteDefinition {} intermediate!( IFootnoteDefinition, - FootnoteDefinition, + &'orig organic::types::FootnoteDefinition<'parse>, original, registry, { @@ -44,14 +44,22 @@ impl IRealFootnoteDefinition { /// Get an ID to refer to the first reference to this footnote definition. /// /// This ID could, for example, be used for the id attribute in HTML for the reference anchor tag. - pub(crate) fn get_reference_id(&self) -> String { - format!("fnr.{}", self.get_display_label()) + pub(crate) fn get_reference_id(&self, id_addition: Option<&str>) -> String { + let id_addition = id_addition + .map(|id_addition| format!("sec{}.", id_addition)) + .unwrap_or(String::default()); + + format!("{}fnr.{}", id_addition, self.get_display_label()) } /// Get an ID to refer to the footnote definition. /// /// This ID could, for example, be used for the id attribute in HTML for the definition anchor tag. - pub(crate) fn get_definition_id(&self) -> String { - format!("fn.{}", self.get_display_label()) + pub(crate) fn get_definition_id(&self, id_addition: Option<&str>) -> String { + let id_addition = id_addition + .map(|id_addition| format!("sec{}.", id_addition)) + .unwrap_or(String::default()); + + format!("{}fn.{}", id_addition, self.get_display_label()) } } diff --git a/src/intermediate/footnote_reference.rs b/src/intermediate/footnote_reference.rs index f9f448c..175edd2 100644 --- a/src/intermediate/footnote_reference.rs +++ b/src/intermediate/footnote_reference.rs @@ -9,14 +9,20 @@ pub(crate) struct IFootnoteReference { duplicate_offset: usize, } -intermediate!(IFootnoteReference, FootnoteReference, original, registry, { - let (footnote_id, reference_count) = - get_footnote_reference_id(registry, original.label, &original.definition).await?; - Ok(IFootnoteReference { - footnote_id, - duplicate_offset: reference_count, - }) -}); +intermediate!( + IFootnoteReference, + &'orig organic::types::FootnoteReference<'parse>, + original, + registry, + { + let (footnote_id, reference_count) = + get_footnote_reference_id(registry, original.label, &original.definition).await?; + Ok(IFootnoteReference { + footnote_id, + duplicate_offset: reference_count, + }) + } +); impl IFootnoteReference { pub(crate) fn get_display_label(&self) -> String { @@ -26,20 +32,28 @@ impl IFootnoteReference { /// Get an ID to refer to this footnote reference. /// /// This ID could, for example, be used for the id attribute in HTML for the reference anchor tag. - pub(crate) fn get_reference_id(&self) -> String { + pub(crate) fn get_reference_id(&self, id_addition: Option<&str>) -> String { + let id_addition = id_addition + .map(|id_addition| format!("sec{}.", id_addition)) + .unwrap_or(String::default()); + if self.duplicate_offset == 0 { - format!("fnr.{}", self.get_display_label()) + format!("{}fnr.{}", id_addition, self.get_display_label()) } else { // Org-mode makes all duplicates use "100" but I figure there is no harm in giving each a unique ID. let append = 100 + self.duplicate_offset - 1; - format!("fnr.{}.{}", self.get_display_label(), append) + format!("{}fnr.{}.{}", id_addition, self.get_display_label(), append) } } /// Get an ID to refer to the footnote definition this footnote reference references. /// /// This ID could, for example, be used for the id attribute in HTML for the definition anchor tag. - pub(crate) fn get_definition_id(&self) -> String { - format!("fn.{}", self.get_display_label()) + pub(crate) fn get_definition_id(&self, id_addition: Option<&str>) -> String { + let id_addition = id_addition + .map(|id_addition| format!("sec{}.", id_addition)) + .unwrap_or(String::default()); + + format!("{}fn.{}", id_addition, self.get_display_label()) } } diff --git a/src/intermediate/heading.rs b/src/intermediate/heading.rs index 9446068..74830b3 100644 --- a/src/intermediate/heading.rs +++ b/src/intermediate/heading.rs @@ -11,24 +11,30 @@ pub(crate) struct IHeading { pub(crate) children: Vec, } -intermediate!(IHeading, Heading, original, registry, { - let title = { - let mut ret = Vec::new(); - for obj in original.title.iter() { - ret.push(IObject::new(registry.clone(), obj).await?); - } - ret - }; - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IDocumentElement::new(registry.clone(), obj).await?); - } - ret - }; - Ok(IHeading { - title, - level: original.level, - children, - }) -}); +intermediate!( + IHeading, + &'orig organic::types::Heading<'parse>, + original, + registry, + { + let title = { + let mut ret = Vec::new(); + for obj in original.title.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IDocumentElement::new(registry.clone(), obj).await?); + } + ret + }; + Ok(IHeading { + title, + level: original.level, + children, + }) + } +); diff --git a/src/intermediate/inline_source_block.rs b/src/intermediate/inline_source_block.rs index dd23be8..68e31b2 100644 --- a/src/intermediate/inline_source_block.rs +++ b/src/intermediate/inline_source_block.rs @@ -7,8 +7,14 @@ pub(crate) struct IInlineSourceBlock { pub(crate) value: String, } -intermediate!(IInlineSourceBlock, InlineSourceBlock, original, _registry, { - Ok(IInlineSourceBlock { - value: original.value.to_owned(), - }) -}); +intermediate!( + IInlineSourceBlock, + &'orig organic::types::InlineSourceBlock<'parse>, + original, + _registry, + { + Ok(IInlineSourceBlock { + value: original.value.to_owned(), + }) + } +); diff --git a/src/intermediate/italic.rs b/src/intermediate/italic.rs index 29d3fb3..23ecdc3 100644 --- a/src/intermediate/italic.rs +++ b/src/intermediate/italic.rs @@ -8,14 +8,20 @@ pub(crate) struct IItalic { pub(crate) children: Vec, } -intermediate!(IItalic, Italic, original, registry, { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IObject::new(registry.clone(), obj).await?); - } - ret - }; +intermediate!( + IItalic, + &'orig organic::types::Italic<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; - Ok(IItalic { children }) -}); + Ok(IItalic { children }) + } +); diff --git a/src/intermediate/latex_fragment.rs b/src/intermediate/latex_fragment.rs index 14721fd..75cf59e 100644 --- a/src/intermediate/latex_fragment.rs +++ b/src/intermediate/latex_fragment.rs @@ -7,13 +7,19 @@ pub(crate) struct ILatexFragment { pub(crate) value: String, } -intermediate!(ILatexFragment, LatexFragment, original, _registry, { - let value: String = if original.value.starts_with("$$") && original.value.ends_with("$$") { - format!("\\[{}\\]", &original.value[2..(original.value.len() - 2)]) - } else if original.value.starts_with("$") && original.value.ends_with("$") { - format!("\\({}\\)", &original.value[1..(original.value.len() - 1)]) - } else { - original.value.to_owned() - }; - Ok(ILatexFragment { value }) -}); +intermediate!( + ILatexFragment, + &'orig organic::types::LatexFragment<'parse>, + original, + _registry, + { + let value: String = if original.value.starts_with("$$") && original.value.ends_with("$$") { + format!("\\[{}\\]", &original.value[2..(original.value.len() - 2)]) + } else if original.value.starts_with("$") && original.value.ends_with("$") { + format!("\\({}\\)", &original.value[1..(original.value.len() - 1)]) + } else { + original.value.to_owned() + }; + Ok(ILatexFragment { value }) + } +); diff --git a/src/intermediate/macros.rs b/src/intermediate/macros.rs index cdebb49..b3c5465 100644 --- a/src/intermediate/macros.rs +++ b/src/intermediate/macros.rs @@ -23,14 +23,12 @@ pub(crate) use inoop; /// /// This exists to make changing the type signature easier. macro_rules! intermediate { - ($istruct:ident, $pstruct:ident, $original:ident, $registry:ident, $fnbody:tt) => { + ($istruct:ident, $pstruct:ty, $original:ident, $registry:ident, $fnbody:tt) => { impl $istruct { pub(crate) async fn new<'orig, 'parse>( - registry: crate::intermediate::RefRegistry<'orig, 'parse>, - original: &'orig organic::types::$pstruct<'parse>, + $registry: crate::intermediate::RefRegistry<'orig, 'parse>, + $original: $pstruct, ) -> Result<$istruct, CustomError> { - let $original = original; - let $registry = registry; $fnbody } } diff --git a/src/intermediate/mod.rs b/src/intermediate/mod.rs index fd969ef..4a7595f 100644 --- a/src/intermediate/mod.rs +++ b/src/intermediate/mod.rs @@ -1,6 +1,7 @@ mod angle_link; mod ast_node; mod babel_call; +mod blog_post; mod bold; mod center_block; mod citation; @@ -10,7 +11,6 @@ mod code; mod comment; mod comment_block; mod convert; -mod definition; mod diary_sexp; mod document_element; mod drawer; @@ -67,6 +67,7 @@ mod verse_block; pub(crate) use angle_link::IAngleLink; pub(crate) use ast_node::IAstNode; pub(crate) use babel_call::IBabelCall; +pub(crate) use blog_post::BlogPost; pub(crate) use bold::IBold; pub(crate) use center_block::ICenterBlock; pub(crate) use citation::ICitation; @@ -75,8 +76,7 @@ pub(crate) use clock::IClock; pub(crate) use code::ICode; pub(crate) use comment::IComment; pub(crate) use comment_block::ICommentBlock; -pub(crate) use convert::convert_blog_post_page_to_render_context; -pub(crate) use definition::BlogPost; +pub(crate) use convert::get_web_path; pub(crate) use diary_sexp::IDiarySexp; pub(crate) use document_element::IDocumentElement; pub(crate) use drawer::IDrawer; diff --git a/src/intermediate/page.rs b/src/intermediate/page.rs index 74952fa..8de8e24 100644 --- a/src/intermediate/page.rs +++ b/src/intermediate/page.rs @@ -4,10 +4,28 @@ use crate::error::CustomError; use super::footnote_definition::IRealFootnoteDefinition; +use super::macros::intermediate; use super::IDocumentElement; use super::IHeading; use super::ISection; -use super::RefRegistry; + +#[derive(Debug)] +pub(crate) struct BlogPostPageInput<'b, 'parse> { + path: PathBuf, + document: &'b organic::types::Document<'parse>, +} + +impl<'b, 'parse> BlogPostPageInput<'b, 'parse> { + pub(crate) fn new>( + path: P, + document: &'b organic::types::Document<'parse>, + ) -> BlogPostPageInput<'b, 'parse> { + BlogPostPageInput { + path: path.into(), + document, + } + } +} #[derive(Debug)] pub(crate) struct BlogPostPage { @@ -16,26 +34,26 @@ pub(crate) struct BlogPostPage { pub(crate) title: Option, + pub(crate) date: Option, + pub(crate) children: Vec, pub(crate) footnotes: Vec, } -impl BlogPostPage { - // TODO: Move path into the registry so I can give this a standard interface like the others. - pub(crate) async fn new<'a, 'b, 'parse, P: Into>( - path: P, - registry: RefRegistry<'b, 'parse>, - document: &'b organic::types::Document<'parse>, - ) -> Result { - let path = path.into(); +intermediate!( + BlogPostPage, + BlogPostPageInput<'orig, 'parse>, + original, + registry, + { let mut children = Vec::new(); - if let Some(section) = document.zeroth_section.as_ref() { + if let Some(section) = original.document.zeroth_section.as_ref() { children.push(IDocumentElement::Section( ISection::new(registry.clone(), section).await?, )); } - for heading in document.children.iter() { + for heading in original.document.children.iter() { children.push(IDocumentElement::Heading( IHeading::new(registry.clone(), heading).await?, )); @@ -58,13 +76,16 @@ impl BlogPostPage { }; Ok(BlogPostPage { - path, - title: get_title(&document), + path: original.path, + title: get_title(original.document), + date: get_date(original.document), children, footnotes, }) } +); +impl BlogPostPage { /// Get the output path relative to the post directory. pub(crate) fn get_output_path(&self) -> PathBuf { let mut ret = self.path.clone(); @@ -85,3 +106,14 @@ fn get_title(document: &organic::types::Document<'_>) -> Option { .last() .map(|kw| kw.value.to_owned()) } + +fn get_date(document: &organic::types::Document<'_>) -> Option { + organic::types::AstNode::from(document) + .iter_all_ast_nodes() + .filter_map(|node| match node { + organic::types::AstNode::Keyword(kw) if kw.key.eq_ignore_ascii_case("date") => Some(kw), + _ => None, + }) + .last() + .map(|kw| kw.value.to_owned()) +} diff --git a/src/intermediate/paragraph.rs b/src/intermediate/paragraph.rs index c9cb886..2c2e112 100644 --- a/src/intermediate/paragraph.rs +++ b/src/intermediate/paragraph.rs @@ -8,14 +8,20 @@ pub(crate) struct IParagraph { pub(crate) children: Vec, } -intermediate!(IParagraph, Paragraph, original, registry, { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IObject::new(registry.clone(), obj).await?); - } - ret - }; +intermediate!( + IParagraph, + &'orig organic::types::Paragraph<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; - Ok(IParagraph { children }) -}); + Ok(IParagraph { children }) + } +); diff --git a/src/intermediate/plain_list.rs b/src/intermediate/plain_list.rs index a6e09c2..68dc0b8 100644 --- a/src/intermediate/plain_list.rs +++ b/src/intermediate/plain_list.rs @@ -9,17 +9,23 @@ pub(crate) struct IPlainList { pub(crate) children: Vec, } -intermediate!(IPlainList, PlainList, original, registry, { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IPlainListItem::new(registry.clone(), obj).await?); - } - ret - }; +intermediate!( + IPlainList, + &'orig organic::types::PlainList<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IPlainListItem::new(registry.clone(), obj).await?); + } + ret + }; - Ok(IPlainList { - list_type: original.list_type, - children, - }) -}); + Ok(IPlainList { + list_type: original.list_type, + children, + }) + } +); diff --git a/src/intermediate/plain_list_item.rs b/src/intermediate/plain_list_item.rs index eee5947..ca971c6 100644 --- a/src/intermediate/plain_list_item.rs +++ b/src/intermediate/plain_list_item.rs @@ -10,22 +10,28 @@ pub(crate) struct IPlainListItem { pub(crate) children: Vec, } -intermediate!(IPlainListItem, PlainListItem, original, registry, { - let tag = { - let mut ret = Vec::new(); - for obj in original.tag.iter() { - ret.push(IObject::new(registry.clone(), obj).await?); - } - ret - }; +intermediate!( + IPlainListItem, + &'orig organic::types::PlainListItem<'parse>, + original, + registry, + { + let tag = { + let mut ret = Vec::new(); + for obj in original.tag.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; - let children = { - let mut ret = Vec::new(); - for elem in original.children.iter() { - ret.push(IElement::new(registry.clone(), elem).await?); - } - ret - }; + let children = { + let mut ret = Vec::new(); + for elem in original.children.iter() { + ret.push(IElement::new(registry.clone(), elem).await?); + } + ret + }; - Ok(IPlainListItem { tag, children }) -}); + Ok(IPlainListItem { tag, children }) + } +); diff --git a/src/intermediate/plain_text.rs b/src/intermediate/plain_text.rs index 085ae23..ee419ad 100644 --- a/src/intermediate/plain_text.rs +++ b/src/intermediate/plain_text.rs @@ -8,8 +8,14 @@ pub(crate) struct IPlainText { pub(crate) source: String, } -intermediate!(IPlainText, PlainText, original, _registry, { - Ok(IPlainText { - source: coalesce_whitespace(original.source).into_owned(), - }) -}); +intermediate!( + IPlainText, + &'orig organic::types::PlainText<'parse>, + original, + _registry, + { + Ok(IPlainText { + source: coalesce_whitespace(original.source).into_owned(), + }) + } +); diff --git a/src/intermediate/quote_block.rs b/src/intermediate/quote_block.rs index 17c3dd4..e7485d0 100644 --- a/src/intermediate/quote_block.rs +++ b/src/intermediate/quote_block.rs @@ -8,14 +8,20 @@ pub(crate) struct IQuoteBlock { pub(crate) children: Vec, } -intermediate!(IQuoteBlock, QuoteBlock, original, registry, { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IElement::new(registry.clone(), obj).await?); - } - ret - }; +intermediate!( + IQuoteBlock, + &'orig organic::types::QuoteBlock<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IElement::new(registry.clone(), obj).await?); + } + ret + }; - Ok(IQuoteBlock { children }) -}); + Ok(IQuoteBlock { children }) + } +); diff --git a/src/intermediate/regular_link.rs b/src/intermediate/regular_link.rs index 0fcf77c..5f10fb0 100644 --- a/src/intermediate/regular_link.rs +++ b/src/intermediate/regular_link.rs @@ -9,16 +9,22 @@ pub(crate) struct IRegularLink { pub(crate) children: Vec, } -intermediate!(IRegularLink, RegularLink, original, registry, { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IObject::new(registry.clone(), obj).await?); - } - ret - }; - Ok(IRegularLink { - raw_link: original.get_raw_link().into_owned(), - children, - }) -}); +intermediate!( + IRegularLink, + &'orig organic::types::RegularLink<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; + Ok(IRegularLink { + raw_link: original.get_raw_link().into_owned(), + children, + }) + } +); diff --git a/src/intermediate/section.rs b/src/intermediate/section.rs index 66d5be3..9dc1119 100644 --- a/src/intermediate/section.rs +++ b/src/intermediate/section.rs @@ -8,14 +8,20 @@ pub(crate) struct ISection { pub(crate) children: Vec, } -intermediate!(ISection, Section, original, registry, { - let children = { - let mut ret = Vec::new(); - for elem in original.children.iter() { - ret.push(IElement::new(registry.clone(), elem).await?); - } - ret - }; +intermediate!( + ISection, + &'orig organic::types::Section<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for elem in original.children.iter() { + ret.push(IElement::new(registry.clone(), elem).await?); + } + ret + }; - Ok(ISection { children }) -}); + Ok(ISection { children }) + } +); diff --git a/src/intermediate/src_block.rs b/src/intermediate/src_block.rs index 90824ba..0e1b2d1 100644 --- a/src/intermediate/src_block.rs +++ b/src/intermediate/src_block.rs @@ -7,11 +7,17 @@ pub(crate) struct ISrcBlock { pub(crate) lines: Vec, } -intermediate!(ISrcBlock, SrcBlock, original, _registry, { - let lines = original - .get_value() - .split_inclusive('\n') - .map(|s| s.to_owned()) - .collect(); - Ok(ISrcBlock { lines }) -}); +intermediate!( + ISrcBlock, + &'orig organic::types::SrcBlock<'parse>, + original, + _registry, + { + let lines = original + .get_value() + .split_inclusive('\n') + .map(|s| s.to_owned()) + .collect(); + Ok(ISrcBlock { lines }) + } +); diff --git a/src/intermediate/strike_through.rs b/src/intermediate/strike_through.rs index 430670b..40d0fb5 100644 --- a/src/intermediate/strike_through.rs +++ b/src/intermediate/strike_through.rs @@ -8,14 +8,20 @@ pub(crate) struct IStrikeThrough { pub(crate) children: Vec, } -intermediate!(IStrikeThrough, StrikeThrough, original, registry, { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IObject::new(registry.clone(), obj).await?); - } - ret - }; +intermediate!( + IStrikeThrough, + &'orig organic::types::StrikeThrough<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; - Ok(IStrikeThrough { children }) -}); + Ok(IStrikeThrough { children }) + } +); diff --git a/src/intermediate/table.rs b/src/intermediate/table.rs index aeb7f2f..396d05b 100644 --- a/src/intermediate/table.rs +++ b/src/intermediate/table.rs @@ -8,14 +8,20 @@ pub(crate) struct ITable { pub(crate) children: Vec, } -intermediate!(ITable, Table, original, registry, { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(ITableRow::new(registry.clone(), obj).await?); - } - ret - }; +intermediate!( + ITable, + &'orig organic::types::Table<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(ITableRow::new(registry.clone(), obj).await?); + } + ret + }; - Ok(ITable { children }) -}); + Ok(ITable { children }) + } +); diff --git a/src/intermediate/table_cell.rs b/src/intermediate/table_cell.rs index 212cf02..37691c4 100644 --- a/src/intermediate/table_cell.rs +++ b/src/intermediate/table_cell.rs @@ -8,14 +8,20 @@ pub(crate) struct ITableCell { pub(crate) children: Vec, } -intermediate!(ITableCell, TableCell, original, registry, { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IObject::new(registry.clone(), obj).await?); - } - ret - }; +intermediate!( + ITableCell, + &'orig organic::types::TableCell<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; - Ok(ITableCell { children }) -}); + Ok(ITableCell { children }) + } +); diff --git a/src/intermediate/table_row.rs b/src/intermediate/table_row.rs index 9628d8e..666519d 100644 --- a/src/intermediate/table_row.rs +++ b/src/intermediate/table_row.rs @@ -1,5 +1,4 @@ use super::macros::intermediate; - use super::table_cell::ITableCell; use crate::error::CustomError; @@ -8,14 +7,20 @@ pub(crate) struct ITableRow { pub(crate) children: Vec, } -intermediate!(ITableRow, TableRow, original, registry, { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(ITableCell::new(registry.clone(), obj).await?); - } - ret - }; +intermediate!( + ITableRow, + &'orig organic::types::TableRow<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(ITableCell::new(registry.clone(), obj).await?); + } + ret + }; - Ok(ITableRow { children }) -}); + Ok(ITableRow { children }) + } +); diff --git a/src/intermediate/target.rs b/src/intermediate/target.rs index ea1f3ce..de3b32e 100644 --- a/src/intermediate/target.rs +++ b/src/intermediate/target.rs @@ -8,11 +8,17 @@ pub(crate) struct ITarget { value: String, } -intermediate!(ITarget, Target, original, registry, { - let mut registry = registry.lock().unwrap(); - let id = registry.get_target(original.value); - Ok(ITarget { - id: id.clone(), - value: original.value.to_owned(), - }) -}); +intermediate!( + ITarget, + &'orig organic::types::Target<'parse>, + original, + registry, + { + let mut registry = registry.lock().unwrap(); + let id = registry.get_target(original.value); + Ok(ITarget { + id: id.clone(), + value: original.value.to_owned(), + }) + } +); diff --git a/src/intermediate/underline.rs b/src/intermediate/underline.rs index 2f35db3..1fd1b3c 100644 --- a/src/intermediate/underline.rs +++ b/src/intermediate/underline.rs @@ -8,14 +8,20 @@ pub(crate) struct IUnderline { pub(crate) children: Vec, } -intermediate!(IUnderline, Underline, original, registry, { - let children = { - let mut ret = Vec::new(); - for obj in original.children.iter() { - ret.push(IObject::new(registry.clone(), obj).await?); - } - ret - }; +intermediate!( + IUnderline, + &'orig organic::types::Underline<'parse>, + original, + registry, + { + let children = { + let mut ret = Vec::new(); + for obj in original.children.iter() { + ret.push(IObject::new(registry.clone(), obj).await?); + } + ret + }; - Ok(IUnderline { children }) -}); + Ok(IUnderline { children }) + } +); diff --git a/src/intermediate/verbatim.rs b/src/intermediate/verbatim.rs index cceb972..6e83727 100644 --- a/src/intermediate/verbatim.rs +++ b/src/intermediate/verbatim.rs @@ -7,9 +7,15 @@ pub(crate) struct IVerbatim { pub(crate) contents: String, } -intermediate!(IVerbatim, Verbatim, original, _registry, { - Ok(IVerbatim { - // TODO: Should this coalesce whitespace like PlainText? - contents: original.contents.to_owned(), - }) -}); +intermediate!( + IVerbatim, + &'orig organic::types::Verbatim<'parse>, + original, + _registry, + { + Ok(IVerbatim { + // TODO: Should this coalesce whitespace like PlainText? + contents: original.contents.to_owned(), + }) + } +);