natter/src/context/blog_stream.rs
2025-02-08 19:14:08 -05:00

217 lines
6.0 KiB
Rust

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,
})
}
}