loco_rs/cache/drivers/
inmem.rs1use std::time::{Duration, Instant};
5
6use async_trait::async_trait;
7use moka::{future::Cache, Expiry};
8
9use super::CacheDriver;
10use crate::cache::CacheResult;
11use crate::config::InMemCacheConfig;
12
13#[must_use]
20pub fn new(config: &InMemCacheConfig) -> crate::cache::Cache {
21 let cache: Cache<String, (Expiration, String)> = Cache::builder()
22 .max_capacity(config.max_capacity)
23 .expire_after(InMemExpiry)
24 .build();
25 crate::cache::Cache::new(Inmem::from(cache))
26}
27
28#[derive(Debug)]
30pub struct Inmem {
31 cache: Cache<String, (Expiration, String)>,
32}
33
34impl Inmem {
35 #[must_use]
41 pub fn from(cache: Cache<String, (Expiration, String)>) -> Box<dyn CacheDriver> {
42 Box::new(Self { cache })
43 }
44}
45
46#[async_trait]
47impl CacheDriver for Inmem {
48 async fn ping(&self) -> CacheResult<()> {
54 Ok(())
55 }
56
57 async fn contains_key(&self, key: &str) -> CacheResult<bool> {
63 Ok(self.cache.contains_key(key))
64 }
65
66 async fn get(&self, key: &str) -> CacheResult<Option<String>> {
72 let result = self.cache.get(key).await;
73 match result {
74 None => Ok(None),
75 Some(v) => Ok(Some(v.1)),
76 }
77 }
78
79 async fn insert(&self, key: &str, value: &str) -> CacheResult<()> {
85 self.cache
86 .insert(key.to_string(), (Expiration::Never, value.to_string()))
87 .await;
88 Ok(())
89 }
90
91 async fn insert_with_expiry(
99 &self,
100 key: &str,
101 value: &str,
102 duration: Duration,
103 ) -> CacheResult<()> {
104 self.cache
105 .insert(
106 key.to_string(),
107 (Expiration::AfterDuration(duration), value.to_string()),
108 )
109 .await;
110 Ok(())
111 }
112
113 async fn remove(&self, key: &str) -> CacheResult<()> {
119 self.cache.remove(key).await;
120 Ok(())
121 }
122
123 async fn clear(&self) -> CacheResult<()> {
129 self.cache.invalidate_all();
130 Ok(())
131 }
132}
133
134#[derive(Clone, Copy, Debug, Eq, PartialEq)]
135pub enum Expiration {
136 Never,
137 AfterDuration(Duration),
138}
139
140impl Expiration {
141 #[must_use]
142 pub fn as_duration(&self) -> Option<Duration> {
143 match self {
144 Self::Never => None,
145 Self::AfterDuration(d) => Some(*d),
146 }
147 }
148}
149
150pub struct InMemExpiry;
151
152impl Expiry<String, (Expiration, String)> for InMemExpiry {
153 fn expire_after_create(
154 &self,
155 _key: &String,
156 value: &(Expiration, String),
157 _current_time: Instant,
158 ) -> Option<Duration> {
159 value.0.as_duration()
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use crate::config::InMemCacheConfig;
167
168 fn create_test_config() -> InMemCacheConfig {
169 InMemCacheConfig { max_capacity: 100 }
170 }
171
172 #[tokio::test]
173 async fn ping_returns_pong_when_cache_is_accessible() {
174 let config = create_test_config();
175 let mem = new(&config);
176 assert!(mem.ping().await.is_ok());
177 }
178
179 #[tokio::test]
180 async fn is_contains_key() {
181 let config = create_test_config();
182 let mem = new(&config);
183 assert!(!mem.contains_key("key").await.unwrap());
184 assert!(mem.insert("key", "loco").await.is_ok());
185 assert!(mem.contains_key("key").await.unwrap());
186 }
187
188 #[tokio::test]
189 async fn can_get_key_value() {
190 let config = create_test_config();
191 let mem = new(&config);
192 assert!(mem.insert("key", "loco").await.is_ok());
193 assert_eq!(
194 mem.get::<String>("key").await.unwrap(),
195 Some("loco".to_string())
196 );
197
198 assert_eq!(mem.get::<String>("not-found").await.unwrap(), None);
200 }
201
202 #[tokio::test]
203 async fn can_remove_key() {
204 let config = create_test_config();
205 let mem = new(&config);
206 assert!(mem.insert("key", "loco").await.is_ok());
207 assert!(mem.contains_key("key").await.unwrap());
208 mem.remove("key").await.unwrap();
209 assert!(!mem.contains_key("key").await.unwrap());
210 }
211
212 #[tokio::test]
213 async fn can_clear() {
214 let config = create_test_config();
215 let mem = new(&config);
216
217 let keys = vec!["key", "key2", "key3"];
218 for key in &keys {
219 assert!(mem.insert(key, "loco").await.is_ok());
220 }
221 for key in &keys {
222 assert!(mem.contains_key(key).await.is_ok());
223 }
224 assert!(mem.clear().await.is_ok());
225 for key in &keys {
226 assert!(!mem.contains_key(key).await.unwrap());
227 }
228 }
229}