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(
90 &self,
91 state: &mut RuleState,
92 line: u32,
93 instruction: &Instruction,
94 shell: Option<&ParsedShell>,
95 );
96
97 fn finalize(&self, state: RuleState) -> Vec<CheckFailure> {
100 state.failures
101 }
102
103 fn code(&self) -> &RuleCode;
105
106 fn severity(&self) -> Severity;
108
109 fn message(&self) -> &str;
111}
112
113#[derive(Debug, Clone, Default)]
115pub struct RuleState {
116 pub failures: Vec<CheckFailure>,
118 pub data: RuleData,
120}
121
122impl RuleState {
123 pub fn new() -> Self {
125 Self::default()
126 }
127
128 pub fn add_failure(
130 &mut self,
131 code: impl Into<RuleCode>,
132 severity: Severity,
133 message: impl Into<String>,
134 line: u32,
135 ) {
136 self.failures
137 .push(CheckFailure::new(code, severity, message, line));
138 }
139}
140
141#[derive(Debug, Clone, Default)]
143pub struct RuleData {
144 pub ints: std::collections::HashMap<&'static str, i64>,
146 pub bools: std::collections::HashMap<&'static str, bool>,
148 pub strings: std::collections::HashMap<&'static str, String>,
150 pub string_sets: std::collections::HashMap<&'static str, std::collections::HashSet<String>>,
152}
153
154impl RuleData {
155 pub fn get_int(&self, key: &'static str) -> i64 {
156 self.ints.get(key).copied().unwrap_or(0)
157 }
158
159 pub fn set_int(&mut self, key: &'static str, value: i64) {
160 self.ints.insert(key, value);
161 }
162
163 pub fn get_bool(&self, key: &'static str) -> bool {
164 self.bools.get(key).copied().unwrap_or(false)
165 }
166
167 pub fn set_bool(&mut self, key: &'static str, value: bool) {
168 self.bools.insert(key, value);
169 }
170
171 pub fn get_string(&self, key: &'static str) -> Option<&str> {
172 self.strings.get(key).map(|s| s.as_str())
173 }
174
175 pub fn set_string(&mut self, key: &'static str, value: impl Into<String>) {
176 self.strings.insert(key, value.into());
177 }
178
179 pub fn get_string_set(&self, key: &'static str) -> Option<&std::collections::HashSet<String>> {
180 self.string_sets.get(key)
181 }
182
183 pub fn insert_to_set(&mut self, key: &'static str, value: impl Into<String>) {
184 self.string_sets
185 .entry(key)
186 .or_default()
187 .insert(value.into());
188 }
189
190 pub fn set_contains(&self, key: &'static str, value: &str) -> bool {
191 self.string_sets
192 .get(key)
193 .map(|s| s.contains(value))
194 .unwrap_or(false)
195 }
196}
197
198pub struct SimpleRule<F>
200where
201 F: Fn(&Instruction, Option<&ParsedShell>) -> bool + Send + Sync,
202{
203 code: RuleCode,
204 severity: Severity,
205 message: String,
206 check_fn: F,
207}
208
209impl<F> SimpleRule<F>
210where
211 F: Fn(&Instruction, Option<&ParsedShell>) -> bool + Send + Sync,
212{
213 pub fn new(
215 code: impl Into<RuleCode>,
216 severity: Severity,
217 message: impl Into<String>,
218 check_fn: F,
219 ) -> Self {
220 Self {
221 code: code.into(),
222 severity,
223 message: message.into(),
224 check_fn,
225 }
226 }
227}
228
229impl<F> Rule for SimpleRule<F>
230where
231 F: Fn(&Instruction, Option<&ParsedShell>) -> bool + Send + Sync,
232{
233 fn check(
234 &self,
235 state: &mut RuleState,
236 line: u32,
237 instruction: &Instruction,
238 shell: Option<&ParsedShell>,
239 ) {
240 if !(self.check_fn)(instruction, shell) {
241 state.add_failure(self.code.clone(), self.severity, self.message.clone(), line);
242 }
243 }
244
245 fn code(&self) -> &RuleCode {
246 &self.code
247 }
248
249 fn severity(&self) -> Severity {
250 self.severity
251 }
252
253 fn message(&self) -> &str {
254 &self.message
255 }
256}
257
258pub fn simple_rule<F>(
260 code: impl Into<RuleCode>,
261 severity: Severity,
262 message: impl Into<String>,
263 check_fn: F,
264) -> SimpleRule<F>
265where
266 F: Fn(&Instruction, Option<&ParsedShell>) -> bool + Send + Sync,
267{
268 SimpleRule::new(code, severity, message, check_fn)
269}
270
271pub struct CustomRule<F>
273where
274 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
275{
276 code: RuleCode,
277 severity: Severity,
278 message: String,
279 step_fn: F,
280}
281
282impl<F> CustomRule<F>
283where
284 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
285{
286 pub fn new(
288 code: impl Into<RuleCode>,
289 severity: Severity,
290 message: impl Into<String>,
291 step_fn: F,
292 ) -> Self {
293 Self {
294 code: code.into(),
295 severity,
296 message: message.into(),
297 step_fn,
298 }
299 }
300}
301
302impl<F> Rule for CustomRule<F>
303where
304 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
305{
306 fn check(
307 &self,
308 state: &mut RuleState,
309 line: u32,
310 instruction: &Instruction,
311 shell: Option<&ParsedShell>,
312 ) {
313 (self.step_fn)(state, line, instruction, shell);
314 }
315
316 fn code(&self) -> &RuleCode {
317 &self.code
318 }
319
320 fn severity(&self) -> Severity {
321 self.severity
322 }
323
324 fn message(&self) -> &str {
325 &self.message
326 }
327}
328
329pub fn custom_rule<F>(
331 code: impl Into<RuleCode>,
332 severity: Severity,
333 message: impl Into<String>,
334 step_fn: F,
335) -> CustomRule<F>
336where
337 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
338{
339 CustomRule::new(code, severity, message, step_fn)
340}
341
342pub struct VeryCustomRule<F, D>
344where
345 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
346 D: Fn(RuleState) -> Vec<CheckFailure> + Send + Sync,
347{
348 code: RuleCode,
349 severity: Severity,
350 message: String,
351 step_fn: F,
352 done_fn: D,
353}
354
355impl<F, D> VeryCustomRule<F, D>
356where
357 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
358 D: Fn(RuleState) -> Vec<CheckFailure> + Send + Sync,
359{
360 pub fn new(
362 code: impl Into<RuleCode>,
363 severity: Severity,
364 message: impl Into<String>,
365 step_fn: F,
366 done_fn: D,
367 ) -> Self {
368 Self {
369 code: code.into(),
370 severity,
371 message: message.into(),
372 step_fn,
373 done_fn,
374 }
375 }
376}
377
378impl<F, D> Rule for VeryCustomRule<F, D>
379where
380 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
381 D: Fn(RuleState) -> Vec<CheckFailure> + Send + Sync,
382{
383 fn check(
384 &self,
385 state: &mut RuleState,
386 line: u32,
387 instruction: &Instruction,
388 shell: Option<&ParsedShell>,
389 ) {
390 (self.step_fn)(state, line, instruction, shell);
391 }
392
393 fn finalize(&self, state: RuleState) -> Vec<CheckFailure> {
394 (self.done_fn)(state)
395 }
396
397 fn code(&self) -> &RuleCode {
398 &self.code
399 }
400
401 fn severity(&self) -> Severity {
402 self.severity
403 }
404
405 fn message(&self) -> &str {
406 &self.message
407 }
408}
409
410pub fn very_custom_rule<F, D>(
412 code: impl Into<RuleCode>,
413 severity: Severity,
414 message: impl Into<String>,
415 step_fn: F,
416 done_fn: D,
417) -> VeryCustomRule<F, D>
418where
419 F: Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
420 D: Fn(RuleState) -> Vec<CheckFailure> + Send + Sync,
421{
422 VeryCustomRule::new(code, severity, message, step_fn, done_fn)
423}
424
425pub fn all_rules() -> Vec<Box<dyn Rule>> {
427 vec![
428 Box::new(dl1001::rule()),
430 Box::new(dl3000::rule()),
432 Box::new(dl3001::rule()),
433 Box::new(dl3003::rule()),
434 Box::new(dl3004::rule()),
435 Box::new(dl3005::rule()),
436 Box::new(dl3007::rule()),
437 Box::new(dl3010::rule()),
438 Box::new(dl3011::rule()),
439 Box::new(dl3017::rule()),
440 Box::new(dl3020::rule()),
441 Box::new(dl3021::rule()),
442 Box::new(dl3025::rule()),
443 Box::new(dl3026::rule()),
444 Box::new(dl3027::rule()),
445 Box::new(dl3029::rule()),
446 Box::new(dl3031::rule()),
447 Box::new(dl3035::rule()),
448 Box::new(dl3039::rule()),
449 Box::new(dl3043::rule()),
450 Box::new(dl3044::rule()),
451 Box::new(dl3046::rule()),
452 Box::new(dl3048::rule()),
453 Box::new(dl3049::rule()),
454 Box::new(dl3050::rule()),
455 Box::new(dl3051::rule()),
456 Box::new(dl3052::rule()),
457 Box::new(dl3053::rule()),
458 Box::new(dl3054::rule()),
459 Box::new(dl3055::rule()),
460 Box::new(dl3056::rule()),
461 Box::new(dl3058::rule()),
462 Box::new(dl3061::rule()),
463 Box::new(dl4000::rule()),
465 Box::new(dl4005::rule()),
466 Box::new(dl4006::rule()),
467 Box::new(dl3002::rule()),
469 Box::new(dl3006::rule()),
470 Box::new(dl3012::rule()),
471 Box::new(dl3022::rule()),
472 Box::new(dl3023::rule()),
473 Box::new(dl3024::rule()),
474 Box::new(dl3045::rule()),
475 Box::new(dl3047::rule()),
476 Box::new(dl3057::rule()),
477 Box::new(dl3059::rule()),
478 Box::new(dl3062::rule()),
479 Box::new(dl4001::rule()),
480 Box::new(dl4003::rule()),
481 Box::new(dl4004::rule()),
482 Box::new(dl3008::rule()),
484 Box::new(dl3009::rule()),
485 Box::new(dl3013::rule()),
486 Box::new(dl3014::rule()),
487 Box::new(dl3015::rule()),
488 Box::new(dl3016::rule()),
489 Box::new(dl3018::rule()),
490 Box::new(dl3019::rule()),
491 Box::new(dl3028::rule()),
492 Box::new(dl3030::rule()),
493 Box::new(dl3032::rule()),
494 Box::new(dl3033::rule()),
495 Box::new(dl3034::rule()),
496 Box::new(dl3036::rule()),
497 Box::new(dl3037::rule()),
498 Box::new(dl3038::rule()),
499 Box::new(dl3040::rule()),
500 Box::new(dl3041::rule()),
501 Box::new(dl3042::rule()),
502 Box::new(dl3060::rule()),
503 ]
504}
505
506#[cfg(test)]
507mod tests {
508 use super::*;
509
510 #[test]
511 fn test_simple_rule() {
512 let rule = simple_rule("TEST001", Severity::Warning, "Test message", |instr, _| {
513 !matches!(instr, Instruction::Maintainer(_))
514 });
515
516 let mut state = RuleState::new();
517 let instr = Instruction::Maintainer("test".to_string());
518 rule.check(&mut state, 1, &instr, None);
519
520 assert_eq!(state.failures.len(), 1);
521 assert_eq!(state.failures[0].code.as_str(), "TEST001");
522 }
523
524 #[test]
525 fn test_rule_data() {
526 let mut data = RuleData::default();
527
528 data.set_int("count", 5);
529 assert_eq!(data.get_int("count"), 5);
530
531 data.set_bool("seen", true);
532 assert!(data.get_bool("seen"));
533
534 data.set_string("name", "test");
535 assert_eq!(data.get_string("name"), Some("test"));
536
537 data.insert_to_set("aliases", "builder");
538 assert!(data.set_contains("aliases", "builder"));
539 assert!(!data.set_contains("aliases", "runner"));
540 }
541}