1use tame_index::krate::IndexKrate;
2use tame_index::utils::flock::FileLock;
3
4use url::Url;
5
6use super::errors::*;
7
8#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
9pub enum CertsSource {
10 #[default]
12 Webpki,
13 Native,
15}
16
17pub struct IndexCache {
18 certs_source: CertsSource,
19 index: std::collections::HashMap<Url, AnyIndexCache>,
20}
21
22impl IndexCache {
23 #[inline]
24 pub fn new(certs_source: CertsSource) -> Self {
25 Self {
26 certs_source,
27 index: Default::default(),
28 }
29 }
30
31 #[inline]
33 pub fn has_krate(&mut self, registry: &Url, name: &str) -> CargoResult<bool> {
34 self.index(registry)?.has_krate(name)
35 }
36
37 #[inline]
39 pub fn has_krate_version(
40 &mut self,
41 registry: &Url,
42 name: &str,
43 version: &str,
44 ) -> CargoResult<Option<bool>> {
45 self.index(registry)?.has_krate_version(name, version)
46 }
47
48 #[inline]
49 pub fn update_krate(&mut self, registry: &Url, name: &str) -> CargoResult<()> {
50 self.index(registry)?.update_krate(name);
51 Ok(())
52 }
53
54 pub fn krate(&mut self, registry: &Url, name: &str) -> CargoResult<Option<IndexKrate>> {
55 self.index(registry)?.krate(name)
56 }
57
58 pub fn index<'s>(&'s mut self, registry: &Url) -> CargoResult<&'s mut AnyIndexCache> {
59 if !self.index.contains_key(registry) {
60 let index = AnyIndex::open(registry, self.certs_source)?;
61 let index = AnyIndexCache::new(index);
62 self.index.insert(registry.clone(), index);
63 }
64 Ok(self.index.get_mut(registry).unwrap())
65 }
66}
67
68pub struct AnyIndexCache {
69 index: AnyIndex,
70 cache: std::collections::HashMap<String, Option<IndexKrate>>,
71}
72
73impl AnyIndexCache {
74 #[inline]
75 pub fn new(index: AnyIndex) -> Self {
76 Self {
77 index,
78 cache: std::collections::HashMap::new(),
79 }
80 }
81
82 #[inline]
84 pub fn has_krate(&mut self, name: &str) -> CargoResult<bool> {
85 Ok(self.krate(name)?.map(|_| true).unwrap_or(false))
86 }
87
88 #[inline]
90 pub fn has_krate_version(&mut self, name: &str, version: &str) -> CargoResult<Option<bool>> {
91 let krate = self.krate(name)?;
92 Ok(krate.map(|ik| ik.versions.iter().any(|iv| iv.version == version)))
93 }
94
95 #[inline]
96 pub fn update_krate(&mut self, name: &str) {
97 self.cache.remove(name);
98 }
99
100 pub fn krate(&mut self, name: &str) -> CargoResult<Option<IndexKrate>> {
101 if let Some(entry) = self.cache.get(name) {
102 return Ok(entry.clone());
103 }
104
105 let entry = self.index.krate(name)?;
106 self.cache.insert(name.to_owned(), entry.clone());
107 Ok(entry)
108 }
109}
110
111pub enum AnyIndex {
112 Local(LocalIndex),
113 Remote(RemoteIndex),
114}
115
116impl AnyIndex {
117 pub fn open(url: &Url, certs_source: CertsSource) -> CargoResult<Self> {
118 if url.scheme() == "file" {
119 LocalIndex::open(url).map(Self::Local)
120 } else {
121 RemoteIndex::open(url, certs_source).map(Self::Remote)
122 }
123 }
124
125 pub(crate) fn krate(&mut self, name: &str) -> CargoResult<Option<IndexKrate>> {
126 match self {
127 Self::Local(index) => index.krate(name),
128 Self::Remote(index) => index.krate(name),
129 }
130 }
131}
132
133pub struct LocalIndex {
134 index: tame_index::index::LocalRegistry,
135 root: tame_index::PathBuf,
136}
137
138impl LocalIndex {
139 pub fn open(url: &Url) -> CargoResult<Self> {
140 let path = url
141 .to_file_path()
142 .map_err(|()| anyhow::format_err!("invalid local registry {url}"))?;
143 let path = tame_index::PathBuf::from_path_buf(path)
144 .map_err(|_err| anyhow::format_err!("invalid local registry {url:?}"))?;
145 let index = tame_index::index::LocalRegistry::open(path.clone(), false)?;
146 Ok(Self { index, root: path })
147 }
148
149 pub(crate) fn krate(&mut self, name: &str) -> CargoResult<Option<IndexKrate>> {
150 let name = tame_index::KrateName::cargo(name)?;
151 let entry_path = self.index.krate_path(name);
153 let rel_path = entry_path
154 .strip_prefix(&self.root)
155 .map_err(|_err| anyhow::format_err!("invalid index path {entry_path:?}"))?;
156 let rel_path = rel_path
157 .strip_prefix("index")
158 .map_err(|_err| anyhow::format_err!("invalid index path {entry_path:?}"))?;
159 let entry_path = self.root.join(rel_path);
160 let Ok(entry) = std::fs::read(&entry_path) else {
161 return Ok(None);
162 };
163 let results = IndexKrate::from_slice(&entry)?;
164 Ok(Some(results))
165 }
166}
167
168pub struct RemoteIndex {
169 index: tame_index::SparseIndex,
170 client: tame_index::external::reqwest::blocking::Client,
171 lock: FileLock,
172 etags: Vec<(String, String)>,
173}
174
175impl RemoteIndex {
176 pub fn open(url: &Url, certs_source: CertsSource) -> CargoResult<Self> {
177 let url = url.to_string();
178 let url = tame_index::IndexUrl::NonCratesIo(std::borrow::Cow::Owned(url));
179 let index = tame_index::SparseIndex::new(tame_index::IndexLocation::new(url))?;
180
181 let client = {
182 let builder = tame_index::external::reqwest::blocking::ClientBuilder::new();
183
184 let builder = match certs_source {
185 CertsSource::Webpki => builder.tls_built_in_webpki_certs(true),
186 CertsSource::Native => builder.tls_built_in_native_certs(true),
187 };
188
189 builder.build()?
190 };
191
192 let lock = FileLock::unlocked();
193
194 Ok(Self {
195 index,
196 client,
197 lock,
198 etags: Vec::new(),
199 })
200 }
201
202 pub(crate) fn krate(&mut self, name: &str) -> CargoResult<Option<IndexKrate>> {
203 let etag = self
204 .etags
205 .iter()
206 .find_map(|(krate, etag)| (krate == name).then_some(etag.as_str()))
207 .unwrap_or("");
208
209 let krate_name = name.try_into()?;
210 let req = self
211 .index
212 .make_remote_request(krate_name, Some(etag), &self.lock)?;
213 let (
214 tame_index::external::http::request::Parts {
215 method,
216 uri,
217 version,
218 headers,
219 ..
220 },
221 _,
222 ) = req.into_parts();
223 let mut req = self.client.request(method, uri.to_string());
224 req = req.version(version);
225 req = req.headers(headers);
226 let res = self.client.execute(req.build()?)?;
227
228 if let Some(etag) = res
230 .headers()
231 .get(tame_index::external::reqwest::header::ETAG)
232 {
233 if let Ok(etag) = etag.to_str() {
234 if let Some(i) = self.etags.iter().position(|(krate, _)| krate == name) {
235 etag.clone_into(&mut self.etags[i].1);
236 } else {
237 self.etags.push((name.to_owned(), etag.to_owned()));
238 }
239 }
240 }
241
242 let mut builder = tame_index::external::http::Response::builder()
243 .status(res.status())
244 .version(res.version());
245
246 builder
247 .headers_mut()
248 .unwrap()
249 .extend(res.headers().iter().map(|(k, v)| (k.clone(), v.clone())));
250
251 let body = res.bytes()?;
252 let response = builder
253 .body(body.to_vec())
254 .map_err(|e| tame_index::Error::from(tame_index::error::HttpError::from(e)))?;
255
256 self.index
257 .parse_remote_response(krate_name, response, false, &self.lock)
258 .map_err(Into::into)
259 }
260}