use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Default)]
pub struct OpsSnapshotDiff {
last: Option<Arc<HashMap<String, u64>>>,
}
impl OpsSnapshotDiff {
pub fn delta_and_advance(&mut self, current: HashMap<String, u64>) -> HashMap<String, u64> {
let mut out = HashMap::with_capacity(current.len());
let previous = self.last.as_deref();
for (service, &now) in ¤t {
let before = previous.and_then(|p| p.get(service)).copied().unwrap_or(0);
let delta = now.saturating_sub(before);
if delta > 0 {
out.insert(service.clone(), delta);
}
}
self.last = Some(Arc::new(current));
out
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_call_counts_all() {
let mut diff = OpsSnapshotDiff::default();
let mut current = HashMap::new();
current.insert("svc-a".to_string(), 5);
current.insert("svc-b".to_string(), 12);
let out = diff.delta_and_advance(current);
assert_eq!(out.get("svc-a"), Some(&5));
assert_eq!(out.get("svc-b"), Some(&12));
}
#[test]
fn second_call_subtracts() {
let mut diff = OpsSnapshotDiff::default();
let mut first = HashMap::new();
first.insert("svc-a".to_string(), 5);
diff.delta_and_advance(first);
let mut second = HashMap::new();
second.insert("svc-a".to_string(), 9);
let out = diff.delta_and_advance(second);
assert_eq!(out.get("svc-a"), Some(&4));
}
#[test]
fn no_change_produces_empty() {
let mut diff = OpsSnapshotDiff::default();
let mut first = HashMap::new();
first.insert("svc-a".to_string(), 5);
diff.delta_and_advance(first);
let mut second = HashMap::new();
second.insert("svc-a".to_string(), 5);
let out = diff.delta_and_advance(second);
assert!(out.is_empty());
}
#[test]
fn counter_reset_omits_entry() {
let mut diff = OpsSnapshotDiff::default();
let mut first = HashMap::new();
first.insert("svc-a".to_string(), 12);
diff.delta_and_advance(first);
let mut second = HashMap::new();
second.insert("svc-a".to_string(), 3); let out = diff.delta_and_advance(second);
assert!(!out.contains_key("svc-a"));
}
}