alpm_lint/lint_rules/source_info/
undefined_architecture.rs1use std::collections::BTreeMap;
7
8use alpm_types::{Architectures, SystemArchitecture};
9use colored::Colorize;
10use documented::Documented;
11
12use crate::{
13 internal_prelude::*,
14 issue::SourceInfoIssue,
15 lint_rules::source_info::source_info_from_resource,
16};
17
18#[derive(Clone, Debug, Documented)]
57pub struct UndefinedArchitecture {}
58
59impl UndefinedArchitecture {
60 pub fn new_boxed(_: &LintRuleConfiguration) -> Box<dyn LintRule> {
62 Box::new(Self {})
63 }
64}
65
66fn contains_architecture(left: &Architectures, right: &SystemArchitecture) -> bool {
68 let Architectures::Some(left) = left else {
70 return true;
71 };
72
73 left.contains(right)
74}
75
76impl LintRule for UndefinedArchitecture {
77 fn name(&self) -> &'static str {
78 "undefined_architecture"
79 }
80
81 fn scope(&self) -> LintScope {
82 LintScope::SourceInfo
83 }
84
85 fn documentation(&self) -> String {
86 UndefinedArchitecture::DOCS.into()
87 }
88
89 fn help_text(&self) -> String {
90 r#"Architecture-specific fields should only be used for declared architectures.
91
92Make sure all architecture-specific fields correspond to architectures declared in the 'arch' field.
93"#
94 .into()
95 }
96
97 fn run(&self, resources: &Resources, issues: &mut Vec<LintIssue>) -> Result<(), Error> {
98 let source_info = source_info_from_resource(resources, self.scoped_name())?;
100
101 let base_archs = &source_info.base.architectures;
102
103 for arch in source_info.base.architecture_properties.keys() {
105 if !contains_architecture(base_archs, arch) {
106 issues.push(LintIssue {
107 lint_rule: self.scoped_name(),
108 level: self.level(),
109 help_text: self.help_text(),
110 scope: self.scope(),
111 issue_type: SourceInfoIssue::Generic {
112 summary: "found field for an undefined architecture".to_string(),
113 arrow_line: None,
114 message: format!(
115 "An architecture-specific field is used for the undeclared architecture '{}'",
116 arch.to_string().bold()
117 )
118 }
119 .into(),
120 links: std::collections::BTreeMap::new(),
121 });
122 }
123 }
124
125 for package in &source_info.packages {
127 let package_archs = if let Some(archs) = &package.architectures {
128 archs
129 } else {
130 base_archs
131 };
132
133 for arch in package.architecture_properties.keys() {
134 if !contains_architecture(package_archs, arch) {
135 issues.push(LintIssue::from_rule(self,
136 SourceInfoIssue::Generic {
137 summary: "found variable for an undefined architecture".to_string(),
138 arrow_line: Some(format!("for package '{}'", package.name)),
139 message: format!(
140 "An architecture-specific variable has been for the architecture '{}'",
141 arch.to_string().bold()
142 )
143 }.into(),
144 ));
145 }
146 }
147 }
148
149 Ok(())
150 }
151
152 fn extra_links(&self) -> Option<BTreeMap<String, String>> {
153 let mut links = BTreeMap::new();
154 links.insert(
155 "PKGBUILD man page".to_string(),
156 "https://man.archlinux.org/man/PKGBUILD.5".to_string(),
157 );
158 links.insert(
159 "SRCINFO specification".to_string(),
160 "https://alpm.archlinux.page/specifications/SRCINFO.5.html".to_string(),
161 );
162 links.insert(
163 "alpm-architecture specification".to_string(),
164 "https://alpm.archlinux.page/specifications/alpm-architecture.7.html".to_string(),
165 );
166
167 Some(links)
168 }
169}