alpm_lint/lint_rules/source_info/
unknown_architecture.rs1use std::collections::BTreeMap;
7
8use alpm_types::{Architecture, SystemArchitecture};
9use documented::Documented;
10use strum::VariantNames;
11
12use crate::{
13 internal_prelude::*,
14 issue::SourceInfoIssue,
15 lint_rules::source_info::source_info_from_resource,
16 utils::EditDistance,
17};
18
19#[derive(Clone, Debug, Documented)]
57pub struct UnknownArchitecture {}
58
59impl UnknownArchitecture {
60 const EDIT_DISTANCE_THRESHOLD: usize = 3;
62
63 pub fn new_boxed(_: &LintRuleConfiguration) -> Box<dyn LintRule> {
65 Box::new(Self {})
66 }
67
68 fn known_variants() -> Vec<&'static str> {
70 let mut known = vec!["any"];
71 known.append(
72 &mut SystemArchitecture::VARIANTS
73 .iter()
74 .filter_map(|v| if *v != "unknown" { Some(*v) } else { None })
75 .collect::<Vec<_>>(),
76 );
77 known
78 }
79}
80
81impl LintRule for UnknownArchitecture {
82 fn name(&self) -> &'static str {
83 "unknown_architecture"
84 }
85
86 fn scope(&self) -> LintScope {
87 LintScope::SourceInfo
88 }
89
90 fn documentation(&self) -> String {
91 UnknownArchitecture::DOCS.into()
92 }
93
94 fn help_text(&self) -> String {
95 let known = Self::known_variants()
96 .iter()
97 .map(|arch| format!("- {}", arch))
98 .collect::<Vec<_>>()
99 .join("\n");
100
101 format!(
102 "If you are certain that the architecture name is correct, \
103 you can ignore this warning. \n\
104 Known values include: \n{known}"
105 )
106 }
107
108 fn run(&self, resources: &Resources, issues: &mut Vec<LintIssue>) -> Result<(), Error> {
109 let source_info = source_info_from_resource(resources, self.scoped_name())?;
110
111 for arch in &source_info.base.architectures {
112 if let Architecture::Some(SystemArchitecture::Unknown(arch)) = arch {
113 let closest_match = Self::known_variants()
114 .iter()
115 .map(|known| (*known, arch.to_string().edit_distance(&known.to_string())))
116 .min_by_key(|(_, dist)| *dist);
117
118 let suggestion = if let Some((closest, distance)) = closest_match
119 && distance <= Self::EDIT_DISTANCE_THRESHOLD
120 {
121 Some(format!("\nDid you mean '{closest}'?"))
122 } else {
123 None
124 };
125
126 issues.push(LintIssue::from_rule(
127 self,
128 SourceInfoIssue::Generic {
129 summary: "Uncommon architecture specified - possible typo.".to_string(),
130 arrow_line: None,
131 message: format!(
132 "The architecture '{}' is not common. \
133 This is allowed, but may indicate a typo. \
134 {}",
135 arch,
136 suggestion.unwrap_or_default()
137 ),
138 }
139 .into(),
140 ));
141 }
142 }
143
144 Ok(())
145 }
146
147 fn extra_links(&self) -> Option<BTreeMap<String, String>> {
148 let mut links = BTreeMap::new();
149 links.insert(
150 "SRCINFO specification".to_string(),
151 "https://alpm.archlinux.page/specifications/SRCINFO.5.html".to_string(),
152 );
153 links.insert(
154 "alpm-architecture specification".to_string(),
155 "https://alpm.archlinux.page/specifications/alpm-architecture.7.html".to_string(),
156 );
157
158 Some(links)
159 }
160}