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