1use std::collections::{HashMap, HashSet};
3
4use alpm_types::{
5 Architecture,
6 Epoch,
7 License,
8 MakepkgOption,
9 Name,
10 OpenPGPIdentifier,
11 OptionalDependency,
12 PackageDescription,
13 PackageRelation,
14 PackageRelease,
15 PackageVersion,
16 RelativePath,
17 SkippableChecksum,
18 Source,
19 Url,
20 Version,
21 digests::{Blake2b512, Md5, Sha1, Sha224, Sha256, Sha384, Sha512},
22};
23
24use super::{
25 lints::{
26 duplicate_architecture,
27 missing_architecture_for_property,
28 non_spdx_license,
29 unsafe_checksum,
30 },
31 package::PackageArchitecture,
32 relation::RelationOrSoname,
33};
34use crate::{
35 error::{SourceInfoError, lint, unrecoverable},
36 parser::{self, PackageBaseProperty, RawPackageBase, SharedMetaProperty},
37};
38#[cfg(doc)]
39use crate::{
40 merged::MergedPackage,
41 source_info::{Package, SourceInfo},
42};
43
44#[derive(Debug, Clone)]
52pub struct PackageBase {
53 pub name: Name,
54 pub description: Option<PackageDescription>,
55 pub url: Option<Url>,
56 pub changelog: Option<RelativePath>,
57 pub licenses: Vec<License>,
58
59 pub install: Option<RelativePath>,
61 pub groups: Vec<String>,
62 pub options: Vec<MakepkgOption>,
63 pub backups: Vec<RelativePath>,
64
65 pub version: Version,
67 pub pgp_fingerprints: Vec<OpenPGPIdentifier>,
68
69 pub architectures: HashSet<Architecture>,
71 pub architecture_properties: HashMap<Architecture, PackageBaseArchitecture>,
72
73 pub dependencies: Vec<RelationOrSoname>,
74 pub optional_dependencies: Vec<OptionalDependency>,
75 pub provides: Vec<RelationOrSoname>,
76 pub conflicts: Vec<PackageRelation>,
77 pub replaces: Vec<PackageRelation>,
78 pub check_dependencies: Vec<PackageRelation>,
82 pub make_dependencies: Vec<PackageRelation>,
83
84 pub sources: Vec<Source>,
85 pub no_extracts: Vec<String>,
86 pub b2_checksums: Vec<SkippableChecksum<Blake2b512>>,
87 pub md5_checksums: Vec<SkippableChecksum<Md5>>,
88 pub sha1_checksums: Vec<SkippableChecksum<Sha1>>,
89 pub sha224_checksums: Vec<SkippableChecksum<Sha224>>,
90 pub sha256_checksums: Vec<SkippableChecksum<Sha256>>,
91 pub sha384_checksums: Vec<SkippableChecksum<Sha384>>,
92 pub sha512_checksums: Vec<SkippableChecksum<Sha512>>,
93}
94
95#[derive(Default, Debug, Clone)]
100pub struct PackageBaseArchitecture {
101 pub dependencies: Vec<RelationOrSoname>,
102 pub optional_dependencies: Vec<OptionalDependency>,
103 pub provides: Vec<RelationOrSoname>,
104 pub conflicts: Vec<PackageRelation>,
105 pub replaces: Vec<PackageRelation>,
106 pub check_dependencies: Vec<PackageRelation>,
110 pub make_dependencies: Vec<PackageRelation>,
111
112 pub sources: Vec<Source>,
113 pub no_extracts: Vec<String>,
114 pub b2_checksums: Vec<SkippableChecksum<Blake2b512>>,
115 pub md5_checksums: Vec<SkippableChecksum<Md5>>,
116 pub sha1_checksums: Vec<SkippableChecksum<Sha1>>,
117 pub sha224_checksums: Vec<SkippableChecksum<Sha224>>,
118 pub sha256_checksums: Vec<SkippableChecksum<Sha256>>,
119 pub sha384_checksums: Vec<SkippableChecksum<Sha384>>,
120 pub sha512_checksums: Vec<SkippableChecksum<Sha512>>,
121}
122
123impl PackageBaseArchitecture {
124 pub fn merge_package_properties(&mut self, properties: PackageArchitecture) {
129 if let Some(dependencies) = properties.dependencies {
130 self.dependencies = dependencies;
131 }
132 if let Some(optional_dependencies) = properties.optional_dependencies {
133 self.optional_dependencies = optional_dependencies;
134 }
135 if let Some(provides) = properties.provides {
136 self.provides = provides;
137 }
138 if let Some(conflicts) = properties.conflicts {
139 self.conflicts = conflicts;
140 }
141 if let Some(replaces) = properties.replaces {
142 self.replaces = replaces;
143 }
144 }
145}
146
147macro_rules! package_base_arch_prop {
156 (
157 $line:ident,
158 $errors:ident,
159 $architectures:ident,
160 $architecture_properties:ident,
161 $arch_property:ident,
162 $field_name:ident,
163 ) => {
164 if let Some(architecture) = $arch_property.architecture {
167 let architecture_properties = $architecture_properties
169 .entry(architecture)
170 .or_insert(PackageBaseArchitecture::default());
171
172 architecture_properties
174 .$field_name
175 .push($arch_property.value);
176
177 if !$architectures.contains(&architecture)
181 && !$architectures.contains(&Architecture::Any)
182 {
183 missing_architecture_for_property($errors, $line, architecture);
184 }
185 } else {
186 $field_name.push($arch_property.value)
187 }
188 };
189}
190
191impl PackageBase {
192 pub fn from_parsed(
209 line_start: usize,
210 parsed: RawPackageBase,
211 errors: &mut Vec<SourceInfoError>,
212 ) -> Self {
213 let mut description = None;
214 let mut url = None;
215 let mut licenses = Vec::new();
216 let mut changelog = None;
217 let mut architectures = HashSet::new();
218 let mut architecture_properties = HashMap::new();
219
220 let mut install = None;
222 let mut groups = Vec::new();
223 let mut options = Vec::new();
224 let mut backups = Vec::new();
225
226 let mut package_release: Option<PackageRelease> = None;
229 let mut package_epoch: Option<Epoch> = None;
232 let mut package_version: Option<PackageVersion> = None;
233 let mut pgp_fingerprints = Vec::new();
234
235 let mut dependencies = Vec::new();
236 let mut optional_dependencies = Vec::new();
237 let mut provides = Vec::new();
238 let mut conflicts = Vec::new();
239 let mut replaces = Vec::new();
240 let mut check_dependencies = Vec::new();
244 let mut make_dependencies = Vec::new();
245
246 let mut sources = Vec::new();
247 let mut no_extracts = Vec::new();
248 let mut b2_checksums = Vec::new();
249 let mut md5_checksums = Vec::new();
250 let mut sha1_checksums = Vec::new();
251 let mut sha224_checksums = Vec::new();
252 let mut sha256_checksums = Vec::new();
253 let mut sha384_checksums = Vec::new();
254 let mut sha512_checksums = Vec::new();
255
256 for (index, prop) in parsed.properties.iter().enumerate() {
259 let PackageBaseProperty::MetaProperty(SharedMetaProperty::Architecture(architecture)) =
261 prop
262 else {
263 continue;
264 };
265
266 let line = index + line_start;
269
270 if architectures.contains(architecture) {
272 duplicate_architecture(errors, line, *architecture);
273 }
274
275 architectures.insert(*architecture);
277 architecture_properties
278 .entry(*architecture)
279 .or_insert(PackageBaseArchitecture::default());
280 }
281
282 if architectures.is_empty() {
286 errors.push(lint(
287 None,
288 "No architecture has been specified. Assuming `any`.",
289 ));
290 architectures.insert(Architecture::Any);
291 architecture_properties
292 .entry(Architecture::Any)
293 .or_insert(PackageBaseArchitecture::default());
294 }
295
296 for (index, prop) in parsed.properties.into_iter().enumerate() {
297 let line = index + line_start;
299 match prop {
300 PackageBaseProperty::EmptyLine | PackageBaseProperty::Comment(_) => continue,
302 PackageBaseProperty::PackageVersion(inner) => package_version = Some(inner),
303 PackageBaseProperty::PackageRelease(inner) => package_release = Some(inner),
304 PackageBaseProperty::PackageEpoch(inner) => package_epoch = Some(inner),
305 PackageBaseProperty::ValidPgpKeys(inner) => {
306 if let OpenPGPIdentifier::OpenPGPKeyId(_) = &inner {
307 errors.push(lint(
308 Some(line),
309 concat!(
310 "OpenPGP Key IDs are highly discouraged, as the length doesn't guarantee uniqueness.",
311 "\nUse an OpenPGP v4 fingerprint instead.",
312 )
313 ));
314 }
315 pgp_fingerprints.push(inner);
316 }
317 PackageBaseProperty::CheckDependency(arch_property) => {
318 package_base_arch_prop!(
319 line,
320 errors,
321 architectures,
322 architecture_properties,
323 arch_property,
324 check_dependencies,
325 )
326 }
327 PackageBaseProperty::MakeDependency(arch_property) => {
328 package_base_arch_prop!(
329 line,
330 errors,
331 architectures,
332 architecture_properties,
333 arch_property,
334 make_dependencies,
335 )
336 }
337 PackageBaseProperty::MetaProperty(shared_meta_property) => {
338 match shared_meta_property {
339 SharedMetaProperty::Description(inner) => description = Some(inner),
340 SharedMetaProperty::Url(inner) => url = Some(inner),
341 SharedMetaProperty::License(inner) => {
342 if let License::Unknown(_) = &inner {
344 non_spdx_license(errors, line, inner.to_string());
345 }
346
347 licenses.push(inner)
348 }
349 SharedMetaProperty::Architecture(_) => continue,
351 SharedMetaProperty::Changelog(inner) => changelog = Some(inner),
352 SharedMetaProperty::Install(inner) => install = Some(inner),
353 SharedMetaProperty::Group(inner) => groups.push(inner),
354 SharedMetaProperty::Option(inner) => options.push(inner),
355 SharedMetaProperty::Backup(inner) => backups.push(inner),
356 }
357 }
358 PackageBaseProperty::RelationProperty(relation_property) => match relation_property
359 {
360 parser::RelationProperty::Dependency(arch_property) => package_base_arch_prop!(
361 line,
362 errors,
363 architectures,
364 architecture_properties,
365 arch_property,
366 dependencies,
367 ),
368 parser::RelationProperty::OptionalDependency(arch_property) => {
369 package_base_arch_prop!(
370 line,
371 errors,
372 architectures,
373 architecture_properties,
374 arch_property,
375 optional_dependencies,
376 )
377 }
378 parser::RelationProperty::Provides(arch_property) => package_base_arch_prop!(
379 line,
380 errors,
381 architectures,
382 architecture_properties,
383 arch_property,
384 provides,
385 ),
386 parser::RelationProperty::Conflicts(arch_property) => package_base_arch_prop!(
387 line,
388 errors,
389 architectures,
390 architecture_properties,
391 arch_property,
392 conflicts,
393 ),
394 parser::RelationProperty::Replaces(arch_property) => package_base_arch_prop!(
395 line,
396 errors,
397 architectures,
398 architecture_properties,
399 arch_property,
400 replaces,
401 ),
402 },
403 PackageBaseProperty::SourceProperty(source_property) => match source_property {
404 parser::SourceProperty::Source(arch_property) => package_base_arch_prop!(
405 line,
406 errors,
407 architectures,
408 architecture_properties,
409 arch_property,
410 sources,
411 ),
412 parser::SourceProperty::NoExtract(arch_property) => package_base_arch_prop!(
413 line,
414 errors,
415 architectures,
416 architecture_properties,
417 arch_property,
418 no_extracts,
419 ),
420 parser::SourceProperty::B2Checksum(arch_property) => package_base_arch_prop!(
421 line,
422 errors,
423 architectures,
424 architecture_properties,
425 arch_property,
426 b2_checksums,
427 ),
428 parser::SourceProperty::Md5Checksum(arch_property) => {
429 unsafe_checksum(errors, line, "md5");
430 package_base_arch_prop!(
431 line,
432 errors,
433 architectures,
434 architecture_properties,
435 arch_property,
436 md5_checksums,
437 );
438 }
439 parser::SourceProperty::Sha1Checksum(arch_property) => {
440 unsafe_checksum(errors, line, "sha1");
441 package_base_arch_prop!(
442 line,
443 errors,
444 architectures,
445 architecture_properties,
446 arch_property,
447 sha1_checksums,
448 );
449 }
450 parser::SourceProperty::Sha224Checksum(arch_property) => {
451 package_base_arch_prop!(
452 line,
453 errors,
454 architectures,
455 architecture_properties,
456 arch_property,
457 sha224_checksums,
458 )
459 }
460 parser::SourceProperty::Sha256Checksum(arch_property) => {
461 package_base_arch_prop!(
462 line,
463 errors,
464 architectures,
465 architecture_properties,
466 arch_property,
467 sha256_checksums,
468 )
469 }
470 parser::SourceProperty::Sha384Checksum(arch_property) => {
471 package_base_arch_prop!(
472 line,
473 errors,
474 architectures,
475 architecture_properties,
476 arch_property,
477 sha384_checksums,
478 )
479 }
480 parser::SourceProperty::Sha512Checksum(arch_property) => {
481 package_base_arch_prop!(
482 line,
483 errors,
484 architectures,
485 architecture_properties,
486 arch_property,
487 sha512_checksums,
488 )
489 }
490 },
491 }
492 }
493
494 if package_version.is_none() {
496 errors.push(unrecoverable(
497 None,
498 "pkgbase section doesn't contain a 'pkgver' keyword assignment",
499 ));
500 package_version = Some(PackageVersion::new("0".to_string()).unwrap());
502 }
503
504 PackageBase {
505 name: parsed.name,
506 description,
507 url,
508 licenses,
509 changelog,
510 architectures,
511 architecture_properties,
512 install,
513 groups,
514 options,
515 backups,
516 version: Version::new(package_version.unwrap(), package_epoch, package_release),
517 pgp_fingerprints,
518 dependencies,
519 optional_dependencies,
520 provides,
521 conflicts,
522 replaces,
523 check_dependencies,
524 make_dependencies,
525 sources,
526 no_extracts,
527 b2_checksums,
528 md5_checksums,
529 sha1_checksums,
530 sha224_checksums,
531 sha256_checksums,
532 sha384_checksums,
533 sha512_checksums,
534 }
535 }
536}