use crate::drift::{DriftReport, Severity};
use anyhow::Result;
use serde_json::{json, Value};
use std::time::{SystemTime, UNIX_EPOCH};
pub fn render_check(report: &DriftReport) -> Result<()> {
let now_secs = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let errors = report.error_count();
let warnings = report.warning_count();
let events: Vec<Value> = if report.drifts.is_empty() {
vec![json!({
"title": "svccat: No drift detected",
"text": format!(
"%%% \nAll {} declared service{} are in sync with the repo.\n%%%",
report.declared,
if report.declared == 1 { "" } else { "s" }
),
"date_happened": now_secs,
"alert_type": "success",
"source_type_name": "svccat",
"tags": ["svccat", "drift:clean"]
})]
} else {
let mut grouped: std::collections::BTreeMap<String, Vec<&crate::drift::DriftItem>> =
std::collections::BTreeMap::new();
for item in &report.drifts {
grouped.entry(item.service.clone()).or_default().push(item);
}
grouped
.into_iter()
.map(|(service, items)| {
let has_error = items.iter().any(|i| matches!(i.severity, Severity::Error));
let alert_type = if has_error { "error" } else { "warning" };
let lines: Vec<String> = items
.iter()
.map(|i| {
let prefix = match i.severity {
Severity::Error => "[ERROR]",
Severity::Warning => "[WARN]",
};
format!("{prefix} {:?}: {}", i.kind, i.message)
})
.collect();
let body = format!("%%% \n{}\n%%%", lines.join("\n"));
json!({
"title": format!("svccat drift: {service}"),
"text": body,
"date_happened": now_secs,
"alert_type": alert_type,
"source_type_name": "svccat",
"tags": [
"svccat",
format!("service:{service}"),
format!("drift:{}", if has_error { "error" } else { "warning" })
]
})
})
.collect()
};
let summary = json!({
"manifest": report.manifest,
"declared": report.declared,
"discovered": report.discovered,
"errors": errors,
"warnings": warnings,
"events": events
});
println!("{}", serde_json::to_string_pretty(&summary)?);
Ok(())
}