1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3
4use crate::error::Result;
5use crate::facts::FactValues;
6use crate::level::Level;
7use crate::registry::RuleRegistry;
8use crate::walker::FileIndex;
9
10#[derive(Debug, Clone)]
12pub struct Violation {
13 pub path: Option<PathBuf>,
14 pub message: String,
15 pub line: Option<usize>,
16 pub column: Option<usize>,
17}
18
19impl Violation {
20 pub fn new(message: impl Into<String>) -> Self {
21 Self {
22 path: None,
23 message: message.into(),
24 line: None,
25 column: None,
26 }
27 }
28
29 #[must_use]
30 pub fn with_path(mut self, path: impl Into<PathBuf>) -> Self {
31 self.path = Some(path.into());
32 self
33 }
34
35 #[must_use]
36 pub fn with_location(mut self, line: usize, column: usize) -> Self {
37 self.line = Some(line);
38 self.column = Some(column);
39 self
40 }
41}
42
43#[derive(Debug, Clone)]
45pub struct RuleResult {
46 pub rule_id: String,
47 pub level: Level,
48 pub policy_url: Option<String>,
49 pub violations: Vec<Violation>,
50}
51
52impl RuleResult {
53 pub fn passed(&self) -> bool {
54 self.violations.is_empty()
55 }
56}
57
58#[derive(Debug)]
66pub struct Context<'a> {
67 pub root: &'a Path,
68 pub index: &'a FileIndex,
69 pub registry: Option<&'a RuleRegistry>,
70 pub facts: Option<&'a FactValues>,
71 pub vars: Option<&'a HashMap<String, String>>,
72}
73
74pub trait Rule: Send + Sync + std::fmt::Debug {
76 fn id(&self) -> &str;
77 fn level(&self) -> Level;
78 fn policy_url(&self) -> Option<&str> {
79 None
80 }
81 fn evaluate(&self, ctx: &Context<'_>) -> Result<Vec<Violation>>;
82
83 fn fixer(&self) -> Option<&dyn Fixer> {
89 None
90 }
91}
92
93#[derive(Debug)]
95pub struct FixContext<'a> {
96 pub root: &'a Path,
97 pub dry_run: bool,
100}
101
102#[derive(Debug, Clone)]
104pub enum FixOutcome {
105 Applied(String),
109 Skipped(String),
113}
114
115pub trait Fixer: Send + Sync + std::fmt::Debug {
117 fn describe(&self) -> String;
120
121 fn apply(&self, violation: &Violation, ctx: &FixContext<'_>) -> Result<FixOutcome>;
123}