use serde::Serialize; use super::macros::render; use super::render_context::RenderContext; use crate::context::macros::push_file; 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<String>, newer_link: Option<String>, } impl<'a, 'b> RenderBlogStreamInput<'a, 'b> { pub(crate) fn new( original: &'a [&'b BlogPost], older_link: Option<String>, newer_link: Option<String>, ) -> 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<PageHeader>, children: Vec<RenderBlogStreamEntry>, stream_pagination: Option<RenderBlogStreamPagination>, } 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 .iter() .enumerate() .map(|(i, blog_post)| { RenderBlogStreamEntry::new( render_context.clone(), &RenderBlogStreamEntryInput::new(blog_post, i), ) }) .collect::<Result<Vec<_>, _>>()?; 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<String>, self_link: Option<String>, children: Vec<RenderDocumentElement>, footnotes: Vec<RenderRealFootnoteDefinition>, } 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))?; push_file!(render_context, &index_page.src, { let title = index_page.title.clone(); let children = index_page .children .iter() .map(|child| RenderDocumentElement::new(render_context.clone(), child)) .collect::<Result<Vec<_>, _>>()?; 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<String>, newer_link: Option<String>, } impl RenderBlogStreamPagination { fn new( older_link: Option<String>, newer_link: Option<String>, ) -> Result<RenderBlogStreamPagination, CustomError> { Ok(RenderBlogStreamPagination { older_link, newer_link, }) } }