use std::time::{Duration, Instant};
use tokio::sync::Mutex as AsyncMutex;
pub struct TtlCache<T> {
ttl: Duration,
slot: AsyncMutex<Option<(Instant, T)>>,
}
impl<T: Clone> TtlCache<T> {
pub fn new(ttl: Duration) -> Self {
Self {
ttl,
slot: AsyncMutex::new(None),
}
}
pub async fn get_or_refresh<F, Fut, E>(&self, refresh: F) -> Result<T, E>
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = Result<T, E>>,
{
let mut slot = self.slot.lock().await;
if let Some((at, v)) = slot.as_ref()
&& at.elapsed() < self.ttl
{
return Ok(v.clone());
}
let v = refresh().await?;
*slot = Some((Instant::now(), v.clone()));
Ok(v)
}
pub async fn invalidate(&self) {
*self.slot.lock().await = None;
}
}