|
1 | 1 | use std::fmt::{self, Display, Formatter};
|
2 | 2 |
|
3 |
| -use rustc_hash::FxHashMap; |
| 3 | +use rustc_hash::FxHashSet; |
4 | 4 | use url::Url;
|
5 | 5 |
|
6 | 6 | /// When to use authentication.
|
@@ -47,39 +47,68 @@ impl Display for AuthPolicy {
|
47 | 47 | }
|
48 | 48 | }
|
49 | 49 | }
|
| 50 | + |
| 51 | +// TODO(john): We are not using `uv_distribution_types::Index` directly |
| 52 | +// here because it would cause circular crate dependencies. However, this |
| 53 | +// could potentially make sense for a future refactor. |
| 54 | +#[derive(Debug, Clone, Hash, Eq, PartialEq)] |
| 55 | +pub struct Index { |
| 56 | + pub url: Url, |
| 57 | + /// The root endpoint where authentication is applied. |
| 58 | + /// For PEP 503 endpoints, this excludes `/simple`. |
| 59 | + pub root_url: Url, |
| 60 | + pub auth_policy: AuthPolicy, |
| 61 | +} |
| 62 | + |
50 | 63 | #[derive(Debug, Default, Clone, Eq, PartialEq)]
|
51 |
| -pub struct UrlAuthPolicies(FxHashMap<Url, AuthPolicy>); |
| 64 | +pub struct Indexes(FxHashSet<Index>); |
52 | 65 |
|
53 |
| -impl UrlAuthPolicies { |
| 66 | +impl Indexes { |
54 | 67 | pub fn new() -> Self {
|
55 |
| - Self(FxHashMap::default()) |
| 68 | + Self(FxHashSet::default()) |
56 | 69 | }
|
57 | 70 |
|
58 |
| - /// Create a new [`UrlAuthPolicies`] from a list of URL and [`AuthPolicy`] |
59 |
| - /// tuples. |
60 |
| - pub fn from_tuples(tuples: impl IntoIterator<Item = (Url, AuthPolicy)>) -> Self { |
61 |
| - let mut auth_policies = Self::new(); |
62 |
| - for (url, auth_policy) in tuples { |
63 |
| - auth_policies.add_policy(url, auth_policy); |
| 71 | + /// Create a new [`AuthIndexUrls`] from an iterator of [`AuthIndexUrl`]s. |
| 72 | + pub fn from_indexes(urls: impl IntoIterator<Item = Index>) -> Self { |
| 73 | + let mut index_urls = Self::new(); |
| 74 | + for url in urls { |
| 75 | + index_urls.0.insert(url); |
64 | 76 | }
|
65 |
| - auth_policies |
| 77 | + index_urls |
66 | 78 | }
|
67 | 79 |
|
68 |
| - /// An [`AuthPolicy`] for a URL. |
69 |
| - pub fn add_policy(&mut self, url: Url, auth_policy: AuthPolicy) { |
70 |
| - self.0.insert(url, auth_policy); |
| 80 | + /// Get the index URL prefix for a URL if one exists. |
| 81 | + pub fn index_url_for(&self, url: &Url) -> Option<&Url> { |
| 82 | + // TODO(john): There are probably not many URLs to iterate through, |
| 83 | + // but we could use a trie instead of a HashSet here for more |
| 84 | + // efficient search. |
| 85 | + self.0 |
| 86 | + .iter() |
| 87 | + .find(|index| is_url_prefix(&index.root_url, url)) |
| 88 | + .map(|index| &index.url) |
71 | 89 | }
|
72 | 90 |
|
73 | 91 | /// Get the [`AuthPolicy`] for a URL.
|
74 | 92 | pub fn policy_for(&self, url: &Url) -> AuthPolicy {
|
75 | 93 | // TODO(john): There are probably not many URLs to iterate through,
|
76 | 94 | // but we could use a trie instead of a HashMap here for more
|
77 | 95 | // efficient search.
|
78 |
| - for (auth_url, auth_policy) in &self.0 { |
79 |
| - if url.as_str().starts_with(auth_url.as_str()) { |
80 |
| - return *auth_policy; |
| 96 | + for index in &self.0 { |
| 97 | + if is_url_prefix(&index.root_url, url) { |
| 98 | + return index.auth_policy; |
81 | 99 | }
|
82 | 100 | }
|
83 | 101 | AuthPolicy::Auto
|
84 | 102 | }
|
85 | 103 | }
|
| 104 | + |
| 105 | +fn is_url_prefix(base: &Url, url: &Url) -> bool { |
| 106 | + if base.scheme() != url.scheme() |
| 107 | + || base.host_str() != url.host_str() |
| 108 | + || base.port_or_known_default() != url.port_or_known_default() |
| 109 | + { |
| 110 | + return false; |
| 111 | + } |
| 112 | + |
| 113 | + url.path().starts_with(base.path()) |
| 114 | +} |
0 commit comments