Skip to content

Commit 22a112b

Browse files
committed
Auto merge of #8267 - drmikehenry:prefix, r=alexcrichton
Support `{prefix}` and `{lowerprefix}` markers in `config.json` `dl` key Hello, The crates.io-index Git repository uses a nice directory structure to keep individual directory sizes under control. When mirroring crates.io, it's useful to store crate files in a similar directory structure for the same reasons. Cargo provides "markers" for use in the `dl` key of the `config.json` file in crates.io-index to allow flexibility in mapping a crate's name and version into a URL for the crate. The marker `{crate}` is replaced by the crate's name, and the marker `{version}` is replaced with the crate's version. The default URL template is `https://crates.io/api/v1/crates/{crate}/{version}/download`. Currently, if a mirror of crates.io stores crates in a directory structure similar to that of crates.io-index, it's up to the server to construct the directory name from the crate name. This eliminates trivial web servers and `file:` URLs from hosting such a tree of crates. This pull requests adds two new markers for the `dl` key in `config.json`, allowing Cargo to supply the directory name as part of the URL. The marker `{lowerprefix}` is the same directory name used within crates.io-index; it is calculated from the crate name converted to lowercase. The marker `{prefix}` is similar, but it uses the crate name as-is (without case conversion), which is useful for supporting older versions of Cargo that lack these markers; for example, nginx rewrite rules can easily construct `{prefix}` but can't perform case-conversion to construct `{lowerprefix}`. These new markers will provide implementation flexibility and simplicity for crate mirror servers.
2 parents 6f9d808 + b375bea commit 22a112b

File tree

3 files changed

+72
-9
lines changed

3 files changed

+72
-9
lines changed

src/cargo/sources/registry/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ pub const CRATES_IO_INDEX: &str = "https://github.com/rust-lang/crates.io-index"
185185
pub const CRATES_IO_REGISTRY: &str = "crates-io";
186186
const CRATE_TEMPLATE: &str = "{crate}";
187187
const VERSION_TEMPLATE: &str = "{version}";
188+
const PREFIX_TEMPLATE: &str = "{prefix}";
189+
const LOWER_PREFIX_TEMPLATE: &str = "{lowerprefix}";
188190

189191
pub struct RegistrySource<'cfg> {
190192
source_id: SourceId,
@@ -203,10 +205,14 @@ pub struct RegistryConfig {
203205
/// The string is a template which will generate the download URL for the
204206
/// tarball of a specific version of a crate. The substrings `{crate}` and
205207
/// `{version}` will be replaced with the crate's name and version
206-
/// respectively.
208+
/// respectively. The substring `{prefix}` will be replaced with the
209+
/// crate's prefix directory name, and the substring `{lowerprefix}` will
210+
/// be replaced with the crate's prefix directory name converted to
211+
/// lowercase.
207212
///
208-
/// For backwards compatibility, if the string does not contain `{crate}` or
209-
/// `{version}`, it will be extended with `/{crate}/{version}/download` to
213+
/// For backwards compatibility, if the string does not contain any
214+
/// markers (`{crate}`, `{version}`, `{prefix}`, or ``{lowerprefix}`), it
215+
/// will be extended with `/{crate}/{version}/download` to
210216
/// support registries like crates.io which were created before the
211217
/// templating setup was created.
212218
pub dl: String,

src/cargo/sources/registry/remote.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use crate::core::{InternedString, PackageId, SourceId};
22
use crate::sources::git;
33
use crate::sources::registry::MaybeLock;
4-
use crate::sources::registry::{RegistryConfig, RegistryData, CRATE_TEMPLATE, VERSION_TEMPLATE};
4+
use crate::sources::registry::{
5+
RegistryConfig, RegistryData, CRATE_TEMPLATE, LOWER_PREFIX_TEMPLATE, PREFIX_TEMPLATE,
6+
VERSION_TEMPLATE,
7+
};
58
use crate::util::errors::{CargoResult, CargoResultExt};
69
use crate::util::paths;
710
use crate::util::{Config, Filesystem, Sha256};
@@ -16,6 +19,15 @@ use std::mem;
1619
use std::path::Path;
1720
use std::str;
1821

22+
fn make_crate_prefix(name: &str) -> String {
23+
match name.len() {
24+
1 => format!("1"),
25+
2 => format!("2"),
26+
3 => format!("3/{}", &name[..1]),
27+
_ => format!("{}/{}", &name[0..2], &name[2..4]),
28+
}
29+
}
30+
1931
pub struct RemoteRegistry<'cfg> {
2032
index_path: Filesystem,
2133
cache_path: Filesystem,
@@ -250,12 +262,19 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
250262

251263
let config = self.config()?.unwrap();
252264
let mut url = config.dl;
253-
if !url.contains(CRATE_TEMPLATE) && !url.contains(VERSION_TEMPLATE) {
265+
if !url.contains(CRATE_TEMPLATE)
266+
&& !url.contains(VERSION_TEMPLATE)
267+
&& !url.contains(PREFIX_TEMPLATE)
268+
&& !url.contains(LOWER_PREFIX_TEMPLATE)
269+
{
254270
write!(url, "/{}/{}/download", CRATE_TEMPLATE, VERSION_TEMPLATE).unwrap();
255271
}
272+
let prefix = make_crate_prefix(&*pkg.name());
256273
let url = url
257274
.replace(CRATE_TEMPLATE, &*pkg.name())
258-
.replace(VERSION_TEMPLATE, &pkg.version().to_string());
275+
.replace(VERSION_TEMPLATE, &pkg.version().to_string())
276+
.replace(PREFIX_TEMPLATE, &prefix)
277+
.replace(LOWER_PREFIX_TEMPLATE, &prefix.to_lowercase());
259278

260279
Ok(MaybeLock::Download {
261280
url,
@@ -314,3 +333,18 @@ impl<'cfg> Drop for RemoteRegistry<'cfg> {
314333
self.tree.borrow_mut().take();
315334
}
316335
}
336+
337+
#[cfg(test)]
338+
mod tests {
339+
use super::make_crate_prefix;
340+
341+
#[test]
342+
fn crate_prefix() {
343+
assert_eq!(make_crate_prefix("a"), "1");
344+
assert_eq!(make_crate_prefix("ab"), "2");
345+
assert_eq!(make_crate_prefix("abc"), "3/a");
346+
assert_eq!(make_crate_prefix("Abc"), "3/A");
347+
assert_eq!(make_crate_prefix("AbCd"), "Ab/Cd");
348+
assert_eq!(make_crate_prefix("aBcDe"), "aB/cD");
349+
}
350+
}

src/doc/src/reference/registries.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,17 @@ looks like:
127127

128128
The keys are:
129129
- `dl`: This is the URL for downloading crates listed in the index. The value
130-
may have the markers `{crate}` and `{version}` which are replaced with the
131-
name and version of the crate to download. If the markers are not present,
132-
then the value `/{crate}/{version}/download` is appended to the end.
130+
may have the following markers which will be replaced with their
131+
corresponding value:
132+
133+
- `{crate}`: The name of crate.
134+
- `{version}`: The crate version.
135+
- `{prefix}`: A directory prefix computed from the crate name. For example,
136+
a crate named `cargo` has a prefix of `ca/rg`. See below for details.
137+
- `{lowerprefix}`: Lowercase variant of `{prefix}`.
138+
139+
If none of the markers are present, then the value
140+
`/{crate}/{version}/download` is appended to the end.
133141
- `api`: This is the base URL for the web API. This key is optional, but if it
134142
is not specified, commands such as [`cargo publish`] will not work. The web
135143
API is described below.
@@ -159,6 +167,21 @@ directories:
159167
> package names in `Cargo.toml` and the index JSON data are case-sensitive and
160168
> may contain upper and lower case characters.
161169
170+
The directory name above is calculated based on the package name converted to
171+
lowercase; it is represented by the marker `{lowerprefix}`. When the original
172+
package name is used without case conversion, the resulting directory name is
173+
represented by the marker `{prefix}`. For example, the package `MyCrate` would
174+
have a `{prefix}` of `My/Cr` and a `{lowerprefix}` of `my/cr`. In general,
175+
using `{prefix}` is recommended over `{lowerprefix}`, but there are pros and
176+
cons to each choice. Using `{prefix}` on case-insensitive filesystems results
177+
in (harmless-but-inelegant) directory aliasing. For example, `crate` and
178+
`CrateTwo` have `{prefix}` values of `cr/at` and `Cr/at`; these are distinct on
179+
Unix machines but alias to the same directory on Windows. Using directories
180+
with normalized case avoids aliasing, but on case-sensitive filesystems it's
181+
harder to suport older versions of Cargo that lack `{prefix}`/`{lowerprefix}`.
182+
For example, nginx rewrite rules can easily construct `{prefix}` but can't
183+
perform case-conversion to construct `{lowerprefix}`.
184+
162185
Registries should consider enforcing limitations on package names added to
163186
their index. Cargo itself allows names with any [alphanumeric], `-`, or `_`
164187
characters. [crates.io] imposes its own limitations, including the following:

0 commit comments

Comments
 (0)