use dashmap::DashMap;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Default)]
pub enum EvictionPolicy {
#[default]
None,
TimeToLive(Duration),
IdleTimeout(Duration),
Combined {
ttl: Duration,
idle_timeout: Duration,
},
}
#[derive(Debug, Clone)]
pub(crate) struct ObjectMetadata {
pub created_at: Instant,
pub last_used: Instant,
}
impl ObjectMetadata {
pub fn new() -> Self {
let now = Instant::now();
Self {
created_at: now,
last_used: now,
}
}
pub fn touch(&mut self) {
self.last_used = Instant::now();
}
pub fn is_expired(&self, policy: &EvictionPolicy) -> bool {
match policy {
EvictionPolicy::None => false,
EvictionPolicy::TimeToLive(ttl) => {
self.created_at.elapsed() > *ttl
}
EvictionPolicy::IdleTimeout(timeout) => {
self.last_used.elapsed() > *timeout
}
EvictionPolicy::Combined { ttl, idle_timeout } => {
self.created_at.elapsed() > *ttl || self.last_used.elapsed() > *idle_timeout
}
}
}
}
pub(crate) struct EvictionTracker<T> {
metadata: DashMap<usize, ObjectMetadata>,
policy: EvictionPolicy,
_phantom: std::marker::PhantomData<T>,
}
impl<T> EvictionTracker<T> {
pub fn new(policy: EvictionPolicy) -> Self {
Self {
metadata: DashMap::new(),
policy,
_phantom: std::marker::PhantomData,
}
}
pub fn track_object(&self, id: usize) {
if !matches!(self.policy, EvictionPolicy::None) {
self.metadata.insert(id, ObjectMetadata::new());
}
}
pub fn touch_object(&self, id: usize) {
if !matches!(self.policy, EvictionPolicy::None) {
if let Some(mut meta) = self.metadata.get_mut(&id) {
meta.touch();
}
}
}
pub fn is_expired(&self, id: usize) -> bool {
if matches!(self.policy, EvictionPolicy::None) {
return false;
}
self.metadata
.get(&id)
.map_or(false, |meta| meta.is_expired(&self.policy))
}
pub fn remove_object(&self, id: usize) {
self.metadata.remove(&id);
}
#[allow(dead_code)]
pub fn get_expired_objects(&self) -> Vec<usize> {
if matches!(self.policy, EvictionPolicy::None) {
return Vec::new();
}
self.metadata
.iter()
.filter(|entry| entry.value().is_expired(&self.policy))
.map(|entry| *entry.key())
.collect()
}
}