portfu_admin 1.3.3

Library of Admin tools build on toip of Portfu
Documentation
use dashmap::DashMap;
use std::collections::VecDeque;
use std::hash::Hash;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;

#[derive(Default, Clone, Debug)]
pub enum CacheEntry<T> {
    Valid(Arc<T>),
    Expired(Arc<T>),
    #[default]
    Empty,
}

#[derive(Debug)]
pub struct CacheItem<T> {
    value: T,
    stale_timeout: Option<Duration>,
    lifetime: Option<Instant>,
    last_access: Instant,
}
impl<T> CacheItem<T> {
    pub fn new(value: T, stale_timeout: Option<Duration>, lifetime: Option<Duration>) -> Self {
        Self {
            value,
            stale_timeout,
            lifetime: lifetime.map(|d| Instant::now() + d),
            last_access: Instant::now(),
        }
    }
    pub fn get(&self) -> &T {
        &self.value
    }
    pub fn is_expired(&self) -> bool {
        if let Some(stale_timeout) = self.stale_timeout {
            Instant::now() - stale_timeout > self.last_access
        } else if let Some(lifetime) = self.lifetime {
            Instant::now() >= lifetime
        } else {
            false
        }
    }
}

#[derive(Default, Clone, Debug)]
pub struct CacheMap<K: Hash + PartialEq + Eq + Clone, T> {
    depth: usize,
    stale_timeout: Option<Duration>,
    lifetime: Option<Duration>,
    recently_used: Arc<RwLock<VecDeque<K>>>,
    entries: DashMap<K, Arc<CacheItem<T>>>,
}
impl<K: Hash + PartialEq + Eq + Clone, T> CacheMap<K, T> {
    pub fn new(depth: usize, stale_timeout: Option<Duration>, lifetime: Option<Duration>) -> Self {
        Self {
            depth,
            stale_timeout,
            lifetime,
            recently_used: Default::default(),
            entries: Default::default(),
        }
    }

    pub async fn add(&self, key: K, value: T) {
        self.update_recent(&key).await;
        self.entries.insert(
            key,
            Arc::new(CacheItem::new(value, self.stale_timeout, self.lifetime)),
        );
        self.trim_recent().await;
    }

    async fn update_recent(&self, key: &K) {
        let mut locked = self.recently_used.write().await;
        locked.retain(|k| k != key);
        locked.push_front(key.clone());
    }

    async fn trim_recent(&self) {
        let recent_len = self.recently_used.read().await.len();
        if recent_len > self.depth {
            for r in self
                .recently_used
                .write()
                .await
                .drain(self.depth..)
                .collect::<Vec<K>>()
            {
                self.entries.remove(&r);
            }
        }
    }

    pub async fn get(&self, key: &K) -> CacheEntry<CacheItem<T>> {
        if let Some(cache_item) = self.entries.get(key).map(|e| e.clone()) {
            self.update_recent(key).await;
            if cache_item.is_expired() {
                CacheEntry::Expired(cache_item)
            } else {
                CacheEntry::Valid(cache_item)
            }
        } else {
            CacheEntry::Empty
        }
    }
}