diff --git a/src/blogs.rs b/src/blogs.rs index abb1f332a..69990888b 100644 --- a/src/blogs.rs +++ b/src/blogs.rs @@ -37,12 +37,12 @@ pub struct Blog { maintained_by: String, index_html: String, #[serde(serialize_with = "add_postfix_slash")] - prefix: PathBuf, - posts: Vec, + path: PathBuf, + pages: Vec, } impl Blog { - fn load(prefix: PathBuf, dir: &Path) -> eyre::Result { + fn load(path: PathBuf, dir: &Path) -> eyre::Result { let manifest_content = std::fs::read_to_string(dir.join(MANIFEST_FILE))? .strip_prefix("+++\n") .unwrap() @@ -94,8 +94,8 @@ impl Blog { maintained_by: manifest.maintained_by, index_html: manifest.index_html, link_text: manifest.link_text, - prefix, - posts, + path, + pages: posts, }) } @@ -111,12 +111,12 @@ impl Blog { &self.index_title } - pub(crate) fn prefix(&self) -> &Path { - &self.prefix + pub(crate) fn path(&self) -> &Path { + &self.path } pub(crate) fn posts(&self) -> &[Post] { - &self.posts + &self.pages } } @@ -139,10 +139,10 @@ fn load_recursive(base: &Path, current: &Path, blogs: &mut Vec) -> eyre::R let file_name = path.file_name().and_then(|n| n.to_str()); if let (Some(file_name), Some(parent)) = (file_name, path.parent()) { if file_name == MANIFEST_FILE { - let prefix = parent + let path = parent .strip_prefix(base) .map_or_else(|_| PathBuf::new(), Path::to_path_buf); - blogs.push(Blog::load(prefix, parent)?); + blogs.push(Blog::load(path, parent)?); } } } diff --git a/src/lib.rs b/src/lib.rs index 2ff5adb22..862165047 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,6 @@ use rayon::prelude::*; use sass_rs::{Options, compile_file}; use serde::Serialize; use serde_json::{Value, json}; -use std::collections::HashMap; use std::fs::{self, File}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; @@ -33,53 +32,12 @@ struct ReleasePost { url: String, } -fn month_name(month_num: &Value, _args: &HashMap) -> tera::Result { - let month_num = month_num - .as_u64() - .expect("month_num should be an unsigned integer"); - let name = match month_num { - 1 => "Jan.", - 2 => "Feb.", - 3 => "Mar.", - 4 => "Apr.", - 5 => "May", - 6 => "June", - 7 => "July", - 8 => "Aug.", - 9 => "Sept.", - 10 => "Oct.", - 11 => "Nov.", - 12 => "Dec.", - _ => panic!("invalid month! ({month_num})"), - }; - Ok(name.into()) -} - -// Tera and Handlebars escape HTML differently by default. -// Tera: &<>"'/ -// Handlebars: &<>"'`= -// To make the transition testable, this function escapes just like Handlebars. -fn escape_hbs(input: &Value, _args: &HashMap) -> tera::Result { - let input = input.as_str().expect("input should be a string"); - Ok(input - .replace("&", "&") - .replace("<", "<") - .replace(">", ">") - .replace("\"", """) - .replace("'", "'") - .replace("`", "`") - .replace("=", "=") - .into()) -} - impl Generator { fn new( out_directory: impl AsRef, posts_directory: impl AsRef, ) -> eyre::Result { let mut tera = Tera::new("templates/*")?; - tera.register_filter("month_name", month_name); - tera.register_filter("escape_hbs", escape_hbs); tera.autoescape_on(vec![]); // disable auto-escape for .html templates Ok(Generator { tera, @@ -148,7 +106,7 @@ impl Generator { } fn render_blog(&self, blog: &Blog) -> eyre::Result<()> { - std::fs::create_dir_all(self.out_directory.join(blog.prefix()))?; + std::fs::create_dir_all(self.out_directory.join(blog.path()))?; let path = self.render_index(blog)?; @@ -177,24 +135,24 @@ impl Generator { .map(|other_blog| { json!({ "link_text": other_blog.link_text(), - "url": other_blog.prefix().join("index.html"), + "url": other_blog.path().join("index.html"), }) }) .collect(); let data = json!({ "title": blog.index_title(), - "blog": blog, + "section": blog, "other_blogs": other_blogs, }); - let path = blog.prefix().join("index.html"); + let path = blog.path().join("index.html"); self.render_template(&path, "index.html", data)?; Ok(path) } fn render_post(&self, blog: &Blog, post: &Post) -> eyre::Result { let path = blog - .prefix() + .path() .join(format!("{:04}", &post.year)) .join(format!("{:02}", &post.month)) .join(format!("{:02}", &post.day)); @@ -206,8 +164,8 @@ impl Generator { let data = json!({ "title": format!("{} | {}", post.title, blog.title()), - "blog": blog, - "post": post, + "section": blog, + "page": post, }); let path = path.join(filename); @@ -218,12 +176,12 @@ impl Generator { fn render_feed(&self, blog: &Blog) -> eyre::Result<()> { let posts: Vec<_> = blog.posts().iter().take(10).collect(); let data = json!({ - "blog": blog, - "posts": posts, + "section": blog, + "pages": posts, "feed_updated": chrono::Utc::now().with_nanosecond(0).unwrap().to_rfc3339(), }); - self.render_template(blog.prefix().join("feed.xml"), "feed.xml", data)?; + self.render_template(blog.path().join("feed.xml"), "feed.xml", data)?; Ok(()) } @@ -235,8 +193,8 @@ impl Generator { .map(|post| ReleasePost { title: post.title.clone(), url: blog - .prefix() - .join(post.url.clone()) + .path() + .join(post.path.clone()) .to_string_lossy() .to_string(), }) @@ -246,7 +204,7 @@ impl Generator { feed_updated: chrono::Utc::now().with_nanosecond(0).unwrap().to_rfc3339(), }; fs::write( - self.out_directory.join(blog.prefix()).join("releases.json"), + self.out_directory.join(blog.path()).join("releases.json"), serde_json::to_string(&data)?, )?; Ok(()) diff --git a/src/posts.rs b/src/posts.rs index f94d8b8e9..7d0090c32 100644 --- a/src/posts.rs +++ b/src/posts.rs @@ -19,7 +19,7 @@ pub struct Post { pub(crate) month: u8, pub(crate) day: u8, pub(crate) contents: String, - pub(crate) url: String, + pub(crate) path: String, pub(crate) published: String, pub(crate) updated: String, pub(crate) release: bool, @@ -122,7 +122,7 @@ impl Post { month, day, contents, - url, + path: url, published, updated, release, diff --git a/templates/feed.xml b/templates/feed.xml index 7adb07787..6c43ebe29 100644 --- a/templates/feed.xml +++ b/templates/feed.xml @@ -1,28 +1,29 @@ +{% import "macros.html" as macros %} - {{blog.title}} - - - https://blog.rust-lang.org/{{blog.prefix}} - {{blog.title}} - {{blog.description}} + {{ section.title }} + + + https://blog.rust-lang.org/{{ section.path }} + {{ section.title }} + {{ section.description }} - Maintained by {{blog.maintained_by}}. + Maintained by {{ section.maintained_by }}. https://github.com/rust-lang/blog.rust-lang.org/ {{feed_updated}} - {% for post in posts %} + {% for page in pages %} - {{post.title | escape_hbs}} - - {{post.published | escape_hbs}} - {{post.updated | escape_hbs}} - https://blog.rust-lang.org/{{blog.prefix}}{{post.url | escape_hbs}} - {{post.contents | escape_hbs}} + {{ macros::escape_hbs(input=page.title) }} + + {{ macros::escape_hbs(input=page.published) }} + {{ macros::escape_hbs(input=page.updated) }} + https://blog.rust-lang.org/{{ section.path }}{{ macros::escape_hbs(input=page.path) }} + {{ macros::escape_hbs(input=page.contents) }} - {{post.author | escape_hbs}} + {{ macros::escape_hbs(input=page.author) }} {%- endfor %} diff --git a/templates/headers.html b/templates/headers.html index dd5e0291c..7be58147d 100644 --- a/templates/headers.html +++ b/templates/headers.html @@ -1,15 +1,15 @@ -{% macro headers(title, blog) -%} +{% macro headers(title, section) -%} - - + + - - + + @@ -36,7 +36,7 @@ - + diff --git a/templates/index.html b/templates/index.html index fabccbf68..73d6e9266 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,9 +1,10 @@ +{% import "macros.html" as macros %} {% extends "layout.html" %} {% block page %}
-

{{blog.index_html}}

+

{{ section.index_html }}

@@ -11,7 +12,7 @@

See also: {%- for other in other_blogs %} - {{other.link_text | escape_hbs}} + {{ macros::escape_hbs(input=other.link_text) }} {%- endfor %}

@@ -22,14 +23,14 @@
- {%- for post in blog.posts %} - {% if post.show_year %} + {%- for page in section.pages %} + {% if page.show_year %} - + {% endif %} - - + + {%- endfor %}

Posts in {{post.year}}

Posts in {{ page.year }}

{{post.month | month_name}} {{post.day}}{{post.title | escape_hbs}}{{ macros::month_name(num=page.month) }} {{ page.day }}{{ macros::escape_hbs(input=page.title) }}
diff --git a/templates/layout.html b/templates/layout.html index 5e8edd71b..2488029cc 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -1,3 +1,4 @@ +{% import "macros.html" as macros %} {% import "headers.html" as headers %} {% import "nav.html" as nav %} {% import "footer.html" as footer %} @@ -5,13 +6,13 @@ - {{ title | escape_hbs }} + {{ macros::escape_hbs(input=title) }} - {{ headers::headers(title=title, blog=blog) | indent(prefix=" ", blank=true) }} + {{ headers::headers(title=title, section=section) | indent(prefix=" ", blank=true) }} - {{ nav::nav(blog=blog) | indent(prefix=" ", blank=true) }} + {{ nav::nav(section=section) | indent(prefix=" ", blank=true) }} {%- block page %}{% endblock page %} {{ footer::footer() | indent(prefix=" ", blank=true) }} diff --git a/templates/macros.html b/templates/macros.html new file mode 100644 index 000000000..d14cffea4 --- /dev/null +++ b/templates/macros.html @@ -0,0 +1,35 @@ +{% macro month_name(num) %} + {%- if num == 1 %}Jan. + {%- elif num == 2 %}Feb. + {%- elif num == 3 %}Mar. + {%- elif num == 4 %}Apr. + {%- elif num == 5 %}May + {%- elif num == 6 %}June + {%- elif num == 7 %}July + {%- elif num == 8 %}Aug. + {%- elif num == 9 %}Sept. + {%- elif num == 10 %}Oct. + {%- elif num == 11 %}Nov. + {%- elif num == 12 %}Dec. + {%- else %}{{ throw(message="invalid month! " ~ num) }} + {%- endif %} +{%- endmacro month_name %} + +{# + The blog templates used to be written in Handlebars, but Tera and Handlebars + escape HTML differently by default: + Tera: &<>"'/ + Handlebars: &<>"'`= + To keep the output identical, this macro matches the behavior of Handlebars. +#} +{% macro escape_hbs(input) -%} + {{ input + | replace(from="&", to="&") + | replace(from="<", to="<") + | replace(from=">", to=">") + | replace(from='"', to=""") + | replace(from="'", to="'") + | replace(from="`", to="`") + | replace(from="=", to="=") + }} +{%- endmacro escape_hbs %} diff --git a/templates/nav.html b/templates/nav.html index 296974b7f..8759904f4 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -1,9 +1,9 @@ -{% macro nav(blog) -%} +{% macro nav(section) -%}