use std::collections::HashMap;
use std::hash::Hash;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
struct CacheEntry<T> {
value: T,
created_at: Instant,
ttl: Duration,
}
impl<T> CacheEntry<T> {
fn new(value: T, ttl: Duration) -> Self {
Self {
value,
created_at: Instant::now(),
ttl,
}
}
fn is_expired(&self) -> bool {
self.created_at.elapsed() > self.ttl
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct CacheKey {
method: String,
url: String,
query: Option<String>,
body: Option<String>,
}
impl CacheKey {
fn new(method: &str, url: &str, query: Option<&str>, body: Option<&str>) -> Self {
Self {
method: method.to_string(),
url: url.to_string(),
query: query.map(|s| s.to_string()),
body: body.map(|s| s.to_string()),
}
}
}
pub struct ResponseCache<T: Clone + Send + Sync> {
cache: Mutex<HashMap<CacheKey, CacheEntry<T>>>,
default_ttl: Duration,
max_entries: usize,
}
impl<T: Clone + Send + Sync> ResponseCache<T> {
pub fn new(default_ttl: Duration, max_entries: usize) -> Self {
Self {
cache: Mutex::new(HashMap::new()),
default_ttl,
max_entries,
}
}
pub fn get(&self, method: &str, url: &str, query: Option<&str>, body: Option<&str>) -> Option<T> {
let key = CacheKey::new(method, url, query, body);
let mut cache = self.cache.lock().unwrap();
if let Some(entry) = cache.get(&key) {
if entry.is_expired() {
cache.remove(&key);
None
} else {
Some(entry.value.clone())
}
} else {
None
}
}
pub fn set(&self, method: &str, url: &str, query: Option<&str>, body: Option<&str>, value: T, ttl: Option<Duration>) {
let key = CacheKey::new(method, url, query, body);
let ttl = ttl.unwrap_or(self.default_ttl);
let entry = CacheEntry::new(value, ttl);
let mut cache = self.cache.lock().unwrap();
if cache.len() >= self.max_entries {
let expired_keys: Vec<_> = cache.iter()
.filter(|(_, entry)| entry.is_expired())
.map(|(key, _)| key.clone())
.collect();
for key in expired_keys {
cache.remove(&key);
}
if cache.len() >= self.max_entries {
let entries: Vec<_> = cache.iter().collect();
let mut sorted_entries: Vec<_> = entries.iter().collect();
sorted_entries.sort_by_key(|(_, entry)| entry.created_at);
let to_remove = entries.len() - self.max_entries + 1;
let keys_to_remove: Vec<_> = sorted_entries.iter().take(to_remove).map(|(k, _)| (*k).clone()).collect();
for key in keys_to_remove {
cache.remove(&key);
}
}
}
cache.insert(key, entry);
}
pub fn clear(&self) {
let mut cache = self.cache.lock().unwrap();
cache.clear();
}
pub fn cleanup(&self) {
let mut cache = self.cache.lock().unwrap();
let expired_keys: Vec<_> = cache.iter()
.filter(|(_, entry)| entry.is_expired())
.map(|(key, _)| key.clone())
.collect();
for key in expired_keys {
cache.remove(&key);
}
}
}
pub struct CacheManager {
caches: Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>,
}
impl CacheManager {
pub fn new() -> Self {
Self {
caches: Mutex::new(HashMap::new()),
}
}
pub fn get_cache<T: Clone + Send + Sync + 'static>(&self, name: &str) -> Arc<ResponseCache<T>> {
let mut caches = self.caches.lock().unwrap();
if let Some(cache) = caches.get(name) {
if let Some(typed_cache) = cache.clone().downcast_arc::<ResponseCache<T>>().ok() {
return typed_cache;
}
}
let cache = Arc::new(ResponseCache::<T> {
cache: Mutex::new(HashMap::new()),
default_ttl: Duration::from_secs(60),
max_entries: 1000,
});
caches.insert(name.to_string(), cache.clone() as Arc<dyn Any + Send + Sync>);
cache
}
pub fn clear_all(&self) {
let mut caches = self.caches.lock().unwrap();
caches.clear();
}
}
use std::any::{Any, TypeId};
trait ArcAnyExt {
fn downcast_arc<T: 'static>(self) -> Result<Arc<T>, Self> where Self: Sized;
}
impl ArcAnyExt for Arc<dyn Any + Send + Sync> {
fn downcast_arc<T: 'static>(self) -> Result<Arc<T>, Self> {
if (*self).type_id() == TypeId::of::<T>() {
let ptr = Arc::into_raw(self) as *const T;
Ok(unsafe { Arc::from_raw(ptr) })
} else {
Err(self)
}
}
}