1use async_trait::async_trait;
4use crate::CacheResult;
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct CacheEntry<T> {
11 pub data: T,
13
14 pub created_at: DateTime<Utc>,
16
17 pub expires_at: Option<DateTime<Utc>>,
19
20 pub access_count: u64,
22
23 pub last_accessed: Option<DateTime<Utc>>,
25}
26
27impl<T> CacheEntry<T> {
28 pub fn new(data: T, ttl_seconds: Option<u64>) -> Self {
30 let now = Utc::now();
31 let expires_at = ttl_seconds.map(|ttl| now + chrono::Duration::seconds(ttl as i64));
32
33 Self {
34 data,
35 created_at: now,
36 expires_at,
37 access_count: 0,
38 last_accessed: None,
39 }
40 }
41
42 pub fn is_expired(&self) -> bool {
44 self.expires_at
45 .map(|expires_at| Utc::now() > expires_at)
46 .unwrap_or(false)
47 }
48
49 pub fn mark_accessed(&mut self) {
51 self.access_count += 1;
52 self.last_accessed = Some(Utc::now());
53 }
54}
55
56#[async_trait]
58pub trait Cache: Send + Sync {
59 async fn set<T>(&self, key: &str, value: &T, ttl_seconds: Option<u64>) -> CacheResult<()>
61 where
62 T: Serialize + Send + Sync;
63
64 async fn get<T>(&self, key: &str) -> CacheResult<Option<T>>
66 where
67 T: for<'de> Deserialize<'de> + Send + Sync;
68
69 async fn delete(&self, key: &str) -> CacheResult<bool>;
71
72 async fn exists(&self, key: &str) -> CacheResult<bool>;
74
75 async fn expire(&self, key: &str, ttl_seconds: u64) -> CacheResult<bool>;
77
78 async fn ttl(&self, key: &str) -> CacheResult<Option<u64>>;
80
81 async fn clear(&self) -> CacheResult<()>;
83
84 async fn stats(&self) -> CacheResult<CacheStats>;
86
87 async fn mget<T>(&self, keys: Vec<String>) -> CacheResult<Vec<(String, Option<T>)>>
89 where
90 T: for<'de> Deserialize<'de> + Send + Sync;
91
92 async fn mset<T>(&self, entries: Vec<(String, T, Option<u64>)>) -> CacheResult<()>
94 where
95 T: Serialize + Send + Sync;
96
97 async fn mdelete(&self, keys: Vec<String>) -> CacheResult<u64>;
99}
100
101#[derive(Debug, Clone)]
103pub struct CacheStats {
104 pub total_entries: u64,
106
107 pub expired_entries: u64,
109
110 pub memory_usage: Option<u64>,
112
113 pub hit_rate: f64,
115
116 pub hits: u64,
118
119 pub misses: u64,
121
122 pub sets: u64,
124
125 pub deletes: u64,
127}
128
129impl Default for CacheStats {
130 fn default() -> Self {
131 Self {
132 total_entries: 0,
133 expired_entries: 0,
134 memory_usage: None,
135 hit_rate: 0.0,
136 hits: 0,
137 misses: 0,
138 sets: 0,
139 deletes: 0,
140 }
141 }
142}
143
144impl CacheStats {
145 pub fn update_hit_rate(&mut self) {
147 let total_requests = self.hits + self.misses;
148 if total_requests == 0 {
149 return;
150 }
151 self.hit_rate = self.hits as f64 / total_requests as f64;
152 }
153
154 pub fn record_hit(&mut self) {
156 self.hits += 1;
157 self.update_hit_rate();
158 }
159
160 pub fn record_miss(&mut self) {
162 self.misses += 1;
163 self.update_hit_rate();
164 }
165
166 pub fn record_set(&mut self) {
168 self.sets += 1;
169 }
170
171 pub fn record_delete(&mut self) {
173 self.deletes += 1;
174 }
175}