1use crate::error::CacheResult;
4use crate::traits::CacheStore;
5use serde::{Serialize, de::DeserializeOwned};
6use std::sync::Arc;
7use std::time::Duration;
8
9pub struct CacheManager<S: CacheStore> {
11 store: Arc<S>,
12}
13
14impl<S: CacheStore> CacheManager<S> {
15 pub fn new(store: S) -> Self {
17 Self {
18 store: Arc::new(store),
19 }
20 }
21
22 pub async fn get<T: DeserializeOwned>(&self, key: &str) -> CacheResult<Option<T>> {
24 if let Some(json) = self.store.get_json(key).await? {
25 let value: T = serde_json::from_str(&json)
26 .map_err(|e| crate::error::CacheError::Deserialization(e.to_string()))?;
27 Ok(Some(value))
28 } else {
29 Ok(None)
30 }
31 }
32
33 pub async fn set<T: Serialize>(
35 &self,
36 key: &str,
37 value: &T,
38 ttl: Option<Duration>,
39 ) -> CacheResult<()> {
40 let json = serde_json::to_string(value)
41 .map_err(|e| crate::error::CacheError::Serialization(e.to_string()))?;
42 self.store.set_json(key, json, ttl).await
43 }
44
45 pub async fn get_or_set<T, F, Fut>(
50 &self,
51 key: &str,
52 ttl: Option<Duration>,
53 factory: F,
54 ) -> CacheResult<T>
55 where
56 T: Serialize + DeserializeOwned,
57 F: FnOnce() -> Fut,
58 Fut: std::future::Future<Output = CacheResult<T>>,
59 {
60 if let Some(value) = self.get(key).await? {
61 return Ok(value);
62 }
63
64 let value = factory().await?;
65 self.set(key, &value, ttl).await?;
66 Ok(value)
67 }
68
69 pub async fn delete(&self, key: &str) -> CacheResult<()> {
71 self.store.delete(key).await
72 }
73
74 pub async fn exists(&self, key: &str) -> CacheResult<bool> {
76 self.store.exists(key).await
77 }
78
79 pub async fn clear(&self) -> CacheResult<()> {
81 self.store.clear().await
82 }
83
84 pub async fn ttl(&self, key: &str) -> CacheResult<Option<Duration>> {
86 self.store.ttl(key).await
87 }
88
89 pub async fn expire(&self, key: &str, ttl: Duration) -> CacheResult<()> {
91 self.store.expire(key, ttl).await
92 }
93
94 pub fn namespace(&self, prefix: &str) -> NamespacedCache<S> {
96 NamespacedCache {
97 store: self.store.clone(),
98 prefix: prefix.to_string(),
99 }
100 }
101}
102
103pub struct NamespacedCache<S: CacheStore> {
105 store: Arc<S>,
106 prefix: String,
107}
108
109impl<S: CacheStore> NamespacedCache<S> {
110 fn build_key(&self, key: &str) -> String {
111 format!("{}:{}", self.prefix, key)
112 }
113
114 pub async fn get<T: DeserializeOwned>(&self, key: &str) -> CacheResult<Option<T>> {
116 let key = self.build_key(key);
117 if let Some(json) = self.store.get_json(&key).await? {
118 let value: T = serde_json::from_str(&json)
119 .map_err(|e| crate::error::CacheError::Deserialization(e.to_string()))?;
120 Ok(Some(value))
121 } else {
122 Ok(None)
123 }
124 }
125
126 pub async fn set<T: Serialize>(
128 &self,
129 key: &str,
130 value: &T,
131 ttl: Option<Duration>,
132 ) -> CacheResult<()> {
133 let key = self.build_key(key);
134 let json = serde_json::to_string(value)
135 .map_err(|e| crate::error::CacheError::Serialization(e.to_string()))?;
136 self.store.set_json(&key, json, ttl).await
137 }
138
139 pub async fn delete(&self, key: &str) -> CacheResult<()> {
141 let key = self.build_key(key);
142 self.store.delete(&key).await
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_namespace_build_key() {
152 struct MockStore;
153
154 #[async_trait::async_trait]
155 impl CacheStore for MockStore {
156 async fn get_json(&self, _key: &str) -> CacheResult<Option<String>> {
157 Ok(None)
158 }
159 async fn set_json(
160 &self,
161 _key: &str,
162 _value: String,
163 _ttl: Option<Duration>,
164 ) -> CacheResult<()> {
165 Ok(())
166 }
167 async fn delete(&self, _key: &str) -> CacheResult<()> {
168 Ok(())
169 }
170 async fn exists(&self, _key: &str) -> CacheResult<bool> {
171 Ok(false)
172 }
173 async fn clear(&self) -> CacheResult<()> {
174 Ok(())
175 }
176 async fn ttl(&self, _key: &str) -> CacheResult<Option<Duration>> {
177 Ok(None)
178 }
179 async fn expire(&self, _key: &str, _ttl: Duration) -> CacheResult<()> {
180 Ok(())
181 }
182 async fn increment(&self, _key: &str, _delta: i64) -> CacheResult<i64> {
183 Ok(0)
184 }
185 async fn decrement(&self, _key: &str, _delta: i64) -> CacheResult<i64> {
186 Ok(0)
187 }
188 }
189
190 let namespaced = NamespacedCache {
191 store: Arc::new(MockStore),
192 prefix: "users".to_string(),
193 };
194
195 assert_eq!(namespaced.build_key("123"), "users:123");
196 }
197
198 #[test]
199 fn test_namespace_build_key_empty() {
200 struct MockStore;
201
202 #[async_trait::async_trait]
203 impl CacheStore for MockStore {
204 async fn get_json(&self, _key: &str) -> CacheResult<Option<String>> {
205 Ok(None)
206 }
207 async fn set_json(
208 &self,
209 _key: &str,
210 _value: String,
211 _ttl: Option<Duration>,
212 ) -> CacheResult<()> {
213 Ok(())
214 }
215 async fn delete(&self, _key: &str) -> CacheResult<()> {
216 Ok(())
217 }
218 async fn exists(&self, _key: &str) -> CacheResult<bool> {
219 Ok(false)
220 }
221 async fn clear(&self) -> CacheResult<()> {
222 Ok(())
223 }
224 async fn ttl(&self, _key: &str) -> CacheResult<Option<Duration>> {
225 Ok(None)
226 }
227 async fn expire(&self, _key: &str, _ttl: Duration) -> CacheResult<()> {
228 Ok(())
229 }
230 async fn increment(&self, _key: &str, _delta: i64) -> CacheResult<i64> {
231 Ok(0)
232 }
233 async fn decrement(&self, _key: &str, _delta: i64) -> CacheResult<i64> {
234 Ok(0)
235 }
236 }
237
238 let namespaced = NamespacedCache {
239 store: Arc::new(MockStore),
240 prefix: "app".to_string(),
241 };
242
243 assert_eq!(namespaced.build_key(""), "app:");
244 }
245
246 #[test]
247 fn test_namespace_build_key_with_colons() {
248 struct MockStore;
249
250 #[async_trait::async_trait]
251 impl CacheStore for MockStore {
252 async fn get_json(&self, _key: &str) -> CacheResult<Option<String>> {
253 Ok(None)
254 }
255 async fn set_json(
256 &self,
257 _key: &str,
258 _value: String,
259 _ttl: Option<Duration>,
260 ) -> CacheResult<()> {
261 Ok(())
262 }
263 async fn delete(&self, _key: &str) -> CacheResult<()> {
264 Ok(())
265 }
266 async fn exists(&self, _key: &str) -> CacheResult<bool> {
267 Ok(false)
268 }
269 async fn clear(&self) -> CacheResult<()> {
270 Ok(())
271 }
272 async fn ttl(&self, _key: &str) -> CacheResult<Option<Duration>> {
273 Ok(None)
274 }
275 async fn expire(&self, _key: &str, _ttl: Duration) -> CacheResult<()> {
276 Ok(())
277 }
278 async fn increment(&self, _key: &str, _delta: i64) -> CacheResult<i64> {
279 Ok(0)
280 }
281 async fn decrement(&self, _key: &str, _delta: i64) -> CacheResult<i64> {
282 Ok(0)
283 }
284 }
285
286 let namespaced = NamespacedCache {
287 store: Arc::new(MockStore),
288 prefix: "app".to_string(),
289 };
290
291 assert_eq!(namespaced.build_key("user:123"), "app:user:123");
292 }
293
294 #[test]
295 fn test_namespace_multiple_prefixes() {
296 struct MockStore;
297
298 #[async_trait::async_trait]
299 impl CacheStore for MockStore {
300 async fn get_json(&self, _key: &str) -> CacheResult<Option<String>> {
301 Ok(None)
302 }
303 async fn set_json(
304 &self,
305 _key: &str,
306 _value: String,
307 _ttl: Option<Duration>,
308 ) -> CacheResult<()> {
309 Ok(())
310 }
311 async fn delete(&self, _key: &str) -> CacheResult<()> {
312 Ok(())
313 }
314 async fn exists(&self, _key: &str) -> CacheResult<bool> {
315 Ok(false)
316 }
317 async fn clear(&self) -> CacheResult<()> {
318 Ok(())
319 }
320 async fn ttl(&self, _key: &str) -> CacheResult<Option<Duration>> {
321 Ok(None)
322 }
323 async fn expire(&self, _key: &str, _ttl: Duration) -> CacheResult<()> {
324 Ok(())
325 }
326 async fn increment(&self, _key: &str, _delta: i64) -> CacheResult<i64> {
327 Ok(0)
328 }
329 async fn decrement(&self, _key: &str, _delta: i64) -> CacheResult<i64> {
330 Ok(0)
331 }
332 }
333
334 let ns1 = NamespacedCache {
335 store: Arc::new(MockStore),
336 prefix: "app1".to_string(),
337 };
338
339 let ns2 = NamespacedCache {
340 store: Arc::new(MockStore),
341 prefix: "app2".to_string(),
342 };
343
344 assert_eq!(ns1.build_key("key"), "app1:key");
345 assert_eq!(ns2.build_key("key"), "app2:key");
346 assert_ne!(ns1.build_key("key"), ns2.build_key("key"));
347 }
348}