@@ -5,7 +5,7 @@ use fs_err as fs;
55use rustc_hash:: FxHashMap ;
66use serde:: { Deserialize , Serialize } ;
77use thiserror:: Error ;
8- use uv_fs:: { LockedFile , with_added_extension} ;
8+ use uv_fs:: { LockedFile , LockedFileError , LockedFileMode , with_added_extension} ;
99use uv_preview:: { Preview , PreviewFeatures } ;
1010use uv_redacted:: DisplaySafeUrl ;
1111
@@ -28,20 +28,24 @@ pub enum AuthBackend {
2828}
2929
3030impl AuthBackend {
31- pub fn from_settings ( preview : Preview ) -> Result < Self , TomlCredentialError > {
31+ pub async fn from_settings ( preview : Preview ) -> Result < Self , TomlCredentialError > {
3232 // If preview is enabled, we'll use the system-native store
3333 if preview. is_enabled ( PreviewFeatures :: NATIVE_AUTH ) {
3434 return Ok ( Self :: System ( KeyringProvider :: native ( ) ) ) ;
3535 }
3636
3737 // Otherwise, we'll use the plaintext credential store
3838 let path = TextCredentialStore :: default_file ( ) ?;
39- match TextCredentialStore :: read ( & path) {
39+ match TextCredentialStore :: read ( & path) . await {
4040 Ok ( ( store, lock) ) => Ok ( Self :: TextStore ( store, lock) ) ,
41- Err ( TomlCredentialError :: Io ( err) ) if err. kind ( ) == std:: io:: ErrorKind :: NotFound => {
41+ Err ( err)
42+ if err
43+ . as_io_error ( )
44+ . is_some_and ( |err| err. kind ( ) == std:: io:: ErrorKind :: NotFound ) =>
45+ {
4246 Ok ( Self :: TextStore (
4347 TextCredentialStore :: default ( ) ,
44- TextCredentialStore :: lock ( & path) ?,
48+ TextCredentialStore :: lock ( & path) . await ?,
4549 ) )
4650 }
4751 Err ( err) => Err ( err) ,
@@ -69,6 +73,8 @@ pub enum AuthScheme {
6973pub enum TomlCredentialError {
7074 #[ error( transparent) ]
7175 Io ( #[ from] std:: io:: Error ) ,
76+ #[ error( transparent) ]
77+ LockedFile ( #[ from] LockedFileError ) ,
7278 #[ error( "Failed to parse TOML credential file: {0}" ) ]
7379 ParseError ( #[ from] toml:: de:: Error ) ,
7480 #[ error( "Failed to serialize credentials to TOML" ) ]
@@ -83,6 +89,21 @@ pub enum TomlCredentialError {
8389 TokenNotUnicode ( #[ from] std:: string:: FromUtf8Error ) ,
8490}
8591
92+ impl TomlCredentialError {
93+ pub fn as_io_error ( & self ) -> Option < & std:: io:: Error > {
94+ match self {
95+ Self :: Io ( err) => Some ( err) ,
96+ Self :: LockedFile ( err) => err. as_io_error ( ) ,
97+ Self :: ParseError ( _)
98+ | Self :: SerializeError ( _)
99+ | Self :: BasicAuthError ( _)
100+ | Self :: BearerAuthError ( _)
101+ | Self :: CredentialsDirError
102+ | Self :: TokenNotUnicode ( _) => None ,
103+ }
104+ }
105+ }
106+
86107#[ derive( Debug , Error ) ]
87108pub enum BasicAuthError {
88109 #[ error( "`username` is required with `scheme = basic`" ) ]
@@ -233,12 +254,12 @@ impl TextCredentialStore {
233254 }
234255
235256 /// Acquire a lock on the credentials file at the given path.
236- pub fn lock ( path : & Path ) -> Result < LockedFile , TomlCredentialError > {
257+ pub async fn lock ( path : & Path ) -> Result < LockedFile , TomlCredentialError > {
237258 if let Some ( parent) = path. parent ( ) {
238259 fs:: create_dir_all ( parent) ?;
239260 }
240261 let lock = with_added_extension ( path, ".lock" ) ;
241- Ok ( LockedFile :: acquire_blocking ( lock, "credentials store" ) ?)
262+ Ok ( LockedFile :: acquire ( lock, LockedFileMode :: Exclusive , "credentials store" ) . await ?)
242263 }
243264
244265 /// Read credentials from a file.
@@ -269,8 +290,8 @@ impl TextCredentialStore {
269290 /// Returns [`TextCredentialStore`] and a [`LockedFile`] to hold if mutating the store.
270291 ///
271292 /// If the store will not be written to following the read, the lock can be dropped.
272- pub fn read < P : AsRef < Path > > ( path : P ) -> Result < ( Self , LockedFile ) , TomlCredentialError > {
273- let lock = Self :: lock ( path. as_ref ( ) ) ?;
293+ pub async fn read < P : AsRef < Path > > ( path : P ) -> Result < ( Self , LockedFile ) , TomlCredentialError > {
294+ let lock = Self :: lock ( path. as_ref ( ) ) . await ?;
274295 let store = Self :: from_file ( path) ?;
275296 Ok ( ( store, lock) )
276297 }
@@ -450,8 +471,8 @@ mod tests {
450471 assert ! ( store. get_credentials( & url, None ) . is_none( ) ) ;
451472 }
452473
453- #[ test]
454- fn test_file_operations ( ) {
474+ #[ tokio :: test]
475+ async fn test_file_operations ( ) {
455476 let mut temp_file = NamedTempFile :: new ( ) . unwrap ( ) ;
456477 writeln ! (
457478 temp_file,
@@ -487,7 +508,7 @@ password = "pass2"
487508 store
488509 . write (
489510 temp_output. path ( ) ,
490- TextCredentialStore :: lock ( temp_file. path ( ) ) . unwrap ( ) ,
511+ TextCredentialStore :: lock ( temp_file. path ( ) ) . await . unwrap ( ) ,
491512 )
492513 . unwrap ( ) ;
493514
0 commit comments