use std::collections::HashMap;
use std::sync::Arc;
use arc_swap::ArcSwap;
use crate::daemon::query_api::ResolvedTomlAck;
#[derive(Debug, Default)]
pub struct AckTomlState {
inner: ArcSwap<HashMap<String, ResolvedTomlAck>>,
}
impl AckTomlState {
#[must_use]
pub fn new(initial: HashMap<String, ResolvedTomlAck>) -> Self {
Self {
inner: ArcSwap::from_pointee(initial),
}
}
#[must_use]
pub fn load(&self) -> Arc<HashMap<String, ResolvedTomlAck>> {
self.inner.load_full()
}
pub fn store(&self, next: HashMap<String, ResolvedTomlAck>) {
self.inner.store(Arc::new(next));
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.load().len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::acknowledgments::Acknowledgment;
fn ack(sig: &str) -> ResolvedTomlAck {
ResolvedTomlAck {
inner: Acknowledgment {
signature: sig.to_string(),
acknowledged_by: "team".to_string(),
acknowledged_at: "2026-08-14".to_string(),
reason: "test".to_string(),
expires_at: None,
service: None,
source_endpoint: None,
},
expires_at_dt: None,
}
}
#[test]
fn a_reload_is_visible_to_the_next_reader() {
let state = AckTomlState::new(HashMap::new());
assert!(state.is_empty());
let mut next = HashMap::new();
next.insert(
"n_plus_one_sql:svc:_ep:abc".to_string(),
ack("n_plus_one_sql:svc:_ep:abc"),
);
state.store(next);
assert_eq!(state.len(), 1);
assert!(state.load().contains_key("n_plus_one_sql:svc:_ep:abc"));
}
#[test]
fn a_reader_holding_a_snapshot_is_not_disturbed_by_a_reload() {
let mut initial = HashMap::new();
initial.insert("a".to_string(), ack("a"));
let state = AckTomlState::new(initial);
let held = state.load();
state.store(HashMap::new());
assert_eq!(held.len(), 1, "the snapshot in hand stays whole");
assert!(state.is_empty(), "while the next reader sees the new map");
}
}