syncable_cli/analyzer/helmlint/rules/
mod.rs1pub mod hl1xxx;
14pub mod hl2xxx;
15pub mod hl3xxx;
16pub mod hl4xxx;
17pub mod hl5xxx;
18
19use std::collections::HashSet;
20use std::path::Path;
21
22use crate::analyzer::helmlint::parser::chart::ChartMetadata;
23use crate::analyzer::helmlint::parser::helpers::ParsedHelpers;
24use crate::analyzer::helmlint::parser::template::ParsedTemplate;
25use crate::analyzer::helmlint::parser::values::ValuesFile;
26use crate::analyzer::helmlint::types::{CheckFailure, Severity};
27
28#[derive(Debug)]
30pub struct LintContext<'a> {
31 pub chart_path: &'a Path,
33 pub chart_metadata: Option<&'a ChartMetadata>,
35 pub values: Option<&'a ValuesFile>,
37 pub helpers: Option<&'a ParsedHelpers>,
39 pub templates: &'a [ParsedTemplate],
41 pub files: &'a HashSet<String>,
43 pub template_value_refs: HashSet<String>,
45}
46
47impl<'a> LintContext<'a> {
48 pub fn new(
50 chart_path: &'a Path,
51 chart_metadata: Option<&'a ChartMetadata>,
52 values: Option<&'a ValuesFile>,
53 helpers: Option<&'a ParsedHelpers>,
54 templates: &'a [ParsedTemplate],
55 files: &'a HashSet<String>,
56 ) -> Self {
57 let mut template_value_refs = HashSet::new();
59 for template in templates {
60 for var in &template.variables_used {
61 if let Some(path) = var.strip_prefix(".Values.") {
62 template_value_refs.insert(path.to_string());
63 }
64 }
65 }
66
67 Self {
68 chart_path,
69 chart_metadata,
70 values,
71 helpers,
72 templates,
73 files,
74 template_value_refs,
75 }
76 }
77
78 pub fn has_file(&self, name: &str) -> bool {
80 self.files.contains(name) || self.files.iter().any(|f| f.ends_with(name))
81 }
82
83 pub fn has_helper(&self, name: &str) -> bool {
85 self.helpers.map(|h| h.has_helper(name)).unwrap_or(false)
86 }
87
88 pub fn helper_names(&self) -> Vec<&str> {
90 self.helpers
91 .map(|h| h.names().collect())
92 .unwrap_or_default()
93 }
94
95 pub fn template_references(&self) -> HashSet<&str> {
97 let mut refs = HashSet::new();
98 for template in self.templates {
99 for name in &template.referenced_templates {
100 refs.insert(name.as_str());
101 }
102 }
103 refs
104 }
105}
106
107pub trait Rule: Send + Sync {
109 fn code(&self) -> &'static str;
111
112 fn severity(&self) -> Severity;
114
115 fn name(&self) -> &'static str;
117
118 fn description(&self) -> &'static str;
120
121 fn is_fixable(&self) -> bool {
123 false
124 }
125
126 fn check(&self, ctx: &LintContext) -> Vec<CheckFailure>;
128}
129
130pub fn all_rules() -> Vec<Box<dyn Rule>> {
132 let mut rules: Vec<Box<dyn Rule>> = Vec::new();
133
134 rules.extend(hl1xxx::rules());
136
137 rules.extend(hl2xxx::rules());
139
140 rules.extend(hl3xxx::rules());
142
143 rules.extend(hl4xxx::rules());
145
146 rules.extend(hl5xxx::rules());
148
149 rules
150}
151
152pub fn get_rule(code: &str) -> Option<Box<dyn Rule>> {
154 all_rules().into_iter().find(|r| r.code() == code)
155}
156
157pub fn list_rule_codes() -> Vec<&'static str> {
159 vec![
160 "HL1001", "HL1002", "HL1003", "HL1004", "HL1005", "HL1006", "HL1007", "HL1008", "HL1009",
162 "HL1010", "HL1011", "HL1012", "HL1013", "HL1014", "HL1015", "HL1016", "HL1017",
163 "HL2001", "HL2002", "HL2003", "HL2004", "HL2005", "HL2006", "HL2007", "HL2008", "HL2009",
165 "HL3001", "HL3002", "HL3003", "HL3004", "HL3005", "HL3006", "HL3007", "HL3008", "HL3009",
167 "HL3010", "HL3011", "HL4001", "HL4002", "HL4003", "HL4004", "HL4005", "HL4006", "HL4007", "HL4008", "HL4009",
169 "HL4010", "HL4011", "HL4012", "HL5001", "HL5002", "HL5003", "HL5004", "HL5005", "HL5006",
171 ]
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_all_rules_returns_rules() {
180 let rules = all_rules();
181 assert!(!rules.is_empty());
182 }
183
184 #[test]
185 fn test_rule_codes_unique() {
186 let rules = all_rules();
187 let mut codes = HashSet::new();
188 for rule in rules {
189 let code = rule.code();
190 assert!(codes.insert(code), "Duplicate rule code: {}", code);
191 }
192 }
193}