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