use std::collections::HashMap;
use tokio::sync::RwLock;
use std::hash::Hash;
use std::time::{Duration, Instant};
#[derive(Clone, Debug)]
pub struct Item<T> {
pub object: T, expiry: Option<Instant>, }
impl<T> Item<T> {
pub fn new(object: T, item_duration: Option<Duration>) -> Self {
let expiry = item_duration.map(|duration| Instant::now() + duration);
Item { object, expiry }
}
pub fn expired(&self) -> bool {
self.expiry
.map(|expiry| expiry < Instant::now())
.unwrap_or(false)
}
}
#[derive(Debug)]
pub struct Cache<T, V> {
items: RwLock<HashMap<T, Item<V>>>, }
impl<T, V> Cache<T, V> {
pub fn new() -> Self {
Cache {
items: RwLock::new(HashMap::new()),
}
}
pub async fn get(&self, key: &T) -> Option<V>
where
T: Eq + Hash,
V: Clone,
{
self.items
.read() .await
.get(key) .filter(|item| !item.expired()) .map(|item| item.object.clone()) }
pub async fn set_with_seconds(&self, key: T, value: V, seconds: u64) -> Option<V>
where
T: Eq + Hash,
{
self.set(key, value, Some(Duration::from_secs(seconds)))
.await
}
pub async fn set(&self, key: T, value: V, custom_duration: Option<Duration>) -> Option<V>
where
T: Eq + Hash,
{
self.items
.write() .await
.insert(key, Item::new(value, custom_duration)) .map(|item| item.object) }
pub async fn remove_expired(&self)
where
T: Eq + Hash + Clone,
{
let expired_keys = self
.items
.read()
.await
.iter()
.filter(|(_, item)| item.expired())
.map(|(k, _)| k.clone())
.collect::<Vec<T>>();
for key in expired_keys {
self.items.write().await.remove(&key);
}
}
pub async fn remove(&self, key: &T) -> Option<V>
where
T: Eq + Hash,
{
self.items.write().await.remove(key).map(|item| item.object)
}
pub async fn clear(&self) {
self.items.write().await.clear()
}
}