Skip to main content

noether_guard/
report.rs

1use serde::{Deserialize, Serialize};
2
3/// A single conservation violation.
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct Violation {
6    pub law_name: String,
7    pub law_index: usize,
8    pub first_violation: Option<f64>,
9    pub last_violation: Option<f64>,
10    pub violation_count: usize,
11    pub breaking_scale: Option<f64>,
12}
13
14/// Full monitoring report.
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct Report {
17    pub total_ticks: usize,
18    pub health: f64,
19    pub total_violations: usize,
20    pub violations: Vec<Violation>,
21}
22
23impl Report {
24    /// Generate a human-readable text report.
25    pub fn to_text(&self) -> String {
26        let mut out = String::new();
27        out.push_str("=== NoetherGuard Report ===\n");
28        out.push_str(&format!("Ticks:    {}\n", self.total_ticks));
29        out.push_str(&format!("Health:   {:.1}%\n", self.health * 100.0));
30        out.push_str(&format!("Violations: {}\n", self.total_violations));
31
32        if self.violations.is_empty() {
33            out.push_str("\nAll conservation laws satisfied. ✓\n");
34        } else {
35            out.push_str("\nViolations:\n");
36            for v in &self.violations {
37                out.push_str(&format!(
38                    "  [{}] {} — {} violations",
39                    v.law_index, v.law_name, v.violation_count
40                ));
41                if let (Some(first), Some(last)) = (v.first_violation, v.last_violation) {
42                    out.push_str(&format!(" (t={:.3}..{:.3})", first, last));
43                }
44                if let Some(scale) = v.breaking_scale {
45                    out.push_str(&format!(" breaking_scale={:.4}", scale));
46                }
47                out.push('\n');
48            }
49        }
50        out
51    }
52
53    /// Generate JSON report.
54    pub fn to_json(&self) -> String {
55        serde_json::to_string_pretty(self).unwrap_or_else(|_| "{}".to_string())
56    }
57}
58
59impl std::fmt::Display for Report {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        write!(f, "{}", self.to_text())
62    }
63}