mirror of
https://github.com/rust-lang/cargo
synced 2024-10-01 21:43:45 +00:00
feat(spec): Track source kind
This commit is contained in:
parent
005b55fdbe
commit
9b9c683810
|
@ -6,6 +6,7 @@ use semver::Version;
|
|||
use serde::{de, ser};
|
||||
use url::Url;
|
||||
|
||||
use crate::core::GitReference;
|
||||
use crate::core::PackageId;
|
||||
use crate::core::SourceKind;
|
||||
use crate::util::edit_distance;
|
||||
|
@ -104,17 +105,47 @@ impl PackageIdSpec {
|
|||
name: String::from(package_id.name().as_str()),
|
||||
version: Some(package_id.version().clone().into()),
|
||||
url: Some(package_id.source_id().url().clone()),
|
||||
kind: None,
|
||||
kind: Some(package_id.source_id().kind().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to convert a valid `Url` to a `PackageIdSpec`.
|
||||
fn from_url(mut url: Url) -> CargoResult<PackageIdSpec> {
|
||||
let mut kind = None;
|
||||
if let Some((kind_str, scheme)) = url.scheme().split_once('+') {
|
||||
match kind_str {
|
||||
"git" => {
|
||||
let git_ref = GitReference::DefaultBranch;
|
||||
kind = Some(SourceKind::Git(git_ref));
|
||||
url = strip_url_protocol(&url);
|
||||
}
|
||||
"registry" => {
|
||||
kind = Some(SourceKind::Registry);
|
||||
url = strip_url_protocol(&url);
|
||||
}
|
||||
"sparse" => {
|
||||
kind = Some(SourceKind::SparseRegistry);
|
||||
// Leave `sparse` as part of URL, see `SourceId::new`
|
||||
// url = strip_url_protocol(&url);
|
||||
}
|
||||
"path" => {
|
||||
if scheme != "file" {
|
||||
anyhow::bail!("`path+{scheme}` is unsupported; `path+file` and `file` schemes are supported");
|
||||
}
|
||||
kind = Some(SourceKind::Path);
|
||||
url = strip_url_protocol(&url);
|
||||
}
|
||||
kind => anyhow::bail!("unsupported source protocol: {kind}"),
|
||||
}
|
||||
}
|
||||
|
||||
if url.query().is_some() {
|
||||
bail!("cannot have a query string in a pkgid: {}", url)
|
||||
}
|
||||
|
||||
let frag = url.fragment().map(|s| s.to_owned());
|
||||
url.set_fragment(None);
|
||||
|
||||
let (name, version) = {
|
||||
let mut path = url
|
||||
.path_segments()
|
||||
|
@ -148,7 +179,7 @@ impl PackageIdSpec {
|
|||
name,
|
||||
version,
|
||||
url: Some(url),
|
||||
kind: None,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -173,6 +204,14 @@ impl PackageIdSpec {
|
|||
self.url = Some(url);
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> Option<&SourceKind> {
|
||||
self.kind.as_ref()
|
||||
}
|
||||
|
||||
pub fn set_kind(&mut self, kind: SourceKind) {
|
||||
self.kind = Some(kind);
|
||||
}
|
||||
|
||||
/// Checks whether the given `PackageId` matches the `PackageIdSpec`.
|
||||
pub fn matches(&self, package_id: PackageId) -> bool {
|
||||
if self.name() != package_id.name().as_str() {
|
||||
|
@ -191,6 +230,12 @@ impl PackageIdSpec {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(k) = &self.kind {
|
||||
if k != package_id.source_id().kind() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -287,11 +332,20 @@ impl PackageIdSpec {
|
|||
}
|
||||
}
|
||||
|
||||
fn strip_url_protocol(url: &Url) -> Url {
|
||||
// Ridiculous hoop because `Url::set_scheme` errors when changing to http/https
|
||||
let raw = url.to_string();
|
||||
raw.split_once('+').unwrap().1.parse().unwrap()
|
||||
}
|
||||
|
||||
impl fmt::Display for PackageIdSpec {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut printed_name = false;
|
||||
match self.url {
|
||||
Some(ref url) => {
|
||||
if let Some(protocol) = self.kind.as_ref().and_then(|k| k.protocol()) {
|
||||
write!(f, "{protocol}+")?;
|
||||
}
|
||||
write!(f, "{}", url)?;
|
||||
if url.path_segments().unwrap().next_back().unwrap() != &*self.name {
|
||||
printed_name = true;
|
||||
|
@ -332,7 +386,7 @@ impl<'de> de::Deserialize<'de> for PackageIdSpec {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PackageIdSpec;
|
||||
use crate::core::{PackageId, SourceId};
|
||||
use crate::core::{GitReference, PackageId, SourceId, SourceKind};
|
||||
use url::Url;
|
||||
|
||||
#[test]
|
||||
|
@ -407,6 +461,26 @@ mod tests {
|
|||
},
|
||||
"https://crates.io/foo#bar@1.2",
|
||||
);
|
||||
ok(
|
||||
"registry+https://crates.io/foo#bar@1.2",
|
||||
PackageIdSpec {
|
||||
name: String::from("bar"),
|
||||
version: Some("1.2".parse().unwrap()),
|
||||
url: Some(Url::parse("https://crates.io/foo").unwrap()),
|
||||
kind: Some(SourceKind::Registry),
|
||||
},
|
||||
"registry+https://crates.io/foo#bar@1.2",
|
||||
);
|
||||
ok(
|
||||
"sparse+https://crates.io/foo#bar@1.2",
|
||||
PackageIdSpec {
|
||||
name: String::from("bar"),
|
||||
version: Some("1.2".parse().unwrap()),
|
||||
url: Some(Url::parse("sparse+https://crates.io/foo").unwrap()),
|
||||
kind: Some(SourceKind::SparseRegistry),
|
||||
},
|
||||
"sparse+https://crates.io/foo#bar@1.2",
|
||||
);
|
||||
ok(
|
||||
"foo",
|
||||
PackageIdSpec {
|
||||
|
@ -499,6 +573,18 @@ mod tests {
|
|||
},
|
||||
"https://github.com/rust-lang/crates.io-index#regex@1.4.3",
|
||||
);
|
||||
ok(
|
||||
"sparse+https://github.com/rust-lang/crates.io-index#regex@1.4.3",
|
||||
PackageIdSpec {
|
||||
name: String::from("regex"),
|
||||
version: Some("1.4.3".parse().unwrap()),
|
||||
url: Some(
|
||||
Url::parse("sparse+https://github.com/rust-lang/crates.io-index").unwrap(),
|
||||
),
|
||||
kind: Some(SourceKind::SparseRegistry),
|
||||
},
|
||||
"sparse+https://github.com/rust-lang/crates.io-index#regex@1.4.3",
|
||||
);
|
||||
ok(
|
||||
"https://github.com/rust-lang/cargo#0.52.0",
|
||||
PackageIdSpec {
|
||||
|
@ -529,6 +615,16 @@ mod tests {
|
|||
},
|
||||
"ssh://git@github.com/rust-lang/regex.git#regex@1.4.3",
|
||||
);
|
||||
ok(
|
||||
"git+ssh://git@github.com/rust-lang/regex.git#regex@1.4.3",
|
||||
PackageIdSpec {
|
||||
name: String::from("regex"),
|
||||
version: Some("1.4.3".parse().unwrap()),
|
||||
url: Some(Url::parse("ssh://git@github.com/rust-lang/regex.git").unwrap()),
|
||||
kind: Some(SourceKind::Git(GitReference::DefaultBranch)),
|
||||
},
|
||||
"git+ssh://git@github.com/rust-lang/regex.git#regex@1.4.3",
|
||||
);
|
||||
ok(
|
||||
"file:///path/to/my/project/foo",
|
||||
PackageIdSpec {
|
||||
|
@ -549,6 +645,16 @@ mod tests {
|
|||
},
|
||||
"file:///path/to/my/project/foo#1.1.8",
|
||||
);
|
||||
ok(
|
||||
"path+file:///path/to/my/project/foo#1.1.8",
|
||||
PackageIdSpec {
|
||||
name: String::from("foo"),
|
||||
version: Some("1.1.8".parse().unwrap()),
|
||||
url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()),
|
||||
kind: Some(SourceKind::Path),
|
||||
},
|
||||
"path+file:///path/to/my/project/foo#1.1.8",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -560,6 +666,10 @@ mod tests {
|
|||
assert!(PackageIdSpec::parse("baz@^1.0").is_err());
|
||||
assert!(PackageIdSpec::parse("https://baz:1.0").is_err());
|
||||
assert!(PackageIdSpec::parse("https://#baz:1.0").is_err());
|
||||
assert!(
|
||||
PackageIdSpec::parse("foobar+https://github.com/rust-lang/crates.io-index").is_err()
|
||||
);
|
||||
assert!(PackageIdSpec::parse("path+https://github.com/rust-lang/crates.io-index").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -581,6 +691,12 @@ mod tests {
|
|||
assert!(!PackageIdSpec::parse("https://bob.com#foo@1.2")
|
||||
.unwrap()
|
||||
.matches(foo));
|
||||
assert!(PackageIdSpec::parse("registry+https://example.com#foo@1.2")
|
||||
.unwrap()
|
||||
.matches(foo));
|
||||
assert!(!PackageIdSpec::parse("git+https://example.com#foo@1.2")
|
||||
.unwrap()
|
||||
.matches(foo));
|
||||
|
||||
let meta = PackageId::new("meta", "1.2.3+hello", sid).unwrap();
|
||||
assert!(PackageIdSpec::parse("meta").unwrap().matches(meta));
|
||||
|
|
|
@ -373,6 +373,10 @@ impl SourceId {
|
|||
Some(self.inner.url.to_file_path().unwrap())
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &SourceKind {
|
||||
&self.inner.kind
|
||||
}
|
||||
|
||||
/// Returns `true` if this source is from a registry (either local or not).
|
||||
pub fn is_registry(self) -> bool {
|
||||
matches!(
|
||||
|
@ -748,7 +752,7 @@ impl SourceKind {
|
|||
SourceKind::Path => Some("path"),
|
||||
SourceKind::Git(_) => Some("git"),
|
||||
SourceKind::Registry => Some("registry"),
|
||||
// Sparse registry URL already includes the `sparse+` prefix
|
||||
// Sparse registry URL already includes the `sparse+` prefix, see `SourceId::new`
|
||||
SourceKind::SparseRegistry => None,
|
||||
SourceKind::LocalRegistry => Some("local-registry"),
|
||||
SourceKind::Directory => Some("directory"),
|
||||
|
|
|
@ -21,11 +21,12 @@ qualified with a version to make it unique, such as `regex@1.4.3`.
|
|||
The formal grammar for a Package Id Specification is:
|
||||
|
||||
```notrust
|
||||
spec := pkgname
|
||||
| proto "://" hostname-and-path [ "#" ( pkgname | semver ) ]
|
||||
spec := pkgname |
|
||||
[ kind "+" ] proto "://" hostname-and-path [ "#" ( pkgname | semver ) ]
|
||||
pkgname := name [ ("@" | ":" ) semver ]
|
||||
semver := digits [ "." digits [ "." digits [ "-" prerelease ] [ "+" build ]]]
|
||||
|
||||
kind = "registry" | "git" | "file"
|
||||
proto := "http" | "git" | ...
|
||||
```
|
||||
|
||||
|
@ -39,27 +40,30 @@ that come from different sources such as different registries.
|
|||
The following are references to the `regex` package on `crates.io`:
|
||||
|
||||
| Spec | Name | Version |
|
||||
|:------------------------------------------------------------|:-------:|:-------:|
|
||||
|:------------------------------------------------------------------|:-------:|:-------:|
|
||||
| `regex` | `regex` | `*` |
|
||||
| `regex@1.4` | `regex` | `1.4.*` |
|
||||
| `regex@1.4.3` | `regex` | `1.4.3` |
|
||||
| `https://github.com/rust-lang/crates.io-index#regex` | `regex` | `*` |
|
||||
| `https://github.com/rust-lang/crates.io-index#regex@1.4.3` | `regex` | `1.4.3` |
|
||||
| `registry+https://github.com/rust-lang/crates.io-index#regex@1.4.3` | `regex` | `1.4.3` |
|
||||
|
||||
The following are some examples of specs for several different git dependencies:
|
||||
|
||||
| Spec | Name | Version |
|
||||
|:----------------------------------------------------------|:----------------:|:--------:|
|
||||
|:-----------------------------------------------------------|:----------------:|:--------:|
|
||||
| `https://github.com/rust-lang/cargo#0.52.0` | `cargo` | `0.52.0` |
|
||||
| `https://github.com/rust-lang/cargo#cargo-platform@0.1.2` | <nobr>`cargo-platform`</nobr> | `0.1.2` |
|
||||
| `ssh://git@github.com/rust-lang/regex.git#regex@1.4.3` | `regex` | `1.4.3` |
|
||||
| `git+ssh://git@github.com/rust-lang/regex.git#regex@1.4.3` | `regex` | `1.4.3` |
|
||||
|
||||
Local packages on the filesystem can use `file://` URLs to reference them:
|
||||
|
||||
| Spec | Name | Version |
|
||||
|:---------------------------------------|:-----:|:-------:|
|
||||
|:--------------------------------------------|:-----:|:-------:|
|
||||
| `file:///path/to/my/project/foo` | `foo` | `*` |
|
||||
| `file:///path/to/my/project/foo#1.1.8` | `foo` | `1.1.8` |
|
||||
| `path+file:///path/to/my/project/foo#1.1.8` | `foo` | `1.1.8` |
|
||||
|
||||
### Brevity of specifications
|
||||
|
||||
|
|
|
@ -36,7 +36,10 @@ fn local() {
|
|||
p.cargo("generate-lockfile").run();
|
||||
|
||||
p.cargo("pkgid foo")
|
||||
.with_stdout(format!("file://[..]{}#0.1.0", p.root().to_str().unwrap()))
|
||||
.with_stdout(format!(
|
||||
"path+file://[..]{}#0.1.0",
|
||||
p.root().to_str().unwrap()
|
||||
))
|
||||
.run();
|
||||
|
||||
// Bad file URL.
|
||||
|
@ -91,7 +94,7 @@ fn registry() {
|
|||
p.cargo("generate-lockfile").run();
|
||||
|
||||
p.cargo("pkgid crates-io")
|
||||
.with_stdout("https://github.com/rust-lang/crates.io-index#crates-io@0.1.0")
|
||||
.with_stdout("registry+https://github.com/rust-lang/crates.io-index#crates-io@0.1.0")
|
||||
.run();
|
||||
|
||||
// Bad URL.
|
||||
|
@ -145,7 +148,7 @@ fn multiple_versions() {
|
|||
p.cargo("generate-lockfile").run();
|
||||
|
||||
p.cargo("pkgid two-ver:0.2.0")
|
||||
.with_stdout("https://github.com/rust-lang/crates.io-index#two-ver@0.2.0")
|
||||
.with_stdout("registry+https://github.com/rust-lang/crates.io-index#two-ver@0.2.0")
|
||||
.run();
|
||||
|
||||
// Incomplete version.
|
||||
|
@ -165,7 +168,7 @@ Please re-run this command with one of the following specifications:
|
|||
p.cargo("pkgid two-ver@0.2")
|
||||
.with_stdout(
|
||||
"\
|
||||
https://github.com/rust-lang/crates.io-index#two-ver@0.2.0
|
||||
registry+https://github.com/rust-lang/crates.io-index#two-ver@0.2.0
|
||||
",
|
||||
)
|
||||
.run();
|
||||
|
@ -274,8 +277,8 @@ foo v0.1.0 ([..]/foo)
|
|||
"\
|
||||
error: There are multiple `xyz` packages in your project, and the specification `xyz` is ambiguous.
|
||||
Please re-run this command with one of the following specifications:
|
||||
file://[..]/xyz#0.5.0
|
||||
file://[..]/xyz#0.5.0
|
||||
git+file://[..]/xyz#0.5.0
|
||||
git+file://[..]/xyz#0.5.0
|
||||
",
|
||||
)
|
||||
.run();
|
||||
|
|
Loading…
Reference in a new issue