diff --git a/site_test/layout/default.html b/site_test/layout/default.html index b2e3088..3a5c8fb 100644 --- a/site_test/layout/default.html +++ b/site_test/layout/default.html @@ -26,6 +26,7 @@ {% endblock %} + {% block head %}{% endblock %} diff --git a/site_test/tag.html b/site_test/tag.html index a490d05..eee9799 100644 --- a/site_test/tag.html +++ b/site_test/tag.html @@ -6,16 +6,19 @@ {% block content -%} -

{{ tag_name }} posts

+

Posts tagged ‘{{ tag_name }}’

- +{% for year in years %} +

{{ year }}

+ +{% endfor %} {%- endblock %} diff --git a/src/generator/archive.rs b/src/generator/archive.rs index a654b76..e48d021 100644 --- a/src/generator/archive.rs +++ b/src/generator/archive.rs @@ -69,7 +69,7 @@ impl Rule for Entries { } #[derive(InputVisitable)] -struct PostsByYear(Input>); +pub struct PostsByYear(pub Input>); impl Rule for PostsByYear { type Output = PostsYearMap; fn evaluate(&mut self) -> Self::Output { @@ -81,11 +81,11 @@ impl Rule for PostsByYear { } } -#[derive(PartialEq)] -struct PostsYearMap(HashMap>); +#[derive(PartialEq, Clone)] +pub struct PostsYearMap(pub HashMap>); impl PostsYearMap { - fn years(&self) -> Vec { + pub fn years(&self) -> Vec { let mut years = self.0.keys().cloned().collect::>(); years.sort(); years.reverse(); @@ -94,8 +94,8 @@ impl PostsYearMap { } #[derive(PartialEq, Clone, Serialize)] -struct Entry { - permalink: String, - title: String, - year: i32, +pub struct Entry { + pub permalink: String, + pub title: String, + pub year: i32, } diff --git a/src/generator/css.rs b/src/generator/css.rs index 9c79b7f..a9089ab 100644 --- a/src/generator/css.rs +++ b/src/generator/css.rs @@ -161,6 +161,7 @@ impl<'a> Fs for TrackingFs<'a> { struct ReadFile(#[skip_visit] PathBuf); impl Rule for ReadFile { type Output = String; + fn evaluate(&mut self) -> Self::Output { match std::fs::read(&self.0) { Ok(data) => BASE64_STANDARD.encode(data), @@ -170,4 +171,8 @@ impl Rule for ReadFile { } } } + + fn node_label(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.0.display()) + } } diff --git a/src/generator/rss.rs b/src/generator/rss.rs index 2c8474b..3cf12cc 100644 --- a/src/generator/rss.rs +++ b/src/generator/rss.rs @@ -3,7 +3,6 @@ use compute_graph::{ NodeId, builder::GraphBuilder, input::{DynamicInput, Input, InputVisitable}, - node::NodeValue, rule::{DynamicNodeFactory, DynamicRule, DynamicRuleContext, Rule}, synchronicity::Asynchronous, }; @@ -23,25 +22,22 @@ pub fn make_graph( ) -> Input<()> { let recents = builder.add_dynamic_rule(RecentPosts::new(posts)); let with_rss_html = builder.add_dynamic_rule(ConvertRecentsToRSSContent::new(recents)); + builder.add_rule(RenderRSSFeed(with_rss_html)) } #[derive(InputVisitable)] struct RecentPosts { posts: DynamicInput, - factory: DynamicNodeFactory, } impl RecentPosts { fn new(posts: DynamicInput) -> Self { - Self { - posts, - factory: DynamicNodeFactory::new(), - } + Self { posts } } } impl DynamicRule for RecentPosts { type ChildOutput = ReadPostOutput; - fn evaluate(&mut self, ctx: &mut impl DynamicRuleContext) -> Vec> { + fn evaluate(&mut self, _ctx: &mut impl DynamicRuleContext) -> Vec> { let mut posts = vec![]; for post_input in self.posts().inputs.iter() { if let Some(post) = post_input.value().as_ref() { @@ -50,21 +46,11 @@ impl DynamicRule for RecentPosts { } posts.sort_by_key(|post_and_date| post_and_date.1); posts.reverse(); - for (post_input, _) in posts.into_iter().take(10) { - self.factory.add_node(ctx, post_input.node_id(), |ctx| { - ctx.add_rule(Identity(post_input)) - }); - } - self.factory.all_nodes(ctx) - } -} - -#[derive(InputVisitable)] -struct Identity(Input); -impl Rule for Identity { - type Output = T; - fn evaluate(&mut self) -> Self::Output { - self.input_0().clone() + posts + .into_iter() + .map(|(post_input, _)| post_input) + .take(10) + .collect() } } diff --git a/src/generator/tags.rs b/src/generator/tags.rs index 3c13b27..dad32aa 100644 --- a/src/generator/tags.rs +++ b/src/generator/tags.rs @@ -1,16 +1,19 @@ use std::collections::HashMap; +use chrono::Datelike; use compute_graph::{ builder::GraphBuilder, input::{DynamicInput, Input, InputVisitable}, rule::{DynamicNodeFactory, DynamicRule, Rule}, synchronicity::Asynchronous, }; -use serde::Serialize; use tera::Context; +use crate::generator::archive::PostsByYear; + use super::{ FileWatcher, + archive::{Entry, PostsYearMap}, posts::{ReadPostOutput, metadata::Tag}, templates::{AddTemplate, BuildTemplateContext, RenderTemplate, Templates}, util::{MapDynamicToVoid, content_path}, @@ -39,13 +42,17 @@ pub fn make_graph( #[derive(InputVisitable)] struct MakePostsByTags { posts: DynamicInput, - node_factory: DynamicNodeFactory, + posts_by_tag_factory: DynamicNodeFactory>, + posts_by_year_factory: DynamicNodeFactory, + tag_and_posts_factory: DynamicNodeFactory, } impl MakePostsByTags { fn new(posts: DynamicInput) -> Self { Self { posts, - node_factory: DynamicNodeFactory::new(), + posts_by_tag_factory: DynamicNodeFactory::new(), + posts_by_year_factory: DynamicNodeFactory::new(), + tag_and_posts_factory: DynamicNodeFactory::new(), } } } @@ -64,14 +71,35 @@ impl DynamicRule for MakePostsByTags { } } for (slug, name) in all_tags { - self.node_factory.add_node(ctx, slug.clone(), |ctx| { - ctx.add_rule(PostsByTag { - posts: self.posts.clone(), - tag: Tag { slug, name }, - }) - }); + let tag = Tag { + slug: slug.clone(), + name, + }; + + let posts_for_tag = self + .posts_by_tag_factory + .add_node(ctx, slug.clone(), |ctx| { + ctx.add_rule(PostsByTag { + posts: self.posts.clone(), + tag: tag.clone(), + }) + }); + let posts_by_year = self + .posts_by_year_factory + .add_node(ctx, slug.clone(), |ctx| { + ctx.add_rule(PostsByYear(posts_for_tag)) + }); + self.tag_and_posts_factory + .add_node(ctx, slug.clone(), |ctx| { + ctx.add_rule(CreateTagAndPosts { + posts: posts_by_year, + tag, + }) + }); } - self.node_factory.all_nodes(ctx) + self.posts_by_tag_factory.finalize_nodes(ctx); + self.posts_by_year_factory.finalize_nodes(ctx); + self.tag_and_posts_factory.all_nodes(ctx) } } @@ -82,11 +110,10 @@ struct PostsByTag { tag: Tag, } impl Rule for PostsByTag { - type Output = TagAndPosts; + type Output = Vec; fn evaluate(&mut self) -> Self::Output { - let entries = self - .posts() + self.posts() .inputs .iter() .flat_map(|post_input| { @@ -101,16 +128,13 @@ impl Rule for PostsByTag { Some(Entry { permalink: post.permalink(), title: post.metadata.title.clone(), + year: post.metadata.date.year(), }) } else { None } }) - .collect(); - TagAndPosts { - tag: self.tag.clone(), - entries, - } + .collect() } fn node_label(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -118,16 +142,26 @@ impl Rule for PostsByTag { } } +#[derive(InputVisitable)] +struct CreateTagAndPosts { + posts: Input, + #[skip_visit] + tag: Tag, +} +impl Rule for CreateTagAndPosts { + type Output = TagAndPosts; + fn evaluate(&mut self) -> Self::Output { + TagAndPosts { + tag: self.tag.clone(), + posts: self.posts().clone(), + } + } +} + #[derive(PartialEq, Clone)] struct TagAndPosts { tag: Tag, - entries: Vec, -} - -#[derive(PartialEq, Clone, Serialize)] -struct Entry { - permalink: String, - title: String, + posts: PostsYearMap, } #[derive(InputVisitable)] @@ -165,7 +199,8 @@ impl DynamicRule for MakeWriteTagPages { tag_input.clone(), |tag_and_posts, ctx| { ctx.insert("tag_name", &tag_and_posts.tag.name); - ctx.insert("posts", &tag_and_posts.entries); + ctx.insert("years", &tag_and_posts.posts.years()); + ctx.insert("posts_by_year", &tag_and_posts.posts.0); }, )) }); diff --git a/src/generator/templates.rs b/src/generator/templates.rs index 6ca1348..f4939e8 100644 --- a/src/generator/templates.rs +++ b/src/generator/templates.rs @@ -112,6 +112,7 @@ impl ()> BuildTemplateContext { } impl () + 'static> Rule for BuildTemplateContext { type Output = Context; + fn evaluate(&mut self) -> Self::Output { let mut context = Context::new(); (self.func)(&*self.input.value(), &mut context); @@ -126,6 +127,14 @@ impl () + 'static> Rule for BuildTemplate context.insert("_generated_at", &Local::now()); context } + + fn node_label(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self.permalink { + TemplatePermalink::Constant(s) => write!(f, "{s}"), + TemplatePermalink::ConstantOwned(ref s) => write!(f, "{s}"), + TemplatePermalink::Dynamic(ref inp) => write!(f, "{}", *inp.value()), + } + } } #[derive(InputVisitable)]