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}