refactor: move lockfile schemas to cargo-util-schemas

This commit is contained in:
Vito Secona 2025-09-19 00:58:58 +07:00
parent efc42e1342
commit 949e863cb9
5 changed files with 226 additions and 227 deletions

View File

@ -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<u32>,
pub package: Option<Vec<EncodableDependency>>,
/// `root` is optional to allow backward compatibility.
pub root: Option<EncodableDependency>,
pub metadata: Option<Metadata>,
#[serde(default, skip_serializing_if = "Patch::is_empty")]
pub patch: Patch,
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct Patch {
pub unused: Vec<EncodableDependency>,
}
pub type Metadata = BTreeMap<String, String>;
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<EncodableSourceId>,
pub checksum: Option<String>,
pub dependencies: Option<Vec<EncodablePackageId>>,
pub replace: Option<EncodablePackageId>,
}
#[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<Self, EncodableSourceIdError> {
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<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
s.collect_str(&self.as_url())
}
}
impl<'de> de::Deserialize<'de> for EncodableSourceId {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
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<H: std::hash::Hasher>(&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<Ordering> {
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<String>,
pub source: Option<EncodableSourceId>,
}
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<EncodablePackageId, Self::Err> {
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<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
s.collect_str(self)
}
}
impl<'de> de::Deserialize<'de> for EncodablePackageId {
fn deserialize<D>(d: D) -> Result<EncodablePackageId, D::Error>
where
D: de::Deserializer<'de>,
{
String::deserialize(d).and_then(|string| {
string
.parse::<EncodablePackageId>()
.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<EncodableSourceIdError> for EncodablePackageIdError {
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum EncodablePackageIdErrorKind {
enum EncodablePackageIdErrorKind {
#[error("invalid serialied PackageId")]
InvalidSerializedPackageId,

View File

@ -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<u32>,
pub package: Option<Vec<EncodableDependency>>,
/// `root` is optional to allow backward compatibility.
pub root: Option<EncodableDependency>,
pub metadata: Option<Metadata>,
#[serde(default, skip_serializing_if = "Patch::is_empty")]
pub patch: Patch,
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct Patch {
pub unused: Vec<EncodableDependency>,
}
pub type Metadata = BTreeMap<String, String>;
/// 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<EncodableSourceId>,
pub checksum: Option<String>,
pub dependencies: Option<Vec<EncodablePackageId>>,
pub replace: Option<EncodablePackageId>,
}
#[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<Self, EncodableSourceIdError> {
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<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
s.collect_str(&self.as_url())
}
}
impl<'de> de::Deserialize<'de> for EncodableSourceId {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
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<H: std::hash::Hasher>(&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<Ordering> {
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<String>,
pub source: Option<EncodableSourceId>,
}
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<EncodablePackageId, Self::Err> {
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<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
s.collect_str(self)
}
}
impl<'de> de::Deserialize<'de> for EncodablePackageId {
fn deserialize<D>(d: D) -> Result<EncodablePackageId, D::Error>
where
D: de::Deserializer<'de>,
{
String::deserialize(d).and_then(|string| {
string
.parse::<EncodablePackageId>()
.map_err(de::Error::custom)
})
}
}
impl ser::Serialize for Resolve {
#[tracing::instrument(skip_all)]
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>

View File

@ -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};

View File

@ -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;

View File

@ -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";