Skip to main content

aaai_core/audit/
engine.rs

1//! Audit engine: matches DiffEntries against AuditDefinition → AuditResult.
2
3use crate::config::definition::AuditDefinition;
4use crate::diff::entry::DiffEntry;
5use super::result::{AuditResult, AuditStatus, FileAuditResult};
6use super::strategy;
7
8pub struct AuditEngine;
9
10impl AuditEngine {
11    /// Judge every DiffEntry against the AuditDefinition.
12    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    // Diff-level errors (Unreadable / Incomparable) → always Error.
26    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    // Look up the matching entry.
38    let entry = match definition.find_entry(&diff.path) {
39        Some(e) => e,
40        None => {
41            return FileAuditResult {
42                diff: diff.clone(),
43                entry: None,
44                status: AuditStatus::Pending,
45                detail: Some("No audit rule defined for this path.".into()),
46            };
47        }
48    };
49
50    // Disabled entries → Ignored.
51    if !entry.enabled {
52        return FileAuditResult {
53            diff: diff.clone(),
54            entry: Some(entry.clone()),
55            status: AuditStatus::Ignored,
56            detail: Some("Entry is disabled.".into()),
57        };
58    }
59
60    // Diff-type mismatch → Failed.
61    if entry.diff_type != diff.diff_type {
62        return FileAuditResult {
63            diff: diff.clone(),
64            entry: Some(entry.clone()),
65            status: AuditStatus::Failed,
66            detail: Some(format!(
67                "Expected diff type {:?} but found {:?}.",
68                entry.diff_type, diff.diff_type
69            )),
70        };
71    }
72
73    // Content strategy check.
74    match strategy::evaluate(&entry.strategy, diff) {
75        Ok(()) => FileAuditResult {
76            diff: diff.clone(),
77            entry: Some(entry.clone()),
78            status: AuditStatus::Ok,
79            detail: None,
80        },
81        Err(msg) => FileAuditResult {
82            diff: diff.clone(),
83            entry: Some(entry.clone()),
84            status: AuditStatus::Failed,
85            detail: Some(msg),
86        },
87    }
88}