@@ -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,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