alpm_srcinfo/source_info/v1/
package_base.rs

1//! Handling of metadata found in the `pkgbase` section of SRCINFO data.
2use std::collections::BTreeMap;
3
4use alpm_types::{
5    Architecture,
6    Architectures,
7    Epoch,
8    FullVersion,
9    License,
10    MakepkgOption,
11    Name,
12    OpenPGPIdentifier,
13    OptionalDependency,
14    PackageDescription,
15    PackageRelation,
16    PackageRelease,
17    PackageVersion,
18    RelationOrSoname,
19    RelativeFilePath,
20    SkippableChecksum,
21    Source,
22    SystemArchitecture,
23    Url,
24    digests::{Blake2b512, Crc32Cksum, Md5, Sha1, Sha224, Sha256, Sha384, Sha512},
25};
26use serde::{Deserialize, Serialize};
27
28use super::package::PackageArchitecture;
29use crate::{
30    Error,
31    source_info::parser::{self, PackageBaseProperty, RawPackageBase, SharedMetaProperty},
32};
33#[cfg(doc)]
34use crate::{MergedPackage, SourceInfoV1, source_info::v1::package::Package};
35
36/// Package base metadata based on the `pkgbase` section in SRCINFO data.
37///
38/// All values in this struct act as default values for all [`Package`]s in the scope of specific
39/// SRCINFO data.
40///
41/// A [`MergedPackage`] (a full view on a package's metadata) can be created using
42/// [`SourceInfoV1::packages_for_architecture`].
43#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
44pub struct PackageBase {
45    /// The alpm-package-name of the package base.
46    pub name: Name,
47    /// The optional description of the package base.
48    pub description: Option<PackageDescription>,
49    /// The optional upstream URL of the package base.
50    pub url: Option<Url>,
51    /// The optional relative path to a changelog file of the package base.
52    pub changelog: Option<RelativeFilePath>,
53    /// The list of licenses that apply to the package base.
54    pub licenses: Vec<License>,
55
56    // Build or package management related meta fields
57    /// The optional relative path to an alpm-install-scriptlet of the package base.
58    pub install: Option<RelativeFilePath>,
59    /// The optional list of alpm-package-groups the package base is part of.
60    pub groups: Vec<String>,
61    /// The list of build tool options used when building.
62    pub options: Vec<MakepkgOption>,
63    /// The list of relative paths to files in a package that should be backed up.
64    pub backups: Vec<RelativeFilePath>,
65
66    // These metadata fields are PackageBase specific
67    /// The full version of the `pkgbase`.
68    pub version: FullVersion,
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    /// Architectures and architecture specific properties
74    pub architectures: Architectures,
75    /// The map of alpm-architecture specific overrides for package relations of a package base.
76    pub architecture_properties: BTreeMap<SystemArchitecture, PackageBaseArchitecture>,
77
78    /// The list of run-time dependencies of the package base.
79    pub dependencies: Vec<RelationOrSoname>,
80    /// The list of optional dependencies of the package base.
81    pub optional_dependencies: Vec<OptionalDependency>,
82    /// The list of provisions of the package base.
83    pub provides: Vec<RelationOrSoname>,
84    /// The list of conflicts of the package base.
85    pub conflicts: Vec<PackageRelation>,
86    /// The list of replacements of the package base.
87    pub replaces: Vec<PackageRelation>,
88    // The following dependencies are build-time specific dependencies.
89    // `makepkg` expects all dependencies for all split packages to be specified in the
90    // PackageBase.
91    /// The list of test dependencies of the package base.
92    pub check_dependencies: Vec<PackageRelation>,
93    /// The list of build dependencies of the package base.
94    pub make_dependencies: Vec<PackageRelation>,
95
96    /// The list of sources of the package base.
97    pub sources: Vec<Source>,
98    /// The list of sources of the package base that are not extracted.
99    pub no_extracts: Vec<String>,
100    /// The list of Blake2 hash digests for `sources` of the package base.
101    pub b2_checksums: Vec<SkippableChecksum<Blake2b512>>,
102    /// The list of MD-5 hash digests for `sources` of the package base.
103    pub md5_checksums: Vec<SkippableChecksum<Md5>>,
104    /// The list of SHA-1 hash digests for `sources` of the package base.
105    pub sha1_checksums: Vec<SkippableChecksum<Sha1>>,
106    /// The list of SHA-224 hash digests for `sources` of the package base.
107    pub sha224_checksums: Vec<SkippableChecksum<Sha224>>,
108    /// The list of SHA-256 hash digests for `sources` of the package base.
109    pub sha256_checksums: Vec<SkippableChecksum<Sha256>>,
110    /// The list of SHA-384 hash digests for `sources` of the package base.
111    pub sha384_checksums: Vec<SkippableChecksum<Sha384>>,
112    /// The list of SHA-512 hash digests for `sources` of the package base.
113    pub sha512_checksums: Vec<SkippableChecksum<Sha512>>,
114    /// The list of CRC-32/CKSUM hash digests for `sources` of the package base.
115    pub crc_checksums: Vec<SkippableChecksum<Crc32Cksum>>,
116}
117
118/// Architecture specific package base properties for use in [`PackageBase`].
119///
120/// For each [`Architecture`] defined in [`PackageBase::architectures`] a
121/// [`PackageBaseArchitecture`] is present in [`PackageBase::architecture_properties`].
122#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
123pub struct PackageBaseArchitecture {
124    /// The list of run-time dependencies of the package base.
125    pub dependencies: Vec<RelationOrSoname>,
126    /// The list of optional dependencies of the package base.
127    pub optional_dependencies: Vec<OptionalDependency>,
128    /// The list of provisions of the package base.
129    pub provides: Vec<RelationOrSoname>,
130    /// The list of conflicts of the package base.
131    pub conflicts: Vec<PackageRelation>,
132    /// The list of replacements of the package base.
133    pub replaces: Vec<PackageRelation>,
134    // The following dependencies are build-time specific dependencies.
135    // `makepkg` expects all dependencies for all split packages to be specified in the
136    // PackageBase.
137    /// The list of test dependencies of the package base.
138    pub check_dependencies: Vec<PackageRelation>,
139    /// The list of build dependencies of the package base.
140    pub make_dependencies: Vec<PackageRelation>,
141
142    /// The list of sources of the package base.
143    pub sources: Vec<Source>,
144    /// The list of Blake2 hash digests for `sources` of the package base.
145    pub b2_checksums: Vec<SkippableChecksum<Blake2b512>>,
146    /// The list of MD-5 hash digests for `sources` of the package base.
147    pub md5_checksums: Vec<SkippableChecksum<Md5>>,
148    /// The list of SHA-1 hash digests for `sources` of the package base.
149    pub sha1_checksums: Vec<SkippableChecksum<Sha1>>,
150    /// The list of SHA-224 hash digests for `sources` of the package base.
151    pub sha224_checksums: Vec<SkippableChecksum<Sha224>>,
152    /// The list of SHA-256 hash digests for `sources` of the package base.
153    pub sha256_checksums: Vec<SkippableChecksum<Sha256>>,
154    /// The list of SHA-384 hash digests for `sources` of the package base.
155    pub sha384_checksums: Vec<SkippableChecksum<Sha384>>,
156    /// The list of SHA-512 hash digests for `sources` of the package base.
157    pub sha512_checksums: Vec<SkippableChecksum<Sha512>>,
158    /// The list of CRC-32/CKSUM hash digests for `sources` of the package base.
159    pub crc_checksums: Vec<SkippableChecksum<Crc32Cksum>>,
160}
161
162impl PackageBaseArchitecture {
163    /// Merges in the architecture specific properties of a package.
164    ///
165    /// Each existing field of `properties` overrides the architecture-independent pendant on
166    /// `self`.
167    pub fn merge_package_properties(&mut self, properties: PackageArchitecture) {
168        properties.dependencies.merge_vec(&mut self.dependencies);
169        properties
170            .optional_dependencies
171            .merge_vec(&mut self.optional_dependencies);
172        properties.provides.merge_vec(&mut self.provides);
173        properties.conflicts.merge_vec(&mut self.conflicts);
174        properties.replaces.merge_vec(&mut self.replaces);
175    }
176}
177
178/// Handles all potentially architecture specific Vector entries in the [`PackageBase::from_parsed`]
179/// function.
180///
181/// If no architecture is encountered, it simply adds the value on the [`PackageBase`] itself.
182/// Otherwise, it's added to the respective [`PackageBase::architecture_properties`].
183macro_rules! package_base_arch_prop {
184    (
185        $architecture_properties:ident,
186        $arch_property:ident,
187        $field_name:ident,
188    ) => {
189        // Check if the property is architecture specific.
190        // If so, we have to perform some checks and preparation
191        if let Some(architecture) = $arch_property.architecture
192            && let Architecture::Some(system_arch) = architecture
193        {
194            // Make sure the architecture specific properties are initialized.
195            let architecture_properties = $architecture_properties
196                .entry(system_arch)
197                .or_insert(PackageBaseArchitecture::default());
198
199            // Set the architecture specific value.
200            architecture_properties
201                .$field_name
202                .push($arch_property.value);
203        } else {
204            $field_name.push($arch_property.value)
205        }
206    };
207}
208
209impl PackageBase {
210    /// Create a new PackageBase from a [`Name`] and a [`FullVersion`].
211    ///
212    /// Uses the name and version and initializes all remaining fields of [`PackageBase`] with
213    /// default values.
214    ///
215    /// # Example
216    ///
217    /// ```
218    /// use std::str::FromStr;
219    ///
220    /// use alpm_srcinfo::source_info::v1::package_base::PackageBase;
221    /// use alpm_types::{FullVersion, Name};
222    ///
223    /// # fn main() -> testresult::TestResult {
224    ///
225    /// let base = PackageBase::new_with_defaults(
226    ///     Name::from_str("example_package")?,
227    ///     FullVersion::from_str("1:1.0.0-2")?,
228    /// );
229    /// # Ok(())
230    /// # }
231    /// ```
232    pub fn new_with_defaults(name: Name, version: FullVersion) -> Self {
233        PackageBase {
234            name,
235            version,
236            description: None,
237            architectures: Architectures::Some(Vec::new()),
238            url: None,
239            licenses: Vec::new(),
240            groups: Vec::new(),
241            dependencies: Vec::new(),
242            make_dependencies: Vec::new(),
243            check_dependencies: Vec::new(),
244            optional_dependencies: Vec::new(),
245            provides: Vec::new(),
246            conflicts: Vec::new(),
247            replaces: Vec::new(),
248            backups: Vec::new(),
249            options: Vec::new(),
250            install: None,
251            changelog: None,
252            sources: Vec::new(),
253            no_extracts: Vec::new(),
254            md5_checksums: Vec::new(),
255            sha1_checksums: Vec::new(),
256            sha224_checksums: Vec::new(),
257            sha256_checksums: Vec::new(),
258            sha384_checksums: Vec::new(),
259            sha512_checksums: Vec::new(),
260            b2_checksums: Vec::new(),
261            crc_checksums: Vec::new(),
262            pgp_fingerprints: Vec::new(),
263            architecture_properties: BTreeMap::new(),
264        }
265    }
266
267    /// Creates a new [`PackageBase`] instance from a [`RawPackageBase`].
268    ///
269    /// # Parameters
270    ///
271    /// - `parsed`: The [`RawPackageBase`] representation of the SRCINFO data. The input guarantees
272    ///   that the keyword definitions have been parsed correctly, but not yet that they represent
273    ///   valid SRCINFO data as a whole.
274    pub fn from_parsed(parsed: RawPackageBase) -> Result<Self, Error> {
275        let mut description = None;
276        let mut url = None;
277        let mut licenses = Vec::new();
278        let mut changelog = None;
279        let mut architectures = Vec::new();
280        let mut architecture_properties = BTreeMap::new();
281
282        // Build or package management related meta fields
283        let mut install = None;
284        let mut groups = Vec::new();
285        let mut options = Vec::new();
286        let mut backups = Vec::new();
287
288        // These metadata fields are PackageBase specific
289        let mut epoch: Option<Epoch> = None;
290        let mut package_version: Option<PackageVersion> = None;
291        let mut package_release: Option<PackageRelease> = None;
292        let mut pgp_fingerprints = Vec::new();
293
294        let mut dependencies = Vec::new();
295        let mut optional_dependencies = Vec::new();
296        let mut provides = Vec::new();
297        let mut conflicts = Vec::new();
298        let mut replaces = Vec::new();
299        // The following dependencies are build-time specific dependencies.
300        // `makepkg` expects all dependencies for all split packages to be specified in the
301        // PackageBase.
302        let mut check_dependencies = Vec::new();
303        let mut make_dependencies = Vec::new();
304
305        let mut sources = Vec::new();
306        let mut no_extracts = Vec::new();
307        let mut b2_checksums = Vec::new();
308        let mut md5_checksums = Vec::new();
309        let mut sha1_checksums = Vec::new();
310        let mut sha224_checksums = Vec::new();
311        let mut sha256_checksums = Vec::new();
312        let mut sha384_checksums = Vec::new();
313        let mut sha512_checksums = Vec::new();
314        let mut crc_checksums = Vec::new();
315
316        // First up check all input for potential architecture declarations.
317        for prop in parsed.properties.iter() {
318            // We're only interested in architecture properties.
319            let PackageBaseProperty::MetaProperty(SharedMetaProperty::Architecture(architecture)) =
320                prop
321            else {
322                continue;
323            };
324            architectures.push(architecture);
325        }
326
327        // If no architecture is set, `makepkg` errors hard, however it happily creates .SRCINFO
328        // files from it. We handle this as a hard error as well.
329        if architectures.is_empty() {
330            return Err(Error::MissingKeyword { keyword: "arch" });
331        }
332
333        // Try to convert the list of architectures into an `Architectures` instance.
334        // This will fail if "any" is combined with any specific system architecture.
335        let architectures: Architectures = architectures.try_into()?;
336
337        for prop in parsed.properties.into_iter() {
338            match prop {
339                // Skip empty lines and comments
340                PackageBaseProperty::EmptyLine | PackageBaseProperty::Comment(_) => continue,
341                PackageBaseProperty::PackageVersion(inner) => package_version = Some(inner),
342                PackageBaseProperty::PackageRelease(inner) => package_release = Some(inner),
343                PackageBaseProperty::PackageEpoch(inner) => epoch = Some(inner),
344                PackageBaseProperty::ValidPgpKeys(inner) => {
345                    pgp_fingerprints.push(inner);
346                }
347                PackageBaseProperty::CheckDependency(arch_property) => {
348                    package_base_arch_prop!(
349                        architecture_properties,
350                        arch_property,
351                        check_dependencies,
352                    )
353                }
354                PackageBaseProperty::MakeDependency(arch_property) => {
355                    package_base_arch_prop!(
356                        architecture_properties,
357                        arch_property,
358                        make_dependencies,
359                    )
360                }
361                PackageBaseProperty::MetaProperty(shared_meta_property) => {
362                    match shared_meta_property {
363                        SharedMetaProperty::Description(inner) => description = Some(inner),
364                        SharedMetaProperty::Url(inner) => url = Some(inner),
365                        SharedMetaProperty::License(inner) => licenses.push(inner),
366                        // We already handled those above.
367                        SharedMetaProperty::Architecture(_) => continue,
368                        SharedMetaProperty::Changelog(inner) => changelog = Some(inner),
369                        SharedMetaProperty::Install(inner) => install = Some(inner),
370                        SharedMetaProperty::Group(inner) => groups.push(inner),
371                        SharedMetaProperty::Option(inner) => options.push(inner),
372                        SharedMetaProperty::Backup(inner) => backups.push(inner),
373                    }
374                }
375                PackageBaseProperty::RelationProperty(relation_property) => match relation_property
376                {
377                    parser::RelationProperty::Dependency(arch_property) => package_base_arch_prop!(
378                        architecture_properties,
379                        arch_property,
380                        dependencies,
381                    ),
382                    parser::RelationProperty::OptionalDependency(arch_property) => {
383                        package_base_arch_prop!(
384                            architecture_properties,
385                            arch_property,
386                            optional_dependencies,
387                        )
388                    }
389                    parser::RelationProperty::Provides(arch_property) => {
390                        package_base_arch_prop!(architecture_properties, arch_property, provides,)
391                    }
392                    parser::RelationProperty::Conflicts(arch_property) => {
393                        package_base_arch_prop!(architecture_properties, arch_property, conflicts,)
394                    }
395                    parser::RelationProperty::Replaces(arch_property) => {
396                        package_base_arch_prop!(architecture_properties, arch_property, replaces,)
397                    }
398                },
399                PackageBaseProperty::SourceProperty(source_property) => match source_property {
400                    parser::SourceProperty::Source(arch_property) => {
401                        package_base_arch_prop!(architecture_properties, arch_property, sources,)
402                    }
403                    parser::SourceProperty::NoExtract(value) => no_extracts.push(value),
404                    parser::SourceProperty::B2Checksum(arch_property) => package_base_arch_prop!(
405                        architecture_properties,
406                        arch_property,
407                        b2_checksums,
408                    ),
409                    parser::SourceProperty::Md5Checksum(arch_property) => {
410                        package_base_arch_prop!(
411                            architecture_properties,
412                            arch_property,
413                            md5_checksums,
414                        );
415                    }
416                    parser::SourceProperty::Sha1Checksum(arch_property) => {
417                        package_base_arch_prop!(
418                            architecture_properties,
419                            arch_property,
420                            sha1_checksums,
421                        );
422                    }
423                    parser::SourceProperty::Sha224Checksum(arch_property) => {
424                        package_base_arch_prop!(
425                            architecture_properties,
426                            arch_property,
427                            sha224_checksums,
428                        )
429                    }
430                    parser::SourceProperty::Sha256Checksum(arch_property) => {
431                        package_base_arch_prop!(
432                            architecture_properties,
433                            arch_property,
434                            sha256_checksums,
435                        )
436                    }
437                    parser::SourceProperty::Sha384Checksum(arch_property) => {
438                        package_base_arch_prop!(
439                            architecture_properties,
440                            arch_property,
441                            sha384_checksums,
442                        )
443                    }
444                    parser::SourceProperty::Sha512Checksum(arch_property) => {
445                        package_base_arch_prop!(
446                            architecture_properties,
447                            arch_property,
448                            sha512_checksums,
449                        )
450                    }
451                    parser::SourceProperty::CrcChecksum(arch_property) => {
452                        package_base_arch_prop!(
453                            architecture_properties,
454                            arch_property,
455                            crc_checksums,
456                        )
457                    }
458                },
459            }
460        }
461
462        // Handle a missing package_version
463        let package_version = match package_version {
464            Some(package_version) => package_version,
465            None => {
466                return Err(Error::MissingKeyword { keyword: "pkgver" });
467            }
468        };
469
470        // Handle a missing package_release
471        let package_release = match package_release {
472            Some(package_release) => package_release,
473            None => {
474                return Err(Error::MissingKeyword { keyword: "pkgrel" });
475            }
476        };
477        let version = FullVersion::new(package_version, package_release, epoch);
478
479        Ok(PackageBase {
480            name: parsed.name,
481            description,
482            url,
483            licenses,
484            changelog,
485            architectures,
486            architecture_properties,
487            install,
488            groups,
489            options,
490            backups,
491            version,
492            pgp_fingerprints,
493            dependencies,
494            optional_dependencies,
495            provides,
496            conflicts,
497            replaces,
498            check_dependencies,
499            make_dependencies,
500            sources,
501            no_extracts,
502            b2_checksums,
503            md5_checksums,
504            sha1_checksums,
505            sha224_checksums,
506            sha256_checksums,
507            sha384_checksums,
508            sha512_checksums,
509            crc_checksums,
510        })
511    }
512}