1use redis::{Client, AsyncCommands, aio::ConnectionManager};
4use async_trait::async_trait;
5use serde::{Serialize, Deserialize};
6use crate::{CacheResult, CacheError, CacheStats, Cache};
7
8pub struct RedisCache {
10 #[allow(dead_code)] client: Client,
12 connection: ConnectionManager,
13 key_prefix: Option<String>,
14}
15
16impl RedisCache {
17 pub async fn new(redis_url: &str) -> CacheResult<Self> {
19 let client = Client::open(redis_url)
20 .map_err(|e| CacheError::RedisConnection(e.to_string()))?;
21
22 let connection = client.get_connection_manager()
23 .await
24 .map_err(|e| CacheError::RedisConnection(e.to_string()))?;
25
26 Ok(Self {
27 client,
28 connection,
29 key_prefix: None,
30 })
31 }
32
33 pub async fn with_config(redis_url: &str, key_prefix: Option<String>) -> CacheResult<Self> {
35 let mut cache = Self::new(redis_url).await?;
36 cache.key_prefix = key_prefix;
37 Ok(cache)
38 }
39
40 fn build_key(&self, key: &str) -> String {
42 match &self.key_prefix {
43 Some(prefix) => format!("{}:{}", prefix, key),
44 None => key.to_string(),
45 }
46 }
47
48 fn serialize<T: Serialize>(value: &T) -> CacheResult<String> {
50 serde_json::to_string(value)
51 .map_err(|e| CacheError::Serialization(e.to_string()))
52 }
53
54 fn deserialize<T: for<'de> Deserialize<'de>>(value: &str) -> CacheResult<T> {
56 serde_json::from_str(value)
57 .map_err(|e| CacheError::Deserialization(e.to_string()))
58 }
59}
60
61#[async_trait]
62impl Cache for RedisCache {
63 async fn set<T>(&self, key: &str, value: &T, ttl_seconds: Option<u64>) -> CacheResult<()>
64 where
65 T: Serialize + Send + Sync,
66 {
67 let full_key = self.build_key(key);
68 let serialized = Self::serialize(value)?;
69
70 let mut conn = self.connection.clone();
71 let result: redis::RedisResult<()> = match ttl_seconds {
72 Some(ttl) => conn.set_ex(&full_key, serialized, ttl).await,
73 None => conn.set(&full_key, serialized).await,
74 };
75
76 result.map_err(|e| CacheError::RedisOperation(e.to_string()))
77 }
78
79 async fn get<T>(&self, key: &str) -> CacheResult<Option<T>>
80 where
81 T: for<'de> Deserialize<'de> + Send + Sync,
82 {
83 let full_key = self.build_key(key);
84 let mut conn = self.connection.clone();
85
86 let result: Option<String> = conn.get(&full_key).await
87 .map_err(|e| CacheError::RedisOperation(e.to_string()))?;
88
89 let Some(value) = result else {
90 return Ok(None);
91 };
92
93 let deserialized = Self::deserialize::<T>(&value)?;
94 Ok(Some(deserialized))
95 }
96
97 async fn delete(&self, key: &str) -> CacheResult<bool> {
98 let full_key = self.build_key(key);
99 let mut conn = self.connection.clone();
100
101 let count: i32 = conn.del(&full_key).await
102 .map_err(|e| CacheError::RedisOperation(e.to_string()))?;
103
104 Ok(count > 0)
105 }
106
107 async fn exists(&self, key: &str) -> CacheResult<bool> {
108 let full_key = self.build_key(key);
109
110 let mut conn = self.connection.clone();
111 let result: redis::RedisResult<bool> = conn.exists(&full_key).await;
112
113 result.map_err(|e| CacheError::RedisOperation(e.to_string()))
114 }
115
116 async fn expire(&self, key: &str, ttl_seconds: u64) -> CacheResult<bool> {
117 let full_key = self.build_key(key);
118
119 let mut conn = self.connection.clone();
120 let result: redis::RedisResult<bool> = conn.expire(&full_key, ttl_seconds as i64).await;
121
122 result.map_err(|e| CacheError::RedisOperation(e.to_string()))
123 }
124
125 async fn ttl(&self, key: &str) -> CacheResult<Option<u64>> {
126 let full_key = self.build_key(key);
127 let mut conn = self.connection.clone();
128
129 let ttl: i64 = conn.ttl(&full_key).await
130 .map_err(|e| CacheError::RedisOperation(e.to_string()))?;
131
132 match ttl {
133 -1 => Ok(None), -2 => Ok(Some(0)), ttl if ttl >= 0 => Ok(Some(ttl as u64)),
136 _ => Err(CacheError::RedisOperation(format!("Invalid TTL: {}", ttl))),
137 }
138 }
139
140 async fn clear(&self) -> CacheResult<()> {
141 let mut conn = self.connection.clone();
142
143 let Some(prefix) = &self.key_prefix else {
145 return Err(CacheError::Other(
146 "Cannot clear Redis cache without key prefix for safety".to_string()
147 ));
148 };
149
150 let pattern = format!("{}:*", prefix);
151 let keys: redis::RedisResult<Vec<String>> = conn.keys(&pattern).await;
152
153 let key_list = keys.map_err(|e| CacheError::RedisOperation(e.to_string()))?;
154
155 if !key_list.is_empty() {
157 let _: redis::RedisResult<i32> = conn.del(&key_list).await;
158 }
159
160 Ok(())
161 }
162
163 async fn stats(&self) -> CacheResult<CacheStats> {
164 let mut conn = self.connection.clone();
165 let mut stats = CacheStats::default();
166
167 let info: redis::RedisResult<String> = redis::cmd("INFO")
169 .arg("memory")
170 .arg("stats")
171 .query_async(&mut conn)
172 .await;
173
174 if let Ok(info_str) = info {
176 for line in info_str.lines() {
177 let Some(value_str) = line.split(':').nth(1) else {
178 continue;
179 };
180
181 let Ok(value) = value_str.parse::<u64>() else {
182 continue;
183 };
184
185 match line {
186 l if l.starts_with("used_memory:") => stats.memory_usage = Some(value),
187 l if l.starts_with("keyspace_hits:") => stats.hits = value,
188 l if l.starts_with("keyspace_misses:") => stats.misses = value,
189 _ => {}
190 }
191 }
192 }
193
194 if let Some(prefix) = &self.key_prefix {
196 let pattern = format!("{}:*", prefix);
197 let keys: redis::RedisResult<Vec<String>> = conn.keys(&pattern).await;
198
199 if let Ok(key_list) = keys {
200 stats.total_entries = key_list.len() as u64;
201 }
202 }
203
204 stats.update_hit_rate();
205 Ok(stats)
206 }
207
208 async fn mget<T>(&self, keys: Vec<String>) -> CacheResult<Vec<(String, Option<T>)>>
209 where
210 T: for<'de> Deserialize<'de> + Send + Sync,
211 {
212 let full_keys: Vec<String> = keys.iter()
213 .map(|key| self.build_key(key))
214 .collect();
215
216 let mut conn = self.connection.clone();
217 let values: Vec<Option<String>> = conn.mget(&full_keys).await
218 .map_err(|e| CacheError::RedisOperation(e.to_string()))?;
219
220 let mut results = Vec::with_capacity(keys.len());
221
222 for (i, key) in keys.into_iter().enumerate() {
223 let result = match &values[i] {
224 Some(value) => {
225 Self::deserialize::<T>(value)
226 .map(Some)
227 .unwrap_or(None)
228 }
229 None => None,
230 };
231 results.push((key, result));
232 }
233
234 Ok(results)
235 }
236
237 async fn mset<T>(&self, entries: Vec<(String, T, Option<u64>)>) -> CacheResult<()>
238 where
239 T: Serialize + Send + Sync,
240 {
241 if entries.is_empty() {
242 return Ok(());
243 }
244
245 let mut with_ttl = Vec::new();
247 let mut without_ttl = Vec::new();
248
249 for (key, value, ttl) in entries {
250 let full_key = self.build_key(&key);
251 let serialized = Self::serialize(&value)?;
252
253 match ttl {
254 Some(ttl_seconds) => with_ttl.push((full_key, serialized, ttl_seconds)),
255 None => without_ttl.push((full_key, serialized)),
256 }
257 }
258
259 let mut conn = self.connection.clone();
260
261 if !without_ttl.is_empty() {
263 let mut pipeline = redis::pipe();
264 for (key, value) in without_ttl {
265 pipeline.set(&key, value);
266 }
267
268 pipeline.query_async::<_, ()>(&mut conn).await
269 .map_err(|e| CacheError::RedisOperation(e.to_string()))?;
270 }
271
272 for (key, value, ttl_seconds) in with_ttl {
274 conn.set_ex::<_, _, ()>(&key, value, ttl_seconds).await
275 .map_err(|e| CacheError::RedisOperation(e.to_string()))?;
276 }
277
278 Ok(())
279 }
280
281 async fn mdelete(&self, keys: Vec<String>) -> CacheResult<u64> {
282 if keys.is_empty() {
283 return Ok(0);
284 }
285
286 let full_keys: Vec<String> = keys.iter()
287 .map(|key| self.build_key(key))
288 .collect();
289
290 let mut conn = self.connection.clone();
291 let count: i32 = conn.del(&full_keys).await
292 .map_err(|e| CacheError::RedisOperation(e.to_string()))?;
293
294 Ok(count as u64)
295 }
296}