From a28c7e3016fd3dbfa40293ac0bbec9faf4d0b559 Mon Sep 17 00:00:00 2001 From: Wolf Vollprecht Date: Fri, 28 Feb 2025 11:17:07 +0100 Subject: [PATCH 1/2] fix roundtrip of arch/platform in lock files --- .../rattler_conda_types/src/repo_data/mod.rs | 52 ++--- crates/rattler_lock/Cargo.toml | 3 +- crates/rattler_lock/src/lib.rs | 52 ++++- .../src/parse/models/v6/conda_package_data.rs | 18 +- ...uilder__test__merge_records_and_purls.snap | 2 - .../src/utils/serde/raw_conda_package_data.rs | 180 ++++++++++++++++++ .../_libgcc_mutex-0.1-conda_forge.json | 16 ++ 7 files changed, 272 insertions(+), 51 deletions(-) create mode 100644 crates/rattler_lock/src/utils/serde/raw_conda_package_data.rs create mode 100644 test-data/repodata-records/_libgcc_mutex-0.1-conda_forge.json diff --git a/crates/rattler_conda_types/src/repo_data/mod.rs b/crates/rattler_conda_types/src/repo_data/mod.rs index 6a1f056ca8..6e30641f56 100644 --- a/crates/rattler_conda_types/src/repo_data/mod.rs +++ b/crates/rattler_conda_types/src/repo_data/mod.rs @@ -26,7 +26,7 @@ use crate::{ serde::{sort_map_alphabetically, DeserializeFromStrUnchecked}, UrlWithTrailingSlash, }, - Channel, MatchSpec, Matches, NoArchType, PackageName, PackageUrl, ParseMatchSpecError, + Arch, Channel, MatchSpec, Matches, NoArchType, PackageName, PackageUrl, ParseMatchSpecError, ParseStrictness, Platform, RepoDataRecord, VersionWithSource, }; @@ -93,7 +93,10 @@ pub trait RecordFromPath { #[sorted] #[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone, Hash)] pub struct PackageRecord { - /// Optionally the architecture the package supports + /// Optionally the architecture the package supports. This is almost + /// always the second part of the `subdir` string. Except for `64` which + /// maps to `x86_64` and `32` which maps to `x86`. This will be `None` if + /// the package is `noarch`. pub arch: Option, /// The build string of the package @@ -152,8 +155,11 @@ pub struct PackageRecord { #[serde(skip_serializing_if = "NoArchType::is_none")] pub noarch: NoArchType, - /// Optionally the platform the package supports - pub platform: Option, // Note that this does not match the [`Platform`] enum.. + /// Optionally the platform the package supports. + /// Note that this does not match the [`Platform`] enum, but is only the first + /// part of the platform (e.g. `linux`, `osx`, `win`, ...). + /// The `subdir` field contains the `Platform` enum. + pub platform: Option, /// Package identifiers of packages that are equivalent to this package but /// from other ecosystems. @@ -457,33 +463,17 @@ fn determine_subdir( let platform = platform.ok_or(ConvertSubdirError::PlatformEmpty)?; let arch = arch.ok_or(ConvertSubdirError::ArchEmpty)?; - let plat = match platform.as_ref() { - "linux" => match arch.as_ref() { - "x86" => Ok(Platform::Linux32), - "x86_64" => Ok(Platform::Linux64), - "aarch64" => Ok(Platform::LinuxAarch64), - "armv61" => Ok(Platform::LinuxArmV6l), - "armv71" => Ok(Platform::LinuxArmV7l), - "ppc64le" => Ok(Platform::LinuxPpc64le), - "ppc64" => Ok(Platform::LinuxPpc64), - "s390x" => Ok(Platform::LinuxS390X), - _ => Err(ConvertSubdirError::NoKnownCombination { platform, arch }), - }, - "osx" => match arch.as_ref() { - "x86_64" => Ok(Platform::Osx64), - "arm64" => Ok(Platform::OsxArm64), - _ => Err(ConvertSubdirError::NoKnownCombination { platform, arch }), - }, - "windows" => match arch.as_ref() { - "x86" => Ok(Platform::Win32), - "x86_64" => Ok(Platform::Win64), - "arm64" => Ok(Platform::WinArm64), - _ => Err(ConvertSubdirError::NoKnownCombination { platform, arch }), - }, - _ => Err(ConvertSubdirError::NoKnownCombination { platform, arch }), - }?; - // Convert back to Platform string which should correspond to known subdirs - Ok(plat.to_string()) + match arch.parse::() { + Ok(arch) => { + let arch_str = match arch { + Arch::X86 => "32", + Arch::X86_64 => "64", + _ => arch.as_str(), + }; + Ok(format!("{}-{}", platform, arch_str)) + } + Err(_) => Err(ConvertSubdirError::NoKnownCombination { platform, arch }), + } } impl PackageRecord { diff --git a/crates/rattler_lock/Cargo.toml b/crates/rattler_lock/Cargo.toml index 34bbde9baa..c627814c29 100644 --- a/crates/rattler_lock/Cargo.toml +++ b/crates/rattler_lock/Cargo.toml @@ -31,5 +31,6 @@ typed-path = { workspace = true } [dev-dependencies] insta = { workspace = true, features = ["yaml"] } +serde_json = { workspace = true } similar-asserts = { workspace = true } -rstest = { workspace = true } +rstest = { workspace = true } \ No newline at end of file diff --git a/crates/rattler_lock/src/lib.rs b/crates/rattler_lock/src/lib.rs index 8411cda1f7..d82b436fa8 100644 --- a/crates/rattler_lock/src/lib.rs +++ b/crates/rattler_lock/src/lib.rs @@ -527,7 +527,7 @@ mod test { str::FromStr, }; - use rattler_conda_types::Platform; + use rattler_conda_types::{Platform, RepoDataRecord}; use rstest::*; use super::{LockFile, DEFAULT_ENVIRONMENT_NAME}; @@ -661,4 +661,54 @@ mod test { let conda_lock = LockFile::from_path(&path).unwrap(); assert!(!conda_lock.is_empty()); } + + #[test] + fn solve_roundtrip() { + // load repodata from JSON + let path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../test-data/repodata-records/_libgcc_mutex-0.1-conda_forge.json"); + let content = std::fs::read_to_string(&path).unwrap(); + let repodata_record: RepoDataRecord = serde_json::from_str(&content).unwrap(); + + // check that the repodata record is as expected + assert_eq!(repodata_record.package_record.arch, None); + assert_eq!(repodata_record.package_record.platform, None); + + // create a lockfile with the repodata record + let lock_file = LockFile::builder() + .with_conda_package( + DEFAULT_ENVIRONMENT_NAME, + Platform::Linux64, + repodata_record.clone().into(), + ) + .finish(); + + // serialize the lockfile + let rendered_lock_file = lock_file.render_to_string().unwrap(); + + // parse the lockfile + let parsed_lock_file = LockFile::from_str(&rendered_lock_file).unwrap(); + // get repodata record from parsed lockfile + let repodata_records = parsed_lock_file + .environment(DEFAULT_ENVIRONMENT_NAME) + .unwrap() + .conda_repodata_records(Platform::Linux64) + .unwrap() + .unwrap(); + + // These are not equal because the one from `repodata_records[0]` contains arch and platform. + let repodata_record_two = repodata_records[0].clone(); + assert_eq!( + repodata_record_two.package_record.arch, + Some("x86_64".to_string()) + ); + assert_eq!( + repodata_record_two.package_record.platform, + Some("linux".to_string()) + ); + + // But if we render it again, the lockfile should look the same at least + let rerendered_lock_file_two = parsed_lock_file.render_to_string().unwrap(); + assert_eq!(rendered_lock_file, rerendered_lock_file_two); + } } diff --git a/crates/rattler_lock/src/parse/models/v6/conda_package_data.rs b/crates/rattler_lock/src/parse/models/v6/conda_package_data.rs index 30f91fc8aa..9ac05b23c8 100644 --- a/crates/rattler_lock/src/parse/models/v6/conda_package_data.rs +++ b/crates/rattler_lock/src/parse/models/v6/conda_package_data.rs @@ -80,11 +80,6 @@ pub(crate) struct CondaPackageDataModel<'a> { pub extra_depends: Cow<'a, BTreeMap>>, // Additional properties (in semi alphabetic order but grouped by commonality) - #[serde(default, skip_serializing_if = "Option::is_none")] - pub arch: Option>>, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub platform: Option>>, - #[serde(default, skip_serializing_if = "Option::is_none")] pub channel: Option>>, @@ -175,8 +170,8 @@ impl<'a> TryFrom> for CondaPackageData { .or(derived.name) .ok_or_else(|| ConversionError::Missing("name".to_string()))?, noarch, - arch: value.arch.map_or(derived_arch, Cow::into_owned), - platform: value.platform.map_or(derived_platform, Cow::into_owned), + arch: derived_arch, + platform: derived_platform, purls: value.purls.into_owned(), sha256: value.sha256, size: value.size.into_owned(), @@ -241,13 +236,6 @@ impl<'a> From<&'a CondaPackageData> for CondaPackageDataModel<'a> { derived.subdir.as_deref().unwrap_or(&package_record.subdir), derived.build.as_deref().unwrap_or(&package_record.build), ); - let (derived_arch, derived_platform) = derived_fields::derive_arch_and_platform( - derived.subdir.as_deref().unwrap_or(&package_record.subdir), - ); - - // Polyfill the arch and platform values if they are not present. - let arch = package_record.arch.clone().or(derived_arch); - let platform = package_record.platform.clone().or(derived_platform); let channel = value.as_binary().and_then(|binary| binary.channel.as_ref()); let file_name = value.as_binary().map(|binary| binary.file_name.as_str()); @@ -282,8 +270,6 @@ impl<'a> From<&'a CondaPackageData> for CondaPackageDataModel<'a> { depends: Cow::Borrowed(&package_record.depends), constrains: Cow::Borrowed(&package_record.constrains), extra_depends: Cow::Borrowed(&package_record.extra_depends), - arch: (package_record.arch != arch).then_some(Cow::Owned(arch)), - platform: (package_record.platform != platform).then_some(Cow::Owned(platform)), md5: package_record.md5, legacy_bz2_md5: package_record.legacy_bz2_md5, sha256: package_record.sha256, diff --git a/crates/rattler_lock/src/snapshots/rattler_lock__builder__test__merge_records_and_purls.snap b/crates/rattler_lock/src/snapshots/rattler_lock__builder__test__merge_records_and_purls.snap index 25f71a6882..a8a71d82ce 100644 --- a/crates/rattler_lock/src/snapshots/rattler_lock__builder__test__merge_records_and_purls.snap +++ b/crates/rattler_lock/src/snapshots/rattler_lock__builder__test__merge_records_and_purls.snap @@ -16,8 +16,6 @@ environments: - conda: https://prefix.dev/example/linux-64/foobar-1.0.0-build.tar.bz2 packages: - conda: https://prefix.dev/example/linux-64/foobar-1.0.0-build.tar.bz2 - arch: x86_64 - platform: linux channel: null purls: - pkg:pypi/foobar@1.0.0 diff --git a/crates/rattler_lock/src/utils/serde/raw_conda_package_data.rs b/crates/rattler_lock/src/utils/serde/raw_conda_package_data.rs new file mode 100644 index 0000000000..51ec1392ee --- /dev/null +++ b/crates/rattler_lock/src/utils/serde/raw_conda_package_data.rs @@ -0,0 +1,180 @@ +use crate::CondaPackageData; +use rattler_conda_types::{ + BuildNumber, NoArchType, PackageName, PackageRecord, PackageUrl, VersionWithSource, +}; +use rattler_digest::{serde::SerializableHash, Md5Hash, Sha256Hash}; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use std::borrow::Cow; +use std::cmp::Ordering; +use url::Url; + +fn is_default(value: &T) -> bool { + value == &T::default() +} + +/// A helper struct that wraps all fields of a [`CondaPackageData`] and allows for easy conversion +/// between the two. +/// +/// This type provides full control over the order of the fields when serializing. This is important +/// because one of the design goals is that it should be easy to read the lock file. A +/// [`PackageRecord`] is serialized in alphabetic order which might not be the most readable. This +/// type instead puts the "most important" fields at the top followed by more detailed ones. +/// +/// So note that for reproducibility the order of these fields should not change or should be +/// reflected in a version change. +// +/// This type also adds more default values (e.g. for `build_number` and `build_string`). +/// +/// The complexity with `Cow<_>` types is introduced to allow both efficient deserialization and +/// serialization without requiring all data to be cloned when serializing. We want to be able +/// to use the same type of both serialization and deserialization to ensure that when any of the +/// types involved change we are forced to update this struct as well. +#[serde_as] +#[derive(Serialize, Deserialize, Eq, PartialEq)] +pub(crate) struct RawCondaPackageData<'a> { + // Unique identifiers go to the top + pub name: Cow<'a, PackageName>, + pub version: Cow<'a, VersionWithSource>, + #[serde(default, skip_serializing_if = "is_default")] + pub build: Cow<'a, String>, + #[serde(default, skip_serializing_if = "is_default")] + pub build_number: BuildNumber, + #[serde(default)] + pub subdir: Cow<'a, String>, + #[serde(skip_serializing_if = "NoArchType::is_none")] + pub noarch: Cow<'a, NoArchType>, + + // Followed by the URL of the package + pub url: Cow<'a, Url>, + + // Then the hashes + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde_as(as = "Option>")] + pub sha256: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde_as(as = "Option>")] + pub md5: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub legacy_bz2_md5: Cow<'a, Option>, + + // Dependencies + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub depends: Cow<'a, Vec>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub constrains: Cow<'a, Vec>, + + // Additional properties (in semi alphabetic order but grouped by commonality) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub arch: Cow<'a, Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub platform: Cow<'a, Option>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub channel: Cow<'a, Option>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub features: Cow<'a, Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub track_features: Cow<'a, Vec>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub file_name: Cow<'a, Option>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub license: Cow<'a, Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub license_family: Cow<'a, Option>, + + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub purls: Cow<'a, Vec>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub size: Cow<'a, Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub legacy_bz2_size: Cow<'a, Option>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde_as(as = "Option")] + pub timestamp: Option>, +} + +impl<'a> From> for CondaPackageData { + fn from(value: RawCondaPackageData<'a>) -> Self { + Self { + package_record: PackageRecord { + arch: value.arch.into_owned(), + build: value.build.into_owned(), + build_number: value.build_number, + constrains: value.constrains.into_owned(), + depends: value.depends.into_owned(), + features: value.features.into_owned(), + legacy_bz2_md5: value.legacy_bz2_md5.into_owned(), + legacy_bz2_size: value.legacy_bz2_size.into_owned(), + license: value.license.into_owned(), + license_family: value.license_family.into_owned(), + md5: value.md5, + name: value.name.into_owned(), + noarch: value.noarch.into_owned(), + platform: value.platform.into_owned(), + purls: value.purls.into_owned(), + sha256: value.sha256, + size: value.size.into_owned(), + subdir: value.subdir.into_owned(), + timestamp: value.timestamp, + track_features: value.track_features.into_owned(), + version: value.version.into_owned(), + }, + url: value.url.into_owned(), + file_name: value.file_name.into_owned(), + channel: value.channel.into_owned(), + } + } +} + +impl<'a> From<&'a CondaPackageData> for RawCondaPackageData<'a> { + fn from(value: &'a CondaPackageData) -> Self { + Self { + name: Cow::Borrowed(&value.package_record.name), + version: Cow::Borrowed(&value.package_record.version), + build: Cow::Borrowed(&value.package_record.build), + build_number: value.package_record.build_number, + subdir: Cow::Borrowed(&value.package_record.subdir), + noarch: Cow::Borrowed(&value.package_record.noarch), + url: Cow::Borrowed(&value.url), + channel: Cow::Borrowed(&value.channel), + file_name: Cow::Borrowed(&value.file_name), + purls: Cow::Borrowed(&value.package_record.purls), + depends: Cow::Borrowed(&value.package_record.depends), + constrains: Cow::Borrowed(&value.package_record.constrains), + platform: Cow::Borrowed(&value.package_record.platform), + arch: Cow::Borrowed(&value.package_record.arch), + md5: value.package_record.md5, + legacy_bz2_md5: Cow::Borrowed(&value.package_record.legacy_bz2_md5), + sha256: value.package_record.sha256, + size: Cow::Borrowed(&value.package_record.size), + legacy_bz2_size: Cow::Borrowed(&value.package_record.legacy_bz2_size), + timestamp: value.package_record.timestamp, + features: Cow::Borrowed(&value.package_record.features), + track_features: Cow::Borrowed(&value.package_record.track_features), + license: Cow::Borrowed(&value.package_record.license), + license_family: Cow::Borrowed(&value.package_record.license_family), + } + } +} + +impl<'a> PartialOrd for RawCondaPackageData<'a> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl<'a> Ord for RawCondaPackageData<'a> { + fn cmp(&self, other: &Self) -> Ordering { + self.name + .cmp(&other.name) + .then_with(|| self.version.cmp(&other.version)) + .then_with(|| self.build.cmp(&other.build)) + .then_with(|| self.subdir.cmp(&other.subdir)) + } +} diff --git a/test-data/repodata-records/_libgcc_mutex-0.1-conda_forge.json b/test-data/repodata-records/_libgcc_mutex-0.1-conda_forge.json new file mode 100644 index 0000000000..b1bbd36363 --- /dev/null +++ b/test-data/repodata-records/_libgcc_mutex-0.1-conda_forge.json @@ -0,0 +1,16 @@ +{ + "build": "conda_forge", + "build_number": 0, + "depends": [], + "license": "None", + "md5": "d7c89558ba9fa0495403155b64376d81", + "name": "_libgcc_mutex", + "sha256": "fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726", + "size": 2562, + "subdir": "linux-64", + "timestamp": 1578324546067, + "version": "0.1", + "fn": "_libgcc_mutex-0.1-conda_forge.tar.bz2", + "url": "https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2", + "channel": "https://conda.anaconda.org/conda-forge/" +} \ No newline at end of file From 33337610b7149642df6a3f8e1c60b5b117bcadf1 Mon Sep 17 00:00:00 2001 From: Wolf Vollprecht Date: Fri, 28 Feb 2025 11:19:20 +0100 Subject: [PATCH 2/2] remove file --- .../src/utils/serde/raw_conda_package_data.rs | 180 ------------------ 1 file changed, 180 deletions(-) delete mode 100644 crates/rattler_lock/src/utils/serde/raw_conda_package_data.rs diff --git a/crates/rattler_lock/src/utils/serde/raw_conda_package_data.rs b/crates/rattler_lock/src/utils/serde/raw_conda_package_data.rs deleted file mode 100644 index 51ec1392ee..0000000000 --- a/crates/rattler_lock/src/utils/serde/raw_conda_package_data.rs +++ /dev/null @@ -1,180 +0,0 @@ -use crate::CondaPackageData; -use rattler_conda_types::{ - BuildNumber, NoArchType, PackageName, PackageRecord, PackageUrl, VersionWithSource, -}; -use rattler_digest::{serde::SerializableHash, Md5Hash, Sha256Hash}; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; -use std::borrow::Cow; -use std::cmp::Ordering; -use url::Url; - -fn is_default(value: &T) -> bool { - value == &T::default() -} - -/// A helper struct that wraps all fields of a [`CondaPackageData`] and allows for easy conversion -/// between the two. -/// -/// This type provides full control over the order of the fields when serializing. This is important -/// because one of the design goals is that it should be easy to read the lock file. A -/// [`PackageRecord`] is serialized in alphabetic order which might not be the most readable. This -/// type instead puts the "most important" fields at the top followed by more detailed ones. -/// -/// So note that for reproducibility the order of these fields should not change or should be -/// reflected in a version change. -// -/// This type also adds more default values (e.g. for `build_number` and `build_string`). -/// -/// The complexity with `Cow<_>` types is introduced to allow both efficient deserialization and -/// serialization without requiring all data to be cloned when serializing. We want to be able -/// to use the same type of both serialization and deserialization to ensure that when any of the -/// types involved change we are forced to update this struct as well. -#[serde_as] -#[derive(Serialize, Deserialize, Eq, PartialEq)] -pub(crate) struct RawCondaPackageData<'a> { - // Unique identifiers go to the top - pub name: Cow<'a, PackageName>, - pub version: Cow<'a, VersionWithSource>, - #[serde(default, skip_serializing_if = "is_default")] - pub build: Cow<'a, String>, - #[serde(default, skip_serializing_if = "is_default")] - pub build_number: BuildNumber, - #[serde(default)] - pub subdir: Cow<'a, String>, - #[serde(skip_serializing_if = "NoArchType::is_none")] - pub noarch: Cow<'a, NoArchType>, - - // Followed by the URL of the package - pub url: Cow<'a, Url>, - - // Then the hashes - #[serde(default, skip_serializing_if = "Option::is_none")] - #[serde_as(as = "Option>")] - pub sha256: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - #[serde_as(as = "Option>")] - pub md5: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub legacy_bz2_md5: Cow<'a, Option>, - - // Dependencies - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub depends: Cow<'a, Vec>, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub constrains: Cow<'a, Vec>, - - // Additional properties (in semi alphabetic order but grouped by commonality) - #[serde(default, skip_serializing_if = "Option::is_none")] - pub arch: Cow<'a, Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub platform: Cow<'a, Option>, - - #[serde(default, skip_serializing_if = "Option::is_none")] - pub channel: Cow<'a, Option>, - - #[serde(default, skip_serializing_if = "Option::is_none")] - pub features: Cow<'a, Option>, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub track_features: Cow<'a, Vec>, - - #[serde(default, skip_serializing_if = "Option::is_none")] - pub file_name: Cow<'a, Option>, - - #[serde(default, skip_serializing_if = "Option::is_none")] - pub license: Cow<'a, Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub license_family: Cow<'a, Option>, - - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub purls: Cow<'a, Vec>, - - #[serde(default, skip_serializing_if = "Option::is_none")] - pub size: Cow<'a, Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub legacy_bz2_size: Cow<'a, Option>, - - #[serde(default, skip_serializing_if = "Option::is_none")] - #[serde_as(as = "Option")] - pub timestamp: Option>, -} - -impl<'a> From> for CondaPackageData { - fn from(value: RawCondaPackageData<'a>) -> Self { - Self { - package_record: PackageRecord { - arch: value.arch.into_owned(), - build: value.build.into_owned(), - build_number: value.build_number, - constrains: value.constrains.into_owned(), - depends: value.depends.into_owned(), - features: value.features.into_owned(), - legacy_bz2_md5: value.legacy_bz2_md5.into_owned(), - legacy_bz2_size: value.legacy_bz2_size.into_owned(), - license: value.license.into_owned(), - license_family: value.license_family.into_owned(), - md5: value.md5, - name: value.name.into_owned(), - noarch: value.noarch.into_owned(), - platform: value.platform.into_owned(), - purls: value.purls.into_owned(), - sha256: value.sha256, - size: value.size.into_owned(), - subdir: value.subdir.into_owned(), - timestamp: value.timestamp, - track_features: value.track_features.into_owned(), - version: value.version.into_owned(), - }, - url: value.url.into_owned(), - file_name: value.file_name.into_owned(), - channel: value.channel.into_owned(), - } - } -} - -impl<'a> From<&'a CondaPackageData> for RawCondaPackageData<'a> { - fn from(value: &'a CondaPackageData) -> Self { - Self { - name: Cow::Borrowed(&value.package_record.name), - version: Cow::Borrowed(&value.package_record.version), - build: Cow::Borrowed(&value.package_record.build), - build_number: value.package_record.build_number, - subdir: Cow::Borrowed(&value.package_record.subdir), - noarch: Cow::Borrowed(&value.package_record.noarch), - url: Cow::Borrowed(&value.url), - channel: Cow::Borrowed(&value.channel), - file_name: Cow::Borrowed(&value.file_name), - purls: Cow::Borrowed(&value.package_record.purls), - depends: Cow::Borrowed(&value.package_record.depends), - constrains: Cow::Borrowed(&value.package_record.constrains), - platform: Cow::Borrowed(&value.package_record.platform), - arch: Cow::Borrowed(&value.package_record.arch), - md5: value.package_record.md5, - legacy_bz2_md5: Cow::Borrowed(&value.package_record.legacy_bz2_md5), - sha256: value.package_record.sha256, - size: Cow::Borrowed(&value.package_record.size), - legacy_bz2_size: Cow::Borrowed(&value.package_record.legacy_bz2_size), - timestamp: value.package_record.timestamp, - features: Cow::Borrowed(&value.package_record.features), - track_features: Cow::Borrowed(&value.package_record.track_features), - license: Cow::Borrowed(&value.package_record.license), - license_family: Cow::Borrowed(&value.package_record.license_family), - } - } -} - -impl<'a> PartialOrd for RawCondaPackageData<'a> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl<'a> Ord for RawCondaPackageData<'a> { - fn cmp(&self, other: &Self) -> Ordering { - self.name - .cmp(&other.name) - .then_with(|| self.version.cmp(&other.version)) - .then_with(|| self.build.cmp(&other.build)) - .then_with(|| self.subdir.cmp(&other.subdir)) - } -}