alpm_lint/lint_rules/source_info/
duplicate_architecture.rs

1//! Checks that architectures are not specified twice.
2
3use std::collections::{BTreeMap, HashSet};
4
5use documented::Documented;
6
7use crate::{
8    internal_prelude::*,
9    issue::SourceInfoIssue,
10    lint_rules::source_info::source_info_from_resource,
11};
12
13/// # What it does
14///
15/// Checks that architectures (see [alpm-architecture]) are not specified twice.
16///
17/// # Why is this bad?
18///
19/// Duplicate [alpm-architecture] definitions are confusing and do not serve a purpose.
20/// Generally, duplicate definitions are ignored and therefore it is best practice to only specify
21/// an architecture once.
22///
23/// # Example
24///
25/// ```ini,ignore
26/// pkgbase = test
27///     pkgver = 1.0.0
28///     pkgrel = 1
29///     arch = x86_64
30///     arch = x86_64
31/// ```
32///
33/// Use instead:
34///
35/// ```ini,ignore
36/// pkgbase = test
37///     pkgver = 1.0.0
38///     pkgrel = 1
39///     arch = x86_64
40/// ```
41///
42/// [alpm-architecture]: https://alpm.archlinux.page/specifications/alpm-architecture.7.html
43#[derive(Clone, Debug, Documented)]
44pub struct DuplicateArchitecture {}
45
46impl DuplicateArchitecture {
47    /// Create a new, boxed instance of [`DuplicateArchitecture`].
48    ///
49    /// This is used to register the lint on the [`LintStore`](crate::LintStore).
50    pub fn new_boxed(_: &LintRuleConfiguration) -> Box<dyn LintRule> {
51        Box::new(DuplicateArchitecture {})
52    }
53}
54
55impl LintRule for DuplicateArchitecture {
56    fn name(&self) -> &'static str {
57        "duplicate_architecture"
58    }
59
60    fn scope(&self) -> LintScope {
61        LintScope::SourceInfo
62    }
63
64    fn documentation(&self) -> String {
65        DuplicateArchitecture::DOCS.into()
66    }
67
68    fn help_text(&self) -> String {
69        r#"Architecture lists for packages should always be unique.
70
71Duplicate architecture declarations such as `arch=(x86_64 x86_64)` are ignored.
72"#
73        .into()
74    }
75
76    fn run(&self, resources: &Resources, issues: &mut Vec<LintIssue>) -> Result<(), Error> {
77        // Extract the SourceInfo from the given resources.
78        let source_info = source_info_from_resource(resources, self.scoped_name())?;
79
80        let mut known = HashSet::new();
81        for architecture in &source_info.base.architectures {
82            if known.contains(&architecture) {
83                issues.push(LintIssue::from_rule(
84                    self,
85                    SourceInfoIssue::BaseField {
86                        field_name: "arch".to_string(),
87                        value: architecture.to_string(),
88                        context: "Found duplicate architecture".to_string(),
89                        architecture: None,
90                    }
91                    .into(),
92                ));
93            }
94            known.insert(architecture);
95        }
96
97        Ok(())
98    }
99
100    fn extra_links(&self) -> Option<BTreeMap<String, String>> {
101        let mut links = BTreeMap::new();
102        links.insert(
103            "alpm-architecture specification".to_string(),
104            "https://alpm.archlinux.page/specifications/alpm-architecture.7.html".to_string(),
105        );
106        Some(links)
107    }
108}