use std::sync::Mutex;
#[derive(Debug, Default)]
pub struct DegradationTracker {
issues: Mutex<Vec<DegradationIssue>>,
}
#[derive(Debug, Clone)]
pub struct DegradationIssue {
pub phase: String,
pub message: String,
pub impact: String,
}
impl DegradationTracker {
pub fn new() -> Self {
Self::default()
}
pub fn record(
&self,
phase: impl Into<String>,
message: impl Into<String>,
impact: impl Into<String>,
) {
self.issues.lock().unwrap().push(DegradationIssue {
phase: phase.into(),
message: message.into(),
impact: impact.into(),
});
}
pub fn issues(&self) -> Vec<DegradationIssue> {
self.issues.lock().unwrap().clone()
}
pub fn has_issues(&self) -> bool {
!self.issues.lock().unwrap().is_empty()
}
pub fn issue_count(&self) -> usize {
self.issues.lock().unwrap().len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
#[test]
fn empty_tracker_has_no_issues() {
let tracker = DegradationTracker::new();
assert!(!tracker.has_issues());
assert_eq!(tracker.issue_count(), 0);
assert!(tracker.issues().is_empty());
}
#[test]
fn record_and_retrieve_issues() {
let tracker = DegradationTracker::new();
tracker.record(
"SD",
"Source-level analysis failed",
"Composition trees unavailable",
);
tracker.record(
"CSS",
"CSS extraction failed",
"CSS removal rules incomplete",
);
assert!(tracker.has_issues());
assert_eq!(tracker.issue_count(), 2);
let issues = tracker.issues();
assert_eq!(issues[0].phase, "SD");
assert_eq!(issues[0].message, "Source-level analysis failed");
assert_eq!(issues[0].impact, "Composition trees unavailable");
assert_eq!(issues[1].phase, "CSS");
}
#[test]
fn thread_safe_recording() {
let tracker = Arc::new(DegradationTracker::new());
let mut handles = Vec::new();
for i in 0..10 {
let tracker = tracker.clone();
handles.push(std::thread::spawn(move || {
tracker.record("TEST", format!("Issue {}", i), "Test impact");
}));
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(tracker.issue_count(), 10);
}
}