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)]