use async_trait::async_trait;
use crate::CacheResult;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheEntry<T> {
pub data: T,
pub created_at: DateTime<Utc>,
pub expires_at: Option<DateTime<Utc>>,
pub access_count: u64,
pub last_accessed: Option<DateTime<Utc>>,
}
impl<T> CacheEntry<T> {
pub fn new(data: T, ttl_seconds: Option<u64>) -> Self {
let now = Utc::now();
let expires_at = ttl_seconds.map(|ttl| now + chrono::Duration::seconds(ttl as i64));
Self {
data,
created_at: now,
expires_at,
access_count: 0,
last_accessed: None,
}
}
pub fn is_expired(&self) -> bool {
self.expires_at
.map(|expires_at| Utc::now() > expires_at)
.unwrap_or(false)
}
pub fn mark_accessed(&mut self) {
self.access_count += 1;
self.last_accessed = Some(Utc::now());
}
}
#[async_trait]
pub trait Cache: Send + Sync {
async fn set<T>(&self, key: &str, value: &T, ttl_seconds: Option<u64>) -> CacheResult<()>
where
T: Serialize + Send + Sync;
async fn get<T>(&self, key: &str) -> CacheResult<Option<T>>
where
T: for<'de> Deserialize<'de> + Send + Sync;
async fn delete(&self, key: &str) -> CacheResult<bool>;
async fn exists(&self, key: &str) -> CacheResult<bool>;
async fn expire(&self, key: &str, ttl_seconds: u64) -> CacheResult<bool>;
async fn ttl(&self, key: &str) -> CacheResult<Option<u64>>;
async fn clear(&self) -> CacheResult<()>;
async fn stats(&self) -> CacheResult<CacheStats>;
async fn mget<T>(&self, keys: Vec<String>) -> CacheResult<Vec<(String, Option<T>)>>
where
T: for<'de> Deserialize<'de> + Send + Sync;
async fn mset<T>(&self, entries: Vec<(String, T, Option<u64>)>) -> CacheResult<()>
where
T: Serialize + Send + Sync;
async fn mdelete(&self, keys: Vec<String>) -> CacheResult<u64>;
}
#[derive(Debug, Clone)]
pub struct CacheStats {
pub total_entries: u64,
pub expired_entries: u64,
pub memory_usage: Option<u64>,
pub hit_rate: f64,
pub hits: u64,
pub misses: u64,
pub sets: u64,
pub deletes: u64,
}
impl Default for CacheStats {
fn default() -> Self {
Self {
total_entries: 0,
expired_entries: 0,
memory_usage: None,
hit_rate: 0.0,
hits: 0,
misses: 0,
sets: 0,
deletes: 0,
}
}
}
impl CacheStats {
pub fn update_hit_rate(&mut self) {
let total_requests = self.hits + self.misses;
if total_requests == 0 {
return;
}
self.hit_rate = self.hits as f64 / total_requests as f64;
}
pub fn record_hit(&mut self) {
self.hits += 1;
self.update_hit_rate();
}
pub fn record_miss(&mut self) {
self.misses += 1;
self.update_hit_rate();
}
pub fn record_set(&mut self) {
self.sets += 1;
}
pub fn record_delete(&mut self) {
self.deletes += 1;
}
}