use crate::diff::entry::{DiffEntry, LARGE_FILE_THRESHOLD, fmt_size};
use crate::config::definition::{AuditEntry, AuditStrategy};
#[derive(Debug, Clone)]
pub enum AuditWarning {
LargeFileStrategy {
path: String,
strategy: &'static str,
size_bytes: u64,
},
NoStrategyOnModified { path: String },
NoApprover { path: String },
}
impl AuditWarning {
pub fn message(&self) -> String {
match self {
AuditWarning::LargeFileStrategy { path, strategy, size_bytes } =>
format!(
"{path}: {strategy} strategy applied to a large file ({}). \
Consider using Checksum instead.",
fmt_size(*size_bytes)
),
AuditWarning::NoStrategyOnModified { path } =>
format!("{path}: Modified file uses `None` strategy — content not verified."),
AuditWarning::NoApprover { path } =>
format!("{path}: Entry has no `approved_by` field — approver unknown."),
}
}
pub fn kind(&self) -> &'static str {
match self {
AuditWarning::LargeFileStrategy { .. } => "large-file",
AuditWarning::NoStrategyOnModified { .. } => "no-strategy",
AuditWarning::NoApprover { .. } => "no-approver",
}
}
}
pub fn collect(diff: &DiffEntry, entry: &AuditEntry) -> Vec<AuditWarning> {
let mut warns = Vec::new();
let size = diff.after_size.or(diff.before_size).unwrap_or(0);
if size > LARGE_FILE_THRESHOLD {
match &entry.strategy {
AuditStrategy::Exact { .. } | AuditStrategy::LineMatch { .. } => {
warns.push(AuditWarning::LargeFileStrategy {
path: diff.path.clone(),
strategy: entry.strategy.label(),
size_bytes: size,
});
}
_ => {}
}
}
if diff.diff_type == crate::diff::entry::DiffType::Modified {
if let AuditStrategy::None = entry.strategy {
warns.push(AuditWarning::NoStrategyOnModified { path: diff.path.clone() });
}
}
if entry.approved_by.is_none() && !entry.reason.trim().is_empty() {
warns.push(AuditWarning::NoApprover { path: diff.path.clone() });
}
warns
}
#[cfg(test)]
mod tests;