1mod router;
18mod types;
19
20pub use router::{AlertRouter, AlertRouterConfig};
21pub use types::{Alert, AlertChannel, AlertSeverity, DeliveryResult};
22
23#[derive(Debug, Clone)]
25pub struct MessageTemplate {
26 pub template: String,
28}
29
30impl MessageTemplate {
31 pub fn new(template: &str) -> Self {
33 Self {
34 template: template.to_string(),
35 }
36 }
37
38 pub fn render(&self, alert: &Alert) -> String {
40 self.template
41 .replace("{title}", &alert.title)
42 .replace("{message}", &alert.message)
43 .replace("{severity}", alert.severity.name())
44 .replace("{source}", &alert.source)
45 .replace(
46 "{value}",
47 &alert.value.map(|v| format!("{:.2}", v)).unwrap_or_default(),
48 )
49 .replace(
50 "{threshold}",
51 &alert
52 .threshold
53 .map(|t| format!("{:.2}", t))
54 .unwrap_or_default(),
55 )
56 }
57}
58
59pub fn alert_from_anomaly(
61 metric: &str,
62 value: f64,
63 expected: f64,
64 severity: AlertSeverity,
65) -> Alert {
66 let deviation = ((value - expected) / expected * 100.0).abs();
67 Alert::new(
68 &format!("Anomaly detected: {}", metric),
69 &format!(
70 "{} deviated {:.1}% from expected value {:.2}",
71 metric, deviation, expected
72 ),
73 severity,
74 )
75 .with_source(metric)
76 .with_value(value)
77 .with_threshold(expected)
78}
79
80#[cfg(test)]
81mod tests;