mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Use ref-cast for DisplaySafeUrl
(#13696)
By default, Rust does not support safe cast from `&U` to `&T` for `#[repr(transparent)] T(U)` even if the newtype opts in. The dtolnay ref-cast crate fills this gap, allowing to remove `DisplaySafeUrlRef`.
This commit is contained in:
parent
410dc33574
commit
de64f1dfa8
8 changed files with 97 additions and 137 deletions
39
Cargo.lock
generated
39
Cargo.lock
generated
|
@ -728,7 +728,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1094,7 +1094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1920,7 +1920,7 @@ checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37"
|
|||
dependencies = [
|
||||
"hermit-abi 0.4.0",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1980,7 +1980,7 @@ dependencies = [
|
|||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2854,7 +2854,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2966,6 +2966,26 @@ dependencies = [
|
|||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
|
||||
dependencies = [
|
||||
"ref-cast-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast-impl"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reflink-copy"
|
||||
version = "0.1.26"
|
||||
|
@ -3283,7 +3303,7 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3296,7 +3316,7 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.2",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3864,7 +3884,7 @@ dependencies = [
|
|||
"getrandom 0.3.1",
|
||||
"once_cell",
|
||||
"rustix 1.0.7",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5577,6 +5597,7 @@ dependencies = [
|
|||
name = "uv-redacted"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"ref-cast",
|
||||
"schemars",
|
||||
"serde",
|
||||
"url",
|
||||
|
@ -6187,7 +6208,7 @@ version = "0.1.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -138,6 +138,7 @@ procfs = { version = "0.17.0", default-features = false, features = ["flate2"] }
|
|||
pubgrub = { git = "https://github.com/astral-sh/pubgrub", rev = "06ec5a5f59ffaeb6cf5079c6cb184467da06c9db" }
|
||||
quote = { version = "1.0.37" }
|
||||
rayon = { version = "1.10.0" }
|
||||
ref-cast = { version = "1.0.24" }
|
||||
reflink-copy = { version = "0.1.19" }
|
||||
regex = { version = "1.10.6" }
|
||||
regex-automata = { version = "0.4.8", default-features = false, features = ["dfa-build", "dfa-search", "perf", "std", "syntax"] }
|
||||
|
|
|
@ -4,7 +4,6 @@ use base64::write::EncoderWriter;
|
|||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use uv_redacted::DisplaySafeUrl;
|
||||
use uv_redacted::DisplaySafeUrlRef;
|
||||
|
||||
use netrc::Netrc;
|
||||
use reqwest::Request;
|
||||
|
@ -145,7 +144,7 @@ impl Credentials {
|
|||
/// If a username is provided, it must match the login in the netrc file or [`None`] is returned.
|
||||
pub(crate) fn from_netrc(
|
||||
netrc: &Netrc,
|
||||
url: DisplaySafeUrlRef<'_>,
|
||||
url: &DisplaySafeUrl,
|
||||
username: Option<&str>,
|
||||
) -> Option<Self> {
|
||||
let host = url.host_str()?;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{io::Write, process::Stdio};
|
||||
use tokio::process::Command;
|
||||
use tracing::{instrument, trace, warn};
|
||||
use uv_redacted::DisplaySafeUrlRef;
|
||||
use uv_redacted::DisplaySafeUrl;
|
||||
use uv_warnings::warn_user_once;
|
||||
|
||||
use crate::credentials::Credentials;
|
||||
|
@ -36,11 +36,7 @@ impl KeyringProvider {
|
|||
/// Returns [`None`] if no password was found for the username or if any errors
|
||||
/// are encountered in the keyring backend.
|
||||
#[instrument(skip_all, fields(url = % url.to_string(), username))]
|
||||
pub async fn fetch(
|
||||
&self,
|
||||
url: DisplaySafeUrlRef<'_>,
|
||||
username: Option<&str>,
|
||||
) -> Option<Credentials> {
|
||||
pub async fn fetch(&self, url: &DisplaySafeUrl, username: Option<&str>) -> Option<Credentials> {
|
||||
// Validate the request
|
||||
debug_assert!(
|
||||
url.host_str().is_some(),
|
||||
|
@ -229,7 +225,7 @@ mod tests {
|
|||
let keyring = KeyringProvider::empty();
|
||||
// Panics due to debug assertion; returns `None` in production
|
||||
let result = std::panic::AssertUnwindSafe(
|
||||
keyring.fetch(DisplaySafeUrlRef::from(&url), Some("user")),
|
||||
keyring.fetch(DisplaySafeUrl::ref_cast(&url), Some("user")),
|
||||
)
|
||||
.catch_unwind()
|
||||
.await;
|
||||
|
@ -242,7 +238,7 @@ mod tests {
|
|||
let keyring = KeyringProvider::empty();
|
||||
// Panics due to debug assertion; returns `None` in production
|
||||
let result = std::panic::AssertUnwindSafe(
|
||||
keyring.fetch(DisplaySafeUrlRef::from(&url), Some(url.username())),
|
||||
keyring.fetch(DisplaySafeUrl::ref_cast(&url), Some(url.username())),
|
||||
)
|
||||
.catch_unwind()
|
||||
.await;
|
||||
|
@ -255,7 +251,7 @@ mod tests {
|
|||
let keyring = KeyringProvider::empty();
|
||||
// Panics due to debug assertion; returns `None` in production
|
||||
let result = std::panic::AssertUnwindSafe(
|
||||
keyring.fetch(DisplaySafeUrlRef::from(&url), Some(url.username())),
|
||||
keyring.fetch(DisplaySafeUrl::ref_cast(&url), Some(url.username())),
|
||||
)
|
||||
.catch_unwind()
|
||||
.await;
|
||||
|
@ -265,7 +261,7 @@ mod tests {
|
|||
#[tokio::test]
|
||||
async fn fetch_url_no_auth() {
|
||||
let url = Url::parse("https://example.com").unwrap();
|
||||
let url = DisplaySafeUrlRef::from(&url);
|
||||
let url = DisplaySafeUrl::ref_cast(&url);
|
||||
let keyring = KeyringProvider::empty();
|
||||
let credentials = keyring.fetch(url, Some("user"));
|
||||
assert!(credentials.await.is_none());
|
||||
|
@ -277,7 +273,7 @@ mod tests {
|
|||
let keyring = KeyringProvider::dummy([(url.host_str().unwrap(), "user", "password")]);
|
||||
assert_eq!(
|
||||
keyring
|
||||
.fetch(DisplaySafeUrlRef::from(&url), Some("user"))
|
||||
.fetch(DisplaySafeUrl::ref_cast(&url), Some("user"))
|
||||
.await,
|
||||
Some(Credentials::basic(
|
||||
Some("user".to_string()),
|
||||
|
@ -287,7 +283,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
keyring
|
||||
.fetch(
|
||||
DisplaySafeUrlRef::from(&url.join("test").unwrap()),
|
||||
DisplaySafeUrl::ref_cast(&url.join("test").unwrap()),
|
||||
Some("user")
|
||||
)
|
||||
.await,
|
||||
|
@ -303,7 +299,7 @@ mod tests {
|
|||
let url = Url::parse("https://example.com").unwrap();
|
||||
let keyring = KeyringProvider::dummy([("other.com", "user", "password")]);
|
||||
let credentials = keyring
|
||||
.fetch(DisplaySafeUrlRef::from(&url), Some("user"))
|
||||
.fetch(DisplaySafeUrl::ref_cast(&url), Some("user"))
|
||||
.await;
|
||||
assert_eq!(credentials, None);
|
||||
}
|
||||
|
@ -318,7 +314,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
keyring
|
||||
.fetch(
|
||||
DisplaySafeUrlRef::from(&url.join("foo").unwrap()),
|
||||
DisplaySafeUrl::ref_cast(&url.join("foo").unwrap()),
|
||||
Some("user")
|
||||
)
|
||||
.await,
|
||||
|
@ -329,7 +325,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
keyring
|
||||
.fetch(DisplaySafeUrlRef::from(&url), Some("user"))
|
||||
.fetch(DisplaySafeUrl::ref_cast(&url), Some("user"))
|
||||
.await,
|
||||
Some(Credentials::basic(
|
||||
Some("user".to_string()),
|
||||
|
@ -339,7 +335,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
keyring
|
||||
.fetch(
|
||||
DisplaySafeUrlRef::from(&url.join("bar").unwrap()),
|
||||
DisplaySafeUrl::ref_cast(&url.join("bar").unwrap()),
|
||||
Some("user")
|
||||
)
|
||||
.await,
|
||||
|
@ -355,7 +351,7 @@ mod tests {
|
|||
let url = Url::parse("https://example.com").unwrap();
|
||||
let keyring = KeyringProvider::dummy([(url.host_str().unwrap(), "user", "password")]);
|
||||
let credentials = keyring
|
||||
.fetch(DisplaySafeUrlRef::from(&url), Some("user"))
|
||||
.fetch(DisplaySafeUrl::ref_cast(&url), Some("user"))
|
||||
.await;
|
||||
assert_eq!(
|
||||
credentials,
|
||||
|
@ -370,7 +366,7 @@ mod tests {
|
|||
async fn fetch_url_no_username() {
|
||||
let url = Url::parse("https://example.com").unwrap();
|
||||
let keyring = KeyringProvider::dummy([(url.host_str().unwrap(), "user", "password")]);
|
||||
let credentials = keyring.fetch(DisplaySafeUrlRef::from(&url), None).await;
|
||||
let credentials = keyring.fetch(DisplaySafeUrl::ref_cast(&url), None).await;
|
||||
assert_eq!(
|
||||
credentials,
|
||||
Some(Credentials::basic(
|
||||
|
@ -385,14 +381,14 @@ mod tests {
|
|||
let url = Url::parse("https://example.com").unwrap();
|
||||
let keyring = KeyringProvider::dummy([(url.host_str().unwrap(), "foo", "password")]);
|
||||
let credentials = keyring
|
||||
.fetch(DisplaySafeUrlRef::from(&url), Some("bar"))
|
||||
.fetch(DisplaySafeUrl::ref_cast(&url), Some("bar"))
|
||||
.await;
|
||||
assert_eq!(credentials, None);
|
||||
|
||||
// Still fails if we have `foo` in the URL itself
|
||||
let url = Url::parse("https://foo@example.com").unwrap();
|
||||
let credentials = keyring
|
||||
.fetch(DisplaySafeUrlRef::from(&url), Some("bar"))
|
||||
.fetch(DisplaySafeUrl::ref_cast(&url), Some("bar"))
|
||||
.await;
|
||||
assert_eq!(credentials, None);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use anyhow::{anyhow, format_err};
|
||||
use http::{Extensions, StatusCode};
|
||||
use uv_redacted::{DisplaySafeUrl, DisplaySafeUrlRef};
|
||||
use netrc::Netrc;
|
||||
use reqwest::{Request, Response};
|
||||
use reqwest_middleware::{Error, Middleware, Next};
|
||||
use tracing::{debug, trace, warn};
|
||||
|
||||
use crate::{
|
||||
CREDENTIALS_CACHE, CredentialsCache, KeyringProvider,
|
||||
|
@ -10,11 +14,7 @@ use crate::{
|
|||
index::{AuthPolicy, Indexes},
|
||||
realm::Realm,
|
||||
};
|
||||
use anyhow::{anyhow, format_err};
|
||||
use netrc::Netrc;
|
||||
use reqwest::{Request, Response};
|
||||
use reqwest_middleware::{Error, Middleware, Next};
|
||||
use tracing::{debug, trace, warn};
|
||||
use uv_redacted::DisplaySafeUrl;
|
||||
|
||||
/// Strategy for loading netrc files.
|
||||
enum NetrcMode {
|
||||
|
@ -274,7 +274,7 @@ impl Middleware for AuthMiddleware {
|
|||
trace!("Checking for credentials for {url}");
|
||||
(request, None)
|
||||
};
|
||||
let retry_request_url = DisplaySafeUrlRef::from(retry_request.url());
|
||||
let retry_request_url = DisplaySafeUrl::ref_cast(retry_request.url());
|
||||
|
||||
let username = credentials
|
||||
.as_ref()
|
||||
|
@ -283,13 +283,13 @@ impl Middleware for AuthMiddleware {
|
|||
let credentials = if let Some(index_url) = maybe_index_url {
|
||||
self.cache().get_url(index_url, &username).or_else(|| {
|
||||
self.cache()
|
||||
.get_realm(Realm::from(&*retry_request_url), username)
|
||||
.get_realm(Realm::from(&**retry_request_url), username)
|
||||
})
|
||||
} else {
|
||||
// Since there is no known index for this URL, check if there are credentials in
|
||||
// the realm-level cache.
|
||||
self.cache()
|
||||
.get_realm(Realm::from(&*retry_request_url), username)
|
||||
.get_realm(Realm::from(&**retry_request_url), username)
|
||||
}
|
||||
.or(credentials);
|
||||
|
||||
|
@ -433,7 +433,7 @@ impl AuthMiddleware {
|
|||
} else if let Some(credentials) = self
|
||||
.fetch_credentials(
|
||||
Some(&credentials),
|
||||
DisplaySafeUrlRef::from(request.url()),
|
||||
DisplaySafeUrl::ref_cast(request.url()),
|
||||
index_url,
|
||||
auth_policy,
|
||||
)
|
||||
|
@ -468,7 +468,7 @@ impl AuthMiddleware {
|
|||
async fn fetch_credentials(
|
||||
&self,
|
||||
credentials: Option<&Credentials>,
|
||||
url: DisplaySafeUrlRef<'_>,
|
||||
url: &DisplaySafeUrl,
|
||||
maybe_index_url: Option<&DisplaySafeUrl>,
|
||||
auth_policy: AuthPolicy,
|
||||
) -> Option<Arc<Credentials>> {
|
||||
|
@ -481,7 +481,7 @@ impl AuthMiddleware {
|
|||
let key = if let Some(index_url) = maybe_index_url {
|
||||
(FetchUrl::Index(index_url.clone()), username)
|
||||
} else {
|
||||
(FetchUrl::Realm(Realm::from(&*url)), username)
|
||||
(FetchUrl::Realm(Realm::from(&**url)), username)
|
||||
};
|
||||
if !self.cache().fetches.register(key.clone()) {
|
||||
let credentials = self
|
||||
|
@ -529,7 +529,7 @@ impl AuthMiddleware {
|
|||
if let Some(username) = credentials.and_then(|credentials| credentials.username()) {
|
||||
if let Some(index_url) = maybe_index_url {
|
||||
debug!("Checking keyring for credentials for index URL {}@{}", username, index_url);
|
||||
keyring.fetch(DisplaySafeUrlRef::from(index_url), Some(username)).await
|
||||
keyring.fetch(DisplaySafeUrl::ref_cast(index_url), Some(username)).await
|
||||
} else {
|
||||
debug!("Checking keyring for credentials for full URL {}@{}", username, url);
|
||||
keyring.fetch(url, Some(username)).await
|
||||
|
@ -539,7 +539,7 @@ impl AuthMiddleware {
|
|||
debug!(
|
||||
"Checking keyring for credentials for index URL {index_url} without username due to `authenticate = always`"
|
||||
);
|
||||
keyring.fetch(DisplaySafeUrlRef::from(index_url), None).await
|
||||
keyring.fetch(DisplaySafeUrl::ref_cast(index_url), None).await
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ doctest = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
ref-cast = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
serde = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use ref_cast::RefCast;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
@ -37,9 +38,10 @@ use url::Url;
|
|||
/// assert_eq!(url.username(), "");
|
||||
/// assert_eq!(url.password(), None);
|
||||
/// ```
|
||||
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize, RefCast)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[cfg_attr(feature = "schemars", schemars(transparent))]
|
||||
#[repr(transparent)]
|
||||
pub struct DisplaySafeUrl(Url);
|
||||
|
||||
impl DisplaySafeUrl {
|
||||
|
@ -48,6 +50,12 @@ impl DisplaySafeUrl {
|
|||
Ok(Self(Url::parse(input)?))
|
||||
}
|
||||
|
||||
/// Cast a `&Url` to a `&DisplaySafeUrl` using ref-cast.
|
||||
#[inline]
|
||||
pub fn ref_cast(url: &Url) -> &Self {
|
||||
RefCast::ref_cast(url)
|
||||
}
|
||||
|
||||
/// Parse a string as an URL, with this URL as the base URL.
|
||||
#[inline]
|
||||
pub fn join(&self, input: &str) -> Result<Self, url::ParseError> {
|
||||
|
@ -119,7 +127,28 @@ impl Display for DisplaySafeUrl {
|
|||
|
||||
impl Debug for DisplaySafeUrl {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
debug_with_redacted_credentials(&self.0, f)
|
||||
let url = &self.0;
|
||||
let (username, password) = if url.username() != "" && url.password().is_some() {
|
||||
(url.username(), Some("****"))
|
||||
} else if url.username() != "" {
|
||||
("****", None)
|
||||
} else if url.password().is_some() {
|
||||
("", Some("****"))
|
||||
} else {
|
||||
("", None)
|
||||
};
|
||||
|
||||
f.debug_struct("DisplaySafeUrl")
|
||||
.field("scheme", &url.scheme())
|
||||
.field("cannot_be_a_base", &url.cannot_be_a_base())
|
||||
.field("username", &username)
|
||||
.field("password", &password)
|
||||
.field("host", &url.host())
|
||||
.field("port", &url.port())
|
||||
.field("path", &url.path())
|
||||
.field("query", &url.query())
|
||||
.field("fragment", &url.fragment())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,93 +208,6 @@ fn display_with_redacted_credentials(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn debug_with_redacted_credentials(url: &Url, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let (username, password) = if url.username() != "" && url.password().is_some() {
|
||||
(url.username(), Some("****"))
|
||||
} else if url.username() != "" {
|
||||
("****", None)
|
||||
} else if url.password().is_some() {
|
||||
("", Some("****"))
|
||||
} else {
|
||||
("", None)
|
||||
};
|
||||
|
||||
f.debug_struct("DisplaySafeUrl")
|
||||
.field("scheme", &url.scheme())
|
||||
.field("cannot_be_a_base", &url.cannot_be_a_base())
|
||||
.field("username", &username)
|
||||
.field("password", &password)
|
||||
.field("host", &url.host())
|
||||
.field("port", &url.port())
|
||||
.field("path", &url.path())
|
||||
.field("query", &url.query())
|
||||
.field("fragment", &url.fragment())
|
||||
.finish()
|
||||
}
|
||||
|
||||
/// A wrapper around a [`url::Url`] ref that safely handles credentials for
|
||||
/// logging purposes.
|
||||
///
|
||||
/// Uses the same underlying [`Display`] implementation as [`DisplaySafeUrl`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use uv_redacted::DisplaySafeUrl;
|
||||
/// use std::str::FromStr;
|
||||
///
|
||||
/// // Create from a `url::Url` ref
|
||||
/// let url = Url::parse("https://user:password@example.com").unwrap();
|
||||
/// let log_safe_url = DisplaySafeUrlRef::from(&url);
|
||||
///
|
||||
/// // Display will mask secrets
|
||||
/// assert_eq!(url.to_string(), "https://user:****@example.com/");
|
||||
///
|
||||
/// // Since `DisplaySafeUrlRef` provides full access to the underlying `Url` through a
|
||||
/// // `Deref` implementation, you can still access the username and password
|
||||
/// assert_eq!(url.username(), "user");
|
||||
/// assert_eq!(url.password(), Some("password"));
|
||||
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
pub struct DisplaySafeUrlRef<'a>(&'a Url);
|
||||
|
||||
impl<'a> Deref for DisplaySafeUrlRef<'a> {
|
||||
type Target = Url;
|
||||
|
||||
fn deref(&self) -> &'a Self::Target {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DisplaySafeUrlRef<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
display_with_redacted_credentials(self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DisplaySafeUrlRef<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
debug_with_redacted_credentials(self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Url> for DisplaySafeUrlRef<'a> {
|
||||
fn from(url: &'a Url) -> Self {
|
||||
DisplaySafeUrlRef(url)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a DisplaySafeUrl> for DisplaySafeUrlRef<'a> {
|
||||
fn from(url: &'a DisplaySafeUrl) -> Self {
|
||||
DisplaySafeUrlRef(url)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<DisplaySafeUrlRef<'a>> for DisplaySafeUrl {
|
||||
fn from(url: DisplaySafeUrlRef<'a>) -> Self {
|
||||
DisplaySafeUrl(url.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -366,7 +308,7 @@ mod tests {
|
|||
fn log_safe_url_ref() {
|
||||
let url_str = "https://user:pass@pypi-proxy.fly.dev/basic-auth/simple";
|
||||
let url = Url::parse(url_str).unwrap();
|
||||
let log_safe_url = DisplaySafeUrlRef::from(&url);
|
||||
let log_safe_url = DisplaySafeUrl::ref_cast(&url);
|
||||
assert_eq!(log_safe_url.username(), "user");
|
||||
assert!(log_safe_url.password().is_some_and(|p| p == "pass"));
|
||||
assert_eq!(
|
||||
|
|
|
@ -16,7 +16,7 @@ use uv_distribution_types::{Index, IndexCapabilities, IndexLocations, IndexUrl};
|
|||
use uv_publish::{
|
||||
CheckUrlClient, TrustedPublishResult, check_trusted_publishing, files_for_publishing, upload,
|
||||
};
|
||||
use uv_redacted::{DisplaySafeUrl, DisplaySafeUrlRef};
|
||||
use uv_redacted::DisplaySafeUrl;
|
||||
use uv_warnings::warn_user_once;
|
||||
|
||||
use crate::commands::reporters::PublishReporter;
|
||||
|
@ -296,7 +296,7 @@ async fn gather_credentials(
|
|||
if let Some(username) = &username {
|
||||
debug!("Fetching password from keyring");
|
||||
if let Some(keyring_password) = keyring_provider
|
||||
.fetch(DisplaySafeUrlRef::from(&publish_url), Some(username))
|
||||
.fetch(DisplaySafeUrl::ref_cast(&publish_url), Some(username))
|
||||
.await
|
||||
.as_ref()
|
||||
.and_then(|credentials| credentials.password())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue