1use async_trait::async_trait;
4use redis::{aio::ConnectionManager, AsyncCommands, Client};
5use std::time::Duration;
6
7use super::config::CacheConfig;
8use super::store::CacheStore;
9use crate::error::FrameworkError;
10
11pub struct RedisCache {
15 conn: ConnectionManager,
16 prefix: String,
17 default_ttl: Option<Duration>,
18}
19
20impl RedisCache {
21 pub async fn connect(config: &CacheConfig) -> Result<Self, FrameworkError> {
23 let client = Client::open(config.url.as_str()).map_err(|e| {
24 FrameworkError::internal(format!("Redis connection error: {}", e))
25 })?;
26
27 let conn = ConnectionManager::new(client).await.map_err(|e| {
28 FrameworkError::internal(format!("Redis connection manager error: {}", e))
29 })?;
30
31 let default_ttl = if config.default_ttl > 0 {
32 Some(Duration::from_secs(config.default_ttl))
33 } else {
34 None
35 };
36
37 Ok(Self {
38 conn,
39 prefix: config.prefix.clone(),
40 default_ttl,
41 })
42 }
43
44 fn prefixed_key(&self, key: &str) -> String {
45 format!("{}{}", self.prefix, key)
46 }
47}
48
49#[async_trait]
50impl CacheStore for RedisCache {
51 async fn get_raw(&self, key: &str) -> Result<Option<String>, FrameworkError> {
52 let mut conn = self.conn.clone();
53 let key = self.prefixed_key(key);
54
55 let value: Option<String> = conn.get(&key).await.map_err(|e| {
56 FrameworkError::internal(format!("Cache get error: {}", e))
57 })?;
58
59 Ok(value)
60 }
61
62 async fn put_raw(
63 &self,
64 key: &str,
65 value: &str,
66 ttl: Option<Duration>,
67 ) -> Result<(), FrameworkError> {
68 let mut conn = self.conn.clone();
69 let key = self.prefixed_key(key);
70
71 let effective_ttl = ttl.or(self.default_ttl);
72
73 if let Some(duration) = effective_ttl {
74 conn.set_ex::<_, _, ()>(&key, value, duration.as_secs())
75 .await
76 .map_err(|e| FrameworkError::internal(format!("Cache set error: {}", e)))?;
77 } else {
78 conn.set::<_, _, ()>(&key, value)
79 .await
80 .map_err(|e| FrameworkError::internal(format!("Cache set error: {}", e)))?;
81 }
82
83 Ok(())
84 }
85
86 async fn has(&self, key: &str) -> Result<bool, FrameworkError> {
87 let mut conn = self.conn.clone();
88 let key = self.prefixed_key(key);
89
90 let exists: bool = conn.exists(&key).await.map_err(|e| {
91 FrameworkError::internal(format!("Cache exists error: {}", e))
92 })?;
93
94 Ok(exists)
95 }
96
97 async fn forget(&self, key: &str) -> Result<bool, FrameworkError> {
98 let mut conn = self.conn.clone();
99 let key = self.prefixed_key(key);
100
101 let deleted: i64 = conn.del(&key).await.map_err(|e| {
102 FrameworkError::internal(format!("Cache delete error: {}", e))
103 })?;
104
105 Ok(deleted > 0)
106 }
107
108 async fn flush(&self) -> Result<(), FrameworkError> {
109 let mut conn = self.conn.clone();
110
111 let pattern = format!("{}*", self.prefix);
114 let keys: Vec<String> = redis::cmd("KEYS")
115 .arg(&pattern)
116 .query_async(&mut conn)
117 .await
118 .map_err(|e| FrameworkError::internal(format!("Cache flush scan error: {}", e)))?;
119
120 if !keys.is_empty() {
121 conn.del::<_, ()>(keys)
122 .await
123 .map_err(|e| FrameworkError::internal(format!("Cache flush delete error: {}", e)))?;
124 }
125
126 Ok(())
127 }
128
129 async fn increment(&self, key: &str, amount: i64) -> Result<i64, FrameworkError> {
130 let mut conn = self.conn.clone();
131 let key = self.prefixed_key(key);
132
133 let value: i64 = conn.incr(&key, amount).await.map_err(|e| {
134 FrameworkError::internal(format!("Cache increment error: {}", e))
135 })?;
136
137 Ok(value)
138 }
139
140 async fn decrement(&self, key: &str, amount: i64) -> Result<i64, FrameworkError> {
141 let mut conn = self.conn.clone();
142 let key = self.prefixed_key(key);
143
144 let value: i64 = conn.decr(&key, amount).await.map_err(|e| {
145 FrameworkError::internal(format!("Cache decrement error: {}", e))
146 })?;
147
148 Ok(value)
149 }
150}