aaai_core/audit/
engine.rs1use crate::audit::warning;
4use crate::config::definition::AuditDefinition;
5use crate::diff::entry::{DiffEntry, DiffType};
6use super::result::{AuditResult, AuditStatus, FileAuditResult};
7use super::strategy;
8
9pub struct AuditEngine;
10
11impl AuditEngine {
12 pub fn evaluate(diffs: &[DiffEntry], definition: &AuditDefinition) -> AuditResult {
14 let mut results = Vec::new();
15
16 for diff in diffs {
17 let result = judge(diff, definition);
18 results.push(result);
19 }
20
21 AuditResult::new(results)
22 }
23}
24
25fn judge(diff: &DiffEntry, definition: &AuditDefinition) -> FileAuditResult {
26 if diff.diff_type.is_error() {
28 return FileAuditResult {
29 diff: diff.clone(),
30 entry: None,
31 status: AuditStatus::Error,
32 detail: diff.error_detail.clone().or_else(|| {
33 Some("File could not be read or compared.".into())
34 }),
35 warnings: Vec::new(),
36 };
37 }
38
39 if diff.diff_type == DiffType::Unchanged {
41 return FileAuditResult {
42 diff: diff.clone(),
43 entry: definition.find_entry(&diff.path).cloned(),
44 status: AuditStatus::Ok,
45 detail: None,
46 warnings: Vec::new(),
47 };
48 }
49
50 let entry = match definition.find_entry(&diff.path) {
52 Some(e) => e,
53 None => {
54 return FileAuditResult {
55 diff: diff.clone(),
56 entry: None,
57 status: AuditStatus::Pending,
58 detail: Some("No audit rule defined for this path.".into()),
59 warnings: Vec::new(),
60 };
61 }
62 };
63
64 if !entry.enabled {
66 return FileAuditResult {
67 diff: diff.clone(),
68 entry: Some(entry.clone()),
69 status: AuditStatus::Ignored,
70 detail: Some("Entry is disabled.".into()),
71 warnings: Vec::new(),
72 };
73 }
74
75 if entry.reason.trim().is_empty() {
77 return FileAuditResult {
78 diff: diff.clone(),
79 entry: Some(entry.clone()),
80 status: AuditStatus::Pending,
81 detail: Some("Entry exists but has no reason — human approval required.".into()),
82 warnings: Vec::new(),
83 };
84 }
85
86 if entry.diff_type != diff.diff_type {
88 return FileAuditResult {
89 diff: diff.clone(),
90 entry: Some(entry.clone()),
91 status: AuditStatus::Failed,
92 detail: Some(format!(
93 "Expected diff type {:?} but found {:?}.",
94 entry.diff_type, diff.diff_type
95 )),
96 warnings: Vec::new(),
97 };
98 }
99
100 match strategy::evaluate(&entry.strategy, diff) {
102 Ok(()) => {
103 let warns = warning::collect(diff, entry);
104 FileAuditResult {
105 diff: diff.clone(),
106 entry: Some(entry.clone()),
107 status: AuditStatus::Ok,
108 detail: None,
109 warnings: warns,
110 }
111 }
112 Err(msg) => FileAuditResult {
113 diff: diff.clone(),
114 entry: Some(entry.clone()),
115 status: AuditStatus::Failed,
116 detail: Some(msg),
117 warnings: Vec::new(),
118 },
119 }
120}