use super::{Slo, SloStatus};
use crate::error::Result;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceReport {
pub generated_at: DateTime<Utc>,
pub period_start: DateTime<Utc>,
pub period_end: DateTime<Utc>,
pub slo_statuses: Vec<SloStatus>,
pub overall_compliance: f64,
pub slos_met: usize,
pub slos_not_met: usize,
}
impl ComplianceReport {
pub fn new(
period_start: DateTime<Utc>,
period_end: DateTime<Utc>,
slo_statuses: Vec<SloStatus>,
) -> Self {
let slos_met = slo_statuses.iter().filter(|s| s.is_met).count();
let slos_not_met = slo_statuses.len() - slos_met;
let overall_compliance = if !slo_statuses.is_empty() {
(slos_met as f64 / slo_statuses.len() as f64) * 100.0
} else {
0.0
};
Self {
generated_at: Utc::now(),
period_start,
period_end,
slo_statuses,
overall_compliance,
slos_met,
slos_not_met,
}
}
pub fn to_json(&self) -> Result<String> {
serde_json::to_string_pretty(self).map_err(crate::error::ObservabilityError::Serialization)
}
}
pub struct ReportGenerator;
impl ReportGenerator {
pub fn generate(
_slos: &[Slo],
period_start: DateTime<Utc>,
period_end: DateTime<Utc>,
) -> Result<ComplianceReport> {
let slo_statuses = Vec::new();
Ok(ComplianceReport::new(
period_start,
period_end,
slo_statuses,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::slo::ErrorBudget;
#[test]
fn test_compliance_report() {
let statuses = vec![
SloStatus::new(
"slo1".to_string(),
99.95,
99.9,
ErrorBudget::from_target(99.9),
),
SloStatus::new(
"slo2".to_string(),
99.85,
99.9,
ErrorBudget::from_target(99.9),
),
];
let report = ComplianceReport::new(Utc::now(), Utc::now(), statuses);
assert_eq!(report.slos_met, 1);
assert_eq!(report.slos_not_met, 1);
assert_eq!(report.overall_compliance, 50.0);
}
}