1use std::fmt::{self, Display, Formatter};
2
3use rustc_hash::FxHashSet;
4use url::Url;
5use uv_redacted::DisplaySafeUrl;
6
7#[derive(
9 Copy,
10 Clone,
11 Debug,
12 Default,
13 Hash,
14 Eq,
15 PartialEq,
16 Ord,
17 PartialOrd,
18 serde::Serialize,
19 serde::Deserialize,
20)]
21#[serde(rename_all = "kebab-case")]
22#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
23pub enum AuthPolicy {
24 #[default]
30 Auto,
31 Always,
36 Never,
40}
41
42impl Display for AuthPolicy {
43 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
44 match self {
45 Self::Auto => write!(f, "auto"),
46 Self::Always => write!(f, "always"),
47 Self::Never => write!(f, "never"),
48 }
49 }
50}
51
52#[derive(Debug, Clone, Hash, Eq, PartialEq)]
56pub struct Index {
57 pub url: DisplaySafeUrl,
58 pub root_url: DisplaySafeUrl,
61 pub auth_policy: AuthPolicy,
62}
63
64impl Index {
65 pub fn is_prefix_for(&self, url: &Url) -> bool {
66 if self.root_url.scheme() != url.scheme()
67 || self.root_url.host_str() != url.host_str()
68 || self.root_url.port_or_known_default() != url.port_or_known_default()
69 {
70 return false;
71 }
72
73 url.path().starts_with(self.root_url.path())
74 }
75}
76
77#[derive(Debug, Default, Clone, Eq, PartialEq)]
82pub struct Indexes(FxHashSet<Index>);
83
84impl Indexes {
85 pub fn new() -> Self {
86 Self(FxHashSet::default())
87 }
88
89 pub fn from_indexes(urls: impl IntoIterator<Item = Index>) -> Self {
91 let mut index_urls = Self::new();
92 for url in urls {
93 index_urls.0.insert(url);
94 }
95 index_urls
96 }
97
98 pub fn index_for(&self, url: &Url) -> Option<&Index> {
100 self.find_prefix_index(url)
101 }
102
103 pub fn auth_policy_for(&self, url: &Url) -> AuthPolicy {
105 self.find_prefix_index(url)
106 .map(|index| index.auth_policy)
107 .unwrap_or(AuthPolicy::Auto)
108 }
109
110 fn find_prefix_index(&self, url: &Url) -> Option<&Index> {
111 self.0.iter().find(|&index| index.is_prefix_for(url))
112 }
113}