// SPDX-License-Identifier: AGPL-3.0-only pub mod font; pub mod user; use std::future::Future; use std::time::*; use std::collections::HashMap; use std::hash::Hash; use std::sync::Arc; use std::pin::Pin; use tokio::sync::RwLock; use reqwest::StatusCode; #[derive(Debug)] pub struct AsyncCache { func: F, cache: HashMap, interval: Duration } impl AsyncCache where for<'a> F: FnMut(&'a K) -> Fut + 'a, K: Hash + PartialEq + Eq + Clone, Fut: Future> + Send + Sync { pub fn new(interval: Duration, func: F) -> Self { Self{ cache: HashMap::new(), interval, func } } pub async fn get(&mut self, key: &K) -> Result<&V, (StatusCode, &'static str)> { if self.is_stale(key) { log::trace!("MISS : interval = {:?}", self.interval); self.renew(key).await } else { log::trace!("HIT : interval = {:?}", self.interval); Ok(&self.cache.get(key).unwrap().1) } } pub async fn renew(&mut self, key: &K) -> Result<&V, (StatusCode, &'static str)> { let val = (self.func)(key).await?; self.cache.insert(key.clone(), (Instant::now(), val)); Ok(&self.cache.get(key).unwrap().1) } pub fn is_stale(&self, key: &K) -> bool { if let Some((last_update, _)) = self.cache.get(key) { let now = Instant::now(); log::trace!("Key exists, last update {:?} ago.", now - *last_update); now > (*last_update + self.interval) } else { true } } pub async fn get_opt(&self, key: &K) -> Option<&V> { if self.is_stale(key) { self.cache.get(key).map(|(_, v)| v) } else { None } } pub fn interval(&self) -> Duration { self.interval } } impl AsyncCache where for<'a> F: FnMut(&'a K) -> Fut + 'a, K: Hash + PartialEq + Eq + Clone, V: Clone, Fut: Future> + Send + Sync { pub async fn get_owned(&mut self, key: &K) -> Result { self.get(key).await.cloned() } } pub type CacheFuture = Pin> + Send + Sync)>>; pub type CacheGetter = fn(&String) -> CacheFuture; pub type Cache = Arc>>>;