use std::{borrow::Cow, collections::BTreeSet};
use crate::{
conda::CondaBinaryData,
utils::derived_fields::{derive_arch_and_platform, derive_channel_from_location},
CondaPackageData, UrlOrPath,
};
use rattler_conda_types::package::{
ArchiveIdentifier, CondaArchiveType, DistArchiveIdentifier, DistArchiveType,
};
use rattler_conda_types::{
BuildNumber, ChannelUrl, NoArchType, PackageName, PackageRecord, PackageUrl, VersionWithSource,
};
use rattler_digest::{serde::SerializableHash, Md5Hash, Sha256Hash};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use url::Url;
fn is_default<T: Default + Eq>(value: &T) -> bool {
value == &T::default()
}
#[serde_as]
#[derive(Serialize, Deserialize, Eq, PartialEq)]
pub(crate) struct CondaPackageDataModel<'a> {
pub name: Cow<'a, PackageName>,
pub version: Cow<'a, VersionWithSource>,
#[serde(default, skip_serializing_if = "is_default")]
pub build: Cow<'a, str>,
#[serde(default, skip_serializing_if = "is_default")]
pub build_number: BuildNumber,
#[serde(default)]
pub subdir: Cow<'a, str>,
#[serde(skip_serializing_if = "NoArchType::is_none")]
pub noarch: Cow<'a, NoArchType>,
pub url: Cow<'a, Url>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[serde_as(as = "Option<SerializableHash::<rattler_digest::Sha256>>")]
pub sha256: Option<Sha256Hash>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[serde_as(as = "Option<SerializableHash::<rattler_digest::Md5>>")]
pub md5: Option<Md5Hash>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[serde_as(as = "Option<SerializableHash::<rattler_digest::Md5>>")]
pub legacy_bz2_md5: Option<Md5Hash>,
#[serde(default, skip_serializing_if = "<[String]>::is_empty")]
pub depends: Cow<'a, [String]>,
#[serde(default, skip_serializing_if = "<[String]>::is_empty")]
pub constrains: Cow<'a, [String]>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub arch: Cow<'a, Option<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub platform: Cow<'a, Option<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub channel: Cow<'a, Option<Url>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub features: Cow<'a, Option<String>>,
#[serde(default, skip_serializing_if = "<[String]>::is_empty")]
pub track_features: Cow<'a, [String]>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_name: Cow<'a, Option<DistArchiveIdentifier>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub python_site_packages_path: Cow<'a, Option<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub license: Cow<'a, Option<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub license_family: Cow<'a, Option<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub purls: Cow<'a, Option<BTreeSet<PackageUrl>>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub size: Cow<'a, Option<u64>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub legacy_bz2_size: Cow<'a, Option<u64>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[serde_as(as = "Option<crate::utils::serde::Timestamp>")]
pub timestamp: Option<chrono::DateTime<chrono::Utc>>,
}
impl<'a> From<CondaPackageDataModel<'a>> for CondaPackageData {
fn from(value: CondaPackageDataModel<'a>) -> Self {
let location = UrlOrPath::Url(value.url.into_owned());
let subdir = value.subdir.into_owned();
let (derived_arch, derived_platform) = derive_arch_and_platform(&subdir);
let file_name = value
.file_name
.into_owned()
.or_else(|| location.file_name().and_then(|f| f.parse().ok()))
.unwrap_or_else(|| DistArchiveIdentifier {
identifier: ArchiveIdentifier {
name: value.name.as_normalized().to_owned(),
version: value.version.to_string(),
build_string: value.build.to_string(),
},
archive_type: DistArchiveType::Conda(CondaArchiveType::Conda),
});
Self::Binary(CondaBinaryData {
package_record: PackageRecord {
build: value.build.into_owned(),
build_number: value.build_number,
constrains: value.constrains.into_owned(),
depends: value.depends.into_owned(),
experimental_extra_depends: std::collections::BTreeMap::new(),
features: value.features.into_owned(),
legacy_bz2_md5: value.legacy_bz2_md5,
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(),
purls: value.purls.into_owned(),
sha256: value.sha256,
size: value.size.into_owned(),
subdir,
timestamp: value.timestamp.map(Into::into),
track_features: value.track_features.into_owned(),
version: value.version.into_owned(),
run_exports: None,
arch: value.arch.into_owned().or(derived_arch),
platform: value.platform.into_owned().or(derived_platform),
python_site_packages_path: value.python_site_packages_path.into_owned(),
},
channel: value
.channel
.into_owned()
.map(ChannelUrl::from)
.or_else(|| derive_channel_from_location(&location)),
file_name,
location,
})
}
}