use crate::eviction::{EvictionPolicy, EvictionStore, FifoStore, LfuStore, LruStore};
use std::hash::Hash;
use std::time::{Duration, Instant};
#[derive(Clone, Debug)]
struct CacheEntry<V> {
value: V,
inserted_at: Instant,
}
impl<V> CacheEntry<V> {
fn new(value: V) -> Self {
Self {
value,
inserted_at: Instant::now(),
}
}
fn is_expired(&self, ttl: Option<Duration>) -> bool {
if let Some(ttl) = ttl {
self.inserted_at.elapsed() > ttl
} else {
false
}
}
}
pub(crate) struct CacheStore<K, V> {
store: Box<dyn EvictionStore<K, CacheEntry<V>>>,
ttl: Option<Duration>,
}
impl<K: Hash + Eq + Clone + Send + 'static, V: Clone + Send + 'static> CacheStore<K, V> {
pub(crate) fn new(capacity: usize, ttl: Option<Duration>, policy: EvictionPolicy) -> Self {
let store: Box<dyn EvictionStore<K, CacheEntry<V>>> = match policy {
EvictionPolicy::Lru => Box::new(LruStore::new(capacity)),
EvictionPolicy::Lfu => Box::new(LfuStore::new(capacity)),
EvictionPolicy::Fifo => Box::new(FifoStore::new(capacity)),
};
Self { store, ttl }
}
pub(crate) fn get(&mut self, key: &K) -> Option<V> {
let entry = self.store.get(key)?;
if entry.is_expired(self.ttl) {
self.store.remove(key);
None
} else {
Some(entry.value.clone())
}
}
pub(crate) fn insert(&mut self, key: K, value: V) -> Option<V> {
let entry = CacheEntry::new(value);
self.store.insert(key, entry).map(|(_, e)| e.value)
}
pub(crate) fn len(&self) -> usize {
self.store.len()
}
#[allow(dead_code)]
pub(crate) fn clear(&mut self) {
self.store.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread::sleep;
#[test]
fn test_cache_store_basic() {
let mut store = CacheStore::new(2, None, EvictionPolicy::Lru);
store.insert("key1", "value1");
assert_eq!(store.get(&"key1"), Some("value1"));
assert_eq!(store.len(), 1);
assert_eq!(store.get(&"key2"), None);
}
#[test]
fn test_cache_store_lru_eviction() {
let mut store = CacheStore::new(2, None, EvictionPolicy::Lru);
store.insert("key1", "value1");
store.insert("key2", "value2");
let evicted = store.insert("key3", "value3");
assert_eq!(evicted, Some("value1"));
assert_eq!(store.get(&"key1"), None);
assert_eq!(store.get(&"key2"), Some("value2"));
assert_eq!(store.get(&"key3"), Some("value3"));
}
#[test]
fn test_cache_store_ttl_expiration() {
let mut store = CacheStore::new(10, Some(Duration::from_millis(50)), EvictionPolicy::Lru);
store.insert("key1", "value1");
assert_eq!(store.get(&"key1"), Some("value1"));
sleep(Duration::from_millis(60));
assert_eq!(store.get(&"key1"), None);
}
#[test]
fn test_cache_store_clear() {
let mut store = CacheStore::new(10, None, EvictionPolicy::Lru);
store.insert("key1", "value1");
store.insert("key2", "value2");
assert_eq!(store.len(), 2);
store.clear();
assert_eq!(store.len(), 0);
assert_eq!(store.get(&"key1"), None);
}
}