@@ -45,6 +45,7 @@ pub struct Blacksmith {
45
45
rustup : Vec < String > ,
46
46
stable_version : Option < String > ,
47
47
platforms : BTreeMap < String , Platform > ,
48
+ previous_stable_versions : Vec < ( String , Vec < String > ) > ,
48
49
}
49
50
50
51
impl Blacksmith {
@@ -123,6 +124,63 @@ impl Blacksmith {
123
124
}
124
125
}
125
126
127
+ let mut stable_version = SemVersion :: from_str ( & blacksmith. stable_version . clone ( ) . unwrap ( ) ) ;
128
+
129
+ // Go backwards in stable versions
130
+ while let Some ( mut previous_stable_version) = stable_version. previous ( ) {
131
+ let channel_url = if previous_stable_version. patch . is_none ( ) {
132
+ // Download https://static.rust-lang.org/dist/channel-rust-{major.minor}.toml
133
+ // and see if we had patch versions of it.
134
+ format ! (
135
+ "{}{}.{}.toml" ,
136
+ CHANNEL_URL_PREFIX ,
137
+ previous_stable_version. major,
138
+ previous_stable_version. minor
139
+ )
140
+ } else {
141
+ // Download https://static.rust-lang.org/dist/channel-rust-{major.minor.patch}.toml and process it.
142
+ format ! (
143
+ "{}{}.{}.{}.toml" ,
144
+ CHANNEL_URL_PREFIX ,
145
+ previous_stable_version. major,
146
+ previous_stable_version. minor,
147
+ previous_stable_version. patch. unwrap( )
148
+ )
149
+ } ;
150
+
151
+ let content = reqwest:: blocking:: get ( & channel_url) ?. text ( ) ?;
152
+ let rust = toml:: from_str :: < crate :: channel:: Channel > ( & content) ?
153
+ . pkg
154
+ . rust ;
155
+
156
+ log:: info!(
157
+ "Found {} targets for stable v{}" ,
158
+ rust. target. len( ) ,
159
+ rust. version
160
+ ) ;
161
+
162
+ let version = rust. version . split ( ' ' ) . next ( ) . unwrap ( ) . to_string ( ) ;
163
+
164
+ let platforms = rust
165
+ . target
166
+ . into_iter ( )
167
+ . filter_map ( |( target, content) | {
168
+ if content. available {
169
+ Some ( target)
170
+ } else {
171
+ None
172
+ }
173
+ } )
174
+ . collect :: < Vec < _ > > ( ) ;
175
+
176
+ blacksmith
177
+ . previous_stable_versions
178
+ . push ( ( version. clone ( ) , platforms) ) ;
179
+
180
+ previous_stable_version = SemVersion :: from_str ( & version) ;
181
+ stable_version = previous_stable_version;
182
+ }
183
+
126
184
blacksmith. last_update = unix_time ( ) ;
127
185
Ok ( blacksmith)
128
186
}
@@ -209,6 +267,45 @@ impl Blacksmith {
209
267
buffer
210
268
}
211
269
270
+ /// Generates tables of links to the previous stable standalone installer packages for
271
+ /// each platform.
272
+ fn generate_previous_stable_standalone_installers_tables ( & self ) -> String {
273
+ let mut buffer = String :: new ( ) ;
274
+
275
+ for ( stable_version, platforms) in & self . previous_stable_versions {
276
+ writeln ! ( buffer, "## Stable ({})" , stable_version) . unwrap ( ) ;
277
+ writeln ! ( buffer, "" ) . unwrap ( ) ;
278
+
279
+ writeln ! ( buffer, "platform | stable ({})" , stable_version) . unwrap ( ) ;
280
+ writeln ! ( buffer, "---------|--------" ) . unwrap ( ) ;
281
+
282
+ for name in platforms {
283
+ let extension = if name. contains ( "windows" ) {
284
+ "msi"
285
+ } else if name. contains ( "darwin" ) {
286
+ "pkg"
287
+ } else {
288
+ "tar.gz"
289
+ } ;
290
+
291
+ let stable_links =
292
+ generate_standalone_links ( "rust" , stable_version, name, extension) ;
293
+
294
+ writeln ! (
295
+ buffer,
296
+ "`{name}` | {stable}" ,
297
+ name = name,
298
+ stable = stable_links,
299
+ )
300
+ . unwrap ( ) ;
301
+ }
302
+
303
+ writeln ! ( buffer, "" ) . unwrap ( ) ;
304
+ }
305
+
306
+ buffer
307
+ }
308
+
212
309
/// Generates a similar table to `generate_standalone_installers_table`
213
310
/// except for the rust source code packages.
214
311
fn generate_source_code_table ( & self ) -> String {
@@ -288,16 +385,23 @@ impl Preprocessor for Blacksmith {
288
385
289
386
let rustup_init_list = self . generate_rustup_init_list ( ) ;
290
387
let standalone_installers_table = self . generate_standalone_installers_table ( ) ;
388
+ let previous_stable_standalone_installers_tables =
389
+ self . generate_previous_stable_standalone_installers_tables ( ) ;
291
390
let source_code_table = self . generate_source_code_table ( ) ;
292
391
293
392
// TODO: Currently we're performing a global search for any of the
294
393
// variables as that's the most flexible for adding more dynamic
295
- // content, and the time to traverse is fast enough to not be noticable .
394
+ // content, and the time to traverse is fast enough to not be noticeable .
296
395
// However if the processing time begins to become a bottleneck this
297
396
// should change.
298
397
for item in & mut book. sections {
299
398
recursive_replace ( item, "{{#rustup_init_list}}" , & rustup_init_list) ;
300
399
recursive_replace ( item, "{{#installer_table}}" , & standalone_installers_table) ;
400
+ recursive_replace (
401
+ item,
402
+ "{{#previous_stable_standalone_installers_tables}}" ,
403
+ & previous_stable_standalone_installers_tables,
404
+ ) ;
301
405
recursive_replace ( item, "{{#source_code_table}}" , & source_code_table) ;
302
406
}
303
407
@@ -310,3 +414,96 @@ impl Preprocessor for Blacksmith {
310
414
true
311
415
}
312
416
}
417
+
418
+ #[ derive( Eq , PartialEq , Debug ) ]
419
+ struct SemVersion {
420
+ major : u32 ,
421
+ minor : u32 ,
422
+ // None represents that we haven't checked the highest patch version of this
423
+ // stable major.minor Rust.
424
+ patch : Option < u32 > ,
425
+ }
426
+
427
+ impl SemVersion {
428
+ fn new_with_patch ( major : u32 , minor : u32 , patch : u32 ) -> Self {
429
+ assert_eq ! ( major, 1 ) ; // We are not planning Rust v2 yet.
430
+ Self {
431
+ major,
432
+ minor,
433
+ patch : Some ( patch) ,
434
+ }
435
+ }
436
+
437
+ fn from_str ( version : & str ) -> Self {
438
+ let stable_version_parts = version
439
+ . split ( '.' )
440
+ . map ( |p| p. parse :: < u32 > ( ) . unwrap ( ) )
441
+ . collect :: < Vec < _ > > ( ) ;
442
+ assert_eq ! ( stable_version_parts. len( ) , 3 ) ;
443
+
444
+ Self :: new_with_patch (
445
+ stable_version_parts[ 0 ] ,
446
+ stable_version_parts[ 1 ] ,
447
+ stable_version_parts[ 2 ] ,
448
+ )
449
+ }
450
+
451
+ fn previous ( & self ) -> Option < Self > {
452
+ if self . patch . is_none ( ) {
453
+ panic ! ( "Cannot get previous version without knowing the current patch number." ) ;
454
+ }
455
+
456
+ // TODO: Figure out how to get manifests for Rust <= 1.7
457
+ if self . major == 1 && self . minor == 8 && self . patch . unwrap ( ) == 0 {
458
+ return None ;
459
+ }
460
+
461
+ if self . patch . is_some ( ) && self . patch . unwrap ( ) > 0 {
462
+ return Some ( Self {
463
+ major : self . major ,
464
+ minor : self . minor ,
465
+ patch : Some ( self . patch . unwrap ( ) - 1 ) ,
466
+ } ) ;
467
+ }
468
+
469
+ if self . minor > 0 {
470
+ return Some ( Self {
471
+ major : self . major ,
472
+ minor : self . minor - 1 ,
473
+ patch : None ,
474
+ } ) ;
475
+ }
476
+
477
+ // We don't plan Rust v2 yet.
478
+ None
479
+ }
480
+ }
481
+
482
+ #[ cfg( test) ]
483
+ mod tests {
484
+ use super :: * ;
485
+
486
+ #[ test]
487
+ fn sem_version_previous_works_with_patch ( ) {
488
+ let latest = SemVersion :: from_str ( "1.68.2" ) ;
489
+ assert_eq ! ( latest, SemVersion :: new_with_patch( 1 , 68 , 2 ) ) ;
490
+ let previous = latest. previous ( ) . unwrap ( ) ;
491
+ assert_eq ! ( previous, SemVersion :: new_with_patch( 1 , 68 , 1 ) ) ;
492
+ let previous = previous. previous ( ) . unwrap ( ) ;
493
+ assert_eq ! ( previous, SemVersion :: new_with_patch( 1 , 68 , 0 ) ) ;
494
+ let previous = previous. previous ( ) . unwrap ( ) ;
495
+ let expected = SemVersion {
496
+ major : 1 ,
497
+ minor : 67 ,
498
+ patch : None ,
499
+ } ;
500
+ assert_eq ! ( previous, expected) ;
501
+ }
502
+
503
+ #[ test]
504
+ fn sem_version_previous_stops_after_1_8 ( ) {
505
+ let latest = SemVersion :: new_with_patch ( 1 , 8 , 0 ) ;
506
+ let previous = latest. previous ( ) ;
507
+ assert_eq ! ( previous, None ) ;
508
+ }
509
+ }
0 commit comments