1#![warn(missing_docs)]
9
10use serde::{Serialize, de::DeserializeOwned};
11use std::time::Duration;
12use wae_types::WaeError;
13
14pub type CacheResult<T> = Result<T, WaeError>;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum EvictionPolicy {
20 None,
22 Lru,
24 Lfu,
26}
27
28impl Default for EvictionPolicy {
29 fn default() -> Self {
30 Self::None
31 }
32}
33
34#[derive(Debug, Clone)]
36pub struct CacheConfig {
37 pub key_prefix: String,
39 pub default_ttl: Option<Duration>,
41 pub connection_timeout: Duration,
43 pub operation_timeout: Duration,
45 pub max_capacity: Option<usize>,
47 pub eviction_policy: EvictionPolicy,
49}
50
51impl Default for CacheConfig {
52 fn default() -> Self {
53 Self {
54 key_prefix: String::new(),
55 default_ttl: Some(Duration::from_secs(3600)),
56 connection_timeout: Duration::from_secs(5),
57 operation_timeout: Duration::from_secs(3),
58 max_capacity: None,
59 eviction_policy: EvictionPolicy::default(),
60 }
61 }
62}
63
64#[async_trait::async_trait]
69pub trait CacheBackend: Send + Sync {
70 async fn get_bytes(&self, key: &str) -> CacheResult<Option<Vec<u8>>>;
72
73 async fn set_bytes(&self, key: &str, value: &[u8], ttl: Option<Duration>) -> CacheResult<()>;
75
76 async fn delete(&self, key: &str) -> CacheResult<bool>;
78
79 async fn exists(&self, key: &str) -> CacheResult<bool>;
81
82 async fn expire(&self, key: &str, ttl: Duration) -> CacheResult<bool>;
84
85 async fn ttl(&self, key: &str) -> CacheResult<Option<Duration>>;
87
88 async fn mget_bytes(&self, keys: &[&str]) -> CacheResult<Vec<Option<Vec<u8>>>>;
90
91 async fn mset_bytes(&self, items: &[(&str, &[u8])], ttl: Option<Duration>) -> CacheResult<()>;
93
94 async fn mdelete(&self, keys: &[&str]) -> CacheResult<u64>;
96
97 async fn incr(&self, key: &str, delta: i64) -> CacheResult<i64>;
99
100 async fn decr(&self, key: &str, delta: i64) -> CacheResult<i64>;
102
103 async fn clear(&self) -> CacheResult<()>;
105
106 fn config(&self) -> &CacheConfig;
108}
109
110pub struct CacheService {
112 backend: Box<dyn CacheBackend>,
113}
114
115impl CacheService {
116 pub fn new(backend: Box<dyn CacheBackend>) -> Self {
118 Self { backend }
119 }
120
121 pub async fn get<T: DeserializeOwned>(&self, key: &str) -> CacheResult<Option<T>> {
123 let bytes = self.backend.get_bytes(key).await?;
124 match bytes {
125 Some(data) => {
126 let value =
127 serde_json::from_slice(&data).map_err(|_| WaeError::deserialization_failed(std::any::type_name::<T>()))?;
128 Ok(Some(value))
129 }
130 None => Ok(None),
131 }
132 }
133
134 pub async fn set<T: Serialize + ?Sized>(&self, key: &str, value: &T, ttl: Option<Duration>) -> CacheResult<()> {
136 let data = serde_json::to_vec(value).map_err(|_| WaeError::serialization_failed(std::any::type_name::<T>()))?;
137 self.backend.set_bytes(key, &data, ttl).await
138 }
139
140 pub async fn delete(&self, key: &str) -> CacheResult<bool> {
142 self.backend.delete(key).await
143 }
144
145 pub async fn exists(&self, key: &str) -> CacheResult<bool> {
147 self.backend.exists(key).await
148 }
149
150 pub async fn expire(&self, key: &str, ttl: Duration) -> CacheResult<bool> {
152 self.backend.expire(key, ttl).await
153 }
154
155 pub async fn ttl(&self, key: &str) -> CacheResult<Option<Duration>> {
157 self.backend.ttl(key).await
158 }
159
160 pub async fn mget<T: DeserializeOwned>(&self, keys: &[&str]) -> CacheResult<Vec<Option<T>>> {
162 let bytes_list = self.backend.mget_bytes(keys).await?;
163 let mut results = Vec::with_capacity(bytes_list.len());
164 for bytes in bytes_list {
165 match bytes {
166 Some(data) => {
167 let value = serde_json::from_slice(&data)
168 .map_err(|_| WaeError::deserialization_failed(std::any::type_name::<T>()))?;
169 results.push(Some(value));
170 }
171 None => results.push(None),
172 }
173 }
174 Ok(results)
175 }
176
177 pub async fn mset<T: Serialize + ?Sized>(&self, items: &[(&str, &T)], ttl: Option<Duration>) -> CacheResult<()> {
179 let byte_items: Vec<(&str, Vec<u8>)> = items
180 .iter()
181 .map(|(k, v)| {
182 let data = serde_json::to_vec(v).map_err(|_| WaeError::serialization_failed(std::any::type_name::<T>()))?;
183 Ok((*k, data))
184 })
185 .collect::<CacheResult<_>>()?;
186
187 let refs: Vec<(&str, &[u8])> = byte_items.iter().map(|(k, v)| (*k, v.as_slice())).collect();
188 self.backend.mset_bytes(&refs, ttl).await
189 }
190
191 pub async fn mdelete(&self, keys: &[&str]) -> CacheResult<u64> {
193 self.backend.mdelete(keys).await
194 }
195
196 pub async fn incr(&self, key: &str, delta: i64) -> CacheResult<i64> {
198 self.backend.incr(key, delta).await
199 }
200
201 pub async fn decr(&self, key: &str, delta: i64) -> CacheResult<i64> {
203 self.backend.decr(key, delta).await
204 }
205
206 pub async fn clear(&self) -> CacheResult<()> {
208 self.backend.clear().await
209 }
210
211 pub fn config(&self) -> &CacheConfig {
213 self.backend.config()
214 }
215
216 pub fn build_key(&self, key: &str) -> String {
218 let config = self.config();
219 if config.key_prefix.is_empty() { key.to_string() } else { format!("{}:{}", config.key_prefix, key) }
220 }
221}
222
223#[cfg(feature = "redis")]
225pub mod redis;
226
227pub mod memory {
229 use super::*;
230 use std::{collections::HashMap, sync::Arc};
231 use tokio::{sync::RwLock, time::Instant};
232
233 #[derive(Debug, Clone)]
235 struct CacheEntry {
236 data: Vec<u8>,
237 expires_at: Option<Instant>,
238 }
239
240 impl CacheEntry {
241 fn is_expired(&self) -> bool {
242 self.expires_at.map(|exp| Instant::now() >= exp).unwrap_or(false)
243 }
244 }
245
246 struct UnboundedStore {
248 map: HashMap<String, CacheEntry>,
249 }
250
251 impl UnboundedStore {
252 fn new() -> Self {
253 Self { map: HashMap::new() }
254 }
255 }
256
257 struct LruStore {
259 map: HashMap<String, CacheEntry>,
260 order: Vec<String>,
261 max_capacity: Option<usize>,
262 }
263
264 impl LruStore {
265 fn new(max_capacity: Option<usize>) -> Self {
266 Self { map: HashMap::new(), order: Vec::new(), max_capacity }
267 }
268
269 fn touch(&mut self, key: &str) {
270 if let Some(pos) = self.order.iter().position(|k| k == key) {
271 self.order.remove(pos);
272 self.order.push(key.to_string());
273 }
274 }
275
276 fn evict_if_needed(&mut self) {
277 if let Some(max_cap) = self.max_capacity {
278 while self.map.len() > max_cap {
279 if let Some(key) = self.order.first().cloned() {
280 self.map.remove(&key);
281 self.order.remove(0);
282 }
283 else {
284 break;
285 }
286 }
287 }
288 }
289 }
290
291 struct LfuStore {
293 map: HashMap<String, CacheEntry>,
294 frequencies: HashMap<String, u64>,
295 max_capacity: Option<usize>,
296 }
297
298 impl LfuStore {
299 fn new(max_capacity: Option<usize>) -> Self {
300 Self { map: HashMap::new(), frequencies: HashMap::new(), max_capacity }
301 }
302
303 fn increment_frequency(&mut self, key: &str) {
304 *self.frequencies.entry(key.to_string()).or_insert(0) += 1;
305 }
306
307 fn evict_if_needed(&mut self) {
308 if let Some(max_cap) = self.max_capacity {
309 while self.map.len() > max_cap {
310 if let Some(key_to_evict) = self.find_least_frequent_key() {
311 self.map.remove(&key_to_evict);
312 self.frequencies.remove(&key_to_evict);
313 }
314 else {
315 break;
316 }
317 }
318 }
319 }
320
321 fn find_least_frequent_key(&self) -> Option<String> {
322 let mut min_freq = u64::MAX;
323 let mut min_key = None;
324 for (key, &freq) in &self.frequencies {
325 if freq < min_freq {
326 min_freq = freq;
327 min_key = Some(key.clone());
328 }
329 }
330 min_key
331 }
332 }
333
334 enum CacheStore {
336 Unbounded(UnboundedStore),
338 Lru(LruStore),
340 Lfu(LfuStore),
342 }
343
344 impl CacheStore {
345 fn get(&mut self, key: &str) -> Option<CacheEntry> {
346 match self {
347 CacheStore::Unbounded(store) => store.map.get(key).cloned(),
348 CacheStore::Lru(store) => {
349 let has_key = store.map.contains_key(key);
350 if has_key {
351 store.touch(key);
352 store.map.get(key).cloned()
353 }
354 else {
355 None
356 }
357 }
358 CacheStore::Lfu(store) => {
359 let has_key = store.map.contains_key(key);
360 if has_key {
361 store.increment_frequency(key);
362 store.map.get(key).cloned()
363 }
364 else {
365 None
366 }
367 }
368 }
369 }
370
371 fn insert(&mut self, key: String, entry: CacheEntry) {
372 match self {
373 CacheStore::Unbounded(store) => {
374 store.map.insert(key, entry);
375 }
376 CacheStore::Lru(store) => {
377 if store.map.contains_key(&key) {
378 store.touch(&key);
379 }
380 else {
381 store.order.push(key.clone());
382 }
383 store.map.insert(key, entry);
384 store.evict_if_needed();
385 }
386 CacheStore::Lfu(store) => {
387 if store.map.contains_key(&key) {
388 store.increment_frequency(&key);
389 }
390 else {
391 *store.frequencies.entry(key.clone()).or_insert(0) = 1;
392 }
393 store.map.insert(key, entry);
394 store.evict_if_needed();
395 }
396 }
397 }
398
399 fn remove(&mut self, key: &str) -> Option<CacheEntry> {
400 match self {
401 CacheStore::Unbounded(store) => store.map.remove(key),
402 CacheStore::Lru(store) => {
403 if let Some(entry) = store.map.remove(key) {
404 if let Some(pos) = store.order.iter().position(|k| k == key) {
405 store.order.remove(pos);
406 }
407 Some(entry)
408 }
409 else {
410 None
411 }
412 }
413 CacheStore::Lfu(store) => {
414 if let Some(entry) = store.map.remove(key) {
415 store.frequencies.remove(key);
416 Some(entry)
417 }
418 else {
419 None
420 }
421 }
422 }
423 }
424
425 fn contains_key(&self, key: &str) -> bool {
426 match self {
427 CacheStore::Unbounded(store) => store.map.contains_key(key),
428 CacheStore::Lru(store) => store.map.contains_key(key),
429 CacheStore::Lfu(store) => store.map.contains_key(key),
430 }
431 }
432
433 fn get_mut(&mut self, key: &str) -> Option<&mut CacheEntry> {
434 match self {
435 CacheStore::Unbounded(store) => store.map.get_mut(key),
436 CacheStore::Lru(store) => {
437 if store.map.contains_key(key) {
438 store.touch(key);
439 store.map.get_mut(key)
440 }
441 else {
442 None
443 }
444 }
445 CacheStore::Lfu(store) => {
446 if store.map.contains_key(key) {
447 store.increment_frequency(key);
448 store.map.get_mut(key)
449 }
450 else {
451 None
452 }
453 }
454 }
455 }
456
457 fn clear(&mut self) {
458 match self {
459 CacheStore::Unbounded(store) => store.map.clear(),
460 CacheStore::Lru(store) => {
461 store.map.clear();
462 store.order.clear();
463 }
464 CacheStore::Lfu(store) => {
465 store.map.clear();
466 store.frequencies.clear();
467 }
468 }
469 }
470
471 fn retain<F>(&mut self, mut f: F)
472 where
473 F: FnMut(&String, &mut CacheEntry) -> bool,
474 {
475 match self {
476 CacheStore::Unbounded(store) => store.map.retain(|k, v| f(k, v)),
477 CacheStore::Lru(store) => {
478 let mut keys_to_remove = Vec::new();
479 for (key, entry) in &mut store.map {
480 if !f(key, entry) {
481 keys_to_remove.push(key.clone());
482 }
483 }
484 for key in keys_to_remove {
485 store.map.remove(&key);
486 if let Some(pos) = store.order.iter().position(|k| k == &key) {
487 store.order.remove(pos);
488 }
489 }
490 }
491 CacheStore::Lfu(store) => {
492 let mut keys_to_remove = Vec::new();
493 for (key, entry) in &mut store.map {
494 if !f(key, entry) {
495 keys_to_remove.push(key.clone());
496 }
497 }
498 for key in keys_to_remove {
499 store.map.remove(&key);
500 store.frequencies.remove(&key);
501 }
502 }
503 }
504 }
505
506 fn len(&self) -> usize {
507 match self {
508 CacheStore::Unbounded(store) => store.map.len(),
509 CacheStore::Lru(store) => store.map.len(),
510 CacheStore::Lfu(store) => store.map.len(),
511 }
512 }
513 }
514
515 pub struct MemoryCacheBackend {
517 config: CacheConfig,
518 store: Arc<RwLock<CacheStore>>,
519 }
520
521 impl MemoryCacheBackend {
522 pub fn new(config: CacheConfig) -> Self {
524 let store = match config.eviction_policy {
525 EvictionPolicy::None => CacheStore::Unbounded(UnboundedStore::new()),
526 EvictionPolicy::Lru => CacheStore::Lru(LruStore::new(config.max_capacity)),
527 EvictionPolicy::Lfu => CacheStore::Lfu(LfuStore::new(config.max_capacity)),
528 };
529 Self { config, store: Arc::new(RwLock::new(store)) }
530 }
531
532 fn build_key(&self, key: &str) -> String {
533 if self.config.key_prefix.is_empty() { key.to_string() } else { format!("{}:{}", self.config.key_prefix, key) }
534 }
535 }
536
537 #[async_trait::async_trait]
538 impl CacheBackend for MemoryCacheBackend {
539 async fn get_bytes(&self, key: &str) -> CacheResult<Option<Vec<u8>>> {
540 let full_key = self.build_key(key);
541 let mut store = self.store.write().await;
542
543 if let Some(entry) = store.get(&full_key) {
544 if entry.is_expired() {
545 store.remove(&full_key);
546 return Ok(None);
547 }
548 return Ok(Some(entry.data.clone()));
549 }
550 Ok(None)
551 }
552
553 async fn set_bytes(&self, key: &str, value: &[u8], ttl: Option<Duration>) -> CacheResult<()> {
554 let full_key = self.build_key(key);
555 let effective_ttl = ttl.or(self.config.default_ttl);
556 let expires_at = effective_ttl.map(|d| Instant::now() + d);
557
558 let entry = CacheEntry { data: value.to_vec(), expires_at };
559 let mut store = self.store.write().await;
560 store.insert(full_key, entry);
561 Ok(())
562 }
563
564 async fn delete(&self, key: &str) -> CacheResult<bool> {
565 let full_key = self.build_key(key);
566 let mut store = self.store.write().await;
567 Ok(store.remove(&full_key).is_some())
568 }
569
570 async fn exists(&self, key: &str) -> CacheResult<bool> {
571 let full_key = self.build_key(key);
572 let mut store = self.store.write().await;
573 if let Some(entry) = store.get(&full_key) {
574 if entry.is_expired() {
575 store.remove(&full_key);
576 return Ok(false);
577 }
578 return Ok(true);
579 }
580 Ok(false)
581 }
582
583 async fn expire(&self, key: &str, ttl: Duration) -> CacheResult<bool> {
584 let full_key = self.build_key(key);
585 let mut store = self.store.write().await;
586 if let Some(entry) = store.get_mut(&full_key) {
587 if entry.is_expired() {
588 store.remove(&full_key);
589 return Ok(false);
590 }
591 entry.expires_at = Some(Instant::now() + ttl);
592 return Ok(true);
593 }
594 Ok(false)
595 }
596
597 async fn ttl(&self, key: &str) -> CacheResult<Option<Duration>> {
598 let full_key = self.build_key(key);
599 let mut store = self.store.write().await;
600 if let Some(entry) = store.get(&full_key) {
601 if entry.is_expired() {
602 store.remove(&full_key);
603 return Ok(None);
604 }
605 if let Some(expires_at) = entry.expires_at {
606 let now = Instant::now();
607 if expires_at > now {
608 return Ok(Some(expires_at - now));
609 }
610 }
611 }
612 Ok(None)
613 }
614
615 async fn mget_bytes(&self, keys: &[&str]) -> CacheResult<Vec<Option<Vec<u8>>>> {
616 let mut results = Vec::with_capacity(keys.len());
617 for key in keys {
618 results.push(self.get_bytes(key).await?);
619 }
620 Ok(results)
621 }
622
623 async fn mset_bytes(&self, items: &[(&str, &[u8])], ttl: Option<Duration>) -> CacheResult<()> {
624 for (key, value) in items {
625 self.set_bytes(key, value, ttl).await?;
626 }
627 Ok(())
628 }
629
630 async fn mdelete(&self, keys: &[&str]) -> CacheResult<u64> {
631 let mut count = 0u64;
632 for key in keys {
633 if self.delete(key).await? {
634 count += 1;
635 }
636 }
637 Ok(count)
638 }
639
640 async fn incr(&self, key: &str, delta: i64) -> CacheResult<i64> {
641 let full_key = self.build_key(key);
642 let mut store = self.store.write().await;
643
644 if !store.contains_key(&full_key) {
645 store.insert(full_key.clone(), CacheEntry { data: b"0".to_vec(), expires_at: None });
646 }
647
648 let entry = store.get_mut(&full_key).unwrap();
649 let mut value: i64 = String::from_utf8_lossy(&entry.data).parse().unwrap_or(0);
650 value += delta;
651 entry.data = value.to_string().into_bytes();
652 Ok(value)
653 }
654
655 async fn decr(&self, key: &str, delta: i64) -> CacheResult<i64> {
656 self.incr(key, -delta).await
657 }
658
659 async fn clear(&self) -> CacheResult<()> {
660 let mut store = self.store.write().await;
661 if self.config.key_prefix.is_empty() {
662 store.clear();
663 }
664 else {
665 let prefix = format!("{}:", self.config.key_prefix);
666 store.retain(|k, _| !k.starts_with(&prefix));
667 }
668 Ok(())
669 }
670
671 fn config(&self) -> &CacheConfig {
672 &self.config
673 }
674 }
675}
676
677pub fn memory_cache(config: CacheConfig) -> CacheService {
679 CacheService::new(Box::new(memory::MemoryCacheBackend::new(config)))
680}
681
682#[cfg(feature = "redis")]
683pub fn redis_cache(redis_config: redis::RedisConfig, cache_config: CacheConfig) -> CacheResult<CacheService> {
685 let backend = redis::RedisCacheBackend::new(redis_config, cache_config)?;
686 Ok(CacheService::new(Box::new(backend)))
687}