use dashmap::DashMap;
use std::future::Future;
use std::hash::Hash;
use std::sync::Arc;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
struct Entry<V> {
value: V,
inserted_at: Instant,
ttl: Duration,
}
impl<V> Entry<V> {
fn is_fresh(&self) -> bool {
self.inserted_at.elapsed() < self.ttl
}
}
pub struct RestCache<K, V>
where
K: Eq + Hash + Clone,
V: Clone + Send + Sync,
{
inner: Arc<DashMap<K, Entry<V>>>,
default_ttl: Duration,
}
impl<K, V> RestCache<K, V>
where
K: Eq + Hash + Clone,
V: Clone + Send + Sync,
{
pub fn new(default_ttl: Duration) -> Self {
Self {
inner: Arc::new(DashMap::new()),
default_ttl,
}
}
pub fn get(&self, key: &K) -> Option<V> {
let entry = self.inner.get(key)?;
if entry.is_fresh() {
Some(entry.value.clone())
} else {
None
}
}
pub fn insert(&self, key: K, value: V) {
self.insert_with_ttl(key, value, self.default_ttl);
}
pub fn insert_with_ttl(&self, key: K, value: V, ttl: Duration) {
self.inner.insert(
key,
Entry {
value,
inserted_at: Instant::now(),
ttl,
},
);
}
pub async fn get_or_fetch<F, Fut, E>(&self, key: K, loader: F) -> Result<V, E>
where
F: FnOnce() -> Fut,
Fut: Future<Output = Result<V, E>>,
{
if let Some(v) = self.get(&key) {
return Ok(v);
}
let value = loader().await?;
self.insert(key, value.clone());
Ok(value)
}
pub fn invalidate(&self, key: &K) {
self.inner.remove(key);
}
pub fn clear(&self) {
self.inner.clear();
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn sweep_expired(&self) -> usize {
let before = self.inner.len();
self.inner.retain(|_, entry| entry.is_fresh());
before - self.inner.len()
}
}
impl<K, V> Clone for RestCache<K, V>
where
K: Eq + Hash + Clone,
V: Clone + Send + Sync,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
default_ttl: self.default_ttl,
}
}
}