use crate::application::ports::{EvictionCandidate, EvictionPolicy};
#[derive(Debug, Clone)]
pub struct MemoryEviction {
max_bytes: usize,
}
impl MemoryEviction {
pub fn new(max_bytes: usize) -> Self {
Self { max_bytes }
}
pub fn max_bytes(&self) -> usize {
self.max_bytes
}
}
impl<K, V> EvictionPolicy<K, V> for MemoryEviction
where
K: Clone,
V: Clone,
{
fn select_victim(&self, candidates: &[EvictionCandidate<K, V>]) -> Option<K> {
candidates
.iter()
.min_by_key(|candidate| candidate.last_access)
.map(|candidate| candidate.key.clone())
}
fn should_evict(&self, _current_entries: usize, current_memory_bytes: usize) -> bool {
current_memory_bytes >= self.max_bytes
}
fn tracks_memory(&self) -> bool {
true
}
fn estimate_entry_size(&self, _key: &K, _value: &V) -> usize {
let base_size = std::mem::size_of::<K>() + std::mem::size_of::<V>() + 64;
let estimated_heap = 200;
base_size + estimated_heap
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Instant;
#[test]
fn test_memory_select_lru() {
let policy = MemoryEviction::new(1000);
let now = Instant::now();
let candidates = vec![
EvictionCandidate {
key: "key1".to_string(),
value: 100,
last_access: now,
},
EvictionCandidate {
key: "key2".to_string(),
value: 200,
last_access: now
.checked_sub(std::time::Duration::from_secs(10))
.unwrap_or(now),
},
EvictionCandidate {
key: "key3".to_string(),
value: 300,
last_access: now
.checked_sub(std::time::Duration::from_secs(5))
.unwrap_or(now),
},
];
let victim = policy.select_victim(&candidates);
assert_eq!(victim, Some("key2".to_string())); }
#[test]
fn test_memory_should_evict() {
use crate::application::ports::EvictionPolicy;
let policy = MemoryEviction::new(1000);
assert!(!<MemoryEviction as EvictionPolicy<String, i32>>::should_evict(&policy, 0, 999));
assert!(<MemoryEviction as EvictionPolicy<String, i32>>::should_evict(&policy, 0, 1000));
assert!(<MemoryEviction as EvictionPolicy<String, i32>>::should_evict(&policy, 0, 1001));
}
#[test]
fn test_memory_tracks_memory() {
use crate::application::ports::EvictionPolicy;
let policy = MemoryEviction::new(1000);
assert!(<MemoryEviction as EvictionPolicy<String, i32>>::tracks_memory(&policy));
}
#[test]
fn test_memory_estimate_entry_size() {
use crate::application::ports::EvictionPolicy;
let policy = MemoryEviction::new(1000);
let key = "test_key";
let value = 42;
let size = <MemoryEviction as EvictionPolicy<&str, i32>>::estimate_entry_size(
&policy, &key, &value,
);
assert!(size > 0);
}
}