1use crate::analyzer::hadolint::parser::instruction::Instruction;
12use crate::analyzer::hadolint::shell::ParsedShell;
13use crate::analyzer::hadolint::types::{CheckFailure, RuleCode, Severity};
14
15pub mod dl1001;
16pub mod dl3000;
17pub mod dl3001;
18pub mod dl3002;
19pub mod dl3003;
20pub mod dl3004;
21pub mod dl3005;
22pub mod dl3006;
23pub mod dl3007;
24pub mod dl3008;
25pub mod dl3009;
26pub mod dl3010;
27pub mod dl3011;
28pub mod dl3012;
29pub mod dl3013;
30pub mod dl3014;
31pub mod dl3015;
32pub mod dl3016;
33pub mod dl3017;
34pub mod dl3018;
35pub mod dl3019;
36pub mod dl3020;
37pub mod dl3021;
38pub mod dl3022;
39pub mod dl3023;
40pub mod dl3024;
41pub mod dl3025;
42pub mod dl3026;
43pub mod dl3027;
44pub mod dl3028;
45pub mod dl3029;
46pub mod dl3030;
47pub mod dl3031;
48pub mod dl3032;
49pub mod dl3033;
50pub mod dl3034;
51pub mod dl3035;
52pub mod dl3036;
53pub mod dl3037;
54pub mod dl3038;
55pub mod dl3039;
56pub mod dl3040;
57pub mod dl3041;
58pub mod dl3042;
59pub mod dl3043;
60pub mod dl3044;
61pub mod dl3045;
62pub mod dl3046;
63pub mod dl3047;
64pub mod dl3048;
65pub mod dl3049;
66pub mod dl3050;
67pub mod dl3051;
68pub mod dl3052;
69pub mod dl3053;
70pub mod dl3054;
71pub mod dl3055;
72pub mod dl3056;
73pub mod dl3057;
74pub mod dl3058;
75pub mod dl3059;
76pub mod dl3060;
77pub mod dl3061;
78pub mod dl3062;
79pub mod dl4000;
80pub mod dl4001;
81pub mod dl4003;
82pub mod dl4004;
83pub mod dl4005;
84pub mod dl4006;
85
86pub trait Rule: Send + Sync {
88 fn check(&self, state: &mut RuleState, line: u32, instruction: &Instruction, shell: Option<&ParsedShell>);
90
91 fn finalize(&self, state: RuleState) -> Vec<CheckFailure> {
94 state.failures
95 }
96
97 fn code(&self) -> &RuleCode;
99
100 fn severity(&self) -> Severity;
102
103 fn message(&self) -> &str;
105}
106
107#[derive(Debug, Clone, Default)]
109pub struct RuleState {
110 pub failures: Vec<CheckFailure>,
112 pub data: RuleData,
114}
115
116impl RuleState {
117 pub fn new() -> Self {
119 Self::default()
120 }
121
122 pub fn add_failure(&mut self, code: impl Into<RuleCode>, severity: Severity, message: impl Into<String>, line: u32) {
124 self.failures.push(CheckFailure::new(code, severity, message, line));
125 }
126}
127
128#[derive(Debug, Clone, Default)]
130pub struct RuleData {
131 pub ints: std::collections::HashMap<&'static str, i64>,
133 pub bools: std::collections::HashMap<&'static str, bool>,
135 pub strings: std::collections::HashMap<&'static str, String>,
137 pub string_sets: std::collections::HashMap<&'static str, std::collections::HashSet<String>>,
139}
140
141impl RuleData {
142 pub fn get_int(&self, key: &'static str) -> i64 {
143 self.ints.get(key).copied().unwrap_or(0)
144 }
145
146 pub fn set_int(&mut self, key: &'static str, value: i64) {
147 self.ints.insert(key, value);
148 }
149
150 pub fn get_bool(&self, key: &'static str) -> bool {
151 self.bools.get(key).copied().unwrap_or(false)
152 }
153
154 pub fn set_bool(&mut self, key: &'static str, value: bool) {
155 self.bools.insert(key, value);
156 }
157
158 pub fn get_string(&self, key: &'static str) -> Option<&str> {
159 self.strings.get(key).map(|s| s.as_str())
160 }
161
162 pub fn set_string(&mut self, key: &'static str, value: impl Into<String>) {
163 self.strings.insert(key, value.into());
164 }
165
166 pub fn get_string_set(&self, key: &'static str) -> Option<&std::collections::HashSet<String>> {
167 self.string_sets.get(key)
168 }
169
170 pub fn insert_to_set(&mut self, key: &'static str, value: impl Into<String>) {
171 self.string_sets.entry(key).or_default().insert(value.into());
172 }
173
174 pub fn set_contains(&self, key: &'static str, value: &str) -> bool {
175 self.string_sets.get(key).map(|s| s.contains(value)).unwrap_or(false)
176 }
177}
178
179pub struct SimpleRule<F>
181where
182 F: Fn(&Instruction, Option<&ParsedShell>) -> bool + Send + Sync,
183{
184 code: RuleCode,
185 severity: Severity,
186 message: String,
187 check_fn: F,
188}
189
190impl<F> SimpleRule<F>
191where
192 F: Fn(&Instruction, Option<&ParsedShell>) -> bool + Send + Sync,
193{
194 pub fn new(code: impl Into<RuleCode>, severity: Severity, message: impl Into<String>, check_fn: F) -> Self {
196 Self {
197 code: code.into(),
198 severity,
199 message: message.into(),
200 check_fn,
201 }
202 }
203}
204
205impl<F> Rule for SimpleRule<F>
206where
207 F: Fn(&Instruction, Option<&ParsedShell>) -> bool + Send + Sync,
208{
209 fn check(&self, state: &mut RuleState, line: u32, instruction: &Instruction, shell: Option<&ParsedShell>) {
210 if !(self.check_fn)(instruction, shell) {
211 state.add_failure(self.code.clone(), self.severity, self.message.clone(), line);
212 }
213 }
214
215 fn code(&self) -> &RuleCode {
216 &self.code
217 }
218
219 fn severity(&self) -> Severity {
220 self.severity
221 }
222
223 fn message(&self) -> &str {
224 &self.message
225 }
226}
227
228pub fn simple_rule<F>(
230 code: impl Into<RuleCode>,
231 severity: Severity,
232 message: impl Into<String>,
233 check_fn: F,
234) -> SimpleRule<F>
235where
236 F: Fn(&Instruction, Option<&ParsedShell>) -> bool + Send + Sync,
237{
238 SimpleRule::new(code, severity, message, check_fn)
239}
240
241pub struct CustomRule<F>
243where
244 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
245{
246 code: RuleCode,
247 severity: Severity,
248 message: String,
249 step_fn: F,
250}
251
252impl<F> CustomRule<F>
253where
254 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
255{
256 pub fn new(code: impl Into<RuleCode>, severity: Severity, message: impl Into<String>, step_fn: F) -> Self {
258 Self {
259 code: code.into(),
260 severity,
261 message: message.into(),
262 step_fn,
263 }
264 }
265}
266
267impl<F> Rule for CustomRule<F>
268where
269 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
270{
271 fn check(&self, state: &mut RuleState, line: u32, instruction: &Instruction, shell: Option<&ParsedShell>) {
272 (self.step_fn)(state, line, instruction, shell);
273 }
274
275 fn code(&self) -> &RuleCode {
276 &self.code
277 }
278
279 fn severity(&self) -> Severity {
280 self.severity
281 }
282
283 fn message(&self) -> &str {
284 &self.message
285 }
286}
287
288pub fn custom_rule<F>(
290 code: impl Into<RuleCode>,
291 severity: Severity,
292 message: impl Into<String>,
293 step_fn: F,
294) -> CustomRule<F>
295where
296 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
297{
298 CustomRule::new(code, severity, message, step_fn)
299}
300
301pub struct VeryCustomRule<F, D>
303where
304 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
305 D: Fn(RuleState) -> Vec<CheckFailure> + Send + Sync,
306{
307 code: RuleCode,
308 severity: Severity,
309 message: String,
310 step_fn: F,
311 done_fn: D,
312}
313
314impl<F, D> VeryCustomRule<F, D>
315where
316 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
317 D: Fn(RuleState) -> Vec<CheckFailure> + Send + Sync,
318{
319 pub fn new(
321 code: impl Into<RuleCode>,
322 severity: Severity,
323 message: impl Into<String>,
324 step_fn: F,
325 done_fn: D,
326 ) -> Self {
327 Self {
328 code: code.into(),
329 severity,
330 message: message.into(),
331 step_fn,
332 done_fn,
333 }
334 }
335}
336
337impl<F, D> Rule for VeryCustomRule<F, D>
338where
339 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
340 D: Fn(RuleState) -> Vec<CheckFailure> + Send + Sync,
341{
342 fn check(&self, state: &mut RuleState, line: u32, instruction: &Instruction, shell: Option<&ParsedShell>) {
343 (self.step_fn)(state, line, instruction, shell);
344 }
345
346 fn finalize(&self, state: RuleState) -> Vec<CheckFailure> {
347 (self.done_fn)(state)
348 }
349
350 fn code(&self) -> &RuleCode {
351 &self.code
352 }
353
354 fn severity(&self) -> Severity {
355 self.severity
356 }
357
358 fn message(&self) -> &str {
359 &self.message
360 }
361}
362
363pub fn very_custom_rule<F, D>(
365 code: impl Into<RuleCode>,
366 severity: Severity,
367 message: impl Into<String>,
368 step_fn: F,
369 done_fn: D,
370) -> VeryCustomRule<F, D>
371where
372 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
373 D: Fn(RuleState) -> Vec<CheckFailure> + Send + Sync,
374{
375 VeryCustomRule::new(code, severity, message, step_fn, done_fn)
376}
377
378pub fn all_rules() -> Vec<Box<dyn Rule>> {
380 vec![
381 Box::new(dl1001::rule()),
383 Box::new(dl3000::rule()),
385 Box::new(dl3001::rule()),
386 Box::new(dl3003::rule()),
387 Box::new(dl3004::rule()),
388 Box::new(dl3005::rule()),
389 Box::new(dl3007::rule()),
390 Box::new(dl3010::rule()),
391 Box::new(dl3011::rule()),
392 Box::new(dl3017::rule()),
393 Box::new(dl3020::rule()),
394 Box::new(dl3021::rule()),
395 Box::new(dl3025::rule()),
396 Box::new(dl3026::rule()),
397 Box::new(dl3027::rule()),
398 Box::new(dl3029::rule()),
399 Box::new(dl3031::rule()),
400 Box::new(dl3035::rule()),
401 Box::new(dl3039::rule()),
402 Box::new(dl3043::rule()),
403 Box::new(dl3044::rule()),
404 Box::new(dl3046::rule()),
405 Box::new(dl3048::rule()),
406 Box::new(dl3049::rule()),
407 Box::new(dl3050::rule()),
408 Box::new(dl3051::rule()),
409 Box::new(dl3052::rule()),
410 Box::new(dl3053::rule()),
411 Box::new(dl3054::rule()),
412 Box::new(dl3055::rule()),
413 Box::new(dl3056::rule()),
414 Box::new(dl3058::rule()),
415 Box::new(dl3061::rule()),
416 Box::new(dl4000::rule()),
418 Box::new(dl4005::rule()),
419 Box::new(dl4006::rule()),
420 Box::new(dl3002::rule()),
422 Box::new(dl3006::rule()),
423 Box::new(dl3012::rule()),
424 Box::new(dl3022::rule()),
425 Box::new(dl3023::rule()),
426 Box::new(dl3024::rule()),
427 Box::new(dl3045::rule()),
428 Box::new(dl3047::rule()),
429 Box::new(dl3057::rule()),
430 Box::new(dl3059::rule()),
431 Box::new(dl3062::rule()),
432 Box::new(dl4001::rule()),
433 Box::new(dl4003::rule()),
434 Box::new(dl4004::rule()),
435 Box::new(dl3008::rule()),
437 Box::new(dl3009::rule()),
438 Box::new(dl3013::rule()),
439 Box::new(dl3014::rule()),
440 Box::new(dl3015::rule()),
441 Box::new(dl3016::rule()),
442 Box::new(dl3018::rule()),
443 Box::new(dl3019::rule()),
444 Box::new(dl3028::rule()),
445 Box::new(dl3030::rule()),
446 Box::new(dl3032::rule()),
447 Box::new(dl3033::rule()),
448 Box::new(dl3034::rule()),
449 Box::new(dl3036::rule()),
450 Box::new(dl3037::rule()),
451 Box::new(dl3038::rule()),
452 Box::new(dl3040::rule()),
453 Box::new(dl3041::rule()),
454 Box::new(dl3042::rule()),
455 Box::new(dl3060::rule()),
456 ]
457}
458
459#[cfg(test)]
460mod tests {
461 use super::*;
462
463 #[test]
464 fn test_simple_rule() {
465 let rule = simple_rule(
466 "TEST001",
467 Severity::Warning,
468 "Test message",
469 |instr, _| !matches!(instr, Instruction::Maintainer(_)),
470 );
471
472 let mut state = RuleState::new();
473 let instr = Instruction::Maintainer("test".to_string());
474 rule.check(&mut state, 1, &instr, None);
475
476 assert_eq!(state.failures.len(), 1);
477 assert_eq!(state.failures[0].code.as_str(), "TEST001");
478 }
479
480 #[test]
481 fn test_rule_data() {
482 let mut data = RuleData::default();
483
484 data.set_int("count", 5);
485 assert_eq!(data.get_int("count"), 5);
486
487 data.set_bool("seen", true);
488 assert!(data.get_bool("seen"));
489
490 data.set_string("name", "test");
491 assert_eq!(data.get_string("name"), Some("test"));
492
493 data.insert_to_set("aliases", "builder");
494 assert!(data.set_contains("aliases", "builder"));
495 assert!(!data.set_contains("aliases", "runner"));
496 }
497}