1pub mod cache;
5#[cfg(all(feature = "__git", feature = "sparse"))]
6mod combo;
7#[allow(missing_docs)]
8pub mod git;
9#[cfg(feature = "__git")]
10pub(crate) mod git_remote;
11#[cfg(feature = "local")]
12pub mod local;
13pub mod location;
14#[allow(missing_docs)]
15pub mod sparse;
16#[cfg(feature = "sparse")]
17mod sparse_remote;
18
19pub use cache::IndexCache;
20#[cfg(all(feature = "__git", feature = "sparse"))]
21pub use combo::ComboIndex;
22pub use git::GitIndex;
23#[cfg(feature = "__git")]
24pub use git_remote::RemoteGitIndex;
25#[cfg(feature = "local")]
26pub use local::LocalRegistry;
27pub use location::{IndexLocation, IndexPath, IndexUrl};
28pub use sparse::SparseIndex;
29#[cfg(feature = "sparse")]
30pub use sparse_remote::{AsyncRemoteSparseIndex, RemoteSparseIndex};
31
32pub use crate::utils::flock::FileLock;
33
34#[derive(Eq, PartialEq, PartialOrd, Ord, Clone, Debug, serde::Deserialize, serde::Serialize)]
36pub struct IndexConfig {
37 pub dl: String,
39 #[serde(default)]
40 pub api: Option<String>,
42 #[serde(default, rename = "auth-required")]
46 pub auth_required: bool,
47}
48
49impl IndexConfig {
50 pub fn download_url(&self, name: crate::KrateName<'_>, version: &str) -> String {
55 if self.dl == "https://crates.io/api/v1/crates" {
62 return format!("https://static.crates.io/crates/{name}/{name}-{version}.crate");
63 }
64
65 let mut dl = self.dl.clone();
66
67 if dl.contains('{') {
68 while let Some(start) = dl.find("{crate}") {
69 dl.replace_range(start..start + 7, name.0);
70 }
71
72 while let Some(start) = dl.find("{version}") {
73 dl.replace_range(start..start + 9, version);
74 }
75
76 if dl.contains("{prefix}") || dl.contains("{lowerprefix}") {
77 let mut prefix = String::with_capacity(6);
78 name.prefix(&mut prefix, '/');
79
80 while let Some(start) = dl.find("{prefix}") {
81 dl.replace_range(start..start + 8, &prefix);
82 }
83
84 if dl.contains("{lowerprefix}") {
85 prefix.make_ascii_lowercase();
86
87 while let Some(start) = dl.find("{lowerprefix}") {
88 dl.replace_range(start..start + 13, &prefix);
89 }
90 }
91 }
92 } else {
93 if !dl.ends_with('/') {
95 dl.push('/');
96 }
97
98 dl.push_str(name.0);
99 dl.push('/');
100 dl.push_str(version);
101 dl.push('/');
102 dl.push_str("download");
103 }
104
105 dl
106 }
107}
108
109use crate::Error;
110
111#[non_exhaustive]
113pub enum ComboIndexCache {
114 Git(GitIndex),
116 Sparse(SparseIndex),
118 #[cfg(feature = "local")]
120 Local(LocalRegistry),
121}
122
123impl ComboIndexCache {
124 #[inline]
126 pub fn cached_krate(
127 &self,
128 name: crate::KrateName<'_>,
129 lock: &FileLock,
130 ) -> Result<Option<crate::IndexKrate>, Error> {
131 match self {
132 Self::Git(index) => index.cached_krate(name, lock),
133 Self::Sparse(index) => index.cached_krate(name, lock),
134 #[cfg(feature = "local")]
135 Self::Local(lr) => lr.cached_krate(name, lock),
136 }
137 }
138
139 pub fn cache_path(&self, name: crate::KrateName<'_>) -> crate::PathBuf {
141 match self {
142 Self::Git(index) => index.cache.cache_path(name),
143 Self::Sparse(index) => index.cache().cache_path(name),
144 #[cfg(feature = "local")]
145 Self::Local(lr) => lr.krate_path(name),
146 }
147 }
148
149 pub fn new(il: IndexLocation<'_>) -> Result<Self, Error> {
154 #[cfg(feature = "local")]
155 {
156 if let IndexUrl::Local(path) = il.url {
157 return Ok(Self::Local(LocalRegistry::open(path.into(), true)?));
158 }
159 }
160
161 let index = if il.url.is_sparse() {
162 let sparse = SparseIndex::new(il)?;
163 Self::Sparse(sparse)
164 } else {
165 let git = GitIndex::new(il)?;
166 Self::Git(git)
167 };
168
169 Ok(index)
170 }
171}
172
173impl From<SparseIndex> for ComboIndexCache {
174 #[inline]
175 fn from(si: SparseIndex) -> Self {
176 Self::Sparse(si)
177 }
178}
179
180impl From<GitIndex> for ComboIndexCache {
181 #[inline]
182 fn from(gi: GitIndex) -> Self {
183 Self::Git(gi)
184 }
185}
186
187#[cfg(test)]
188mod test {
189 use super::IndexConfig;
190 use crate::kn;
191
192 #[test]
194 fn download_url_crates_io() {
195 let crates_io = IndexConfig {
196 dl: "https://crates.io/api/v1/crates".into(),
197 api: Some("https://crates.io".into()),
198 auth_required: false,
199 };
200
201 assert_eq!(
202 crates_io.download_url(kn!("a"), "1.0.0"),
203 "https://static.crates.io/crates/a/a-1.0.0.crate"
204 );
205 assert_eq!(
206 crates_io.download_url(kn!("aB"), "0.1.0"),
207 "https://static.crates.io/crates/aB/aB-0.1.0.crate"
208 );
209 assert_eq!(
210 crates_io.download_url(kn!("aBc"), "0.1.0"),
211 "https://static.crates.io/crates/aBc/aBc-0.1.0.crate"
212 );
213 assert_eq!(
214 crates_io.download_url(kn!("aBc-123"), "0.1.0"),
215 "https://static.crates.io/crates/aBc-123/aBc-123-0.1.0.crate"
216 );
217 }
218
219 #[test]
221 fn download_url_non_crates_io() {
222 let ic = IndexConfig {
223 dl: "https://dl.cloudsmith.io/public/embark/deny/cargo/{crate}-{version}.crate".into(),
224 api: Some("https://cargo.cloudsmith.io/embark/deny".into()),
225 auth_required: false,
226 };
227
228 assert_eq!(
229 ic.download_url(kn!("a"), "1.0.0"),
230 "https://dl.cloudsmith.io/public/embark/deny/cargo/a-1.0.0.crate"
231 );
232 assert_eq!(
233 ic.download_url(kn!("aB"), "0.1.0"),
234 "https://dl.cloudsmith.io/public/embark/deny/cargo/aB-0.1.0.crate"
235 );
236 assert_eq!(
237 ic.download_url(kn!("aBc"), "0.1.0"),
238 "https://dl.cloudsmith.io/public/embark/deny/cargo/aBc-0.1.0.crate"
239 );
240 assert_eq!(
241 ic.download_url(kn!("aBc-123"), "0.1.0"),
242 "https://dl.cloudsmith.io/public/embark/deny/cargo/aBc-123-0.1.0.crate"
243 );
244 }
245
246 #[test]
249 fn download_url_complex() {
250 let ic = IndexConfig {
251 dl: "https://complex.io/ohhi/embark/rust/cargo/{lowerprefix}/{crate}/{crate}/{prefix}-{version}".into(),
252 api: None,
253 auth_required: false,
254 };
255
256 assert_eq!(
257 ic.download_url(kn!("a"), "1.0.0"),
258 "https://complex.io/ohhi/embark/rust/cargo/1/a/a/1-1.0.0"
259 );
260 assert_eq!(
261 ic.download_url(kn!("aB"), "0.1.0"),
262 "https://complex.io/ohhi/embark/rust/cargo/2/aB/aB/2-0.1.0"
263 );
264 assert_eq!(
265 ic.download_url(kn!("ABc"), "0.1.0"),
266 "https://complex.io/ohhi/embark/rust/cargo/3/a/ABc/ABc/3/A-0.1.0"
267 );
268 assert_eq!(
269 ic.download_url(kn!("aBc-123"), "0.1.0"),
270 "https://complex.io/ohhi/embark/rust/cargo/ab/c-/aBc-123/aBc-123/aB/c--0.1.0"
271 );
272 }
273}