mirror of https://github.com/astral-sh/uv
114 lines
3.4 KiB
Rust
114 lines
3.4 KiB
Rust
use std::fmt::{self, Display, Formatter};
|
|
|
|
use rustc_hash::FxHashSet;
|
|
use url::Url;
|
|
use uv_redacted::DisplaySafeUrl;
|
|
|
|
/// When to use authentication.
|
|
#[derive(
|
|
Copy,
|
|
Clone,
|
|
Debug,
|
|
Default,
|
|
Hash,
|
|
Eq,
|
|
PartialEq,
|
|
Ord,
|
|
PartialOrd,
|
|
serde::Serialize,
|
|
serde::Deserialize,
|
|
)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
|
pub enum AuthPolicy {
|
|
/// Authenticate when necessary.
|
|
///
|
|
/// If credentials are provided, they will be used. Otherwise, an unauthenticated request will
|
|
/// be attempted first. If the request fails, uv will search for credentials. If credentials are
|
|
/// found, an authenticated request will be attempted.
|
|
#[default]
|
|
Auto,
|
|
/// Always authenticate.
|
|
///
|
|
/// If credentials are not provided, uv will eagerly search for credentials. If credentials
|
|
/// cannot be found, uv will error instead of attempting an unauthenticated request.
|
|
Always,
|
|
/// Never authenticate.
|
|
///
|
|
/// If credentials are provided, uv will error. uv will not search for credentials.
|
|
Never,
|
|
}
|
|
|
|
impl Display for AuthPolicy {
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
match self {
|
|
Self::Auto => write!(f, "auto"),
|
|
Self::Always => write!(f, "always"),
|
|
Self::Never => write!(f, "never"),
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO(john): We are not using `uv_distribution_types::Index` directly
|
|
// here because it would cause circular crate dependencies. However, this
|
|
// could potentially make sense for a future refactor.
|
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
|
pub struct Index {
|
|
pub url: DisplaySafeUrl,
|
|
/// The root endpoint where authentication is applied.
|
|
/// For PEP 503 endpoints, this excludes `/simple`.
|
|
pub root_url: DisplaySafeUrl,
|
|
pub auth_policy: AuthPolicy,
|
|
}
|
|
|
|
impl Index {
|
|
pub fn is_prefix_for(&self, url: &Url) -> bool {
|
|
if self.root_url.scheme() != url.scheme()
|
|
|| self.root_url.host_str() != url.host_str()
|
|
|| self.root_url.port_or_known_default() != url.port_or_known_default()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
url.path().starts_with(self.root_url.path())
|
|
}
|
|
}
|
|
|
|
// TODO(john): Multiple methods in this struct need to iterate over
|
|
// all the indexes in the set. There are probably not many URLs to
|
|
// iterate through, but we could use a trie instead of a HashSet here
|
|
// for more efficient search.
|
|
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
|
pub struct Indexes(FxHashSet<Index>);
|
|
|
|
impl Indexes {
|
|
pub fn new() -> Self {
|
|
Self(FxHashSet::default())
|
|
}
|
|
|
|
/// Create a new [`Indexes`] instance from an iterator of [`Index`]s.
|
|
pub fn from_indexes(urls: impl IntoIterator<Item = Index>) -> Self {
|
|
let mut index_urls = Self::new();
|
|
for url in urls {
|
|
index_urls.0.insert(url);
|
|
}
|
|
index_urls
|
|
}
|
|
|
|
/// Get the index for a URL if one exists.
|
|
pub fn index_for(&self, url: &Url) -> Option<&Index> {
|
|
self.find_prefix_index(url)
|
|
}
|
|
|
|
/// Get the [`AuthPolicy`] for a URL.
|
|
pub fn auth_policy_for(&self, url: &Url) -> AuthPolicy {
|
|
self.find_prefix_index(url)
|
|
.map(|index| index.auth_policy)
|
|
.unwrap_or(AuthPolicy::Auto)
|
|
}
|
|
|
|
fn find_prefix_index(&self, url: &Url) -> Option<&Index> {
|
|
self.0.iter().find(|&index| index.is_prefix_for(url))
|
|
}
|
|
}
|