diff --git a/src/cargo/core/package_id_spec.rs b/src/cargo/core/package_id_spec.rs index d3f0abc465e..c617c1f7ab0 100644 --- a/src/cargo/core/package_id_spec.rs +++ b/src/cargo/core/package_id_spec.rs @@ -180,10 +180,13 @@ impl PackageIdSpec { } } - match self.url { - Some(ref u) => u == package_id.source_id().url(), - None => true, + if let Some(u) = &self.url { + if u != package_id.source_id().url() { + return false; + } } + + true } /// Checks a list of `PackageId`s to find 1 that matches this `PackageIdSpec`. If 0, 2, or @@ -331,7 +334,10 @@ mod tests { fn ok(spec: &str, expected: PackageIdSpec, expected_rendered: &str) { let parsed = PackageIdSpec::parse(spec).unwrap(); assert_eq!(parsed, expected); - assert_eq!(parsed.to_string(), expected_rendered); + let rendered = parsed.to_string(); + assert_eq!(rendered, expected_rendered); + let reparsed = PackageIdSpec::parse(&rendered).unwrap(); + assert_eq!(reparsed, expected); } ok( @@ -424,6 +430,98 @@ mod tests { }, "foo@1.2", ); + + // pkgid-spec.md + ok( + "regex", + PackageIdSpec { + name: String::from("regex"), + version: None, + url: None, + }, + "regex", + ); + ok( + "regex@1.4", + PackageIdSpec { + name: String::from("regex"), + version: Some("1.4".parse().unwrap()), + url: None, + }, + "regex@1.4", + ); + ok( + "regex@1.4.3", + PackageIdSpec { + name: String::from("regex"), + version: Some("1.4.3".parse().unwrap()), + url: None, + }, + "regex@1.4.3", + ); + ok( + "https://github.com/rust-lang/crates.io-index#regex", + PackageIdSpec { + name: String::from("regex"), + version: None, + url: Some(Url::parse("https://github.com/rust-lang/crates.io-index").unwrap()), + }, + "https://github.com/rust-lang/crates.io-index#regex", + ); + ok( + "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("https://github.com/rust-lang/crates.io-index").unwrap()), + }, + "https://github.com/rust-lang/crates.io-index#regex@1.4.3", + ); + ok( + "https://github.com/rust-lang/cargo#0.52.0", + PackageIdSpec { + name: String::from("cargo"), + version: Some("0.52.0".parse().unwrap()), + url: Some(Url::parse("https://github.com/rust-lang/cargo").unwrap()), + }, + "https://github.com/rust-lang/cargo#0.52.0", + ); + ok( + "https://github.com/rust-lang/cargo#cargo-platform@0.1.2", + PackageIdSpec { + name: String::from("cargo-platform"), + version: Some("0.1.2".parse().unwrap()), + url: Some(Url::parse("https://github.com/rust-lang/cargo").unwrap()), + }, + "https://github.com/rust-lang/cargo#cargo-platform@0.1.2", + ); + ok( + "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()), + }, + "ssh://git@github.com/rust-lang/regex.git#regex@1.4.3", + ); + ok( + "file:///path/to/my/project/foo", + PackageIdSpec { + name: String::from("foo"), + version: None, + url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()), + }, + "file:///path/to/my/project/foo", + ); + ok( + "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()), + }, + "file:///path/to/my/project/foo#1.1.8", + ); } #[test] @@ -450,6 +548,12 @@ mod tests { assert!(PackageIdSpec::parse("foo@1.2.3").unwrap().matches(foo)); assert!(!PackageIdSpec::parse("foo@1.2.2").unwrap().matches(foo)); assert!(PackageIdSpec::parse("foo@1.2").unwrap().matches(foo)); + assert!(PackageIdSpec::parse("https://example.com#foo@1.2") + .unwrap() + .matches(foo)); + assert!(!PackageIdSpec::parse("https://bob.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)); diff --git a/src/cargo/core/source_id.rs b/src/cargo/core/source_id.rs index a8c162fbf18..e53b1704db0 100644 --- a/src/cargo/core/source_id.rs +++ b/src/cargo/core/source_id.rs @@ -188,17 +188,7 @@ impl SourceId { match kind { "git" => { let mut url = url.into_url()?; - let mut reference = GitReference::DefaultBranch; - for (k, v) in url.query_pairs() { - match &k[..] { - // Map older 'ref' to branch. - "branch" | "ref" => reference = GitReference::Branch(v.into_owned()), - - "rev" => reference = GitReference::Rev(v.into_owned()), - "tag" => reference = GitReference::Tag(v.into_owned()), - _ => {} - } - } + let reference = GitReference::from_query(url.query_pairs()); let precise = url.fragment().map(|s| s.to_owned()); url.set_fragment(None); url.set_query(None); @@ -752,6 +742,20 @@ impl PartialEq for SourceIdInner { } } +impl SourceKind { + pub(crate) fn protocol(&self) -> Option<&str> { + match self { + SourceKind::Path => Some("path"), + SourceKind::Git(_) => Some("git"), + SourceKind::Registry => Some("registry"), + // Sparse registry URL already includes the `sparse+` prefix + SourceKind::SparseRegistry => None, + SourceKind::LocalRegistry => Some("local-registry"), + SourceKind::Directory => Some("directory"), + } + } +} + /// Forwards to `Ord` impl PartialOrd for SourceKind { fn partial_cmp(&self, other: &SourceKind) -> Option { @@ -848,57 +852,46 @@ pub struct SourceIdAsUrl<'a> { impl<'a> fmt::Display for SourceIdAsUrl<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self.inner { - SourceIdInner { - kind: SourceKind::Path, - ref url, - .. - } => write!(f, "path+{}", url), - SourceIdInner { - kind: SourceKind::Git(ref reference), - ref url, - ref precise, - .. - } => { - write!(f, "git+{}", url)?; - if let Some(pretty) = reference.pretty_ref(self.encoded) { - write!(f, "?{}", pretty)?; - } - if let Some(precise) = precise.as_ref() { - write!(f, "#{}", precise)?; - } - Ok(()) - } - SourceIdInner { - kind: SourceKind::Registry, - ref url, - .. - } => { - write!(f, "registry+{url}") + if let Some(protocol) = self.inner.kind.protocol() { + write!(f, "{protocol}+")?; + } + write!(f, "{}", self.inner.url)?; + if let SourceIdInner { + kind: SourceKind::Git(ref reference), + ref precise, + .. + } = *self.inner + { + if let Some(pretty) = reference.pretty_ref(self.encoded) { + write!(f, "?{}", pretty)?; } - SourceIdInner { - kind: SourceKind::SparseRegistry, - ref url, - .. - } => { - // Sparse registry URL already includes the `sparse+` prefix - write!(f, "{url}") + if let Some(precise) = precise.as_ref() { + write!(f, "#{}", precise)?; } - SourceIdInner { - kind: SourceKind::LocalRegistry, - ref url, - .. - } => write!(f, "local-registry+{}", url), - SourceIdInner { - kind: SourceKind::Directory, - ref url, - .. - } => write!(f, "directory+{}", url), } + Ok(()) } } impl GitReference { + pub fn from_query( + query_pairs: impl Iterator, impl AsRef)>, + ) -> Self { + let mut reference = GitReference::DefaultBranch; + for (k, v) in query_pairs { + let v = v.as_ref(); + match k.as_ref() { + // Map older 'ref' to branch. + "branch" | "ref" => reference = GitReference::Branch(v.to_owned()), + + "rev" => reference = GitReference::Rev(v.to_owned()), + "tag" => reference = GitReference::Tag(v.to_owned()), + _ => {} + } + } + reference + } + /// Returns a `Display`able view of this git reference, or None if using /// the head of the default branch pub fn pretty_ref(&self, url_encoded: bool) -> Option> {