@@ -4,11 +4,14 @@ use std::fmt::Display;
44use std:: fs;
55use std:: io:: { BufRead , Write } ;
66use std:: path:: Path ;
7+ use std:: str:: FromStr ;
78use std:: sync:: LazyLock ;
9+ use std:: time:: { SystemTime , UNIX_EPOCH } ;
810use std:: { cmp, env} ;
911
1012use anstyle:: Style ;
1113use anyhow:: { Context , Result , anyhow} ;
14+ use chrono:: { DateTime , NaiveDate } ;
1215use clap_cargo:: style:: { CONTEXT , ERROR , UPDATE_ADDED , UPDATE_UNCHANGED , UPDATE_UPGRADED } ;
1316use futures_util:: future:: join_all;
1417use git_testament:: { git_testament, render_testament} ;
@@ -17,15 +20,18 @@ use tracing_subscriber::{EnvFilter, Registry, reload::Handle};
1720
1821use crate :: {
1922 config:: Cfg ,
20- dist:: { DistOptions , TargetTuple , ToolchainDesc } ,
23+ dist:: { DistOptions , PartialToolchainDesc , TargetTuple , ToolchainDesc } ,
2124 errors:: RustupError ,
2225 install:: { InstallMethod , UpdateStatus } ,
2326 process:: Process ,
24- toolchain:: { LocalToolchainName , Toolchain , ToolchainName } ,
27+ toolchain:: { DistributableToolchain , LocalToolchainName , Toolchain , ToolchainName } ,
2528 utils:: { self , ExitCode } ,
2629} ;
2730
2831pub ( crate ) const WARN_COMPLETE_PROFILE : & str = "downloading with complete profile isn't recommended unless you are a developer of the rust language" ;
32+ const RELEASE_CYCLE_DAYS : i64 = 42 ;
33+ const NOTIFY_INTERVAL_SECS : u64 = 24 * 60 * 60 ;
34+ const FALLBACK_RELEASE_DATE : & str = "2026-04-17" ;
2935
3036pub ( crate ) fn confirm ( question : & str , default : bool , process : & Process ) -> Result < bool > {
3137 write ! ( process. stdout( ) . lock( ) , "{question} " ) ?;
@@ -569,3 +575,61 @@ pub(super) fn update_console_filter(
569575 . expect ( "error reloading `EnvFilter` for console_logger" ) ;
570576 }
571577}
578+
579+ /// Notifies a user with a hint whenever a new Rust release is available.
580+ /// This is only shown at max once per day and only if not in proxy mode.
581+ pub ( crate ) fn notify_release ( cfg : & Cfg < ' _ > ) -> Result < ( ) > {
582+ if cfg. process . var ( "RUSTUP_RELEASE_HINT" ) . ok ( ) . as_deref ( ) == Some ( "0" ) {
583+ return Ok ( ( ) ) ;
584+ }
585+
586+ let time_now = SystemTime :: now ( )
587+ . duration_since ( UNIX_EPOCH )
588+ . unwrap_or_default ( )
589+ . as_secs ( ) ;
590+
591+ // Limit notifications to at most once per day.
592+ // This is checked before loading the manifest to avoid unnecessary disk I/O.
593+ let last_notified = cfg
594+ . settings_file
595+ . with ( |s| Ok ( s. last_release_notified_secs . unwrap_or ( 0 ) ) ) ?;
596+ if time_now. saturating_sub ( last_notified) < NOTIFY_INTERVAL_SECS {
597+ return Ok ( ( ) ) ;
598+ }
599+
600+ let default_host = cfg. default_host_tuple ( ) ?;
601+ let stable_desc = PartialToolchainDesc :: from_str ( "stable" ) ?. resolve ( & default_host) ?;
602+ let distributable = DistributableToolchain :: new ( cfg, stable_desc)
603+ . map_err ( |e| anyhow ! ( "stable toolchain unavailable: {e}" ) ) ?;
604+ let release_date_str = match distributable. get_manifestation ( ) {
605+ Ok ( manifestation) => match manifestation. load_manifest ( ) {
606+ Ok ( Some ( manifest) ) => manifest. date ,
607+ Ok ( None ) | Err ( _) => FALLBACK_RELEASE_DATE . to_owned ( ) ,
608+ } ,
609+ Err ( _) => FALLBACK_RELEASE_DATE . to_owned ( ) ,
610+ } ;
611+
612+ let today = DateTime :: from_timestamp ( time_now as i64 , 0 )
613+ . unwrap_or_default ( )
614+ . date_naive ( ) ;
615+
616+ let release_date = NaiveDate :: parse_from_str ( & release_date_str, "%Y-%m-%d" )
617+ . map_err ( |e| anyhow ! ( "could not parse release date '{}': {e}" , release_date_str) ) ?;
618+
619+ // Skip the hint if fewer than 6 weeks have passed since the last known release.
620+ if ( today - release_date) . num_days ( ) < RELEASE_CYCLE_DAYS {
621+ return Ok ( ( ) ) ;
622+ }
623+
624+ cfg. settings_file . with_mut ( |s| {
625+ s. last_release_notified_secs = Some ( time_now) ;
626+ Ok ( ( ) )
627+ } ) ?;
628+
629+ writeln ! (
630+ cfg. process. stderr( ) . lock( ) ,
631+ "hint: a new stable Rust release is available. Run `rustup update stable` to install it."
632+ ) ?;
633+
634+ Ok ( ( ) )
635+ }
0 commit comments