11use anyhow:: { anyhow, Context , Result } ;
2- use std:: { env:: var, fs:: read_to_string, sync:: OnceLock } ;
2+ use once_cell:: sync:: Lazy ;
3+ use std:: {
4+ env:: var,
5+ fs:: { create_dir_all, read_to_string, File , OpenOptions } ,
6+ io:: { stdin, Write } ,
7+ path:: PathBuf ,
8+ sync:: OnceLock ,
9+ } ;
10+
11+ #[ allow( clippy:: unwrap_used) ]
12+ static CONFIG_DIRECTORY : Lazy < PathBuf > = Lazy :: new ( || {
13+ #[ cfg( not( windows) ) ]
14+ {
15+ let base_directories = xdg:: BaseDirectories :: new ( ) . unwrap ( ) ;
16+ base_directories. get_config_file ( "cargo-unmaintained" )
17+ }
18+ #[ cfg( windows) ]
19+ {
20+ let local_app_data = var ( "LOCALAPPDATA" ) . unwrap ( ) ;
21+ PathBuf :: from ( local_app_data) . join ( "cargo-unmaintained" )
22+ }
23+ } ) ;
24+
25+ static TOKEN_PATH : Lazy < PathBuf > = Lazy :: new ( || CONFIG_DIRECTORY . join ( "token.txt" ) ) ;
326
427pub ( super ) static PERSONAL_TOKEN : OnceLock < String > = OnceLock :: new ( ) ;
528
@@ -16,11 +39,19 @@ pub(crate) fn load_token(f: impl FnOnce(String) -> Result<()>) -> Result<bool> {
1639 ) ;
1740 }
1841 token
42+ } else if TOKEN_PATH . try_exists ( ) . with_context ( || {
43+ format ! (
44+ "failed to determine whether `{}` exists" ,
45+ TOKEN_PATH . display( )
46+ )
47+ } ) ? {
48+ read_to_string ( & * TOKEN_PATH ) . with_context ( || format ! ( "failed to read {TOKEN_PATH:?}" ) ) ?
1949 } else {
2050 #[ cfg( __warnings) ]
2151 crate :: warn!(
22- "`GITHUB_TOKEN_PATH` and `GITHUB_TOKEN` are not set; archival statuses will not be \
23- checked",
52+ "`GITHUB_TOKEN_PATH` and `GITHUB_TOKEN` are not set and no file was found at {}; \
53+ archival statuses will not be checked",
54+ TOKEN_PATH . display( )
2455 ) ;
2556 return Ok ( false ) ;
2657 } ;
@@ -31,3 +62,56 @@ pub(crate) fn load_token(f: impl FnOnce(String) -> Result<()>) -> Result<bool> {
3162 f ( token) ?;
3263 Ok ( true )
3364}
65+
66+ pub ( crate ) fn save_token ( ) -> Result < ( ) > {
67+ println ! ( "Please paste a personal access token below. The token needs no scopes." ) ;
68+
69+ let mut buf = String :: new ( ) ;
70+
71+ {
72+ let n = stdin ( )
73+ . read_line ( & mut buf)
74+ . with_context ( || "failed to read stdin" ) ?;
75+ assert_eq ! ( buf. len( ) , n) ;
76+ }
77+
78+ create_dir_all ( & * CONFIG_DIRECTORY ) . with_context ( || "failed to create config directory" ) ?;
79+
80+ let mut file = OpenOptions :: new ( )
81+ . create ( true )
82+ . truncate ( true )
83+ . write ( true )
84+ . open ( & * TOKEN_PATH )
85+ . with_context ( || format ! ( "failed to open `{}`" , TOKEN_PATH . display( ) ) ) ?;
86+ set_permissions ( & file, 0o600 ) ?;
87+ file. write_all ( buf. as_bytes ( ) )
88+ . with_context ( || format ! ( "failed to write `{}`" , TOKEN_PATH . display( ) ) ) ?;
89+
90+ println ! (
91+ "Personal access token written to `{}`" ,
92+ TOKEN_PATH . display( )
93+ ) ;
94+
95+ Ok ( ( ) )
96+ }
97+
98+ type CargoResult < T > = Result < T > ;
99+
100+ // smoelius: The below definitions of `set_permissions` were copied from:
101+ // https://github.com/rust-lang/cargo/blob/1e6828485eea0f550ed7be46ef96107b46aeb162/src/cargo/util/config.rs#L1010-L1024
102+ #[ cfg( unix) ]
103+ #[ cfg_attr( dylint_lib = "try_io_result" , allow( try_io_result) ) ]
104+ fn set_permissions ( file : & File , mode : u32 ) -> CargoResult < ( ) > {
105+ use std:: os:: unix:: fs:: PermissionsExt ;
106+
107+ let mut perms = file. metadata ( ) ?. permissions ( ) ;
108+ perms. set_mode ( mode) ;
109+ file. set_permissions ( perms) ?;
110+ Ok ( ( ) )
111+ }
112+
113+ #[ cfg( not( unix) ) ]
114+ #[ allow( unused, clippy:: unnecessary_wraps) ]
115+ fn set_permissions ( file : & File , mode : u32 ) -> CargoResult < ( ) > {
116+ Ok ( ( ) )
117+ }
0 commit comments