alpm_srcinfo/source_info/v1/
merged.rs

1//! Provides fully resolved package metadata derived from SRCINFO data.
2use alpm_types::{
3    Architecture,
4    Epoch,
5    License,
6    MakepkgOption,
7    Name,
8    OpenPGPIdentifier,
9    OptionalDependency,
10    PackageDescription,
11    PackageRelation,
12    PackageRelease,
13    PackageVersion,
14    RelativePath,
15    SkippableChecksum,
16    Source,
17    Url,
18    digests::{Blake2b512, Md5, Sha1, Sha224, Sha256, Sha384, Sha512},
19};
20use serde::{Deserialize, Serialize};
21
22#[cfg(doc)]
23use crate::source_info::v1::package::Override;
24use crate::{
25    SourceInfoV1,
26    relation::RelationOrSoname,
27    source_info::v1::{
28        package::Package,
29        package_base::{PackageBase, PackageBaseArchitecture},
30    },
31};
32
33/// Fully resolved metadata of a single package based on SRCINFO data.
34///
35/// This struct incorporates all [`PackageBase`] properties and the [`Package`] specific overrides
36/// in an architecture-specific representation of a package. It can be created using
37/// [`SourceInfoV1::packages_for_architecture`].
38#[derive(Clone, Debug, Deserialize, Serialize)]
39pub struct MergedPackage {
40    pub name: Name,
41    pub description: Option<PackageDescription>,
42    pub url: Option<Url>,
43    pub licenses: Vec<License>,
44    pub architecture: Architecture,
45    pub changelog: Option<RelativePath>,
46
47    // Build or package management related meta fields
48    pub install: Option<RelativePath>,
49    pub groups: Vec<String>,
50    pub options: Vec<MakepkgOption>,
51    pub backups: Vec<RelativePath>,
52
53    /// The version of the package
54    pub package_version: PackageVersion,
55    /// The release of the package
56    pub package_release: PackageRelease,
57    /// The epoch of the package
58    pub epoch: Option<Epoch>,
59    pub pgp_fingerprints: Vec<OpenPGPIdentifier>,
60
61    pub dependencies: Vec<RelationOrSoname>,
62    pub optional_dependencies: Vec<OptionalDependency>,
63    pub provides: Vec<RelationOrSoname>,
64    pub conflicts: Vec<PackageRelation>,
65    pub replaces: Vec<PackageRelation>,
66    pub check_dependencies: Vec<PackageRelation>,
67    pub make_dependencies: Vec<PackageRelation>,
68
69    pub sources: Vec<MergedSource>,
70    pub no_extracts: Vec<String>,
71}
72
73/// An iterator over all packages of a specific architecture.
74pub struct MergedPackagesIterator<'a> {
75    pub(crate) architecture: Architecture,
76    pub(crate) source_info: &'a SourceInfoV1,
77    pub(crate) package_iterator: std::slice::Iter<'a, Package>,
78}
79
80impl Iterator for MergedPackagesIterator<'_> {
81    type Item = MergedPackage;
82
83    fn next(&mut self) -> Option<MergedPackage> {
84        // Search for the next package that is valid for the the architecture we're looping over.
85        let package = self.package_iterator.find(|package| {
86            // If the package provides target architecture overrides, use those, otherwise fallback
87            // to package base architectures.
88            let architectures = match &package.architectures {
89                Some(value) => value,
90                None => &self.source_info.base.architectures,
91            };
92
93            architectures.contains(&self.architecture) || architectures.contains(&Architecture::Any)
94        })?;
95
96        Some(MergedPackage::from_base_and_package(
97            self.architecture,
98            &self.source_info.base,
99            package,
100        ))
101    }
102}
103
104/// A merged representation of source related information.
105///
106/// SRCINFO provides this info as separate lists. This struct resolves that list representation and
107/// provides a convenient aggregated representation for a single source.
108#[derive(Clone, Debug, Deserialize, Serialize)]
109pub struct MergedSource {
110    pub source: Source,
111    pub b2_checksum: Option<SkippableChecksum<Blake2b512>>,
112    pub md5_checksum: Option<SkippableChecksum<Md5>>,
113    pub sha1_checksum: Option<SkippableChecksum<Sha1>>,
114    pub sha224_checksum: Option<SkippableChecksum<Sha224>>,
115    pub sha256_checksum: Option<SkippableChecksum<Sha256>>,
116    pub sha384_checksum: Option<SkippableChecksum<Sha384>>,
117    pub sha512_checksum: Option<SkippableChecksum<Sha512>>,
118}
119
120/// A convenience iterator to build a list of [`MergedSource`] from the disjoint vectors of sources
121/// and digests.
122///
123/// The checksums and sources are by convention all in the same order, which makes this quite
124/// convenient to convert into a aggregated struct representation.
125pub struct MergedSourceIterator<'a> {
126    sources: std::slice::Iter<'a, Source>,
127    b2_checksums: std::slice::Iter<'a, SkippableChecksum<Blake2b512>>,
128    md5_checksums: std::slice::Iter<'a, SkippableChecksum<Md5>>,
129    sha1_checksums: std::slice::Iter<'a, SkippableChecksum<Sha1>>,
130    sha224_checksums: std::slice::Iter<'a, SkippableChecksum<Sha224>>,
131    sha256_checksums: std::slice::Iter<'a, SkippableChecksum<Sha256>>,
132    sha384_checksums: std::slice::Iter<'a, SkippableChecksum<Sha384>>,
133    sha512_checksums: std::slice::Iter<'a, SkippableChecksum<Sha512>>,
134}
135
136impl Iterator for MergedSourceIterator<'_> {
137    type Item = MergedSource;
138
139    fn next(&mut self) -> Option<MergedSource> {
140        let source = self.sources.next()?;
141
142        Some(MergedSource {
143            source: source.clone(),
144            b2_checksum: self.b2_checksums.next().cloned(),
145            md5_checksum: self.md5_checksums.next().cloned(),
146            sha1_checksum: self.sha1_checksums.next().cloned(),
147            sha224_checksum: self.sha224_checksums.next().cloned(),
148            sha256_checksum: self.sha256_checksums.next().cloned(),
149            sha384_checksum: self.sha384_checksums.next().cloned(),
150            sha512_checksum: self.sha512_checksums.next().cloned(),
151        })
152    }
153}
154
155impl MergedPackage {
156    /// Creates the fully resolved, architecture-specific metadata representation of a package.
157    ///
158    /// Takes an [`Architecture`] (which defines the architecture for which to create the
159    /// representation), as well as a [`PackageBase`] and a [`Package`] (from which to derive the
160    /// metadata).
161    ///
162    /// The metadata representation is created using the following steps:
163    /// 1. [`MergedPackage::from_base`] is called to create a basic representation of a
164    ///    [`MergedPackage`] based on the default values in [`PackageBase`].
165    /// 2. [`MergedPackage::merge_package`] is called to merge all architecture-agnostic fields of
166    ///    the [`Package`] into the [`MergedPackage`].
167    /// 3. The architecture-specific properties of the [`PackageBase`] and [`Package`] are
168    ///    extracted.
169    /// 4. [`PackageBaseArchitecture::merge_package_properties`] is called to merge the
170    ///    architecture-specific properties of the [`Package`] into those of the [`PackageBase`].
171    /// 5. [`MergedPackage::merge_architecture_properties`] is called to merge the combined
172    ///    architecture-specific properties into the [`MergedPackage`].
173    pub fn from_base_and_package(
174        architecture: Architecture,
175        base: &PackageBase,
176        package: &Package,
177    ) -> MergedPackage {
178        let name = package.name.clone();
179        // Step 1
180        let mut merged_package = Self::from_base(&architecture, name, base);
181
182        // Step 2
183        merged_package.merge_package(package);
184
185        // Get the architecture specific properties from the PackageBase.
186        // Use an empty default without any properties as default if none are found.
187        let mut architecture_properties =
188            if let Some(properties) = base.architecture_properties.get(&architecture) {
189                properties.clone()
190            } else {
191                PackageBaseArchitecture::default()
192            };
193
194        // Apply package specific overrides for architecture specific properties.
195        if let Some(package_properties) = package.architecture_properties.get(&architecture) {
196            architecture_properties.merge_package_properties(package_properties.clone());
197        }
198
199        // Merge the architecture specific properties into the final MergedPackage.
200        merged_package.merge_architecture_properties(&architecture_properties);
201
202        merged_package
203    }
204
205    /// Creates a basic, architecture-specific, but incomplete [`MergedPackage`].
206    ///
207    /// Takes an [`Architecture`] (which defines the architecture for which to create the
208    /// representation), a [`Name`] which defines the name of the package and a [`PackageBase`]
209    /// which provides the initial data.
210    ///
211    /// # Note
212    ///
213    /// The returned [`MergedPackage`] is not complete, as it neither contains package-specific nor
214    /// architecture-specific overrides for its fields.
215    /// Use [`from_base_and_package`](MergedPackage::from_base_and_package) to create a fully
216    /// resolved representation of a package.
217    pub fn from_base(architecture: &Architecture, name: Name, base: &PackageBase) -> MergedPackage {
218        // Merge all source related info into aggregated structs.
219        let merged_sources = MergedSourceIterator {
220            sources: base.sources.iter(),
221            b2_checksums: base.b2_checksums.iter(),
222            md5_checksums: base.md5_checksums.iter(),
223            sha1_checksums: base.sha1_checksums.iter(),
224            sha224_checksums: base.sha224_checksums.iter(),
225            sha256_checksums: base.sha256_checksums.iter(),
226            sha384_checksums: base.sha384_checksums.iter(),
227            sha512_checksums: base.sha512_checksums.iter(),
228        };
229
230        MergedPackage {
231            name,
232            description: base.description.clone(),
233            url: base.url.clone(),
234            licenses: base.licenses.clone(),
235            architecture: *architecture,
236            changelog: base.changelog.clone(),
237            install: base.install.clone(),
238            groups: base.groups.clone(),
239            options: base.options.clone(),
240            backups: base.backups.clone(),
241            package_version: base.package_version.clone(),
242            package_release: base.package_release.clone(),
243            epoch: base.epoch,
244            pgp_fingerprints: base.pgp_fingerprints.clone(),
245            dependencies: base.dependencies.clone(),
246            optional_dependencies: base.optional_dependencies.clone(),
247            provides: base.provides.clone(),
248            conflicts: base.conflicts.clone(),
249            replaces: base.replaces.clone(),
250            check_dependencies: base.check_dependencies.clone(),
251            make_dependencies: base.make_dependencies.clone(),
252            sources: merged_sources.collect(),
253            no_extracts: base.no_extracts.clone(),
254        }
255    }
256
257    /// Merges the non-architecture specific fields of a [`Package`] into `self`.
258    ///
259    /// Any field on `package` that is not [`Override::No`] overrides the pendant on `self`.
260    pub fn merge_package(&mut self, package: &Package) {
261        let package = package.clone();
262        package.description.merge_option(&mut self.description);
263
264        package.url.merge_option(&mut self.url);
265        package.changelog.merge_option(&mut self.changelog);
266        package.licenses.merge_vec(&mut self.licenses);
267        package.install.merge_option(&mut self.install);
268        package.groups.merge_vec(&mut self.groups);
269        package.options.merge_vec(&mut self.options);
270        package.backups.merge_vec(&mut self.backups);
271        package.dependencies.merge_vec(&mut self.dependencies);
272        package
273            .optional_dependencies
274            .merge_vec(&mut self.optional_dependencies);
275        package.provides.merge_vec(&mut self.provides);
276        package.conflicts.merge_vec(&mut self.conflicts);
277        package.replaces.merge_vec(&mut self.replaces);
278    }
279
280    /// Merges in architecture-specific overrides for fields.
281    ///
282    /// Takes a [`PackageBaseArchitecture`] and extends the non-architecture specific values
283    /// with the architecture specific ones.
284    /// This is an accumulative and non-destructive operation.
285    pub fn merge_architecture_properties(&mut self, base_architecture: &PackageBaseArchitecture) {
286        // Merge all source related info into aggregated structs.
287        let merged_sources = MergedSourceIterator {
288            sources: base_architecture.sources.iter(),
289            b2_checksums: base_architecture.b2_checksums.iter(),
290            md5_checksums: base_architecture.md5_checksums.iter(),
291            sha1_checksums: base_architecture.sha1_checksums.iter(),
292            sha224_checksums: base_architecture.sha224_checksums.iter(),
293            sha256_checksums: base_architecture.sha256_checksums.iter(),
294            sha384_checksums: base_architecture.sha384_checksums.iter(),
295            sha512_checksums: base_architecture.sha512_checksums.iter(),
296        };
297
298        self.dependencies
299            .extend_from_slice(&base_architecture.dependencies);
300        self.optional_dependencies
301            .extend_from_slice(&base_architecture.optional_dependencies);
302        self.provides.extend_from_slice(&base_architecture.provides);
303        self.conflicts
304            .extend_from_slice(&base_architecture.conflicts);
305        self.replaces.extend_from_slice(&base_architecture.replaces);
306        self.check_dependencies
307            .extend_from_slice(&base_architecture.check_dependencies);
308        self.make_dependencies
309            .extend_from_slice(&base_architecture.make_dependencies);
310
311        self.sources
312            .extend_from_slice(&merged_sources.collect::<Vec<MergedSource>>());
313        self.no_extracts
314            .extend_from_slice(&base_architecture.no_extracts);
315    }
316}