From 949e863cb908ca7b4d45e2f5ec9437b9f695ce76 Mon Sep 17 00:00:00 2001 From: Vito Secona Date: Fri, 19 Sep 2025 00:58:58 +0700 Subject: [PATCH] refactor: move lockfile schemas to `cargo-util-schemas` --- crates/cargo-util-schemas/src/lockfile.rs | 225 +++++++++++++++++++++- src/cargo/core/resolver/encode.rs | 222 +-------------------- src/cargo/core/resolver/mod.rs | 2 - src/cargo/core/resolver/resolve.rs | 2 +- src/cargo/ops/lockfile.rs | 2 +- 5 files changed, 226 insertions(+), 227 deletions(-) diff --git a/crates/cargo-util-schemas/src/lockfile.rs b/crates/cargo-util-schemas/src/lockfile.rs index 23323c93b..84bf4b0ac 100644 --- a/crates/cargo-util-schemas/src/lockfile.rs +++ b/crates/cargo-util-schemas/src/lockfile.rs @@ -1,10 +1,229 @@ +use std::collections::BTreeMap; +use std::fmt; +use std::{cmp::Ordering, str::FromStr}; + +use serde::{Deserialize, Serialize, de, ser}; +use url::Url; + +use crate::core::{GitReference, SourceKind}; + +/// The `Cargo.lock` structure. +#[derive(Serialize, Deserialize, Debug)] +pub struct EncodableResolve { + pub version: Option, + pub package: Option>, + /// `root` is optional to allow backward compatibility. + pub root: Option, + pub metadata: Option, + #[serde(default, skip_serializing_if = "Patch::is_empty")] + pub patch: Patch, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct Patch { + pub unused: Vec, +} + +pub type Metadata = BTreeMap; + +impl Patch { + fn is_empty(&self) -> bool { + self.unused.is_empty() + } +} + +#[derive(Serialize, Deserialize, Debug, PartialOrd, Ord, PartialEq, Eq)] +pub struct EncodableDependency { + pub name: String, + pub version: String, + pub source: Option, + pub checksum: Option, + pub dependencies: Option>, + pub replace: Option, +} + +#[derive(Debug, Clone)] +pub struct EncodableSourceId { + /// Full string of the source + source_str: String, + /// Used for sources ordering + kind: SourceKind, + /// Used for sources ordering + url: Url, +} + +impl EncodableSourceId { + pub fn new(source: String) -> Result { + let source_str = source.clone(); + let (kind, url) = source.split_once('+').ok_or_else(|| { + EncodableSourceIdError(EncodableSourceIdErrorKind::InvalidSource(source.clone()).into()) + })?; + + let url = Url::parse(url).map_err(|msg| EncodableSourceIdErrorKind::InvalidUrl { + url: url.to_string(), + msg: msg.to_string(), + })?; + + let kind = match kind { + "git" => { + let reference = GitReference::from_query(url.query_pairs()); + SourceKind::Git(reference) + } + "registry" => SourceKind::Registry, + "sparse" => SourceKind::SparseRegistry, + "path" => SourceKind::Path, + kind => { + return Err(EncodableSourceIdErrorKind::UnsupportedSource(kind.to_string()).into()); + } + }; + + Ok(Self { + source_str, + kind, + url, + }) + } + + pub fn kind(&self) -> &SourceKind { + &self.kind + } + + pub fn url(&self) -> &Url { + &self.url + } + + pub fn source_str(&self) -> &String { + &self.source_str + } + + pub fn as_url(&self) -> impl fmt::Display + '_ { + self.source_str.clone() + } +} + +impl ser::Serialize for EncodableSourceId { + fn serialize(&self, s: S) -> Result + where + S: ser::Serializer, + { + s.collect_str(&self.as_url()) + } +} + +impl<'de> de::Deserialize<'de> for EncodableSourceId { + fn deserialize(d: D) -> Result + where + D: de::Deserializer<'de>, + { + let s = String::deserialize(d)?; + Ok(EncodableSourceId::new(s).map_err(de::Error::custom)?) + } +} + +impl std::hash::Hash for EncodableSourceId { + fn hash(&self, state: &mut H) { + self.kind.hash(state); + self.url.hash(state); + } +} + +impl std::cmp::PartialEq for EncodableSourceId { + fn eq(&self, other: &Self) -> bool { + self.kind == other.kind && self.url == other.url + } +} + +impl std::cmp::Eq for EncodableSourceId {} + +impl PartialOrd for EncodableSourceId { + fn partial_cmp(&self, other: &EncodableSourceId) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for EncodableSourceId { + fn cmp(&self, other: &EncodableSourceId) -> Ordering { + self.kind + .cmp(&other.kind) + .then_with(|| self.url.cmp(&other.url)) + } +} + +#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone)] +pub struct EncodablePackageId { + pub name: String, + pub version: Option, + pub source: Option, +} + +impl fmt::Display for EncodablePackageId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name)?; + if let Some(s) = &self.version { + write!(f, " {}", s)?; + } + if let Some(s) = &self.source { + write!(f, " ({})", s.as_url())?; + } + Ok(()) + } +} + +impl FromStr for EncodablePackageId { + type Err = EncodablePackageIdError; + + fn from_str(s: &str) -> Result { + let mut s = s.splitn(3, ' '); + let name = s.next().unwrap(); + let version = s.next(); + let source_id = match s.next() { + Some(s) => { + if let Some(s) = s.strip_prefix('(').and_then(|s| s.strip_suffix(')')) { + Some(EncodableSourceId::new(s.to_string())?) + } else { + return Err(EncodablePackageIdErrorKind::InvalidSerializedPackageId.into()); + } + } + None => None, + }; + + Ok(EncodablePackageId { + name: name.to_string(), + version: version.map(|v| v.to_string()), + source: source_id, + }) + } +} + +impl ser::Serialize for EncodablePackageId { + fn serialize(&self, s: S) -> Result + where + S: ser::Serializer, + { + s.collect_str(self) + } +} + +impl<'de> de::Deserialize<'de> for EncodablePackageId { + fn deserialize(d: D) -> Result + where + D: de::Deserializer<'de>, + { + String::deserialize(d).and_then(|string| { + string + .parse::() + .map_err(de::Error::custom) + }) + } +} + #[derive(Debug, thiserror::Error)] #[error(transparent)] -pub struct EncodableSourceIdError(#[from] pub EncodableSourceIdErrorKind); +pub struct EncodableSourceIdError(#[from] EncodableSourceIdErrorKind); #[non_exhaustive] #[derive(Debug, thiserror::Error)] -pub enum EncodableSourceIdErrorKind { +enum EncodableSourceIdErrorKind { #[error("invalid source `{0}`")] InvalidSource(String), @@ -27,7 +246,7 @@ impl From for EncodablePackageIdError { #[non_exhaustive] #[derive(Debug, thiserror::Error)] -pub enum EncodablePackageIdErrorKind { +enum EncodablePackageIdErrorKind { #[error("invalid serialied PackageId")] InvalidSerializedPackageId, diff --git a/src/cargo/core/resolver/encode.rs b/src/cargo/core/resolver/encode.rs index 3569bc9a1..793c311c1 100644 --- a/src/cargo/core/resolver/encode.rs +++ b/src/cargo/core/resolver/encode.rs @@ -117,39 +117,12 @@ use crate::util::errors::CargoResult; use crate::util::interning::InternedString; use crate::util::{Graph, internal}; use anyhow::{Context as _, bail}; -use cargo_util_schemas::core::SourceKind; use cargo_util_schemas::lockfile::{ - EncodablePackageIdError, EncodablePackageIdErrorKind, EncodableSourceIdError, - EncodableSourceIdErrorKind, + EncodableDependency, EncodablePackageId, EncodableResolve, EncodableSourceId, Patch, }; -use serde::de; use serde::ser; -use serde::{Deserialize, Serialize}; -use std::cmp::Ordering; -use std::collections::{BTreeMap, HashMap, HashSet}; -use std::fmt; -use std::str::FromStr; +use std::collections::{HashMap, HashSet}; use tracing::debug; -use url::Url; - -/// The `Cargo.lock` structure. -#[derive(Serialize, Deserialize, Debug)] -pub struct EncodableResolve { - pub version: Option, - pub package: Option>, - /// `root` is optional to allow backward compatibility. - pub root: Option, - pub metadata: Option, - #[serde(default, skip_serializing_if = "Patch::is_empty")] - pub patch: Patch, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct Patch { - pub unused: Vec, -} - -pub type Metadata = BTreeMap; /// Convert a `Cargo.lock` to a Resolve. /// @@ -527,197 +500,6 @@ fn build_path_deps( } } -impl Patch { - fn is_empty(&self) -> bool { - self.unused.is_empty() - } -} - -#[derive(Serialize, Deserialize, Debug, PartialOrd, Ord, PartialEq, Eq)] -pub struct EncodableDependency { - pub name: String, - pub version: String, - pub source: Option, - pub checksum: Option, - pub dependencies: Option>, - pub replace: Option, -} - -#[derive(Debug, Clone)] -pub struct EncodableSourceId { - /// Full string of the source - source_str: String, - /// Used for sources ordering - kind: SourceKind, - /// Used for sources ordering - url: Url, -} - -impl EncodableSourceId { - pub fn new(source: String) -> Result { - let source_str = source.clone(); - let (kind, url) = source.split_once('+').ok_or_else(|| { - EncodableSourceIdError(EncodableSourceIdErrorKind::InvalidSource(source.clone()).into()) - })?; - - let url = Url::parse(url).map_err(|msg| EncodableSourceIdErrorKind::InvalidUrl { - url: url.to_string(), - msg: msg.to_string(), - })?; - - let kind = match kind { - "git" => { - let reference = GitReference::from_query(url.query_pairs()); - SourceKind::Git(reference) - } - "registry" => SourceKind::Registry, - "sparse" => SourceKind::SparseRegistry, - "path" => SourceKind::Path, - kind => { - return Err(EncodableSourceIdErrorKind::UnsupportedSource(kind.to_string()).into()); - } - }; - - Ok(Self { - source_str, - kind, - url, - }) - } - - pub fn kind(&self) -> &SourceKind { - &self.kind - } - - pub fn url(&self) -> &Url { - &self.url - } - - pub fn source_str(&self) -> &String { - &self.source_str - } - - pub fn as_url(&self) -> impl fmt::Display + '_ { - self.source_str.clone() - } -} - -impl ser::Serialize for EncodableSourceId { - fn serialize(&self, s: S) -> Result - where - S: ser::Serializer, - { - s.collect_str(&self.as_url()) - } -} - -impl<'de> de::Deserialize<'de> for EncodableSourceId { - fn deserialize(d: D) -> Result - where - D: de::Deserializer<'de>, - { - let s = String::deserialize(d)?; - Ok(EncodableSourceId::new(s).map_err(de::Error::custom)?) - } -} - -impl std::hash::Hash for EncodableSourceId { - fn hash(&self, state: &mut H) { - self.kind.hash(state); - self.url.hash(state); - } -} - -impl std::cmp::PartialEq for EncodableSourceId { - fn eq(&self, other: &Self) -> bool { - self.kind == other.kind && self.url == other.url - } -} - -impl std::cmp::Eq for EncodableSourceId {} - -impl PartialOrd for EncodableSourceId { - fn partial_cmp(&self, other: &EncodableSourceId) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for EncodableSourceId { - fn cmp(&self, other: &EncodableSourceId) -> Ordering { - self.kind - .cmp(&other.kind) - .then_with(|| self.url.cmp(&other.url)) - } -} - -#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone)] -pub struct EncodablePackageId { - pub name: String, - pub version: Option, - pub source: Option, -} - -impl fmt::Display for EncodablePackageId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.name)?; - if let Some(s) = &self.version { - write!(f, " {}", s)?; - } - if let Some(s) = &self.source { - write!(f, " ({})", s.as_url())?; - } - Ok(()) - } -} - -impl FromStr for EncodablePackageId { - type Err = EncodablePackageIdError; - - fn from_str(s: &str) -> Result { - let mut s = s.splitn(3, ' '); - let name = s.next().unwrap(); - let version = s.next(); - let source_id = match s.next() { - Some(s) => { - if let Some(s) = s.strip_prefix('(').and_then(|s| s.strip_suffix(')')) { - Some(EncodableSourceId::new(s.to_string())?) - } else { - return Err(EncodablePackageIdErrorKind::InvalidSerializedPackageId.into()); - } - } - None => None, - }; - - Ok(EncodablePackageId { - name: name.to_string(), - version: version.map(|v| v.to_string()), - source: source_id, - }) - } -} - -impl ser::Serialize for EncodablePackageId { - fn serialize(&self, s: S) -> Result - where - S: ser::Serializer, - { - s.collect_str(self) - } -} - -impl<'de> de::Deserialize<'de> for EncodablePackageId { - fn deserialize(d: D) -> Result - where - D: de::Deserializer<'de>, - { - String::deserialize(d).and_then(|string| { - string - .parse::() - .map_err(de::Error::custom) - }) - } -} - impl ser::Serialize for Resolve { #[tracing::instrument(skip_all)] fn serialize(&self, s: S) -> Result diff --git a/src/cargo/core/resolver/mod.rs b/src/cargo/core/resolver/mod.rs index 1e82c9e72..dd65e17d3 100644 --- a/src/cargo/core/resolver/mod.rs +++ b/src/cargo/core/resolver/mod.rs @@ -76,8 +76,6 @@ use self::features::RequestedFeatures; use self::types::{ConflictMap, ConflictReason, DepsFrame}; use self::types::{FeaturesSet, RcVecIter, RemainingDeps, ResolverProgress}; -pub use self::encode::Metadata; -pub use self::encode::{EncodableDependency, EncodablePackageId, EncodableResolve}; pub use self::errors::{ActivateError, ActivateResult, ResolveError}; pub use self::features::{CliFeatures, ForceAllTargets, HasDevUnits}; pub use self::resolve::{Resolve, ResolveVersion}; diff --git a/src/cargo/core/resolver/resolve.rs b/src/cargo/core/resolver/resolve.rs index ef5b7f221..536a22631 100644 --- a/src/cargo/core/resolver/resolve.rs +++ b/src/cargo/core/resolver/resolve.rs @@ -1,12 +1,12 @@ use cargo_util_schemas::core::PartialVersion; use cargo_util_schemas::manifest::RustVersion; -use super::encode::Metadata; use crate::core::dependency::DepKind; use crate::core::{Dependency, PackageId, PackageIdSpec, PackageIdSpecQuery, Summary, Target}; use crate::util::Graph; use crate::util::errors::CargoResult; use crate::util::interning::InternedString; +use cargo_util_schemas::lockfile::Metadata; use std::borrow::Borrow; use std::collections::{HashMap, HashSet}; use std::fmt; diff --git a/src/cargo/ops/lockfile.rs b/src/cargo/ops/lockfile.rs index 5102df9d0..9f441283c 100644 --- a/src/cargo/ops/lockfile.rs +++ b/src/cargo/ops/lockfile.rs @@ -1,12 +1,12 @@ use std::io::prelude::*; -use crate::core::resolver::encode::EncodableResolve; use crate::core::resolver::encode::into_resolve; use crate::core::{Resolve, ResolveVersion, Workspace}; use crate::util::Filesystem; use crate::util::errors::CargoResult; use anyhow::Context as _; +use cargo_util_schemas::lockfile::EncodableResolve; pub const LOCKFILE_NAME: &str = "Cargo.lock";