@@ -5,14 +5,55 @@ use toml::value::Date;
5
5
/// The front matter of a markdown blog post.
6
6
#[ derive( Debug , PartialEq , Serialize , Deserialize ) ]
7
7
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 ,
10
23
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 > ,
12
30
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) ]
13
37
pub team : Option < String > ,
38
+ /// Moved to the `extra` table.
14
39
#[ serde( default , skip_serializing_if = "std::ops::Not::not" ) ]
15
40
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
+ }
16
57
}
17
58
18
59
/// Extracts the front matter from a markdown file.
@@ -32,8 +73,42 @@ pub fn parse(markdown: &str) -> eyre::Result<(FrontMatter, &str)> {
32
73
}
33
74
34
75
/// 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
+ }
37
112
38
113
Ok ( format ! (
39
114
"\
@@ -62,8 +137,16 @@ mod tests {
62
137
. filter ( |p| p. is_file ( ) && p. file_name ( ) != Some ( "_index.md" . as_ref ( ) ) ) ;
63
138
64
139
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
+
65
148
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| {
67
150
panic ! ( "failed to normalize {:?}: {err}" , post. file_name( ) . unwrap( ) ) ;
68
151
} ) ;
69
152
@@ -98,7 +181,7 @@ The post {post} has abnormal front matter.
98
181
│ │
99
182
│ You can fix this automatically by running: │
100
183
│ │
101
- │ FIX_FRONT_MATTER=1 cargo test --all front_matter_is_normalized │
184
+ │ FIX_FRONT_MATTER=1 cargo test -p front_matter │
102
185
│ │
103
186
└──────────────────────────────────────────────────────────────────────────┘
104
187
" ,
0 commit comments