use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant};
use crate::dto::GrowthBookResponse;
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait FeatureCache: Send + Sync {
fn get(
&self,
key: &str,
) -> BoxFuture<'_, Option<GrowthBookResponse>>;
fn set(
&self,
key: &str,
value: GrowthBookResponse,
) -> BoxFuture<'_, ()>;
}
#[derive(Clone)]
pub struct InMemoryCache {
cache: Arc<RwLock<HashMap<String, (GrowthBookResponse, Instant)>>>,
ttl: Duration,
}
impl InMemoryCache {
pub fn new(ttl: Duration) -> Self {
Self {
cache: Arc::new(RwLock::new(HashMap::new())),
ttl,
}
}
}
impl FeatureCache for InMemoryCache {
fn get(
&self,
key: &str,
) -> BoxFuture<'_, Option<GrowthBookResponse>> {
let key = key.to_string();
let cache = self.cache.clone();
Box::pin(async move {
let cache = cache.read().unwrap();
if let Some((value, expires_at)) = cache.get(&key) {
if Instant::now() < *expires_at {
return Some(value.clone());
}
}
None
})
}
fn set(
&self,
key: &str,
value: GrowthBookResponse,
) -> BoxFuture<'_, ()> {
let key = key.to_string();
let cache = self.cache.clone();
let ttl = self.ttl;
Box::pin(async move {
let mut cache = cache.write().unwrap();
cache.insert(key, (value, Instant::now() + ttl));
})
}
}