use std::collections::HashMap;
use std::sync::Mutex;
use crate::persistence::Message;
#[derive(Debug, Clone)]
pub(crate) struct ContextCacheEntry {
pub messages: Vec<Message>,
pub max_id_returned: Option<i64>,
pub compaction_gen_snapshot: u64,
}
#[derive(Debug, Default)]
pub(crate) struct ContextCache {
entries: Mutex<HashMap<String, ContextCacheEntry>>,
}
impl ContextCache {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn snapshot(&self, session_id: &str) -> Option<ContextCacheEntry> {
self.entries
.lock()
.expect("ContextCache mutex poisoned")
.get(session_id)
.cloned()
}
pub(crate) fn store(&self, session_id: &str, entry: ContextCacheEntry) {
self.entries
.lock()
.expect("ContextCache mutex poisoned")
.insert(session_id.to_string(), entry);
}
pub(crate) fn invalidate(&self, session_id: &str) {
self.entries
.lock()
.expect("ContextCache mutex poisoned")
.remove(session_id);
}
#[cfg(test)]
pub(crate) fn clear_all(&self) {
self.entries
.lock()
.expect("ContextCache mutex poisoned")
.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
fn entry(max_id: Option<i64>, gen_snap: u64) -> ContextCacheEntry {
ContextCacheEntry {
messages: vec![],
max_id_returned: max_id,
compaction_gen_snapshot: gen_snap,
}
}
#[test]
fn snapshot_returns_none_when_empty() {
let cache = ContextCache::new();
assert!(cache.snapshot("s1").is_none());
}
#[test]
fn store_then_snapshot_round_trip() {
let cache = ContextCache::new();
cache.store("s1", entry(Some(42), 7));
let got = cache.snapshot("s1").unwrap();
assert_eq!(got.max_id_returned, Some(42));
assert_eq!(got.compaction_gen_snapshot, 7);
}
#[test]
fn invalidate_removes_only_target_session() {
let cache = ContextCache::new();
cache.store("s1", entry(Some(10), 0));
cache.store("s2", entry(Some(20), 0));
cache.invalidate("s1");
assert!(cache.snapshot("s1").is_none());
assert!(cache.snapshot("s2").is_some());
}
#[test]
fn store_overwrites_existing_entry() {
let cache = ContextCache::new();
cache.store("s1", entry(Some(10), 0));
cache.store("s1", entry(Some(99), 5));
let got = cache.snapshot("s1").unwrap();
assert_eq!(got.max_id_returned, Some(99));
assert_eq!(got.compaction_gen_snapshot, 5);
}
#[test]
fn clear_all_removes_every_entry() {
let cache = ContextCache::new();
cache.store("s1", entry(Some(10), 0));
cache.store("s2", entry(Some(20), 0));
cache.clear_all();
assert!(cache.snapshot("s1").is_none());
assert!(cache.snapshot("s2").is_none());
}
}