Fix posts not being written to new path on permalink changing
This commit is contained in:
parent
657f90c39c
commit
55b91944b2
@ -210,7 +210,8 @@ impl Rule for ReadPost {
|
||||
struct MakeWritePosts {
|
||||
posts: DynamicInput<ReadPostOutput>,
|
||||
article_template: Input<Templates>,
|
||||
unwrapped_factory: DynamicNodeFactory<PathBuf, Post<HtmlContent>>,
|
||||
permalink_factory: DynamicNodeFactory<PathBuf, String>,
|
||||
output_path_factory: DynamicNodeFactory<PathBuf, PathBuf>,
|
||||
build_context_factory: DynamicNodeFactory<PathBuf, Context>,
|
||||
render_factory: DynamicNodeFactory<PathBuf, ()>,
|
||||
}
|
||||
@ -219,7 +220,8 @@ impl MakeWritePosts {
|
||||
Self {
|
||||
posts,
|
||||
article_template: templates,
|
||||
unwrapped_factory: DynamicNodeFactory::new(),
|
||||
permalink_factory: DynamicNodeFactory::new(),
|
||||
output_path_factory: DynamicNodeFactory::new(),
|
||||
build_context_factory: DynamicNodeFactory::new(),
|
||||
render_factory: DynamicNodeFactory::new(),
|
||||
}
|
||||
@ -230,11 +232,17 @@ impl DynamicRule for MakeWritePosts {
|
||||
fn evaluate(&mut self, ctx: &mut impl DynamicRuleContext) -> Vec<Input<Self::ChildOutput>> {
|
||||
for post_input in self.posts.value().inputs.iter() {
|
||||
if let Some(post) = post_input.value().as_ref() {
|
||||
let permalink = self
|
||||
.permalink_factory
|
||||
.add_node(ctx, post.path.clone(), |ctx| {
|
||||
ctx.add_rule(PostPermalink(post_input.clone()))
|
||||
});
|
||||
|
||||
let context = self
|
||||
.build_context_factory
|
||||
.add_node(ctx, post.path.clone(), |ctx| {
|
||||
ctx.add_rule(BuildTemplateContext::new(
|
||||
post.permalink().into(),
|
||||
permalink.clone().into(),
|
||||
post_input.clone(),
|
||||
|post_opt, ctx| {
|
||||
let post = post_opt.as_ref().unwrap();
|
||||
@ -244,24 +252,49 @@ impl DynamicRule for MakeWritePosts {
|
||||
))
|
||||
});
|
||||
|
||||
let mut output_path = PathBuf::from(post.permalink());
|
||||
output_path.push("index.html");
|
||||
let output_path =
|
||||
self.output_path_factory
|
||||
.add_node(ctx, post.path.clone(), |ctx| {
|
||||
ctx.add_rule(PostOutputPath(permalink))
|
||||
});
|
||||
|
||||
self.render_factory.add_node(ctx, post.path.clone(), |ctx| {
|
||||
ctx.add_rule(RenderTemplate {
|
||||
name: "article",
|
||||
output_path,
|
||||
output_path: output_path.into(),
|
||||
templates: self.article_template.clone(),
|
||||
context,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
self.unwrapped_factory.finalize_nodes(ctx);
|
||||
self.permalink_factory.finalize_nodes(ctx);
|
||||
self.output_path_factory.finalize_nodes(ctx);
|
||||
self.build_context_factory.finalize_nodes(ctx);
|
||||
self.render_factory.all_nodes(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(InputVisitable)]
|
||||
struct PostPermalink(Input<ReadPostOutput>);
|
||||
impl Rule for PostPermalink {
|
||||
type Output = String;
|
||||
fn evaluate(&mut self) -> Self::Output {
|
||||
self.input_0().as_ref().unwrap().permalink()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(InputVisitable)]
|
||||
struct PostOutputPath(Input<String>);
|
||||
impl Rule for PostOutputPath {
|
||||
type Output = PathBuf;
|
||||
fn evaluate(&mut self) -> Self::Output {
|
||||
let mut path = PathBuf::from(&*self.input_0());
|
||||
path.push("index.html");
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
/// Flattens Vec<Option<Post<HtmlContent>>> into Vec<Post<HtmlContent>>
|
||||
#[derive(InputVisitable)]
|
||||
struct AllPosts(DynamicInput<ReadPostOutput>);
|
||||
|
@ -56,11 +56,12 @@ impl DynamicRule for MakePostsByTags {
|
||||
) -> Vec<Input<Self::ChildOutput>> {
|
||||
let mut all_tags = HashMap::new();
|
||||
for post_input in self.posts().inputs.iter() {
|
||||
let post = post_input.value();
|
||||
for tag in post.as_ref().unwrap().metadata.tags.iter().flatten() {
|
||||
if let Some(post) = post_input.value().as_ref() {
|
||||
for tag in post.metadata.tags.iter().flatten() {
|
||||
all_tags.insert(tag.slug.clone(), tag.name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (slug, name) in all_tags {
|
||||
self.node_factory.add_node(ctx, slug.clone(), |ctx| {
|
||||
ctx.add_rule(PostsByTag {
|
||||
@ -87,10 +88,14 @@ impl Rule for PostsByTag {
|
||||
.inputs
|
||||
.iter()
|
||||
.flat_map(|post_input| {
|
||||
let post_ = post_input.value();
|
||||
let post = post_.as_ref().unwrap();
|
||||
let mut tags = post.metadata.tags.iter().flatten();
|
||||
if tags.any(|t| t.slug == self.tag.slug) {
|
||||
if let Some(post) = post_input.value().as_ref()
|
||||
&& post
|
||||
.metadata
|
||||
.tags
|
||||
.iter()
|
||||
.flatten()
|
||||
.any(|t| t.slug == self.tag.slug)
|
||||
{
|
||||
Some(Entry {
|
||||
permalink: post.permalink(),
|
||||
title: post.metadata.title.clone(),
|
||||
|
@ -1,5 +1,9 @@
|
||||
use std::{borrow::Cow, path::PathBuf, time::SystemTime};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
use chrono::Local;
|
||||
use compute_graph::{
|
||||
builder::GraphBuilder,
|
||||
node::NodeValue,
|
||||
@ -82,14 +86,13 @@ static CB: Lazy<u64> = Lazy::new(|| {
|
||||
.as_secs()
|
||||
});
|
||||
|
||||
#[derive(InputVisitable)]
|
||||
pub struct BuildTemplateContext<T, F> {
|
||||
permalink: Cow<'static, str>,
|
||||
permalink: TemplatePermalink,
|
||||
input: Input<T>,
|
||||
func: F,
|
||||
}
|
||||
impl<T, F: Fn(&T, &mut Context) -> ()> BuildTemplateContext<T, F> {
|
||||
pub fn new(permalink: Cow<'static, str>, input: Input<T>, func: F) -> Self {
|
||||
pub fn new(permalink: TemplatePermalink, input: Input<T>, func: F) -> Self {
|
||||
Self {
|
||||
permalink,
|
||||
input,
|
||||
@ -97,46 +100,122 @@ impl<T, F: Fn(&T, &mut Context) -> ()> BuildTemplateContext<T, F> {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T, F> InputVisitable for BuildTemplateContext<T, F> {
|
||||
fn visit_inputs(&self, visitor: &mut impl compute_graph::rule::InputVisitor) {
|
||||
if let TemplatePermalink::Dynamic(ref input) = self.permalink {
|
||||
visitor.visit(input);
|
||||
}
|
||||
visitor.visit(&self.input);
|
||||
}
|
||||
}
|
||||
impl<T: 'static, F: Fn(&T, &mut Context) -> () + 'static> Rule for BuildTemplateContext<T, F> {
|
||||
type Output = Context;
|
||||
fn evaluate(&mut self) -> Self::Output {
|
||||
let mut context = Context::new();
|
||||
(self.func)(&*self.input(), &mut context);
|
||||
(self.func)(&*self.input.value(), &mut context);
|
||||
context.insert("_domain", &*DOMAIN);
|
||||
context.insert("_permalink", &self.permalink);
|
||||
match &self.permalink {
|
||||
TemplatePermalink::Constant(s) => context.insert("_permalink", s),
|
||||
TemplatePermalink::ConstantOwned(s) => context.insert("_permalink", s),
|
||||
TemplatePermalink::Dynamic(input) => context.insert("_permalink", &*input.value()),
|
||||
}
|
||||
context.insert("_stylesheet_cache_buster", &*CB);
|
||||
context.insert("_development", &cfg!(debug_assertions));
|
||||
context.insert("_generated_at", &Local::now());
|
||||
context
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(InputVisitable)]
|
||||
pub enum TemplatePermalink {
|
||||
Constant(&'static str),
|
||||
ConstantOwned(String),
|
||||
Dynamic(Input<String>),
|
||||
}
|
||||
impl From<&'static str> for TemplatePermalink {
|
||||
fn from(value: &'static str) -> Self {
|
||||
Self::Constant(value)
|
||||
}
|
||||
}
|
||||
impl From<String> for TemplatePermalink {
|
||||
fn from(value: String) -> Self {
|
||||
Self::ConstantOwned(value)
|
||||
}
|
||||
}
|
||||
impl From<Input<String>> for TemplatePermalink {
|
||||
fn from(value: Input<String>) -> Self {
|
||||
Self::Dynamic(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RenderTemplate {
|
||||
pub name: &'static str,
|
||||
pub output_path: PathBuf,
|
||||
pub output_path: TemplateOutputPath,
|
||||
pub templates: Input<Templates>,
|
||||
pub context: Input<Context>,
|
||||
}
|
||||
// TODO: derive InputVisitable for enums?
|
||||
// make Input impl InputVisitable, then the derived impl can just call visit_inputs on everything
|
||||
impl InputVisitable for RenderTemplate {
|
||||
fn visit_inputs(&self, visitor: &mut impl compute_graph::rule::InputVisitor) {
|
||||
if let TemplateOutputPath::Dynamic(ref input) = self.output_path {
|
||||
visitor.visit(input);
|
||||
}
|
||||
visitor.visit(&self.templates);
|
||||
visitor.visit(&self.context);
|
||||
}
|
||||
}
|
||||
impl Rule for RenderTemplate {
|
||||
type Output = ();
|
||||
|
||||
fn evaluate(&mut self) -> Self::Output {
|
||||
let templates = self.templates();
|
||||
let templates = self.templates.value();
|
||||
assert!(templates.tera.get_template_names().any(|n| n == self.name));
|
||||
let writer = output_writer(&self.output_path).expect("output writer");
|
||||
let path: &Path = match self.output_path {
|
||||
TemplateOutputPath::Constant(ref p) => p,
|
||||
TemplateOutputPath::Dynamic(ref input) => &input.value(),
|
||||
};
|
||||
let writer = output_writer(path).expect("output writer");
|
||||
let result = templates
|
||||
.tera
|
||||
.render_to(&self.name, &*self.context(), writer);
|
||||
.render_to(&self.name, &*self.context.value(), writer);
|
||||
if let Err(e) = result {
|
||||
error!(
|
||||
"Error rendering template to {:?}: {:?}",
|
||||
&self.output_path, e
|
||||
);
|
||||
error!("Error rendering template to {path:?}: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn node_label(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", self.output_path.display())
|
||||
match self.output_path {
|
||||
TemplateOutputPath::Constant(ref p) => write!(f, "{}", p.display()),
|
||||
TemplateOutputPath::Dynamic(ref input) => write!(f, "{}", input.value().display()),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub enum TemplateOutputPath {
|
||||
Constant(PathBuf),
|
||||
Dynamic(Input<PathBuf>),
|
||||
}
|
||||
impl<'a> From<&'a str> for TemplateOutputPath {
|
||||
fn from(value: &'a str) -> Self {
|
||||
Self::Constant(value.into())
|
||||
}
|
||||
}
|
||||
// TODO: i feel like it should be possible to unify this impl with the &'a str one above
|
||||
impl From<String> for TemplateOutputPath {
|
||||
fn from(value: String) -> Self {
|
||||
Self::Constant(value.into())
|
||||
}
|
||||
}
|
||||
impl From<PathBuf> for TemplateOutputPath {
|
||||
fn from(value: PathBuf) -> Self {
|
||||
Self::Constant(value)
|
||||
}
|
||||
}
|
||||
impl From<Input<PathBuf>> for TemplateOutputPath {
|
||||
fn from(value: Input<PathBuf>) -> Self {
|
||||
Self::Dynamic(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod filters {
|
||||
use chrono::{DateTime, Datelike, Local};
|
||||
use serde::Deserialize;
|
||||
|
Loading…
x
Reference in New Issue
Block a user