@@ -5,14 +5,55 @@ use toml::value::Date;
55/// The front matter of a markdown blog post.
66#[ derive( Debug , PartialEq , Serialize , Deserialize ) ]
77pub 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,41 @@ 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 ) -> 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+ "{year}/{month:02}/{day:02}/{slug}" ,
99+ year = date. year,
100+ month = date. month,
101+ day = date. day,
102+ // remove @ suffix, used for disambiguation only in the source
103+ slug = slug. split_once( '@' ) . map( |( s, _) | s) . unwrap_or( slug) ,
104+ ) ;
105+ }
106+ front_matter. aliases = vec ! [ format!( "{}.html" , front_matter. path) ] ;
107+
108+ if front_matter. extra . team . is_some ( ) ^ front_matter. extra . team_url . is_some ( ) {
109+ bail ! ( "extra.team and extra.team_url must always come in a pair" ) ;
110+ }
37111
38112 Ok ( format ! (
39113 "\
@@ -62,8 +136,10 @@ mod tests {
62136 . filter ( |p| p. is_file ( ) && p. file_name ( ) != Some ( "_index.md" . as_ref ( ) ) ) ;
63137
64138 for post in posts {
139+ let slug = post. file_stem ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
140+
65141 let content = fs:: read_to_string ( & post) . unwrap ( ) ;
66- let normalized = normalize ( & content) . unwrap_or_else ( |err| {
142+ let normalized = normalize ( & content, slug ) . unwrap_or_else ( |err| {
67143 panic ! ( "failed to normalize {:?}: {err}" , post. file_name( ) . unwrap( ) ) ;
68144 } ) ;
69145
@@ -98,7 +174,7 @@ The post {post} has abnormal front matter.
98174 │ │
99175 │ You can fix this automatically by running: │
100176 │ │
101- │ FIX_FRONT_MATTER=1 cargo test --all front_matter_is_normalized │
177+ │ FIX_FRONT_MATTER=1 cargo test -p front_matter │
102178 │ │
103179 └──────────────────────────────────────────────────────────────────────────┘
104180" ,
0 commit comments