alpm_srcinfo/source_info/v1/
package.rs

1//! Handling of metadata found in a `pkgname` section of SRCINFO data.
2use std::collections::BTreeMap;
3
4use alpm_types::{
5    Architecture,
6    Backup,
7    Changelog,
8    Install,
9    License,
10    MakepkgOption,
11    Name,
12    OptionalDependency,
13    PackageDescription,
14    PackageRelation,
15    RelationOrSoname,
16    Url,
17};
18use serde::{Deserialize, Serialize};
19
20#[cfg(doc)]
21use crate::{MergedPackage, SourceInfoV1, source_info::v1::package_base::PackageBase};
22use crate::{
23    error::SourceInfoError,
24    lints::{
25        duplicate_architecture,
26        missing_architecture_for_property,
27        non_spdx_license,
28        reassigned_cleared_property,
29    },
30    source_info::parser::{
31        ClearableProperty,
32        PackageProperty,
33        RawPackage,
34        RelationProperty,
35        SharedMetaProperty,
36    },
37};
38
39/// A [`Package`] property that can override its respective defaults in [`PackageBase`].
40///
41/// This type is similar to [`Option`], which has special serialization behavior.
42/// However, in some file formats (e.g. JSON) it is not possible to represent data such as
43/// `Option<Option<T>>`, as serialization would flatten the structure. This type enables
44/// representation of this type of data.
45#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
46#[serde(tag = "override")]
47pub enum Override<T> {
48    /// The property is not overridden.
49    #[default]
50    No,
51    /// The property is cleared.
52    Clear,
53    /// The property is overridden.
54    Yes {
55        /// The value with which the property is overridden.
56        value: T,
57    },
58}
59
60impl<T> Override<T> {
61    /// Applies `self` onto an `Option<T>`.
62    ///
63    /// - `Override::No` -> `other` stays untouched.
64    /// - `Override::Clear` -> `other` is set to `None`.
65    /// - `Override::Yes { value }` -> `other` is set to `Some(value)`.
66    #[inline]
67    pub fn merge_option(self, other: &mut Option<T>) {
68        match self {
69            Override::No => (),
70            Override::Clear => *other = None,
71            Override::Yes { value } => *other = Some(value),
72        }
73    }
74
75    /// If `Override::Yes`, its value will be returned.
76    /// If `self` is something else, `self` will be set to a `Override::Yes { value: default }`.
77    ///
78    /// Similar to as [Option::get_or_insert].
79    #[inline]
80    pub fn get_or_insert(&mut self, default: T) -> &mut T {
81        if let Override::Yes { value } = self {
82            return value;
83        }
84
85        *self = Override::Yes { value: default };
86
87        // This is infallible.
88        if let Override::Yes { value } = self {
89            return value;
90        }
91        unreachable!()
92    }
93}
94
95impl<T> Override<Vec<T>> {
96    /// Applies `self` onto an `Vec<T>`.
97    ///
98    /// - `Override::No` -> `other` stays untouched.
99    /// - `Override::Clear` -> `other` is set to `Vec::new()`.
100    /// - `Override::Yes { value }` -> `other` is set to `value`.
101    #[inline]
102    pub fn merge_vec(self, other: &mut Vec<T>) {
103        match self {
104            Override::No => (),
105            Override::Clear => *other = Vec::new(),
106            Override::Yes { value } => *other = value,
107        }
108    }
109}
110
111/// Package metadata based on a `pkgname` section in SRCINFO data.
112///
113/// This struct only contains package specific overrides.
114/// Only in combination with [`PackageBase`] data a full view on a package's metadata is possible.
115///
116/// All values and nested structs inside this struct, except the `name` field, are either nested
117/// [`Option`]s (e.g. `Override<Option<String>>`) or optional collections (e.g. `Option<Vec>`).
118/// This is due to the fact that all fields are overrides for the defaults set by the
119/// [`PackageBase`] struct.
120/// - If a value is `Override::No`, this indicates that the [`PackageBase`]'s value should be used.
121/// - If a value is `Override::Yes<None>`, this means that the value should be empty and the
122///   [`PackageBase`] should be ignored. The same goes for collections in the sense of
123///   `Override::Yes(Vec::new())`.
124/// - If a value is `Override::Yes(Some(value))` or `Override::Yes(vec![values])`, these values
125///   should then be used.
126///
127/// This struct merely contains the overrides that should be applied on top of the
128/// [PackageBase] to get the final definition of this package.
129//
130/// Take a look at [SourceInfoV1::packages_for_architecture] on how to get the merged representation
131/// [MergedPackage] of a package.
132#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
133pub struct Package {
134    /// The alpm-package-name of the package.
135    pub name: Name,
136    /// The (potentially overridden) description of the package.
137    pub description: Override<PackageDescription>,
138    /// The (potentially overridden) upstream URL of the package.
139    pub url: Override<Url>,
140    /// The (potentially overridden) relative path to a changelog file of the package.
141    pub changelog: Override<Changelog>,
142    /// The (potentially overridden) list of licenses that apply to the package.
143    pub licenses: Override<Vec<License>>,
144
145    // Build or package management related meta fields
146    /// The (potentially overridden) relative path to an alpm-install-scriptlet of the package.
147    pub install: Override<Install>,
148    /// The (potentially overridden) list of alpm-package-groups the package is part of.
149    pub groups: Override<Vec<String>>,
150    /// The (potentially overridden) list of build tool options used when building the package.
151    pub options: Override<Vec<MakepkgOption>>,
152    /// The (potentially overridden) list of relative paths to files in the package that should be
153    /// backed up.
154    pub backups: Override<Vec<Backup>>,
155
156    /// These are all override fields that may be architecture specific.
157    /// Despite being overridable, `architectures` field isn't of the `Override` type, as it
158    /// **cannot** be cleared.
159    pub architectures: Option<Vec<Architecture>>,
160    /// The map of alpm-architecture specific overrides for package relations of a package.
161    pub architecture_properties: BTreeMap<Architecture, PackageArchitecture>,
162
163    /// The (potentially overridden) list of run-time dependencies of the package.
164    pub dependencies: Override<Vec<RelationOrSoname>>,
165    /// The (potentially overridden) list of optional dependencies of the package.
166    pub optional_dependencies: Override<Vec<OptionalDependency>>,
167    /// The (potentially overridden) list of provisions of the package.
168    pub provides: Override<Vec<RelationOrSoname>>,
169    /// The (potentially overridden) list of conflicts of the package.
170    pub conflicts: Override<Vec<PackageRelation>>,
171    /// The (potentially overridden) list of replacements of the package.
172    pub replaces: Override<Vec<PackageRelation>>,
173}
174
175impl From<Name> for Package {
176    /// Creates a new [`Package`] from a [`Name`].
177    ///
178    /// Uses `name` and initializes all remaining fields of [`Package`] with default values.
179    fn from(value: Name) -> Self {
180        Package {
181            name: value,
182            description: Default::default(),
183            url: Default::default(),
184            changelog: Default::default(),
185            licenses: Default::default(),
186            install: Default::default(),
187            groups: Default::default(),
188            options: Default::default(),
189            backups: Default::default(),
190            architectures: Default::default(),
191            architecture_properties: Default::default(),
192            dependencies: Default::default(),
193            optional_dependencies: Default::default(),
194            provides: Default::default(),
195            conflicts: Default::default(),
196            replaces: Default::default(),
197        }
198    }
199}
200
201/// Architecture specific package properties for use in [`Package`].
202///
203/// For each [`Architecture`] defined in [`Package::architectures`] a [`PackageArchitecture`] is
204/// present in [`Package::architecture_properties`].
205#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
206pub struct PackageArchitecture {
207    /// The (potentially overridden) list of run-time dependencies of the package.
208    pub dependencies: Override<Vec<RelationOrSoname>>,
209    /// The (potentially overridden) list of optional dependencies of the package.
210    pub optional_dependencies: Override<Vec<OptionalDependency>>,
211    /// The (potentially overridden) list of provisions of the package.
212    pub provides: Override<Vec<RelationOrSoname>>,
213    /// The (potentially overridden) list of conflicts of the package.
214    pub conflicts: Override<Vec<PackageRelation>>,
215    /// The (potentially overridden) list of replacements of the package.
216    pub replaces: Override<Vec<PackageRelation>>,
217}
218
219/// Handles all potentially architecture specific, clearable entries in the [`Package::from_parsed`]
220/// function.
221///
222/// If no architecture is encountered, it simply clears the value on the [`Package`] itself.
223/// Otherwise, it's added to the respective [`PackageBase::architecture_properties`].
224///
225/// Furthermore, adds linter warnings if an architecture is encountered that doesn't exist in the
226/// [`PackageBase::architectures`] or [`Package::architectures`] if overridden.
227macro_rules! clearable_arch_vec {
228    (
229        $line:ident,
230        $errors:ident,
231        $lint_architectures:ident,
232        $architecture_properties:ident,
233        $architecture:ident,
234        $field_name:ident,
235    ) => {
236        // Check if the property is architecture specific.
237        // If so, we have to perform some checks and preparations
238        if let Some(architecture) = $architecture {
239            let properties = $architecture_properties.entry(*architecture).or_default();
240            properties.$field_name = Override::Clear;
241
242            // Throw an error for all architecture specific properties that don't have
243            // an explicit `arch` statement. This is considered bad style.
244            // Also handle the special `Any` [Architecture], which allows all architectures.
245            if !$lint_architectures.contains(&architecture)
246                && !$lint_architectures.contains(&Architecture::Any)
247            {
248                missing_architecture_for_property($errors, $line, *architecture);
249            }
250        } else {
251            $field_name = Override::Clear;
252        }
253    };
254}
255
256/// Handles all potentially architecture specific Vector entries in the [`Package::from_parsed`]
257/// function.
258///
259/// If no architecture is encountered, it simply adds the value on the [`Package`] itself.
260/// Otherwise, it clears the value on the respective [`Package::architecture_properties`] entry.
261///
262/// Furthermore, adds linter warnings if an architecture is encountered that doesn't exist in the
263/// [`PackageBase::architectures`] or [`Package::architectures`] if overridden.
264macro_rules! package_arch_prop {
265    (
266        $line:ident,
267        $errors:ident,
268        $lint_architectures:ident,
269        $architecture_properties:ident,
270        $arch_property:ident,
271        $field_name:ident,
272    ) => {
273        // Check if the property is architecture specific.
274        // If so, we have to perform some checks and preparations
275        if let Some(architecture) = $arch_property.architecture {
276            // Make sure the architecture specific properties are initialized.
277            let architecture_properties = $architecture_properties
278                .entry(architecture)
279                .or_insert(PackageArchitecture::default());
280
281            // Set the architecture specific value.
282            architecture_properties
283                .$field_name
284                .get_or_insert(Vec::new())
285                .push($arch_property.value);
286
287            // Throw an error for all architecture specific properties that don't have
288            // an explicit `arch` statement. This is considered bad style.
289            // Also handle the special `Any` [Architecture], which allows all architectures.
290            if !$lint_architectures.contains(&architecture)
291                && !$lint_architectures.contains(&Architecture::Any)
292            {
293                missing_architecture_for_property($errors, $line, architecture);
294            }
295        } else {
296            $field_name
297                .get_or_insert(Vec::new())
298                .push($arch_property.value)
299        }
300    };
301}
302
303impl Package {
304    /// Creates a new [`Package`] instance from a [`RawPackage`].
305    ///
306    /// # Parameters
307    ///
308    /// - `line_start`: The number of preceding lines, so that error/lint messages can reference the
309    ///   correct lines.
310    /// - `parsed`: The [`RawPackage`] representation of the SRCINFO data. The input guarantees that
311    ///   the keyword assignments have been parsed correctly, but not yet that they represent valid
312    ///   SRCINFO data as a whole.
313    /// - `errors`: All errors and lints encountered during the creation of the [`Package`].
314    ///
315    /// # Errors
316    ///
317    /// This function does not return a [`Result`], but instead relies on aggregating all lints,
318    /// warnings and errors in `errors`.
319    /// This allows to keep the function call recoverable, so that all errors and lints can
320    /// be returned all at once.
321    pub fn from_parsed(
322        line_start: usize,
323        package_base_architectures: &Vec<Architecture>,
324        parsed: RawPackage,
325        errors: &mut Vec<SourceInfoError>,
326    ) -> Self {
327        let mut description = Override::No;
328        let mut url = Override::No;
329        let mut licenses = Override::No;
330        let mut changelog = Override::No;
331        let mut architectures = None;
332        let mut architecture_properties: BTreeMap<Architecture, PackageArchitecture> =
333            BTreeMap::new();
334
335        // Build or package management related meta fields
336        let mut install = Override::No;
337        let mut groups = Override::No;
338        let mut options = Override::No;
339        let mut backups = Override::No;
340
341        let mut dependencies = Override::No;
342        let mut optional_dependencies = Override::No;
343        let mut provides = Override::No;
344        let mut conflicts = Override::No;
345        let mut replaces = Override::No;
346
347        // First up, check all input for potential architecture overrides.
348        // We need this to do proper linting when doing our actual pass through the file.
349        for (index, prop) in parsed.properties.iter().enumerate() {
350            // We're only interested in architecture properties.
351            let PackageProperty::MetaProperty(SharedMetaProperty::Architecture(architecture)) =
352                prop
353            else {
354                continue;
355            };
356
357            // Calculate the actual line in the document based on any preceding lines.
358            // We have to add one, as lines aren't 0 indexed.
359            let line = index + line_start;
360
361            // Make sure to set the value of the HashSet to
362            let architectures = architectures.get_or_insert(Vec::new());
363
364            // Lint to make sure there aren't duplicate architectures declarations.
365            if architectures.contains(architecture) {
366                duplicate_architecture(errors, line, *architecture);
367            }
368
369            // Add the architecture in case it hasn't already.
370            architectures.push(*architecture);
371        }
372
373        // If there's an overrides for architectures of this package, we need to use those
374        // architectures for linting. If there isn't, we have to fall back to the PackageBase
375        // architectures, which are then used instead.
376        let architectures_for_lint = match &architectures {
377            Some(architectures) => architectures,
378            None => package_base_architectures,
379        };
380
381        // Save all ClearableProperties so that we may use them for linting lateron.
382        let mut cleared_properties = Vec::new();
383
384        // Next, check if there're any [ClearableProperty] overrides.
385        // These indicate that a value or a vector should be overridden and set to None or an empty
386        // vector, based on the property.
387        for (index, prop) in parsed.properties.iter().enumerate() {
388            // Calculate the actual line in the document based on any preceding lines.
389            // We have to add one, as lines aren't 0 indexed.
390            let line = index + line_start;
391
392            // We're only interested in clearable properties.
393            let PackageProperty::Clear(clearable_property) = prop else {
394                continue;
395            };
396
397            cleared_properties.push(clearable_property.clone());
398
399            match clearable_property {
400                ClearableProperty::Description => description = Override::Clear,
401                ClearableProperty::Url => url = Override::Clear,
402                ClearableProperty::Licenses => licenses = Override::Clear,
403                ClearableProperty::Changelog => changelog = Override::Clear,
404                ClearableProperty::Install => install = Override::Clear,
405                ClearableProperty::Groups => groups = Override::Clear,
406                ClearableProperty::Options => options = Override::Clear,
407                ClearableProperty::Backups => backups = Override::Clear,
408                ClearableProperty::Dependencies(architecture) => clearable_arch_vec!(
409                    line,
410                    errors,
411                    architectures_for_lint,
412                    architecture_properties,
413                    architecture,
414                    dependencies,
415                ),
416                ClearableProperty::OptionalDependencies(architecture) => {
417                    clearable_arch_vec!(
418                        line,
419                        errors,
420                        architectures_for_lint,
421                        architecture_properties,
422                        architecture,
423                        optional_dependencies,
424                    )
425                }
426                ClearableProperty::Provides(architecture) => clearable_arch_vec!(
427                    line,
428                    errors,
429                    architectures_for_lint,
430                    architecture_properties,
431                    architecture,
432                    provides,
433                ),
434                ClearableProperty::Conflicts(architecture) => clearable_arch_vec!(
435                    line,
436                    errors,
437                    architectures_for_lint,
438                    architecture_properties,
439                    architecture,
440                    conflicts,
441                ),
442                ClearableProperty::Replaces(architecture) => clearable_arch_vec!(
443                    line,
444                    errors,
445                    architectures_for_lint,
446                    architecture_properties,
447                    architecture,
448                    replaces,
449                ),
450            }
451        }
452
453        /// Mini helper macro that crates a filter closure to filter a specific SharedMetaProperty.
454        /// Needed in the following ClearableProperty lint check.
455        /// The function must be boxed as we mix this with closures from
456        /// `relation_property_filter`.
457        macro_rules! meta_property_filter {
458            ($pattern:pat) => {
459                Box::new(|(_, property): &(usize, &PackageProperty)| {
460                    matches!(property, PackageProperty::MetaProperty($pattern))
461                })
462            };
463        }
464
465        /// Mini helper macro that crates a filter closure to filter a specific RelationProperty.
466        /// Needed in the following ClearableProperty lint check.
467        macro_rules! relation_property_filter {
468            ($architecture:ident, $pattern:pat) => {{
469                // Clone the cleared architecture so that it may be copied into the closure
470                let cleared_architecture = $architecture.clone();
471                Box::new(move |(_, property): &(usize, &PackageProperty)| {
472                    // Make sure we have a relation
473                    let PackageProperty::RelationProperty(relation) = property else {
474                        return false;
475                    };
476                    // Make sure we match the pattern
477                    if !matches!(relation, $pattern) {
478                        return false;
479                    }
480
481                    // Check whether the architecture matches
482                    cleared_architecture == relation.architecture()
483                })
484            }};
485        }
486
487        // Ensures that cleared properties don't get overwritten again in the same scope of a
488        // package. E.g.
489        // ```txt
490        // depends =
491        // depends = vim
492        // ```
493        for clearable in cleared_properties {
494            #[allow(clippy::type_complexity)]
495            // Return a filter closure/function that's used to search all properties for a certain
496            // enum variant. In the case of architecture specific properties, the closure also
497            // looks for properties that use the same architecture as the cleared property.
498            //
499            // This needs to be boxed as we're working with closures in the context of architecture
500            // specific properties. They capture the cleared property's architecture for comparison.
501            let filter: Box<dyn Fn(&(usize, &PackageProperty)) -> bool> = match clearable {
502                ClearableProperty::Description => {
503                    meta_property_filter!(SharedMetaProperty::Description(_))
504                }
505                ClearableProperty::Url => {
506                    meta_property_filter!(SharedMetaProperty::Url(_))
507                }
508                ClearableProperty::Licenses => {
509                    meta_property_filter!(SharedMetaProperty::License(_))
510                }
511                ClearableProperty::Changelog => {
512                    meta_property_filter!(SharedMetaProperty::Changelog(_))
513                }
514                ClearableProperty::Install => {
515                    meta_property_filter!(SharedMetaProperty::Install(_))
516                }
517                ClearableProperty::Groups => {
518                    meta_property_filter!(SharedMetaProperty::Group(_))
519                }
520                ClearableProperty::Options => {
521                    meta_property_filter!(SharedMetaProperty::Option(_))
522                }
523                ClearableProperty::Backups => {
524                    meta_property_filter!(SharedMetaProperty::Backup(_))
525                }
526                ClearableProperty::Dependencies(architecture) => {
527                    relation_property_filter!(architecture, RelationProperty::Dependency(_))
528                }
529                ClearableProperty::OptionalDependencies(architecture) => {
530                    relation_property_filter!(architecture, RelationProperty::OptionalDependency(_))
531                }
532                ClearableProperty::Provides(architecture) => {
533                    relation_property_filter!(architecture, RelationProperty::Provides(_))
534                }
535                ClearableProperty::Conflicts(architecture) => {
536                    relation_property_filter!(architecture, RelationProperty::Conflicts(_))
537                }
538                ClearableProperty::Replaces(architecture) => {
539                    relation_property_filter!(architecture, RelationProperty::Replaces(_))
540                }
541            };
542
543            // Check if we found a declaration even though the field is also being cleared.
544            let Some((index, _)) = parsed.properties.iter().enumerate().find(filter) else {
545                continue;
546            };
547
548            // Calculate the actual line in the document based on any preceding lines.
549            let line = index + line_start;
550
551            // Create the lint error
552            reassigned_cleared_property(errors, line);
553        }
554
555        // Set all of the package's properties.
556        for (line, prop) in parsed.properties.into_iter().enumerate() {
557            // Calculate the actual line in the document based on any preceding lines.
558            let line = line + line_start;
559            match prop {
560                // Skip empty lines and comments
561                PackageProperty::EmptyLine | PackageProperty::Comment(_) => continue,
562                PackageProperty::MetaProperty(shared_meta_property) => {
563                    match shared_meta_property {
564                        SharedMetaProperty::Description(inner) => {
565                            description = Override::Yes { value: inner }
566                        }
567                        SharedMetaProperty::Url(inner) => url = Override::Yes { value: inner },
568                        SharedMetaProperty::License(inner) => {
569                            // Create lints for non-spdx licenses.
570                            if let License::Unknown(_) = &inner {
571                                non_spdx_license(errors, line, inner.to_string());
572                            }
573                            licenses.get_or_insert(Vec::new()).push(inner)
574                        }
575                        SharedMetaProperty::Changelog(inner) => {
576                            changelog = Override::Yes { value: inner }
577                        }
578                        SharedMetaProperty::Install(inner) => {
579                            install = Override::Yes { value: inner }
580                        }
581                        SharedMetaProperty::Group(inner) => {
582                            groups.get_or_insert(Vec::new()).push(inner)
583                        }
584                        SharedMetaProperty::Option(inner) => {
585                            options.get_or_insert(Vec::new()).push(inner)
586                        }
587                        SharedMetaProperty::Backup(inner) => {
588                            backups.get_or_insert(Vec::new()).push(inner)
589                        }
590                        // We already handled these at the start of the function in a previous pass.
591                        SharedMetaProperty::Architecture(_) => continue,
592                    }
593                }
594                PackageProperty::RelationProperty(relation_property) => match relation_property {
595                    RelationProperty::Dependency(arch_property) => package_arch_prop!(
596                        line,
597                        errors,
598                        architectures_for_lint,
599                        architecture_properties,
600                        arch_property,
601                        dependencies,
602                    ),
603                    RelationProperty::OptionalDependency(arch_property) => {
604                        package_arch_prop!(
605                            line,
606                            errors,
607                            architectures_for_lint,
608                            architecture_properties,
609                            arch_property,
610                            optional_dependencies,
611                        )
612                    }
613                    RelationProperty::Provides(arch_property) => package_arch_prop!(
614                        line,
615                        errors,
616                        architectures_for_lint,
617                        architecture_properties,
618                        arch_property,
619                        provides,
620                    ),
621                    RelationProperty::Conflicts(arch_property) => package_arch_prop!(
622                        line,
623                        errors,
624                        architectures_for_lint,
625                        architecture_properties,
626                        arch_property,
627                        conflicts,
628                    ),
629                    RelationProperty::Replaces(arch_property) => package_arch_prop!(
630                        line,
631                        errors,
632                        architectures_for_lint,
633                        architecture_properties,
634                        arch_property,
635                        replaces,
636                    ),
637                },
638                // We already handled at the start in a separate pass.
639                PackageProperty::Clear(_) => continue,
640            }
641        }
642
643        Package {
644            name: parsed.name,
645            description,
646            url,
647            changelog,
648            licenses,
649            architectures,
650            architecture_properties,
651            install,
652            groups,
653            options,
654            backups,
655            dependencies,
656            optional_dependencies,
657            provides,
658            conflicts,
659            replaces,
660        }
661    }
662}