diff --git a/crates/uv-auth/src/credentials.rs b/crates/uv-auth/src/credentials.rs index 9cda2c87c..fee61a476 100644 --- a/crates/uv-auth/src/credentials.rs +++ b/crates/uv-auth/src/credentials.rs @@ -80,10 +80,12 @@ impl Password { Self(password) } + /// Return the [`Password`] as a string slice. pub fn as_str(&self) -> &str { self.0.as_str() } + /// Convert the [`Password`] into its underlying [`String`]. pub fn into_string(self) -> String { self.0 } diff --git a/crates/uv-auth/src/keyring.rs b/crates/uv-auth/src/keyring.rs index e5969c438..c791bbf44 100644 --- a/crates/uv-auth/src/keyring.rs +++ b/crates/uv-auth/src/keyring.rs @@ -159,11 +159,11 @@ impl KeyringProvider { // Validate the request debug_assert!( url.host_str().is_some(), - "Should only use keyring for urls with host" + "Should only use keyring for URLs with host" ); debug_assert!( url.password().is_none(), - "Should only use keyring for urls without a password" + "Should only use keyring for URLs without a password" ); debug_assert!( !username.map(str::is_empty).unwrap_or(false), @@ -286,7 +286,7 @@ impl KeyringProvider { // N.B. We do not show the `service_name` here because we'll show the warning twice // otherwise, once for the URL and once for the realm. warn_user_once!( - "Attempted to fetch credentials using the `keyring` command, but it does not support `--mode creds`; upgrade to `keyring>=v25.2.1` for support or provide a username" + "Attempted to fetch credentials using the `keyring` command, but it does not support `--mode creds`; upgrade to `keyring>=v25.2.1` or provide a username" ); } else if username.is_none() { // If we captured stderr, display it in case it's helpful to the user @@ -314,8 +314,7 @@ impl KeyringProvider { } Err(err) => { warn_user_once!( - "Unable to fetch credentials for {service} from system keyring: {}", - err + "Unable to fetch credentials for {service} from system keyring: {err}" ); } } diff --git a/crates/uv-auth/src/middleware.rs b/crates/uv-auth/src/middleware.rs index 46bffb1d6..3a1b288aa 100644 --- a/crates/uv-auth/src/middleware.rs +++ b/crates/uv-auth/src/middleware.rs @@ -94,7 +94,7 @@ impl Default for TextStoreMode { } impl TextStoreMode { - /// Get the parsed credential store if enabled. + /// Get the parsed credential store, if enabled. fn get(&self) -> Option<&TextCredentialStore> { match self { Self::Automatic(lock) => lock.as_ref(), diff --git a/crates/uv-auth/src/service.rs b/crates/uv-auth/src/service.rs index 0a634de36..227557174 100644 --- a/crates/uv-auth/src/service.rs +++ b/crates/uv-auth/src/service.rs @@ -1,11 +1,12 @@ use serde::{Deserialize, Serialize}; use std::str::FromStr; use thiserror::Error; +use url::Url; use uv_redacted::DisplaySafeUrl; #[derive(Error, Debug)] pub enum ServiceParseError { - #[error("failed to parse URL: {0}")] + #[error(transparent)] InvalidUrl(#[from] url::ParseError), #[error("only HTTPS is supported")] UnsupportedScheme, @@ -31,7 +32,7 @@ impl Service { } /// Validate that the URL scheme is supported. - fn check_scheme(url: &DisplaySafeUrl) -> Result<(), ServiceParseError> { + fn check_scheme(url: &Url) -> Result<(), ServiceParseError> { match url.scheme() { "https" => Ok(()), #[cfg(test)] diff --git a/crates/uv-auth/src/store.rs b/crates/uv-auth/src/store.rs index dd3bbbd3e..91c1df2fb 100644 --- a/crates/uv-auth/src/store.rs +++ b/crates/uv-auth/src/store.rs @@ -18,12 +18,13 @@ use crate::realm::Realm; use crate::service::Service; /// Authentication scheme to use. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum AuthScheme { /// HTTP Basic Authentication /// /// Uses a username and password. + #[default] Basic, /// Bearer token authentication. /// @@ -31,12 +32,6 @@ pub enum AuthScheme { Bearer, } -impl Default for AuthScheme { - fn default() -> Self { - Self::Basic - } -} - /// Errors that can occur when working with TOML credential storage. #[derive(Debug, Error)] pub enum TomlCredentialError { @@ -75,11 +70,11 @@ pub enum BearerAuthError { } /// A single credential entry in a TOML credentials file. -// TODO(zanieb): It's a little clunky that we need don't nest the scheme-specific fields under a +// TODO(zanieb): It's a little clunky that we need don't nest the scheme-specific fields under // that scheme, but I want the username / password case to be easily accessible without // understanding authentication schemes. We should consider a better structure here, e.g., by // adding an internal type that we cast to after validation. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct TomlCredential { /// The service URL for this credential. pub service: Service, @@ -172,10 +167,10 @@ impl TomlCredential { } #[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct TomlCredentials { +struct TomlCredentials { /// Array of credential entries. #[serde(rename = "credential")] - pub credentials: Vec, + credentials: Vec, } /// A credential store with a plain text storage backend. @@ -215,8 +210,8 @@ impl TextCredentialStore { // TODO(zanieb): Determine a better strategy for invalid credential entries if let Err(err) = credential.validate() { debug!( - "Skipping invalid credential for {}: {}", - credential.service, err + "Skipping invalid credential for {}: {err}", + credential.service ); return None; } diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 7a44dcd0d..9a5c112b1 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -5548,7 +5548,7 @@ pub struct AuthLogoutArgs { #[derive(Args)] pub struct AuthLoginArgs { - /// The service to login to. + /// The service to log into. pub service: Service, /// The username to use for the service. diff --git a/crates/uv/src/commands/auth/login.rs b/crates/uv/src/commands/auth/login.rs index 00b98ef51..43e6664cd 100644 --- a/crates/uv/src/commands/auth/login.rs +++ b/crates/uv/src/commands/auth/login.rs @@ -24,15 +24,14 @@ pub(crate) async fn login( printer: Printer, preview: Preview, ) -> Result { - let service_clone = service.clone(); - let url = service_clone.url(); let backend = AuthBackend::from_settings(keyring_provider.as_ref(), preview)?; // If the URL includes a known index URL suffix, strip it // TODO(zanieb): Use a shared abstraction across `login` and `logout`? + let url = service.url().clone(); let (service, url) = match IndexUrl::from(VerbatimUrl::from_url(url.clone())).root() { Some(root) => (Service::try_from(root.clone())?, root), - None => (service, url.clone()), + None => (service, url), }; // Extract credentials from URL if present @@ -125,7 +124,7 @@ pub(crate) async fn login( writeln!( printer.stderr(), "Stored credentials for {}", - display_url.cyan() + display_url.bold().cyan() )?; Ok(ExitStatus::Success) } diff --git a/crates/uv/src/commands/auth/logout.rs b/crates/uv/src/commands/auth/logout.rs index 2fe08712c..9776bb257 100644 --- a/crates/uv/src/commands/auth/logout.rs +++ b/crates/uv/src/commands/auth/logout.rs @@ -23,14 +23,13 @@ pub(crate) async fn logout( printer: Printer, preview: Preview, ) -> Result { - let service_clone = service.clone(); - let url = service_clone.url(); let backend = AuthBackend::from_settings(keyring_provider.as_ref(), preview)?; // TODO(zanieb): Use a shared abstraction across `login` and `logout`? + let url = service.url().clone(); let (service, url) = match IndexUrl::from(VerbatimUrl::from_url(url.clone())).root() { Some(root) => (Service::try_from(root.clone())?, root), - None => (service, url.clone()), + None => (service, url), }; // Extract credentials from URL if present @@ -75,7 +74,7 @@ pub(crate) async fn logout( writeln!( printer.stderr(), "Removed credentials for {}", - display_url.cyan() + display_url.bold().cyan() )?; Ok(ExitStatus::Success) diff --git a/crates/uv/src/commands/auth/token.rs b/crates/uv/src/commands/auth/token.rs index 31aa73d15..92e0a9215 100644 --- a/crates/uv/src/commands/auth/token.rs +++ b/crates/uv/src/commands/auth/token.rs @@ -18,8 +18,8 @@ pub(crate) async fn token( printer: Printer, preview: Preview, ) -> Result { - let url = service.url(); let backend = AuthBackend::from_settings(keyring_provider.as_ref(), preview)?; + let url = service.url(); // Extract credentials from URL if present let url_credentials = Credentials::from_url(url); diff --git a/crates/uv/tests/it/auth.rs b/crates/uv/tests/it/auth.rs index 6484d0a9e..fa4a2daed 100644 --- a/crates/uv/tests/it/auth.rs +++ b/crates/uv/tests/it/auth.rs @@ -815,7 +815,7 @@ fn login_native_keyring_url() { ----- stdout ----- ----- stderr ----- - error: invalid value 'not a valid url' for '': failed to parse URL: invalid international domain name + error: invalid value 'not a valid url' for '': invalid international domain name For more information, try '--help'. "); diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs index 4117d05c2..9fc00b19e 100644 --- a/crates/uv/tests/it/lock.rs +++ b/crates/uv/tests/it/lock.rs @@ -21067,7 +21067,7 @@ fn lock_keyring_credentials_always_authenticate_unsupported_mode() -> Result<()> ----- stdout ----- ----- stderr ----- - warning: Attempted to fetch credentials using the `keyring` command, but it does not support `--mode creds`; upgrade to `keyring>=v25.2.1` for support or provide a username + warning: Attempted to fetch credentials using the `keyring` command, but it does not support `--mode creds`; upgrade to `keyring>=v25.2.1` or provide a username error: Failed to fetch: `https://pypi-proxy.fly.dev/basic-auth/simple/iniconfig/` Caused by: Missing credentials for https://pypi-proxy.fly.dev/basic-auth/simple/iniconfig/ "); diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 4e3c05d4a..19b9a01a9 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -63,7 +63,7 @@ uv auth login [OPTIONS]

Arguments

-
SERVICE

The service to login to

+
SERVICE

The service to log into

Options