uv/crates/uv-auth/src/index.rs

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))
}
}