use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use super::rules::{AggregationFunction, ConditionExpression, ThresholdOperator};
#[derive(Debug, Clone)]
pub struct MetricDataPoint {
pub name: String,
pub value: f64,
pub labels: HashMap<String, String>,
pub timestamp: DateTime<Utc>,
}
pub trait MetricProvider: Send + Sync {
fn get_metric(&self, name: &str) -> Option<f64>;
fn get_metric_range(
&self,
name: &str,
duration_seconds: u64,
) -> Vec<MetricDataPoint>;
fn has_metric(&self, name: &str) -> bool {
self.get_metric(name).is_some()
}
}
pub struct ConditionEvaluator<P: MetricProvider> {
provider: Arc<P>,
}
impl<P: MetricProvider> ConditionEvaluator<P> {
pub fn new(provider: Arc<P>) -> Self {
Self { provider }
}
pub fn evaluate(&self, condition: &ConditionExpression) -> bool {
match condition {
ConditionExpression::Threshold {
metric,
operator,
value,
} => {
self.provider
.get_metric(metric)
.map(|v| operator.evaluate(v, *value))
.unwrap_or(false)
}
ConditionExpression::AggregatedThreshold {
metric,
aggregation,
window_seconds,
operator,
value,
} => {
let data_points = self.provider.get_metric_range(metric, *window_seconds);
if data_points.is_empty() {
return false;
}
let aggregated_value = self.aggregate(&data_points, aggregation);
operator.evaluate(aggregated_value, *value)
}
ConditionExpression::RateOfChange {
metric,
window_seconds,
operator,
rate_threshold,
} => {
let data_points = self.provider.get_metric_range(metric, *window_seconds);
if data_points.len() < 2 {
return false;
}
let rate = self.calculate_rate(&data_points);
operator.evaluate(rate, *rate_threshold)
}
ConditionExpression::Absent {
metric,
for_seconds: _,
} => !self.provider.has_metric(metric),
ConditionExpression::And(conditions) => {
conditions.iter().all(|c| self.evaluate(c))
}
ConditionExpression::Or(conditions) => {
conditions.iter().any(|c| self.evaluate(c))
}
ConditionExpression::Not(condition) => !self.evaluate(condition),
ConditionExpression::LabelMatch { label: _, pattern: _ } => {
true
}
}
}
fn aggregate(&self, data_points: &[MetricDataPoint], aggregation: &AggregationFunction) -> f64 {
if data_points.is_empty() {
return 0.0;
}
let values: Vec<f64> = data_points.iter().map(|p| p.value).collect();
match aggregation {
AggregationFunction::Avg => {
values.iter().sum::<f64>() / values.len() as f64
}
AggregationFunction::Sum => values.iter().sum(),
AggregationFunction::Min => values.iter().cloned().fold(f64::INFINITY, f64::min),
AggregationFunction::Max => values.iter().cloned().fold(f64::NEG_INFINITY, f64::max),
AggregationFunction::Count => values.len() as f64,
AggregationFunction::Rate => self.calculate_rate(data_points),
AggregationFunction::Percentile(p) => {
self.calculate_percentile(&values, *p)
}
}
}
fn calculate_rate(&self, data_points: &[MetricDataPoint]) -> f64 {
if data_points.len() < 2 {
return 0.0;
}
let first = &data_points[0];
let last = &data_points[data_points.len() - 1];
let value_diff = last.value - first.value;
let time_diff = (last.timestamp - first.timestamp).num_seconds() as f64;
if time_diff > 0.0 {
value_diff / time_diff
} else {
0.0
}
}
fn calculate_percentile(&self, values: &[f64], percentile: u8) -> f64 {
if values.is_empty() {
return 0.0;
}
let mut sorted = values.to_vec();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
let index = (percentile as f64 / 100.0 * (sorted.len() - 1) as f64).round() as usize;
sorted.get(index).copied().unwrap_or(0.0)
}
}