Skip to content

Commit d2beba8

Browse files
committed
Add Zola migration for front matter
The front matter is defined in such a way that it can parse both the previous and the new format that's required for Zola. An automatic migration can be triggered with: FIX_FRONT_MATTER=1 cargo test -p front_matter which will be done in a separate commit.
1 parent 63a1799 commit d2beba8

File tree

4 files changed

+101
-12
lines changed

4 files changed

+101
-12
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,14 @@ that it's something that will eventually be accepted.
4848
When writing a new blog post, keep in mind the file headers:
4949
```md
5050
+++
51-
layout = "post"
52-
date = 2015-03-15
51+
path = "2015/03/15/some-slug"
5352
title = "Title of the blog post"
54-
author = "Blog post author (or on behalf of which team)"
53+
authors = ["Blog post author (or on behalf of which team)"]
54+
description = "(optional)"
55+
56+
[extra] # optional section
57+
team = "Team Name" # if post is made on behalf of a team
58+
team_url = "https://www.rust-lang.org/governance/teams/..." # required if team is set
5559
release = true # (to be only used for official posts about Rust releases announcements)
5660
+++
5761
```

content/_index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
+++
22
title = "Rust Blog"
3-
index_title = "The Rust Programming Language Blog"
43
description = "Empowering everyone to build reliable and efficient software."
4+
[extra]
5+
index_title = "The Rust Programming Language Blog"
56
index_html = """
67
This is the <b>main Rust blog</b>. \
78
<a href="https://www.rust-lang.org/governance/">Rust teams</a> \

content/inside-rust/_index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
+++
22
title = "Inside Rust Blog"
3-
index_title = 'The "Inside Rust" Blog'
43
description = "Want to follow along with Rust development? Curious how you might get involved? Take a look!"
4+
[extra]
5+
index_title = 'The "Inside Rust" Blog'
56
index_html = """
67
This is the <b>"Inside Rust"</b> blog. This blog is aimed at those who wish \
78
to follow along with Rust development. The various \

front_matter/src/lib.rs

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,55 @@ use toml::value::Date;
55
/// The front matter of a markdown blog post.
66
#[derive(Debug, PartialEq, Serialize, Deserialize)]
77
pub struct FrontMatter {
8-
pub layout: String,
9-
pub date: Date,
8+
/// Deprecated. The plan was probably to have more specialized templates
9+
/// at some point. That didn't materialize, all posts are rendered with the
10+
/// same template. Once we migrate to Zola, this can be achieved with the
11+
/// "template" key.
12+
#[serde(default, skip_serializing)]
13+
pub layout: Option<String>,
14+
/// Deprecated. Zola doesn't do any path templating based on things like
15+
/// the date. So, in order to preserve our URL structure (YYYY/MM/DD/...)
16+
/// we have to set the path explicitly. Duplicating the date would
17+
/// be inconvenient for content authors who need to keep the date of
18+
/// publication updated.
19+
#[serde(default, skip_serializing)]
20+
pub date: Option<Date>,
21+
#[serde(default)]
22+
pub path: String,
1023
pub title: String,
11-
pub author: String,
24+
/// Deprecated. Zola uses an "authors" key with an array instead. The front
25+
/// matter tests can do the migration automatically.
26+
#[serde(default, skip_serializing)]
27+
pub author: Option<String>,
28+
#[serde(default)]
29+
pub authors: Vec<String>,
1230
pub description: Option<String>,
31+
/// Used to generate redirects from the old URL scheme to preserve
32+
/// permalinks.
33+
#[serde(default)]
34+
pub aliases: Vec<String>,
35+
/// Moved to the `extra` table.
36+
#[serde(default, skip_serializing)]
1337
pub team: Option<String>,
38+
/// Moved to the `extra` table.
1439
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
1540
pub release: bool,
41+
#[serde(default, skip_serializing_if = "Extra::is_empty")]
42+
pub extra: Extra,
43+
}
44+
45+
#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
46+
pub struct Extra {
47+
pub team: Option<String>,
48+
pub team_url: Option<String>,
49+
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
50+
pub release: bool,
51+
}
52+
53+
impl Extra {
54+
fn is_empty(&self) -> bool {
55+
self.team.is_none() && !self.release
56+
}
1657
}
1758

1859
/// Extracts the front matter from a markdown file.
@@ -32,8 +73,42 @@ pub fn parse(markdown: &str) -> eyre::Result<(FrontMatter, &str)> {
3273
}
3374

3475
/// Normalizes the front matter of a markdown file.
35-
pub fn normalize(markdown: &str) -> eyre::Result<String> {
36-
let (front_matter, content) = parse(markdown)?;
76+
pub fn normalize(markdown: &str, slug: &str, inside_rust: bool) -> eyre::Result<String> {
77+
let (mut front_matter, content) = parse(markdown)?;
78+
79+
// migrate "author" to "authors" key
80+
if let Some(author) = front_matter.author.take() {
81+
front_matter.authors = vec![author];
82+
}
83+
// migrate "team" to "extra" section
84+
if let Some(team) = front_matter.team.take() {
85+
let (team, url) = team.split_once(" <").unwrap();
86+
let url = url.strip_suffix('>').unwrap();
87+
front_matter.extra.team = Some(team.into());
88+
front_matter.extra.team_url = Some(url.into());
89+
}
90+
// migrate "release" to "extra" section
91+
if front_matter.release {
92+
front_matter.release = false;
93+
front_matter.extra.release = true;
94+
}
95+
// migrate "date" to "path" key
96+
if let Some(date) = front_matter.date.take() {
97+
front_matter.path = format!(
98+
"{inside_rust}{year}/{month:02}/{day:02}/{slug}",
99+
inside_rust = if inside_rust { "inside-rust/" } else { "" },
100+
year = date.year,
101+
month = date.month,
102+
day = date.day,
103+
// remove @ suffix, used for disambiguation only in the source
104+
slug = slug.split_once('@').map(|(s, _)| s).unwrap_or(slug),
105+
);
106+
}
107+
front_matter.aliases = vec![format!("{}.html", front_matter.path)];
108+
109+
if front_matter.extra.team.is_some() ^ front_matter.extra.team_url.is_some() {
110+
bail!("extra.team and extra.team_url must always come in a pair");
111+
}
37112

38113
Ok(format!(
39114
"\
@@ -62,8 +137,16 @@ mod tests {
62137
.filter(|p| p.is_file() && p.file_name() != Some("_index.md".as_ref()));
63138

64139
for post in posts {
140+
let slug = post.file_stem().unwrap().to_str().unwrap();
141+
142+
let inside_rust = post
143+
.as_os_str()
144+
.to_str()
145+
.unwrap()
146+
.contains("content/inside-rust/");
147+
65148
let content = fs::read_to_string(&post).unwrap();
66-
let normalized = normalize(&content).unwrap_or_else(|err| {
149+
let normalized = normalize(&content, slug, inside_rust).unwrap_or_else(|err| {
67150
panic!("failed to normalize {:?}: {err}", post.file_name().unwrap());
68151
});
69152

@@ -98,7 +181,7 @@ The post {post} has abnormal front matter.
98181
│ │
99182
│ You can fix this automatically by running: │
100183
│ │
101-
│ FIX_FRONT_MATTER=1 cargo test --all front_matter_is_normalized
184+
FIX_FRONT_MATTER=1 cargo test -p front_matter
102185
│ │
103186
└──────────────────────────────────────────────────────────────────────────┘
104187
",

0 commit comments

Comments
 (0)