1use serde::{Deserialize, Serialize};
6use std::time::Duration;
7
8#[allow(async_fn_in_trait)]
12pub trait Cache<K, V>: Send + Sync
13where
14 K: Send + Sync,
15 V: Send + Sync,
16{
17 type Error: std::error::Error + Send + Sync + 'static;
19
20 async fn get(&self, key: &K) -> Result<Option<V>, Self::Error>;
22
23 async fn set(&self, key: &K, value: V, ttl: Duration) -> Result<(), Self::Error>;
25
26 async fn delete(&self, key: &K) -> Result<bool, Self::Error>;
28
29 async fn exists(&self, key: &K) -> Result<bool, Self::Error>;
31
32 async fn expire(&self, key: &K, ttl: Duration) -> Result<bool, Self::Error>;
34
35 async fn ttl(&self, key: &K) -> Result<Option<Duration>, Self::Error>;
37
38 async fn clear(&self) -> Result<(), Self::Error>;
40
41 async fn size(&self) -> Result<usize, Self::Error>;
43
44 async fn get_many(&self, keys: &[K]) -> Result<Vec<Option<V>>, Self::Error> {
46 let mut results = Vec::with_capacity(keys.len());
47 for key in keys {
48 results.push(self.get(key).await?);
49 }
50 Ok(results)
51 }
52
53 async fn set_many(&self, items: &[(K, V, Duration)]) -> Result<(), Self::Error>
55 where
56 K: Clone,
57 V: Clone,
58 {
59 for (key, value, ttl) in items {
60 self.set(key, value.clone(), *ttl).await?;
61 }
62 Ok(())
63 }
64}
65
66pub trait CacheKey: Send + Sync + Clone + std::fmt::Debug + std::hash::Hash + Eq {
70 fn to_cache_key(&self) -> String;
72
73 fn from_cache_key(s: &str) -> Result<Self, CacheError>
75 where
76 Self: Sized;
77}
78
79pub trait CacheValue: Send + Sync + Clone + std::fmt::Debug {
83 fn to_bytes(&self) -> Result<Vec<u8>, CacheError>;
85
86 fn from_bytes(bytes: &[u8]) -> Result<Self, CacheError>
88 where
89 Self: Sized;
90}
91
92impl CacheKey for String {
94 fn to_cache_key(&self) -> String {
95 self.clone()
96 }
97
98 fn from_cache_key(s: &str) -> Result<Self, CacheError> {
99 Ok(s.to_string())
100 }
101}
102
103impl<T> CacheValue for T
105where
106 T: Serialize + for<'de> Deserialize<'de> + Send + Sync + Clone + std::fmt::Debug,
107{
108 fn to_bytes(&self) -> Result<Vec<u8>, CacheError> {
109 rmp_serde::to_vec(self).map_err(CacheError::Serialization)
110 }
111
112 fn from_bytes(bytes: &[u8]) -> Result<Self, CacheError> {
113 rmp_serde::from_slice(bytes).map_err(CacheError::Deserialization)
114 }
115}
116
117#[derive(Debug, Clone)]
119pub struct CacheStats {
120 pub hits: u64,
122 pub misses: u64,
124 pub key_count: usize,
126 pub memory_usage: usize,
128 pub hit_rate: f64,
130}
131
132impl CacheStats {
133 pub fn new() -> Self {
134 Self {
135 hits: 0,
136 misses: 0,
137 key_count: 0,
138 memory_usage: 0,
139 hit_rate: 0.0,
140 }
141 }
142
143 pub fn calculate_hit_rate(&mut self) {
144 let total = self.hits + self.misses;
145 if total > 0 {
146 self.hit_rate = self.hits as f64 / total as f64;
147 }
148 }
149}
150
151impl Default for CacheStats {
152 fn default() -> Self {
153 Self::new()
154 }
155}
156
157#[allow(async_fn_in_trait)]
159pub trait CacheWithStats<K, V>: Cache<K, V>
160where
161 K: Send + Sync,
162 V: Send + Sync,
163{
164 async fn stats(&self) -> Result<CacheStats, Self::Error>;
166
167 async fn reset_stats(&self) -> Result<(), Self::Error>;
169}
170
171#[derive(Debug, Clone)]
173pub enum CacheEvent<K, V> {
174 Hit { key: K },
176 Miss { key: K },
178 Set { key: K, value: V },
180 Delete { key: K },
182 Expire { key: K },
184 Clear,
186}
187
188#[allow(async_fn_in_trait)]
190pub trait CacheEventListener<K, V>: Send + Sync
191where
192 K: Send + Sync,
193 V: Send + Sync,
194{
195 async fn on_event(&self, event: CacheEvent<K, V>);
197}
198
199#[derive(Debug, thiserror::Error)]
201pub enum CacheError {
202 #[error("Connection failed: {0}")]
203 Connection(String),
204
205 #[error("Serialization failed: {0}")]
206 Serialization(#[from] rmp_serde::encode::Error),
207
208 #[error("Deserialization failed: {0}")]
209 Deserialization(rmp_serde::decode::Error),
210
211 #[error("Key not found: {key}")]
212 KeyNotFound { key: String },
213
214 #[error("Cache is full")]
215 CacheFull,
216
217 #[error("Invalid TTL: {ttl_ms}ms")]
218 InvalidTTL { ttl_ms: u64 },
219
220 #[error("Cache operation timeout")]
221 Timeout,
222
223 #[error("Cache backend error: {0}")]
224 Backend(String),
225
226 #[error("Other cache error: {0}")]
227 Other(String),
228}
229
230impl CacheError {
231 pub fn connection(msg: impl Into<String>) -> Self {
232 Self::Connection(msg.into())
233 }
234
235 pub fn key_not_found(key: impl Into<String>) -> Self {
236 Self::KeyNotFound { key: key.into() }
237 }
238
239 pub fn backend(msg: impl Into<String>) -> Self {
240 Self::Backend(msg.into())
241 }
242
243 pub fn other(msg: impl Into<String>) -> Self {
244 Self::Other(msg.into())
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
255 fn test_string_cache_key_to_cache_key() {
256 let key = "my-cache-key".to_string();
257 assert_eq!(key.to_cache_key(), "my-cache-key");
258 }
259
260 #[test]
261 fn test_string_cache_key_from_cache_key() {
262 let key = String::from_cache_key("restored-key").unwrap();
263 assert_eq!(key, "restored-key");
264 }
265
266 #[test]
267 fn test_string_cache_key_roundtrip() {
268 let original = "test-key-123".to_string();
269 let serialized = original.to_cache_key();
270 let restored = String::from_cache_key(&serialized).unwrap();
271 assert_eq!(original, restored);
272 }
273
274 #[test]
277 fn test_cache_value_to_bytes_string() {
278 let value = "hello world".to_string();
279 let bytes = value.to_bytes();
280 assert!(bytes.is_ok());
281 assert!(!bytes.unwrap().is_empty());
282 }
283
284 #[test]
285 fn test_cache_value_from_bytes_string() {
286 let value = "test value".to_string();
287 let bytes = value.to_bytes().unwrap();
288 let restored = String::from_bytes(&bytes).unwrap();
289 assert_eq!(value, restored);
290 }
291
292 #[test]
293 fn test_cache_value_roundtrip_integer() {
294 let value: i32 = 42;
295 let bytes = value.to_bytes().unwrap();
296 let restored = i32::from_bytes(&bytes).unwrap();
297 assert_eq!(value, restored);
298 }
299
300 #[test]
301 fn test_cache_value_roundtrip_complex() {
302 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
303 struct TestData {
304 id: u64,
305 name: String,
306 active: bool,
307 }
308
309 let value = TestData {
310 id: 123,
311 name: "test".to_string(),
312 active: true,
313 };
314 let bytes = value.to_bytes().unwrap();
315 let restored = TestData::from_bytes(&bytes).unwrap();
316 assert_eq!(value, restored);
317 }
318
319 #[test]
322 fn test_cache_stats_new() {
323 let stats = CacheStats::new();
324 assert_eq!(stats.hits, 0);
325 assert_eq!(stats.misses, 0);
326 assert_eq!(stats.key_count, 0);
327 assert_eq!(stats.memory_usage, 0);
328 assert!((stats.hit_rate - 0.0).abs() < f64::EPSILON);
329 }
330
331 #[test]
332 fn test_cache_stats_default() {
333 let stats = CacheStats::default();
334 assert_eq!(stats.hits, 0);
335 assert_eq!(stats.misses, 0);
336 }
337
338 #[test]
339 fn test_cache_stats_calculate_hit_rate_zero_total() {
340 let mut stats = CacheStats::new();
341 stats.calculate_hit_rate();
342 assert!((stats.hit_rate - 0.0).abs() < f64::EPSILON);
343 }
344
345 #[test]
346 fn test_cache_stats_calculate_hit_rate_all_hits() {
347 let mut stats = CacheStats::new();
348 stats.hits = 100;
349 stats.misses = 0;
350 stats.calculate_hit_rate();
351 assert!((stats.hit_rate - 1.0).abs() < f64::EPSILON);
352 }
353
354 #[test]
355 fn test_cache_stats_calculate_hit_rate_all_misses() {
356 let mut stats = CacheStats::new();
357 stats.hits = 0;
358 stats.misses = 100;
359 stats.calculate_hit_rate();
360 assert!((stats.hit_rate - 0.0).abs() < f64::EPSILON);
361 }
362
363 #[test]
364 fn test_cache_stats_calculate_hit_rate_mixed() {
365 let mut stats = CacheStats::new();
366 stats.hits = 75;
367 stats.misses = 25;
368 stats.calculate_hit_rate();
369 assert!((stats.hit_rate - 0.75).abs() < 0.001);
370 }
371
372 #[test]
373 fn test_cache_stats_clone() {
374 let mut stats = CacheStats::new();
375 stats.hits = 10;
376 stats.key_count = 5;
377 let cloned = stats.clone();
378 assert_eq!(stats.hits, cloned.hits);
379 assert_eq!(stats.key_count, cloned.key_count);
380 }
381
382 #[test]
383 fn test_cache_stats_debug() {
384 let stats = CacheStats::new();
385 let debug = format!("{:?}", stats);
386 assert!(debug.contains("CacheStats"));
387 }
388
389 #[test]
392 fn test_cache_event_hit() {
393 let event: CacheEvent<String, i32> = CacheEvent::Hit {
394 key: "key1".to_string(),
395 };
396 assert!(matches!(event, CacheEvent::Hit { key } if key == "key1"));
397 }
398
399 #[test]
400 fn test_cache_event_miss() {
401 let event: CacheEvent<String, i32> = CacheEvent::Miss {
402 key: "key2".to_string(),
403 };
404 assert!(matches!(event, CacheEvent::Miss { key } if key == "key2"));
405 }
406
407 #[test]
408 fn test_cache_event_set() {
409 let event = CacheEvent::Set {
410 key: "key3".to_string(),
411 value: 42,
412 };
413 assert!(matches!(event, CacheEvent::Set { key, value } if key == "key3" && value == 42));
414 }
415
416 #[test]
417 fn test_cache_event_delete() {
418 let event: CacheEvent<String, i32> = CacheEvent::Delete {
419 key: "key4".to_string(),
420 };
421 assert!(matches!(event, CacheEvent::Delete { key } if key == "key4"));
422 }
423
424 #[test]
425 fn test_cache_event_expire() {
426 let event: CacheEvent<String, i32> = CacheEvent::Expire {
427 key: "key5".to_string(),
428 };
429 assert!(matches!(event, CacheEvent::Expire { key } if key == "key5"));
430 }
431
432 #[test]
433 fn test_cache_event_clear() {
434 let event: CacheEvent<String, i32> = CacheEvent::Clear;
435 assert!(matches!(event, CacheEvent::Clear));
436 }
437
438 #[test]
439 fn test_cache_event_clone() {
440 let event = CacheEvent::Set {
441 key: "key".to_string(),
442 value: 100,
443 };
444 let cloned = event.clone();
445 assert!(matches!(cloned, CacheEvent::Set { key, value } if key == "key" && value == 100));
446 }
447
448 #[test]
451 fn test_cache_error_connection() {
452 let err = CacheError::connection("Redis connection failed");
453 assert!(matches!(err, CacheError::Connection(_)));
454 assert!(err.to_string().contains("Connection failed"));
455 }
456
457 #[test]
458 fn test_cache_error_key_not_found() {
459 let err = CacheError::key_not_found("missing-key");
460 assert!(matches!(err, CacheError::KeyNotFound { .. }));
461 assert!(err.to_string().contains("Key not found"));
462 assert!(err.to_string().contains("missing-key"));
463 }
464
465 #[test]
466 fn test_cache_error_cache_full() {
467 let err = CacheError::CacheFull;
468 assert!(err.to_string().contains("Cache is full"));
469 }
470
471 #[test]
472 fn test_cache_error_invalid_ttl() {
473 let err = CacheError::InvalidTTL { ttl_ms: 0 };
474 assert!(err.to_string().contains("Invalid TTL"));
475 }
476
477 #[test]
478 fn test_cache_error_timeout() {
479 let err = CacheError::Timeout;
480 assert!(err.to_string().contains("timeout"));
481 }
482
483 #[test]
484 fn test_cache_error_backend() {
485 let err = CacheError::backend("Backend failure");
486 assert!(matches!(err, CacheError::Backend(_)));
487 assert!(err.to_string().contains("Backend"));
488 }
489
490 #[test]
491 fn test_cache_error_other() {
492 let err = CacheError::other("Some other error");
493 assert!(matches!(err, CacheError::Other(_)));
494 }
495
496 #[test]
497 fn test_cache_error_display() {
498 let err = CacheError::connection("test error");
499 let display = format!("{}", err);
500 assert!(!display.is_empty());
501 }
502
503 #[test]
504 fn test_cache_error_debug() {
505 let err = CacheError::CacheFull;
506 let debug = format!("{:?}", err);
507 assert!(debug.contains("CacheFull"));
508 }
509}