use std::collections::BTreeMap;
use alpm_types::{
Architecture,
Architectures,
Backup,
Changelog,
Install,
License,
MakepkgOption,
Name,
OptionalDependency,
PackageDescription,
PackageRelation,
RelationOrSoname,
SystemArchitecture,
Url,
};
use serde::{Deserialize, Serialize};
use crate::{
Error,
source_info::parser::{
ClearableProperty,
PackageProperty,
RawPackage,
RelationProperty,
SharedMetaProperty,
},
};
#[cfg(doc)]
use crate::{MergedPackage, SourceInfoV1, source_info::v1::package_base::PackageBase};
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(tag = "override")]
pub enum Override<T> {
#[default]
No,
Clear,
Yes {
value: T,
},
}
impl<T> Override<T> {
#[inline]
pub fn merge_option(self, other: &mut Option<T>) {
match self {
Override::No => (),
Override::Clear => *other = None,
Override::Yes { value } => *other = Some(value),
}
}
#[inline]
pub fn get_or_insert(&mut self, default: T) -> &mut T {
if let Override::Yes { value } = self {
return value;
}
*self = Override::Yes { value: default };
if let Override::Yes { value } = self {
return value;
}
unreachable!()
}
}
impl<T> Override<Vec<T>> {
#[inline]
pub fn merge_vec(self, other: &mut Vec<T>) {
match self {
Override::No => (),
Override::Clear => *other = Vec::new(),
Override::Yes { value } => *other = value,
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Package {
pub name: Name,
pub description: Override<PackageDescription>,
pub url: Override<Url>,
pub changelog: Override<Changelog>,
pub licenses: Override<Vec<License>>,
pub install: Override<Install>,
pub groups: Override<Vec<String>>,
pub options: Override<Vec<MakepkgOption>>,
pub backups: Override<Vec<Backup>>,
pub architectures: Option<Architectures>,
pub architecture_properties: BTreeMap<SystemArchitecture, PackageArchitecture>,
pub dependencies: Override<Vec<RelationOrSoname>>,
pub optional_dependencies: Override<Vec<OptionalDependency>>,
pub provides: Override<Vec<RelationOrSoname>>,
pub conflicts: Override<Vec<PackageRelation>>,
pub replaces: Override<Vec<PackageRelation>>,
}
impl From<Name> for Package {
fn from(value: Name) -> Self {
Package::new_with_defaults(value)
}
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct PackageArchitecture {
pub dependencies: Override<Vec<RelationOrSoname>>,
pub optional_dependencies: Override<Vec<OptionalDependency>>,
pub provides: Override<Vec<RelationOrSoname>>,
pub conflicts: Override<Vec<PackageRelation>>,
pub replaces: Override<Vec<PackageRelation>>,
}
macro_rules! clearable_arch_vec {
(
$architecture_properties:ident,
$architecture:ident,
$field_name:ident,
) => {
if let Some(architecture) = $architecture
&& let Architecture::Some(system_arch) = architecture
{
let properties = $architecture_properties
.entry(system_arch.clone())
.or_default();
properties.$field_name = Override::Clear;
} else {
$field_name = Override::Clear;
}
};
}
macro_rules! package_arch_prop {
(
$architecture_properties:ident,
$arch_property:ident,
$field_name:ident,
) => {
if let Some(architecture) = $arch_property.architecture
&& let Architecture::Some(system_arch) = architecture
{
let architecture_properties = $architecture_properties
.entry(system_arch)
.or_insert(PackageArchitecture::default());
architecture_properties
.$field_name
.get_or_insert(Vec::new())
.push($arch_property.value);
} else {
$field_name
.get_or_insert(Vec::new())
.push($arch_property.value)
}
};
}
impl Package {
pub fn new_with_defaults(value: Name) -> Self {
Package {
name: value,
description: Default::default(),
url: Default::default(),
changelog: Default::default(),
licenses: Default::default(),
install: Default::default(),
groups: Default::default(),
options: Default::default(),
backups: Default::default(),
architectures: Default::default(),
architecture_properties: Default::default(),
dependencies: Default::default(),
optional_dependencies: Default::default(),
provides: Default::default(),
conflicts: Default::default(),
replaces: Default::default(),
}
}
pub fn from_parsed(parsed: RawPackage) -> Result<Self, Error> {
let mut description = Override::No;
let mut url = Override::No;
let mut licenses = Override::No;
let mut changelog = Override::No;
let mut architectures = None;
let mut architecture_properties: BTreeMap<SystemArchitecture, PackageArchitecture> =
BTreeMap::new();
let mut install = Override::No;
let mut groups = Override::No;
let mut options = Override::No;
let mut backups = Override::No;
let mut dependencies = Override::No;
let mut optional_dependencies = Override::No;
let mut provides = Override::No;
let mut conflicts = Override::No;
let mut replaces = Override::No;
for prop in parsed.properties.iter() {
let PackageProperty::MetaProperty(SharedMetaProperty::Architecture(architecture)) =
prop
else {
continue;
};
let architectures = architectures.get_or_insert(Vec::new());
architectures.push(architecture);
}
let architectures = if let Some(arch_vec) = architectures {
let architectures: Architectures = arch_vec.try_into()?;
Some(architectures)
} else {
None
};
for prop in parsed.properties.iter() {
let PackageProperty::Clear(clearable_property) = prop else {
continue;
};
match clearable_property {
ClearableProperty::Description => description = Override::Clear,
ClearableProperty::Url => url = Override::Clear,
ClearableProperty::Licenses => licenses = Override::Clear,
ClearableProperty::Changelog => changelog = Override::Clear,
ClearableProperty::Install => install = Override::Clear,
ClearableProperty::Groups => groups = Override::Clear,
ClearableProperty::Options => options = Override::Clear,
ClearableProperty::Backups => backups = Override::Clear,
ClearableProperty::Dependencies(architecture) => {
clearable_arch_vec!(architecture_properties, architecture, dependencies,)
}
ClearableProperty::OptionalDependencies(architecture) => {
clearable_arch_vec!(
architecture_properties,
architecture,
optional_dependencies,
)
}
ClearableProperty::Provides(architecture) => {
clearable_arch_vec!(architecture_properties, architecture, provides,)
}
ClearableProperty::Conflicts(architecture) => {
clearable_arch_vec!(architecture_properties, architecture, conflicts,)
}
ClearableProperty::Replaces(architecture) => {
clearable_arch_vec!(architecture_properties, architecture, replaces,)
}
}
}
for prop in parsed.properties.into_iter() {
match prop {
PackageProperty::EmptyLine | PackageProperty::Comment(_) => continue,
PackageProperty::MetaProperty(shared_meta_property) => {
match shared_meta_property {
SharedMetaProperty::Description(inner) => {
description = Override::Yes { value: inner }
}
SharedMetaProperty::Url(inner) => url = Override::Yes { value: inner },
SharedMetaProperty::License(inner) => {
licenses.get_or_insert(Vec::new()).push(inner)
}
SharedMetaProperty::Changelog(inner) => {
changelog = Override::Yes { value: inner }
}
SharedMetaProperty::Install(inner) => {
install = Override::Yes { value: inner }
}
SharedMetaProperty::Group(inner) => {
groups.get_or_insert(Vec::new()).push(inner)
}
SharedMetaProperty::Option(inner) => {
options.get_or_insert(Vec::new()).push(inner)
}
SharedMetaProperty::Backup(inner) => {
backups.get_or_insert(Vec::new()).push(inner)
}
SharedMetaProperty::Architecture(_) => continue,
}
}
PackageProperty::RelationProperty(relation_property) => match relation_property {
RelationProperty::Dependency(arch_property) => {
package_arch_prop!(architecture_properties, arch_property, dependencies,)
}
RelationProperty::OptionalDependency(arch_property) => {
package_arch_prop!(
architecture_properties,
arch_property,
optional_dependencies,
)
}
RelationProperty::Provides(arch_property) => {
package_arch_prop!(architecture_properties, arch_property, provides,)
}
RelationProperty::Conflicts(arch_property) => {
package_arch_prop!(architecture_properties, arch_property, conflicts,)
}
RelationProperty::Replaces(arch_property) => {
package_arch_prop!(architecture_properties, arch_property, replaces,)
}
},
PackageProperty::Clear(_) => continue,
}
}
Ok(Package {
name: parsed.name,
description,
url,
changelog,
licenses,
architectures,
architecture_properties,
install,
groups,
options,
backups,
dependencies,
optional_dependencies,
provides,
conflicts,
replaces,
})
}
}