Skip to content

Commit ef7814f

Browse files
committed
refactor: replace SourceId in EncodableSourceId
1 parent 6a97f64 commit ef7814f

File tree

1 file changed

+85
-59
lines changed

1 file changed

+85
-59
lines changed

src/cargo/core/resolver/encode.rs

Lines changed: 85 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,16 @@ use crate::util::errors::CargoResult;
117117
use crate::util::interning::InternedString;
118118
use crate::util::{Graph, internal};
119119
use anyhow::{Context as _, bail};
120+
use cargo_util_schemas::core::SourceKind;
120121
use serde::de;
121122
use serde::ser;
122123
use serde::{Deserialize, Serialize};
124+
use std::cmp::Ordering;
123125
use std::collections::{BTreeMap, HashMap, HashSet};
124126
use std::fmt;
125127
use std::str::FromStr;
126128
use tracing::debug;
129+
use url::Url;
127130

128131
/// The `Cargo.lock` structure.
129132
#[derive(Serialize, Deserialize, Debug)]
@@ -207,16 +210,18 @@ pub fn into_resolve(
207210
}
208211
let id = match pkg
209212
.source
210-
.as_deref()
211-
.or_else(|| get_source_id(&path_deps, pkg))
213+
.as_ref()
214+
.map(|source| SourceId::from_url(&source.source_str()))
215+
.transpose()?
216+
.or_else(|| get_source_id(&path_deps, &pkg).copied())
212217
{
213218
// We failed to find a local package in the workspace.
214219
// It must have been removed and should be ignored.
215220
None => {
216221
debug!("path dependency now missing {} v{}", pkg.name, pkg.version);
217222
continue;
218223
}
219-
Some(&source) => PackageId::try_new(&pkg.name, &pkg.version, source)?,
224+
Some(source) => PackageId::try_new(&pkg.name, &pkg.version, source)?,
220225
};
221226

222227
// If a package has a checksum listed directly on it then record
@@ -274,7 +279,9 @@ pub fn into_resolve(
274279
// format. That means we have to handle the `None` case a bit more
275280
// carefully.
276281
match &enc_id.source {
277-
Some(source) => by_source.get(source).cloned(),
282+
Some(source) => by_source
283+
.get(&SourceId::from_url(&source.source_str()).unwrap())
284+
.cloned(),
278285
None => {
279286
// Look through all possible packages ids for this
280287
// name/version. If there's only one `path` dependency then
@@ -373,10 +380,12 @@ pub fn into_resolve(
373380
for pkg in resolve.patch.unused {
374381
let id = match pkg
375382
.source
376-
.as_deref()
377-
.or_else(|| get_source_id(&path_deps, &pkg))
383+
.as_ref()
384+
.map(|source| SourceId::from_url(&source.source_str()))
385+
.transpose()?
386+
.or_else(|| get_source_id(&path_deps, &pkg).copied())
378387
{
379-
Some(&src) => PackageId::try_new(&pkg.name, &pkg.version, src)?,
388+
Some(src) => PackageId::try_new(&pkg.name, &pkg.version, src)?,
380389
None => continue,
381390
};
382391
unused_patches.push(id);
@@ -530,54 +539,58 @@ pub struct EncodableDependency {
530539
replace: Option<EncodablePackageId>,
531540
}
532541

533-
/// Pretty much equivalent to [`SourceId`] with a different serialization method.
534-
///
535-
/// The serialization for `SourceId` doesn't do URL encode for parameters.
536-
/// In contrast, this type is aware of that whenever [`ResolveVersion`] allows
537-
/// us to do so (v4 or later).
538-
#[derive(Debug, PartialOrd, Ord, Clone)]
542+
#[derive(Debug, Clone)]
539543
pub struct EncodableSourceId {
540-
inner: SourceId,
541-
/// We don't care about the deserialization of this, as the `url` crate
542-
/// will always decode as the URL was encoded. Only when a [`Resolve`]
543-
/// turns into a [`EncodableResolve`] will it set the value accordingly
544-
/// via [`encodable_source_id`].
545-
encoded: bool,
544+
/// Full string of the source
545+
source_str: String,
546+
/// Used for sources ordering
547+
kind: SourceKind,
548+
/// Used for sources ordering
549+
url: Url,
546550
}
547551

548552
impl EncodableSourceId {
549-
/// Creates a `EncodableSourceId` that always encodes URL params.
550-
fn new(inner: SourceId) -> Self {
551-
Self {
552-
inner,
553-
encoded: true,
554-
}
553+
fn new(source: String) -> CargoResult<Self> {
554+
let source_str = source.clone();
555+
let (kind, url) = source
556+
.split_once('+')
557+
.ok_or_else(|| anyhow::format_err!("invalid source `{}`", source_str))?;
558+
559+
let url =
560+
Url::parse(url).map_err(|s| anyhow::format_err!("invalid url `{}`: {}", url, s))?;
561+
562+
let kind = match kind {
563+
"git" => {
564+
let reference = GitReference::from_query(url.query_pairs());
565+
SourceKind::Git(reference)
566+
}
567+
"registry" => SourceKind::Registry,
568+
"sparse" => SourceKind::SparseRegistry,
569+
"path" => SourceKind::Path,
570+
kind => anyhow::bail!("unsupported source protocol: {}", kind),
571+
};
572+
573+
Ok(Self {
574+
source_str,
575+
kind,
576+
url,
577+
})
555578
}
556579

557-
/// Creates a `EncodableSourceId` that doesn't encode URL params. This is
558-
/// for backward compatibility for order lockfile version.
559-
fn without_url_encoded(inner: SourceId) -> Self {
560-
Self {
561-
inner,
562-
encoded: false,
563-
}
580+
pub fn kind(&self) -> &SourceKind {
581+
&self.kind
564582
}
565583

566-
/// Encodes the inner [`SourceId`] as a URL.
567-
fn as_url(&self) -> impl fmt::Display + '_ {
568-
if self.encoded {
569-
self.inner.as_encoded_url()
570-
} else {
571-
self.inner.as_url()
572-
}
584+
pub fn url(&self) -> &Url {
585+
&self.url
573586
}
574-
}
575587

576-
impl std::ops::Deref for EncodableSourceId {
577-
type Target = SourceId;
588+
pub fn source_str(&self) -> &String {
589+
&self.source_str
590+
}
578591

579-
fn deref(&self) -> &Self::Target {
580-
&self.inner
592+
fn as_url(&self) -> impl fmt::Display + '_ {
593+
self.source_str.clone()
581594
}
582595
}
583596

@@ -596,28 +609,39 @@ impl<'de> de::Deserialize<'de> for EncodableSourceId {
596609
D: de::Deserializer<'de>,
597610
{
598611
let s = String::deserialize(d)?;
599-
let sid = SourceId::from_url(&s).map_err(de::Error::custom)?;
600-
Ok(EncodableSourceId {
601-
inner: sid,
602-
encoded: false,
603-
})
612+
Ok(EncodableSourceId::new(s).map_err(de::Error::custom)?)
604613
}
605614
}
606615

607616
impl std::hash::Hash for EncodableSourceId {
608617
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
609-
self.inner.hash(state)
618+
self.kind.hash(state);
619+
self.url.hash(state);
610620
}
611621
}
612622

613623
impl std::cmp::PartialEq for EncodableSourceId {
614624
fn eq(&self, other: &Self) -> bool {
615-
self.inner == other.inner
625+
self.kind == other.kind && self.url == other.url
616626
}
617627
}
618628

619629
impl std::cmp::Eq for EncodableSourceId {}
620630

631+
impl PartialOrd for EncodableSourceId {
632+
fn partial_cmp(&self, other: &EncodableSourceId) -> Option<Ordering> {
633+
Some(self.cmp(other))
634+
}
635+
}
636+
637+
impl Ord for EncodableSourceId {
638+
fn cmp(&self, other: &EncodableSourceId) -> Ordering {
639+
self.kind
640+
.cmp(&other.kind)
641+
.then_with(|| self.url.cmp(&other.url))
642+
}
643+
}
644+
621645
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
622646
pub struct EncodablePackageId {
623647
name: String,
@@ -648,7 +672,7 @@ impl FromStr for EncodablePackageId {
648672
let source_id = match s.next() {
649673
Some(s) => {
650674
if let Some(s) = s.strip_prefix('(').and_then(|s| s.strip_suffix(')')) {
651-
Some(SourceId::from_url(s)?)
675+
Some(EncodableSourceId::new(s.to_string())?)
652676
} else {
653677
anyhow::bail!("invalid serialized PackageId")
654678
}
@@ -659,8 +683,7 @@ impl FromStr for EncodablePackageId {
659683
Ok(EncodablePackageId {
660684
name: name.to_string(),
661685
version: version.map(|v| v.to_string()),
662-
// Default to url encoded.
663-
source: source_id.map(EncodableSourceId::new),
686+
source: source_id,
664687
})
665688
}
666689
}
@@ -850,10 +873,13 @@ fn encodable_source_id(id: SourceId, version: ResolveVersion) -> Option<Encodabl
850873
if id.is_path() {
851874
None
852875
} else {
853-
Some(if version >= ResolveVersion::V4 {
854-
EncodableSourceId::new(id)
855-
} else {
856-
EncodableSourceId::without_url_encoded(id)
857-
})
876+
Some(
877+
if version >= ResolveVersion::V4 {
878+
EncodableSourceId::new(id.as_encoded_url().to_string())
879+
} else {
880+
EncodableSourceId::new(id.as_url().to_string())
881+
}
882+
.expect("source ID should have valid URLs"),
883+
)
858884
}
859885
}

0 commit comments

Comments
 (0)