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 Architectures,
7 Backup,
8 Changelog,
9 Install,
10 License,
11 MakepkgOption,
12 Name,
13 OptionalDependency,
14 PackageDescription,
15 PackageRelation,
16 RelationOrSoname,
17 SystemArchitecture,
18 Url,
19};
20use serde::{Deserialize, Serialize};
21
22use crate::{
23 Error,
24 source_info::parser::{
25 ClearableProperty,
26 PackageProperty,
27 RawPackage,
28 RelationProperty,
29 SharedMetaProperty,
30 },
31};
32#[cfg(doc)]
33use crate::{MergedPackage, SourceInfoV1, source_info::v1::package_base::PackageBase};
34
35/// A [`Package`] property that can override its respective defaults in [`PackageBase`].
36///
37/// This type is similar to [`Option`], which has special serialization behavior.
38/// However, in some file formats (e.g. JSON) it is not possible to represent data such as
39/// `Option<Option<T>>`, as serialization would flatten the structure. This type enables
40/// representation of this type of data.
41#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
42#[serde(tag = "override")]
43pub enum Override<T> {
44 /// The property is not overridden.
45 #[default]
46 No,
47 /// The property is cleared.
48 Clear,
49 /// The property is overridden.
50 Yes {
51 /// The value with which the property is overridden.
52 value: T,
53 },
54}
55
56impl<T> Override<T> {
57 /// Applies `self` onto an `Option<T>`.
58 ///
59 /// - `Override::No` -> `other` stays untouched.
60 /// - `Override::Clear` -> `other` is set to `None`.
61 /// - `Override::Yes { value }` -> `other` is set to `Some(value)`.
62 #[inline]
63 pub fn merge_option(self, other: &mut Option<T>) {
64 match self {
65 Override::No => (),
66 Override::Clear => *other = None,
67 Override::Yes { value } => *other = Some(value),
68 }
69 }
70
71 /// If `Override::Yes`, its value will be returned.
72 /// If `self` is something else, `self` will be set to a `Override::Yes { value: default }`.
73 ///
74 /// Similar to as [Option::get_or_insert].
75 #[inline]
76 pub fn get_or_insert(&mut self, default: T) -> &mut T {
77 if let Override::Yes { value } = self {
78 return value;
79 }
80
81 *self = Override::Yes { value: default };
82
83 // This is infallible.
84 if let Override::Yes { value } = self {
85 return value;
86 }
87 unreachable!()
88 }
89}
90
91impl<T> Override<Vec<T>> {
92 /// Applies `self` onto an `Vec<T>`.
93 ///
94 /// - `Override::No` -> `other` stays untouched.
95 /// - `Override::Clear` -> `other` is set to `Vec::new()`.
96 /// - `Override::Yes { value }` -> `other` is set to `value`.
97 #[inline]
98 pub fn merge_vec(self, other: &mut Vec<T>) {
99 match self {
100 Override::No => (),
101 Override::Clear => *other = Vec::new(),
102 Override::Yes { value } => *other = value,
103 }
104 }
105}
106
107/// Package metadata based on a `pkgname` section in SRCINFO data.
108///
109/// This struct only contains package specific overrides.
110/// Only in combination with [`PackageBase`] data a full view on a package's metadata is possible.
111///
112/// All values and nested structs inside this struct, except the `name` field, are either nested
113/// [`Option`]s (e.g. `Override<Option<String>>`) or optional collections (e.g. `Option<Vec>`).
114/// This is due to the fact that all fields are overrides for the defaults set by the
115/// [`PackageBase`] struct.
116/// - If a value is `Override::No`, this indicates that the [`PackageBase`]'s value should be used.
117/// - If a value is `Override::Yes<None>`, this means that the value should be empty and the
118/// [`PackageBase`] should be ignored. The same goes for collections in the sense of
119/// `Override::Yes(Vec::new())`.
120/// - If a value is `Override::Yes(Some(value))` or `Override::Yes(vec![values])`, these values
121/// should then be used.
122///
123/// This struct merely contains the overrides that should be applied on top of the
124/// [PackageBase] to get the final definition of this package.
125//
126/// Take a look at [SourceInfoV1::packages_for_architecture] on how to get the merged representation
127/// [MergedPackage] of a package.
128#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
129pub struct Package {
130 /// The alpm-package-name of the package.
131 pub name: Name,
132 /// The (potentially overridden) description of the package.
133 pub description: Override<PackageDescription>,
134 /// The (potentially overridden) upstream URL of the package.
135 pub url: Override<Url>,
136 /// The (potentially overridden) relative path to a changelog file of the package.
137 pub changelog: Override<Changelog>,
138 /// The (potentially overridden) list of licenses that apply to the package.
139 pub licenses: Override<Vec<License>>,
140
141 // Build or package management related meta fields
142 /// The (potentially overridden) relative path to an alpm-install-scriptlet of the package.
143 pub install: Override<Install>,
144 /// The (potentially overridden) list of alpm-package-groups the package is part of.
145 pub groups: Override<Vec<String>>,
146 /// The (potentially overridden) list of build tool options used when building the package.
147 pub options: Override<Vec<MakepkgOption>>,
148 /// The (potentially overridden) list of relative paths to files in the package that should be
149 /// backed up.
150 pub backups: Override<Vec<Backup>>,
151
152 /// The architectures that are supported by this package.
153 // Despite being overridable, `architectures` field isn't of the `Override` type, as it
154 // **cannot** be cleared.
155 pub architectures: Option<Architectures>,
156 /// The map of alpm-architecture specific overrides for package relations of a package.
157 pub architecture_properties: BTreeMap<SystemArchitecture, PackageArchitecture>,
158
159 /// The (potentially overridden) list of run-time dependencies of the package.
160 pub dependencies: Override<Vec<RelationOrSoname>>,
161 /// The (potentially overridden) list of optional dependencies of the package.
162 pub optional_dependencies: Override<Vec<OptionalDependency>>,
163 /// The (potentially overridden) list of provisions of the package.
164 pub provides: Override<Vec<RelationOrSoname>>,
165 /// The (potentially overridden) list of conflicts of the package.
166 pub conflicts: Override<Vec<PackageRelation>>,
167 /// The (potentially overridden) list of replacements of the package.
168 pub replaces: Override<Vec<PackageRelation>>,
169}
170
171impl From<Name> for Package {
172 /// Creates a new [`Package`] from a [`Name`] by calling [`Package::new_with_defaults`].
173 fn from(value: Name) -> Self {
174 Package::new_with_defaults(value)
175 }
176}
177
178/// Architecture specific package properties for use in [`Package`].
179///
180/// For each [`Architecture`] defined in [`Package::architectures`] a [`PackageArchitecture`] is
181/// present in [`Package::architecture_properties`].
182#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
183pub struct PackageArchitecture {
184 /// The (potentially overridden) list of run-time dependencies of the package.
185 pub dependencies: Override<Vec<RelationOrSoname>>,
186 /// The (potentially overridden) list of optional dependencies of the package.
187 pub optional_dependencies: Override<Vec<OptionalDependency>>,
188 /// The (potentially overridden) list of provisions of the package.
189 pub provides: Override<Vec<RelationOrSoname>>,
190 /// The (potentially overridden) list of conflicts of the package.
191 pub conflicts: Override<Vec<PackageRelation>>,
192 /// The (potentially overridden) list of replacements of the package.
193 pub replaces: Override<Vec<PackageRelation>>,
194}
195
196/// Handles all potentially architecture specific, clearable entries in the [`Package::from_parsed`]
197/// function.
198///
199/// If no architecture is encountered, it simply clears the value on the [`Package`] itself.
200/// Otherwise, it's added to the respective [`PackageBase::architecture_properties`].
201macro_rules! clearable_arch_vec {
202 (
203 $architecture_properties:ident,
204 $architecture:ident,
205 $field_name:ident,
206 ) => {
207 // Check if the property is architecture specific.
208 // If so, we have to perform some checks and preparations
209 if let Some(architecture) = $architecture
210 && let Architecture::Some(system_arch) = architecture
211 {
212 let properties = $architecture_properties
213 .entry(system_arch.clone())
214 .or_default();
215 properties.$field_name = Override::Clear;
216 } else {
217 $field_name = Override::Clear;
218 }
219 };
220}
221
222/// Handles all potentially architecture specific Vector entries in the [`Package::from_parsed`]
223/// function.
224///
225/// If no architecture is encountered, it simply adds the value on the [`Package`] itself.
226/// Otherwise, it clears the value on the respective [`Package::architecture_properties`] entry.
227macro_rules! package_arch_prop {
228 (
229 $architecture_properties:ident,
230 $arch_property:ident,
231 $field_name:ident,
232 ) => {
233 // Check if the property is architecture specific.
234 // If so, we have to perform some checks and preparations
235 if let Some(architecture) = $arch_property.architecture
236 && let Architecture::Some(system_arch) = architecture
237 {
238 // Make sure the architecture specific properties are initialized.
239 let architecture_properties = $architecture_properties
240 .entry(system_arch)
241 .or_insert(PackageArchitecture::default());
242
243 // Set the architecture specific value.
244 architecture_properties
245 .$field_name
246 .get_or_insert(Vec::new())
247 .push($arch_property.value);
248 } else {
249 $field_name
250 .get_or_insert(Vec::new())
251 .push($arch_property.value)
252 }
253 };
254}
255
256impl Package {
257 /// Creates a new [`Package`] from a [`Name`].
258 ///
259 /// Uses `name` and initializes all remaining fields of [`Package`] with default values.
260 ///
261 /// # Example
262 ///
263 /// ```
264 /// use std::str::FromStr;
265 ///
266 /// use alpm_srcinfo::source_info::v1::package::Package;
267 /// use alpm_types::Name;
268 ///
269 /// # fn main() -> testresult::TestResult {
270 ///
271 /// let package = Package::new_with_defaults(Name::from_str("example_package")?);
272 /// # Ok(())
273 /// # }
274 /// ```
275 pub fn new_with_defaults(value: Name) -> Self {
276 Package {
277 name: value,
278 description: Default::default(),
279 url: Default::default(),
280 changelog: Default::default(),
281 licenses: Default::default(),
282 install: Default::default(),
283 groups: Default::default(),
284 options: Default::default(),
285 backups: Default::default(),
286 architectures: Default::default(),
287 architecture_properties: Default::default(),
288 dependencies: Default::default(),
289 optional_dependencies: Default::default(),
290 provides: Default::default(),
291 conflicts: Default::default(),
292 replaces: Default::default(),
293 }
294 }
295
296 /// Creates a new [`Package`] instance from a [`RawPackage`].
297 ///
298 /// # Parameters
299 ///
300 /// - `parsed`: The [`RawPackage`] representation of the SRCINFO data. The input guarantees that
301 /// the keyword assignments have been parsed correctly, but not yet that they represent valid
302 /// SRCINFO data as a whole.
303 pub fn from_parsed(parsed: RawPackage) -> Result<Self, Error> {
304 let mut description = Override::No;
305 let mut url = Override::No;
306 let mut licenses = Override::No;
307 let mut changelog = Override::No;
308 let mut architectures = None;
309 let mut architecture_properties: BTreeMap<SystemArchitecture, PackageArchitecture> =
310 BTreeMap::new();
311
312 // Build or package management related meta fields
313 let mut install = Override::No;
314 let mut groups = Override::No;
315 let mut options = Override::No;
316 let mut backups = Override::No;
317
318 let mut dependencies = Override::No;
319 let mut optional_dependencies = Override::No;
320 let mut provides = Override::No;
321 let mut conflicts = Override::No;
322 let mut replaces = Override::No;
323
324 // First up, check all input for potential architecture overrides.
325 for prop in parsed.properties.iter() {
326 // We're only interested in architecture properties.
327 let PackageProperty::Meta(SharedMetaProperty::Architecture(architecture)) = prop else {
328 continue;
329 };
330 let architectures = architectures.get_or_insert(Vec::new());
331 architectures.push(architecture);
332 }
333
334 let architectures = if let Some(arch_vec) = architectures {
335 // Try to convert the list of architectures into an `Architectures` instance.
336 // This will fail if "any" is combined with any specific system architecture.
337 let architectures: Architectures = arch_vec.try_into()?;
338 Some(architectures)
339 } else {
340 None
341 };
342
343 // Next, check if there are any ClearableProperty overrides.
344 // These indicate that a value or a vector should be overridden and set to None or an empty
345 // vector, based on the property.
346 for prop in parsed.properties.iter() {
347 // We're only interested in clearable properties.
348 let PackageProperty::Clear(clearable_property) = prop else {
349 continue;
350 };
351
352 match clearable_property {
353 ClearableProperty::Description => description = Override::Clear,
354 ClearableProperty::Url => url = Override::Clear,
355 ClearableProperty::Licenses => licenses = Override::Clear,
356 ClearableProperty::Changelog => changelog = Override::Clear,
357 ClearableProperty::Install => install = Override::Clear,
358 ClearableProperty::Groups => groups = Override::Clear,
359 ClearableProperty::Options => options = Override::Clear,
360 ClearableProperty::Backups => backups = Override::Clear,
361 ClearableProperty::Dependencies(architecture) => {
362 clearable_arch_vec!(architecture_properties, architecture, dependencies,)
363 }
364 ClearableProperty::OptionalDependencies(architecture) => {
365 clearable_arch_vec!(
366 architecture_properties,
367 architecture,
368 optional_dependencies,
369 )
370 }
371 ClearableProperty::Provides(architecture) => {
372 clearable_arch_vec!(architecture_properties, architecture, provides,)
373 }
374 ClearableProperty::Conflicts(architecture) => {
375 clearable_arch_vec!(architecture_properties, architecture, conflicts,)
376 }
377 ClearableProperty::Replaces(architecture) => {
378 clearable_arch_vec!(architecture_properties, architecture, replaces,)
379 }
380 }
381 }
382
383 // Set all of the package's properties.
384 for prop in parsed.properties.into_iter() {
385 match prop {
386 // Skip empty lines and comments
387 PackageProperty::EmptyLine | PackageProperty::Comment(_) => continue,
388 PackageProperty::Meta(shared_meta_property) => {
389 match shared_meta_property {
390 SharedMetaProperty::Description(inner) => {
391 description = Override::Yes { value: inner }
392 }
393 SharedMetaProperty::Url(inner) => url = Override::Yes { value: inner },
394 SharedMetaProperty::License(inner) => {
395 licenses.get_or_insert(Vec::new()).push(inner)
396 }
397 SharedMetaProperty::Changelog(inner) => {
398 changelog = Override::Yes { value: inner }
399 }
400 SharedMetaProperty::Install(inner) => {
401 install = Override::Yes { value: inner }
402 }
403 SharedMetaProperty::Group(inner) => {
404 groups.get_or_insert(Vec::new()).push(inner)
405 }
406 SharedMetaProperty::Option(inner) => {
407 options.get_or_insert(Vec::new()).push(inner)
408 }
409 SharedMetaProperty::Backup(inner) => {
410 backups.get_or_insert(Vec::new()).push(inner)
411 }
412 // We already handled these at the start of the function in a previous pass.
413 SharedMetaProperty::Architecture(_) => continue,
414 }
415 }
416 PackageProperty::Relation(relation_property) => match relation_property {
417 RelationProperty::Dependency(arch_property) => {
418 package_arch_prop!(architecture_properties, arch_property, dependencies,)
419 }
420 RelationProperty::OptionalDependency(arch_property) => {
421 package_arch_prop!(
422 architecture_properties,
423 arch_property,
424 optional_dependencies,
425 )
426 }
427 RelationProperty::Provides(arch_property) => {
428 package_arch_prop!(architecture_properties, arch_property, provides,)
429 }
430 RelationProperty::Conflicts(arch_property) => {
431 package_arch_prop!(architecture_properties, arch_property, conflicts,)
432 }
433 RelationProperty::Replaces(arch_property) => {
434 package_arch_prop!(architecture_properties, arch_property, replaces,)
435 }
436 },
437 // We already handled at the start in a separate pass.
438 PackageProperty::Clear(_) => continue,
439 }
440 }
441
442 Ok(Package {
443 name: parsed.name,
444 description,
445 url,
446 changelog,
447 licenses,
448 architectures,
449 architecture_properties,
450 install,
451 groups,
452 options,
453 backups,
454 dependencies,
455 optional_dependencies,
456 provides,
457 conflicts,
458 replaces,
459 })
460 }
461}