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