@@ -28,8 +28,8 @@ pub struct FrontMatter {
28
28
#[ serde( default ) ]
29
29
pub authors : Vec < String > ,
30
30
pub description : Option < String > ,
31
- /// Used to generate redirects from the old URL scheme to preserve
32
- /// permalinks.
31
+ /// Used for `releases/X.XX.X` redirects and ones from the old URL scheme to
32
+ /// preserve permalinks (e.g. slug.html => slug/) .
33
33
#[ serde( default ) ]
34
34
pub aliases : Vec < String > ,
35
35
/// Moved to the `extra` table.
@@ -113,12 +113,26 @@ pub fn normalize(
113
113
bail ! ( "extra.team and extra.team_url must always come in a pair" ) ;
114
114
}
115
115
116
+ if front_matter. extra . release && !front_matter. aliases . iter ( ) . any ( |a| a. contains ( "releases" ) ) {
117
+ // Make sure release posts have a matching `releases/X.XX.X` alias.
118
+ let version = guess_version_from_path ( & front_matter. path ) . unwrap_or ( "?.??.?" . into ( ) ) ;
119
+ front_matter. aliases . push ( format ! ( "releases/{version}" ) ) ;
120
+ }
121
+
116
122
let serialized = toml:: to_string_pretty ( & front_matter) ?;
117
123
let deserialized = toml:: from_str ( & serialized) ?;
118
124
119
125
Ok ( deserialized)
120
126
}
121
127
128
+ fn guess_version_from_path ( path : & str ) -> Option < String > {
129
+ let mut iter = path. split ( [ '-' , '.' ] ) . filter_map ( |s| s. parse :: < u32 > ( ) . ok ( ) ) ;
130
+ let major = iter. next ( ) ?;
131
+ let minor = iter. next ( ) ?;
132
+ let patch = iter. next ( ) . unwrap_or ( 0 ) ; // some posts omit the patch version
133
+ Some ( format ! ( "{major}.{minor}.{patch}" ) )
134
+ }
135
+
122
136
#[ cfg( test) ]
123
137
mod tests {
124
138
use std:: { env, fs, path:: PathBuf } ;
0 commit comments