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    /// Crate documentation cache TTL (seconds)
69    #[serde(default = "default_crate_docs_ttl")]
70    pub crate_docs_ttl_secs: Option<u64>,
71
72    /// Item documentation cache TTL (seconds)
73    #[serde(default = "default_item_docs_ttl")]
74    pub item_docs_ttl_secs: Option<u64>,
75
76    /// Search results cache TTL (seconds)
77    #[serde(default = "default_search_results_ttl")]
78    pub search_results_ttl_secs: Option<u64>,
79}
80
81/// Default crate docs TTL (1 hour)
82#[must_use]
83pub fn default_crate_docs_ttl() -> Option<u64> {
84    Some(3600)
85}
86
87/// Default item docs TTL (30 minutes)
88#[must_use]
89pub fn default_item_docs_ttl() -> Option<u64> {
90    Some(1800)
91}
92
93/// Default search results TTL (5 minutes)
94#[must_use]
95pub fn default_search_results_ttl() -> Option<u64> {
96    Some(300)
97}
98
99/// Default key prefix
100#[must_use]
101pub fn default_key_prefix() -> String {
102    String::new()
103}
104
105impl Default for CacheConfig {
106    fn default() -> Self {
107        Self {
108            cache_type: "memory".to_string(),
109            memory_size: Some(1000),
110            redis_url: None,
111            key_prefix: String::new(),
112            default_ttl: Some(3600), // 1 hour
113            crate_docs_ttl_secs: default_crate_docs_ttl(),
114            item_docs_ttl_secs: default_item_docs_ttl(),
115            search_results_ttl_secs: default_search_results_ttl(),
116        }
117    }
118}
119
120/// Create cache instance
121///
122/// # Errors
123///
124/// Returns an error if cache type is not supported or configuration is invalid
125pub fn create_cache(config: &CacheConfig) -> Result<Box<dyn Cache>, crate::error::Error> {
126    match config.cache_type.as_str() {
127        "memory" => {
128            #[cfg(feature = "cache-memory")]
129            {
130                let size = config.memory_size.unwrap_or(1000);
131                Ok(Box::new(memory::MemoryCache::new(size)))
132            }
133            #[cfg(not(feature = "cache-memory"))]
134            {
135                Err(crate::error::Error::Config(
136                    "memory cache feature is not enabled".to_string(),
137                ))
138            }
139        }
140        "redis" => {
141            #[cfg(feature = "cache-redis")]
142            {
143                // Note: Redis cache requires async initialization, this returns a placeholder
144                // In practice, use the create_cache_async function
145                Err(crate::error::Error::Config(
146                    "Redis cache requires async initialization. Use create_cache_async instead."
147                        .to_string(),
148                ))
149            }
150            #[cfg(not(feature = "cache-redis"))]
151            {
152                Err(crate::error::Error::Config(
153                    "redis cache feature is not enabled".to_string(),
154                ))
155            }
156        }
157        _ => Err(crate::error::Error::Config(format!(
158            "unsupported cache type: {}",
159            config.cache_type
160        ))),
161    }
162}
163
164/// Create cache instance asynchronously
165///
166/// # Errors
167///
168/// Returns an error if cache type is not supported or configuration is invalid
169#[cfg(feature = "cache-redis")]
170pub async fn create_cache_async(
171    config: &CacheConfig,
172) -> Result<Box<dyn Cache>, crate::error::Error> {
173    match config.cache_type.as_str() {
174        "memory" => {
175            let size = config.memory_size.unwrap_or(1000);
176            Ok(Box::new(memory::MemoryCache::new(size)))
177        }
178        "redis" => {
179            let url = config
180                .redis_url
181                .as_ref()
182                .ok_or_else(|| crate::error::Error::Config("redis_url is required".to_string()))?;
183            Ok(Box::new(
184                redis::RedisCache::new(url, config.key_prefix.clone()).await?,
185            ))
186        }
187        _ => Err(crate::error::Error::Config(format!(
188            "unsupported cache type: {}",
189            config.cache_type
190        ))),
191    }
192}