Skip to content

Commit 4b2f914

Browse files
committed
fix: encode URL params correctly for SourceId in Cargo.lock
The `Display` of SourceId stays the same, keeping it human-readable.
1 parent a53ffdf commit 4b2f914

File tree

2 files changed

+95
-11
lines changed

2 files changed

+95
-11
lines changed

src/cargo/core/resolver/encode.rs

Lines changed: 93 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,13 @@ impl EncodableResolve {
196196
let enc_id = EncodablePackageId {
197197
name: pkg.name.clone(),
198198
version: Some(pkg.version.clone()),
199-
source: pkg.source,
199+
source: pkg.source.clone(),
200200
};
201201

202202
if !all_pkgs.insert(enc_id.clone()) {
203203
anyhow::bail!("package `{}` is specified twice in the lockfile", pkg.name);
204204
}
205-
let id = match pkg.source.as_ref().or_else(|| path_deps.get(&pkg.name)) {
205+
let id = match pkg.source.as_deref().or_else(|| path_deps.get(&pkg.name)) {
206206
// We failed to find a local package in the workspace.
207207
// It must have been removed and should be ignored.
208208
None => {
@@ -366,7 +366,7 @@ impl EncodableResolve {
366366

367367
let mut unused_patches = Vec::new();
368368
for pkg in self.patch.unused {
369-
let id = match pkg.source.as_ref().or_else(|| path_deps.get(&pkg.name)) {
369+
let id = match pkg.source.as_deref().or_else(|| path_deps.get(&pkg.name)) {
370370
Some(&src) => PackageId::new(&pkg.name, &pkg.version, src)?,
371371
None => continue,
372372
};
@@ -488,17 +488,95 @@ impl Patch {
488488
pub struct EncodableDependency {
489489
name: String,
490490
version: String,
491-
source: Option<SourceId>,
491+
source: Option<EncodableSourceId>,
492492
checksum: Option<String>,
493493
dependencies: Option<Vec<EncodablePackageId>>,
494494
replace: Option<EncodablePackageId>,
495495
}
496496

497+
/// Pretty much equivalent to [`SourceId`] with a different serialization method.
498+
///
499+
/// The serialization for `SourceId` doesn't do URL encode for parameters.
500+
/// In contrast, this type is aware of that whenever [`ResolveVersion`] allows
501+
/// us to do so (v4 or later).
502+
///
503+
/// [`EncodableResolve`] turns into a `
504+
#[derive(Deserialize, Debug, PartialOrd, Ord, Clone)]
505+
#[serde(transparent)]
506+
pub struct EncodableSourceId {
507+
inner: SourceId,
508+
/// We don't care about the deserialization of this, as the `url` crate
509+
/// will always decode as the URL was encoded. Only when a [`Resolve`]
510+
/// turns into a [`EncodableResolve`] will it set the value accordingly
511+
/// via [`encodable_source_id`].
512+
#[serde(skip)]
513+
encoded: bool,
514+
}
515+
516+
impl EncodableSourceId {
517+
/// Creates a `EncodableSourceId` that always encodes URL params.
518+
fn new(inner: SourceId) -> Self {
519+
Self {
520+
inner,
521+
encoded: true,
522+
}
523+
}
524+
525+
/// Creates a `EncodableSourceId` that doesn't encode URL params. This is
526+
/// for backward compatibility for order lockfile version.
527+
fn without_url_encoded(inner: SourceId) -> Self {
528+
Self {
529+
inner,
530+
encoded: false,
531+
}
532+
}
533+
534+
/// Encodes the inner [`SourceId`] as a URL.
535+
fn as_url(&self) -> impl fmt::Display + '_ {
536+
if self.encoded {
537+
self.inner.as_encoded_url()
538+
} else {
539+
self.inner.as_url()
540+
}
541+
}
542+
}
543+
544+
impl std::ops::Deref for EncodableSourceId {
545+
type Target = SourceId;
546+
547+
fn deref(&self) -> &Self::Target {
548+
&self.inner
549+
}
550+
}
551+
552+
impl ser::Serialize for EncodableSourceId {
553+
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
554+
where
555+
S: ser::Serializer,
556+
{
557+
s.collect_str(&self.as_url())
558+
}
559+
}
560+
561+
impl std::hash::Hash for EncodableSourceId {
562+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
563+
self.inner.hash(state)
564+
}
565+
}
566+
567+
impl std::cmp::PartialEq for EncodableSourceId {
568+
fn eq(&self, other: &Self) -> bool {
569+
self.inner == other.inner
570+
}
571+
}
572+
573+
impl std::cmp::Eq for EncodableSourceId {}
574+
497575
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
498576
pub struct EncodablePackageId {
499577
name: String,
500578
version: Option<String>,
501-
source: Option<SourceId>,
579+
source: Option<EncodableSourceId>,
502580
}
503581

504582
impl fmt::Display for EncodablePackageId {
@@ -535,7 +613,8 @@ impl FromStr for EncodablePackageId {
535613
Ok(EncodablePackageId {
536614
name: name.to_string(),
537615
version: version.map(|v| v.to_string()),
538-
source: source_id,
616+
// Default to url encoded.
617+
source: source_id.map(EncodableSourceId::new),
539618
})
540619
}
541620
}
@@ -603,7 +682,7 @@ impl ser::Serialize for Resolve {
603682
.map(|id| EncodableDependency {
604683
name: id.name().to_string(),
605684
version: id.version().to_string(),
606-
source: encode_source(id.source_id()),
685+
source: encodable_source_id(id.source_id(), self.version()),
607686
dependencies: None,
608687
replace: None,
609688
checksum: if self.version() >= ResolveVersion::V2 {
@@ -676,7 +755,7 @@ fn encodable_resolve_node(
676755
EncodableDependency {
677756
name: id.name().to_string(),
678757
version: id.version().to_string(),
679-
source: encode_source(id.source_id()),
758+
source: encodable_source_id(id.source_id(), resolve.version()),
680759
dependencies: deps,
681760
replace,
682761
checksum: if resolve.version() >= ResolveVersion::V2 {
@@ -702,7 +781,7 @@ pub fn encodable_package_id(
702781
}
703782
}
704783
}
705-
let mut source = encode_source(id_to_encode).map(|s| s.with_precise(None));
784+
let mut source = encodable_source_id(id_to_encode.with_precise(None), resolve_version);
706785
if let Some(counts) = &state.counts {
707786
let version_counts = &counts[&id.name()];
708787
if version_counts[&id.version()] == 1 {
@@ -719,10 +798,13 @@ pub fn encodable_package_id(
719798
}
720799
}
721800

722-
fn encode_source(id: SourceId) -> Option<SourceId> {
801+
fn encodable_source_id(id: SourceId, version: ResolveVersion) -> Option<EncodableSourceId> {
723802
if id.is_path() {
724803
None
725804
} else {
726-
Some(id)
805+
Some(match version {
806+
ResolveVersion::V4 => EncodableSourceId::new(id),
807+
_ => EncodableSourceId::without_url_encoded(id),
808+
})
727809
}
728810
}

src/cargo/core/resolver/resolve.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ pub enum ResolveVersion {
8383
/// Unstable. Will collect a certain amount of changes and then go.
8484
///
8585
/// Changes made:
86+
///
87+
/// * SourceId URL serialization is aware of URL encoding.
8688
V4,
8789
}
8890

0 commit comments

Comments
 (0)