use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;
#[derive(Clone)]
pub struct ReadFileCacheEntry {
pub modified: SystemTime,
pub len: u64,
pub output: String,
}
pub struct ReadFileTurnCache {
inner: Mutex<ReadFileTurnCacheInner>,
}
struct ReadFileTurnCacheInner {
map: HashMap<String, ReadFileCacheEntry>,
max_entries: usize,
}
impl ReadFileTurnCache {
pub fn new(max_entries: usize) -> Self {
Self {
inner: Mutex::new(ReadFileTurnCacheInner {
map: HashMap::new(),
max_entries: max_entries.max(1),
}),
}
}
pub fn clear(&self) {
if let Ok(mut g) = self.inner.lock() {
g.map.clear();
}
}
pub fn try_get(&self, key: &str, modified: SystemTime, len: u64) -> Option<String> {
let g = self.inner.lock().ok()?;
let e = g.map.get(key)?;
if e.modified == modified && e.len == len {
Some(e.output.clone())
} else {
None
}
}
pub fn insert(&self, key: String, modified: SystemTime, len: u64, output: String) {
let Ok(mut g) = self.inner.lock() else {
return;
};
if g.map.len() >= g.max_entries {
g.map.clear();
}
g.map.insert(
key,
ReadFileCacheEntry {
modified,
len,
output,
},
);
}
}
pub type ReadFileTurnCacheHandle = Arc<ReadFileTurnCache>;
pub fn new_turn_cache_handle(max_entries: usize) -> ReadFileTurnCacheHandle {
Arc::new(ReadFileTurnCache::new(max_entries))
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Duration, UNIX_EPOCH};
#[test]
fn cache_hit_when_mtime_len_match() {
let c = ReadFileTurnCache::new(8);
let t = UNIX_EPOCH + Duration::from_secs(1_700_000_000);
c.insert("k1".into(), t, 42, "hello".into());
assert_eq!(c.try_get("k1", t, 42).as_deref(), Some("hello"));
assert!(c.try_get("k1", t, 41).is_none());
}
#[test]
fn clear_removes_entries() {
let c = ReadFileTurnCache::new(8);
let t = UNIX_EPOCH;
c.insert("k".into(), t, 1, "x".into());
c.clear();
assert!(c.try_get("k", t, 1).is_none());
}
}