Skip to main content

crates_docs/cache/
mod.rs

1//! Cache module
2//!
3//! Provides memory cache and Redis cache support.
4
5#[cfg(feature = "cache-memory")]
6pub mod memory;
7
8#[cfg(feature = "cache-redis")]
9pub mod redis;
10
11use std::time::Duration;
12
13/// Cache trait
14#[async_trait::async_trait]
15pub trait Cache: Send + Sync {
16    /// Get cache value
17    async fn get(&self, key: &str) -> Option<String>;
18
19    /// Set cache value
20    ///
21    /// # Errors
22    ///
23    /// Returns an error if the cache operation fails
24    async fn set(
25        &self,
26        key: String,
27        value: String,
28        ttl: Option<Duration>,
29    ) -> crate::error::Result<()>;
30
31    /// Delete cache value
32    ///
33    /// # Errors
34    ///
35    /// Returns an error if the cache operation fails
36    async fn delete(&self, key: &str) -> crate::error::Result<()>;
37
38    /// Clear all cache entries with the configured key prefix
39    ///
40    /// # Errors
41    ///
42    /// Returns an error if the cache operation fails
43    async fn clear(&self) -> crate::error::Result<()>;
44
45    /// Check if key exists
46    async fn exists(&self, key: &str) -> bool;
47}
48
49/// Cache configuration
50#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
51pub struct CacheConfig {
52    /// Cache type: memory or redis
53    pub cache_type: String,
54
55    /// Memory cache size (number of entries)
56    pub memory_size: Option<usize>,
57
58    /// Redis connection URL
59    pub redis_url: Option<String>,
60
61    /// Key prefix for Redis cache (to isolate cache entries from different services)
62    #[serde(default = "default_key_prefix")]
63    pub key_prefix: String,
64
65    /// Default TTL (seconds)
66    pub default_ttl: Option<u64>,
67}
68
69/// Default key prefix
70#[must_use]
71pub fn default_key_prefix() -> String {
72    String::new()
73}
74
75impl Default for CacheConfig {
76    fn default() -> Self {
77        Self {
78            cache_type: "memory".to_string(),
79            memory_size: Some(1000),
80            redis_url: None,
81            key_prefix: String::new(),
82            default_ttl: Some(3600), // 1 hour
83        }
84    }
85}
86
87/// Create cache instance
88///
89/// # Errors
90///
91/// Returns an error if cache type is not supported or configuration is invalid
92pub fn create_cache(config: &CacheConfig) -> Result<Box<dyn Cache>, crate::error::Error> {
93    match config.cache_type.as_str() {
94        "memory" => {
95            #[cfg(feature = "cache-memory")]
96            {
97                let size = config.memory_size.unwrap_or(1000);
98                Ok(Box::new(memory::MemoryCache::new(size)))
99            }
100            #[cfg(not(feature = "cache-memory"))]
101            {
102                Err(crate::error::Error::Config(
103                    "memory cache feature is not enabled".to_string(),
104                ))
105            }
106        }
107        "redis" => {
108            #[cfg(feature = "cache-redis")]
109            {
110                // Note: Redis cache requires async initialization, this returns a placeholder
111                // In practice, use the create_cache_async function
112                Err(crate::error::Error::Config(
113                    "Redis cache requires async initialization. Use create_cache_async instead."
114                        .to_string(),
115                ))
116            }
117            #[cfg(not(feature = "cache-redis"))]
118            {
119                Err(crate::error::Error::Config(
120                    "redis cache feature is not enabled".to_string(),
121                ))
122            }
123        }
124        _ => Err(crate::error::Error::Config(format!(
125            "unsupported cache type: {}",
126            config.cache_type
127        ))),
128    }
129}
130
131/// Create cache instance asynchronously
132///
133/// # Errors
134///
135/// Returns an error if cache type is not supported or configuration is invalid
136#[cfg(feature = "cache-redis")]
137pub async fn create_cache_async(
138    config: &CacheConfig,
139) -> Result<Box<dyn Cache>, crate::error::Error> {
140    match config.cache_type.as_str() {
141        "memory" => {
142            let size = config.memory_size.unwrap_or(1000);
143            Ok(Box::new(memory::MemoryCache::new(size)))
144        }
145        "redis" => {
146            let url = config
147                .redis_url
148                .as_ref()
149                .ok_or_else(|| crate::error::Error::Config("redis_url is required".to_string()))?;
150            Ok(Box::new(
151                redis::RedisCache::new(url, config.key_prefix.clone()).await?,
152            ))
153        }
154        _ => Err(crate::error::Error::Config(format!(
155            "unsupported cache type: {}",
156            config.cache_type
157        ))),
158    }
159}