use chrono::Local;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditTrail {
pub timestamp: String,
pub rules_executed: usize,
pub files_changed: usize,
pub file_hashes: HashMap<String, String>,
pub metadata: ExecutionMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionMetadata {
pub ggen_version: String,
pub manifest_path: String,
pub ontology_path: String,
pub spec_hash: String,
pub duration_ms: u64,
}
impl AuditTrail {
pub fn new(ggen_version: &str, manifest_path: &str, ontology_path: &str) -> Self {
let timestamp = Local::now().to_rfc3339();
AuditTrail {
timestamp,
rules_executed: 0,
files_changed: 0,
file_hashes: HashMap::new(),
metadata: ExecutionMetadata {
ggen_version: ggen_version.to_string(),
manifest_path: manifest_path.to_string(),
ontology_path: ontology_path.to_string(),
spec_hash: String::new(),
duration_ms: 0,
},
}
}
pub fn record_rule_executed(&mut self) {
self.rules_executed += 1;
}
pub fn record_file_change(&mut self, path: String, hash: String) {
self.files_changed += 1;
self.file_hashes.insert(path, hash);
}
pub fn to_json(&self) -> Result<String, Box<dyn std::error::Error>> {
Ok(serde_json::to_string_pretty(self)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_audit_trail_creation() {
let audit = AuditTrail::new("5.0.2", "test.toml", "test.ttl");
assert_eq!(audit.rules_executed, 0);
assert_eq!(audit.files_changed, 0);
}
#[test]
fn test_record_rule_executed() {
let mut audit = AuditTrail::new("5.0.2", "test.toml", "test.ttl");
audit.record_rule_executed();
assert_eq!(audit.rules_executed, 1);
}
#[test]
fn test_to_json_serialization() {
let audit = AuditTrail::new("5.0.2", "test.toml", "test.ttl");
let json = audit.to_json().expect("Failed to serialize");
assert!(json.contains("\"rules_executed\""));
assert!(json.contains("\"files_changed\""));
}
}
pub mod writer;