use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MetricType {
OTDistance,
TopologyCoherence,
H0Persistence,
IBKL,
DiffusionEnergy,
FisherRao,
AttentionEntropy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricValue {
pub metric_type: MetricType,
pub value: f32,
pub normalized: f32,
pub is_healthy: bool,
pub warning_threshold: f32,
pub critical_threshold: f32,
}
impl MetricValue {
pub fn new(
metric_type: MetricType,
value: f32,
min_expected: f32,
max_expected: f32,
warning_threshold: f32,
critical_threshold: f32,
) -> Self {
let range = max_expected - min_expected;
let normalized = if range > 0.0 {
((value - min_expected) / range).clamp(0.0, 1.0)
} else {
0.5
};
let is_healthy = value >= min_expected && value <= max_expected;
Self {
metric_type,
value,
normalized,
is_healthy,
warning_threshold,
critical_threshold,
}
}
pub fn is_warning(&self) -> bool {
self.value > self.warning_threshold && self.value <= self.critical_threshold
}
pub fn is_critical(&self) -> bool {
self.value > self.critical_threshold
}
pub fn status(&self) -> &'static str {
if self.is_critical() {
"CRITICAL"
} else if self.is_warning() {
"WARNING"
} else if self.is_healthy {
"OK"
} else {
"UNKNOWN"
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metric_value() {
let metric = MetricValue::new(MetricType::TopologyCoherence, 0.7, 0.0, 1.0, 0.3, 0.1);
assert_eq!(metric.metric_type, MetricType::TopologyCoherence);
assert!((metric.normalized - 0.7).abs() < 1e-5);
assert!(metric.is_healthy);
}
#[test]
fn test_warning_critical() {
let metric = MetricValue::new(
MetricType::OTDistance,
5.0, 0.0,
10.0,
3.0, 7.0, );
assert!(metric.is_warning());
assert!(!metric.is_critical());
assert_eq!(metric.status(), "WARNING");
}
}