Skip to main content

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::Meta(SharedMetaProperty::Architecture(architecture)) = prop
320            else {
321                continue;
322            };
323            architectures.push(architecture);
324        }
325
326        // If no architecture is set, `makepkg` errors hard, however it happily creates .SRCINFO
327        // files from it. We handle this as a hard error as well.
328        if architectures.is_empty() {
329            return Err(Error::MissingKeyword { keyword: "arch" });
330        }
331
332        // Try to convert the list of architectures into an `Architectures` instance.
333        // This will fail if "any" is combined with any specific system architecture.
334        let architectures: Architectures = architectures.try_into()?;
335
336        for prop in parsed.properties.into_iter() {
337            match prop {
338                // Skip empty lines and comments
339                PackageBaseProperty::EmptyLine | PackageBaseProperty::Comment(_) => continue,
340                PackageBaseProperty::PackageVersion(inner) => package_version = Some(inner),
341                PackageBaseProperty::PackageRelease(inner) => package_release = Some(inner),
342                PackageBaseProperty::PackageEpoch(inner) => epoch = Some(inner),
343                PackageBaseProperty::ValidPgpKeys(inner) => {
344                    pgp_fingerprints.push(inner);
345                }
346                PackageBaseProperty::CheckDependency(arch_property) => {
347                    package_base_arch_prop!(
348                        architecture_properties,
349                        arch_property,
350                        check_dependencies,
351                    )
352                }
353                PackageBaseProperty::MakeDependency(arch_property) => {
354                    package_base_arch_prop!(
355                        architecture_properties,
356                        arch_property,
357                        make_dependencies,
358                    )
359                }
360                PackageBaseProperty::Meta(shared_meta_property) => {
361                    match shared_meta_property {
362                        SharedMetaProperty::Description(inner) => description = Some(inner),
363                        SharedMetaProperty::Url(inner) => url = Some(inner),
364                        SharedMetaProperty::License(inner) => licenses.push(inner),
365                        // We already handled those above.
366                        SharedMetaProperty::Architecture(_) => continue,
367                        SharedMetaProperty::Changelog(inner) => changelog = Some(inner),
368                        SharedMetaProperty::Install(inner) => install = Some(inner),
369                        SharedMetaProperty::Group(inner) => groups.push(inner),
370                        SharedMetaProperty::Option(inner) => options.push(inner),
371                        SharedMetaProperty::Backup(inner) => backups.push(inner),
372                    }
373                }
374                PackageBaseProperty::Relation(relation_property) => match relation_property {
375                    parser::RelationProperty::Dependency(arch_property) => package_base_arch_prop!(
376                        architecture_properties,
377                        arch_property,
378                        dependencies,
379                    ),
380                    parser::RelationProperty::OptionalDependency(arch_property) => {
381                        package_base_arch_prop!(
382                            architecture_properties,
383                            arch_property,
384                            optional_dependencies,
385                        )
386                    }
387                    parser::RelationProperty::Provides(arch_property) => {
388                        package_base_arch_prop!(architecture_properties, arch_property, provides,)
389                    }
390                    parser::RelationProperty::Conflicts(arch_property) => {
391                        package_base_arch_prop!(architecture_properties, arch_property, conflicts,)
392                    }
393                    parser::RelationProperty::Replaces(arch_property) => {
394                        package_base_arch_prop!(architecture_properties, arch_property, replaces,)
395                    }
396                },
397                PackageBaseProperty::Source(source_property) => match source_property {
398                    parser::SourceProperty::Source(arch_property) => {
399                        package_base_arch_prop!(architecture_properties, arch_property, sources,)
400                    }
401                    parser::SourceProperty::NoExtract(value) => no_extracts.push(value),
402                    parser::SourceProperty::B2Checksum(arch_property) => package_base_arch_prop!(
403                        architecture_properties,
404                        arch_property,
405                        b2_checksums,
406                    ),
407                    parser::SourceProperty::Md5Checksum(arch_property) => {
408                        package_base_arch_prop!(
409                            architecture_properties,
410                            arch_property,
411                            md5_checksums,
412                        );
413                    }
414                    parser::SourceProperty::Sha1Checksum(arch_property) => {
415                        package_base_arch_prop!(
416                            architecture_properties,
417                            arch_property,
418                            sha1_checksums,
419                        );
420                    }
421                    parser::SourceProperty::Sha224Checksum(arch_property) => {
422                        package_base_arch_prop!(
423                            architecture_properties,
424                            arch_property,
425                            sha224_checksums,
426                        )
427                    }
428                    parser::SourceProperty::Sha256Checksum(arch_property) => {
429                        package_base_arch_prop!(
430                            architecture_properties,
431                            arch_property,
432                            sha256_checksums,
433                        )
434                    }
435                    parser::SourceProperty::Sha384Checksum(arch_property) => {
436                        package_base_arch_prop!(
437                            architecture_properties,
438                            arch_property,
439                            sha384_checksums,
440                        )
441                    }
442                    parser::SourceProperty::Sha512Checksum(arch_property) => {
443                        package_base_arch_prop!(
444                            architecture_properties,
445                            arch_property,
446                            sha512_checksums,
447                        )
448                    }
449                    parser::SourceProperty::CrcChecksum(arch_property) => {
450                        package_base_arch_prop!(
451                            architecture_properties,
452                            arch_property,
453                            crc_checksums,
454                        )
455                    }
456                },
457            }
458        }
459
460        // Handle a missing package_version
461        let package_version = match package_version {
462            Some(package_version) => package_version,
463            None => {
464                return Err(Error::MissingKeyword { keyword: "pkgver" });
465            }
466        };
467
468        // Handle a missing package_release
469        let package_release = match package_release {
470            Some(package_release) => package_release,
471            None => {
472                return Err(Error::MissingKeyword { keyword: "pkgrel" });
473            }
474        };
475        let version = FullVersion::new(package_version, package_release, epoch);
476
477        Ok(PackageBase {
478            name: parsed.name,
479            description,
480            url,
481            licenses,
482            changelog,
483            architectures,
484            architecture_properties,
485            install,
486            groups,
487            options,
488            backups,
489            version,
490            pgp_fingerprints,
491            dependencies,
492            optional_dependencies,
493            provides,
494            conflicts,
495            replaces,
496            check_dependencies,
497            make_dependencies,
498            sources,
499            no_extracts,
500            b2_checksums,
501            md5_checksums,
502            sha1_checksums,
503            sha224_checksums,
504            sha256_checksums,
505            sha384_checksums,
506            sha512_checksums,
507            crc_checksums,
508        })
509    }
510}