memory_cache/lib.rs
1extern crate once_cell;
2
3mod entry;
4pub mod macros;
5
6use crate::entry::*;
7use std::collections::hash_map::Entry;
8use std::collections::HashMap;
9use std::hash::Hash;
10use std::time::{Duration, SystemTime};
11
12/// Represents a local in-memory cache.
13pub struct MemoryCache<A, B> {
14 cache_table: HashMap<A, CacheEntry<B>>,
15 full_scan_frequency: Option<Duration>,
16 created_time: SystemTime,
17 last_scan_time: Option<SystemTime>,
18}
19
20impl<A: Hash + Eq, B> MemoryCache<A, B> {
21 /// Creates an empty `MemoryCache`.
22 ///
23 /// # Example
24 /// ```
25 /// use memory_cache::MemoryCache;
26 /// use std::time::Duration;
27 ///
28 /// let mut cache = MemoryCache::new();
29 ///
30 /// let key: &'static str = "key";
31 /// let value: &'static str = "Hello, World!";
32 ///
33 /// cache.insert(key, value, None);
34 ///
35 /// assert_eq!(cache.get(&key), Some(&value));
36 /// ```
37 pub fn new() -> Self {
38 Self {
39 cache_table: HashMap::new(),
40 full_scan_frequency: None,
41 created_time: SystemTime::now(),
42 last_scan_time: None,
43 }
44 }
45
46 /// Creates an empty `MemoryCache` with periodic full scan to identify expired keys.
47 ///
48 /// # Example
49 /// ```
50 /// use memory_cache::MemoryCache;
51 /// use std::time::Duration;
52 ///
53 /// let scan_frequency = Duration::from_secs(60);
54 ///
55 /// let mut cache = MemoryCache::with_full_scan(scan_frequency);
56 ///
57 /// let key: &'static str = "key";
58 /// let value: &'static str = "Hello, World!";
59 ///
60 /// cache.insert(key, value, None);
61 ///
62 /// assert_eq!(cache.get(&key), Some(&value));
63 /// ```
64 pub fn with_full_scan(full_scan_frequency: Duration) -> Self {
65 Self {
66 cache_table: HashMap::new(),
67 full_scan_frequency: Some(full_scan_frequency),
68 created_time: SystemTime::now(),
69 last_scan_time: None,
70 }
71 }
72
73 /// Determines whether the `MemoryCache<A, B>` contains the specified key.
74 ///
75 /// # Example
76 /// ```
77 /// use memory_cache::MemoryCache;
78 /// use std::time::Duration;
79 ///
80 /// let mut cache = MemoryCache::new();
81 ///
82 /// let key: &'static str = "key";
83 /// let value: &'static str = "Hello, World!";
84 ///
85 /// cache.insert(key, value, None);
86 ///
87 /// assert!(cache.contains_key(&key));
88 /// ```
89 pub fn contains_key(&self, key: &A) -> bool {
90 let now = SystemTime::now();
91
92 self.cache_table
93 .get(key)
94 .filter(|cache_entry| !cache_entry.is_expired(now))
95 .is_some()
96 }
97
98 /// Gets the last scan time.
99 ///
100 /// - [`None`] If there were no scans.
101 ///
102 /// # Example
103 /// ```
104 /// use memory_cache::MemoryCache;
105 /// use std::time::{Duration, SystemTime};
106 ///
107 /// let mut cache = MemoryCache::new();
108 ///
109 /// let key: &'static str = "key";
110 /// let value: &'static str = "Hello, World!";
111 ///
112 /// cache.insert(key, value, None);
113 ///
114 /// assert_eq!(cache.get_last_scan_time(), None);
115 /// ```
116 pub fn get_last_scan_time(&self) -> Option<SystemTime> {
117 self.last_scan_time
118 }
119
120 /// Gets the full scan frequency.
121 ///
122 /// # Example
123 /// ```
124 /// use memory_cache::MemoryCache;
125 /// use std::time::{Duration, SystemTime};
126 ///
127 /// let scan_frequency = Duration::from_secs(60);
128 ///
129 /// let mut cache = MemoryCache::with_full_scan(scan_frequency);
130 ///
131 /// let key: &'static str = "key";
132 /// let value: &'static str = "Hello, World!";
133 ///
134 /// cache.insert(key, value, None);
135 ///
136 /// assert_eq!(cache.get_full_scan_frequency(), Some(scan_frequency));
137 /// ```
138 pub fn get_full_scan_frequency(&self) -> Option<Duration> {
139 self.full_scan_frequency
140 }
141
142 /// Gets the value associated with the specified key.
143 ///
144 /// # Example
145 /// ```
146 /// use memory_cache::MemoryCache;
147 /// use std::time::Duration;
148 ///
149 /// let mut cache = MemoryCache::new();
150 ///
151 /// let key: &'static str = "key";
152 /// let value: &'static str = "Hello, World!";
153 ///
154 /// cache.insert(key, value, None);
155 ///
156 /// assert_eq!(cache.get(&key), Some(&value));
157 /// ```
158 pub fn get(&self, key: &A) -> Option<&B> {
159 let now = SystemTime::now();
160
161 self.cache_table
162 .get(&key)
163 .filter(|cache_entry| !cache_entry.is_expired(now))
164 .map(|cache_entry| &cache_entry.value)
165 }
166
167 /// Gets the value associated with the specified key, or if the key can not be found,
168 /// creates and insert value using the `factory` function.
169 ///
170 /// # Example
171 /// ```
172 /// use memory_cache::MemoryCache;
173 /// use std::time::Duration;
174 ///
175 /// let mut cache = MemoryCache::new();
176 ///
177 /// let key: &'static str = "key";
178 /// let value: &'static str = "Hello, World!";
179 ///
180 /// assert_eq!(cache.get_or_insert(key, move || value, None), &value);
181 /// assert!(cache.contains_key(&key));
182 /// ```
183 pub fn get_or_insert<F>(&mut self, key: A, factory: F, lifetime: Option<Duration>) -> &B
184 where
185 F: Fn() -> B,
186 {
187 let now = SystemTime::now();
188
189 self.try_full_scan_expired_items(now);
190
191 match self.cache_table.entry(key) {
192 Entry::Occupied(mut occupied) => {
193 if occupied.get().is_expired(now) {
194 occupied.insert(CacheEntry::new(factory(), lifetime));
195 }
196
197 &occupied.into_mut().value
198 }
199 Entry::Vacant(vacant) => &vacant.insert(CacheEntry::new(factory(), lifetime)).value,
200 }
201 }
202
203 /// Inserts a key-value pair into the cache.
204 ///
205 /// If the cache did not have this key present, `None` is returned.
206 /// If the cache did have this key present, the value is updated, and the old value is returned.
207 ///
208 /// # Example
209 /// ```
210 /// use memory_cache::MemoryCache;
211 /// use std::time::Duration;
212 ///
213 /// let mut cache = MemoryCache::new();
214 ///
215 /// let key: &'static str = "key";
216 /// let value: &'static str = "Hello, World!";
217 ///
218 /// cache.insert(key, value, None);
219 ///
220 /// assert_eq!(cache.get(&key), Some(&value));
221 /// ```
222 pub fn insert(&mut self, key: A, value: B, lifetime: Option<Duration>) -> Option<B> {
223 let now = SystemTime::now();
224
225 self.try_full_scan_expired_items(now);
226
227 self.cache_table
228 .insert(key, CacheEntry::new(value, lifetime))
229 .filter(|cache_entry| !cache_entry.is_expired(now))
230 .map(|cache_entry| cache_entry.value)
231 }
232
233 /// Removes a key from the cache, returning the value at the key if the key was previously in the cache.
234 ///
235 /// # Example
236 /// ```
237 /// use memory_cache::MemoryCache;
238 /// use std::time::Duration;
239 ///
240 /// let mut cache = MemoryCache::new();
241 ///
242 /// let key: &'static str = "key";
243 /// let value: &'static str = "Hello, World!";
244 ///
245 /// cache.insert(key, value, None);
246 ///
247 /// assert_eq!(cache.remove(&key), Some(value));
248 /// assert!(!cache.contains_key(&key));
249 /// ```
250 pub fn remove(&mut self, key: &A) -> Option<B> {
251 let now = SystemTime::now();
252
253 self.try_full_scan_expired_items(now);
254
255 self.cache_table
256 .remove(key)
257 .filter(|cache_entry| !cache_entry.is_expired(now))
258 .map(|cache_entry| cache_entry.value)
259 }
260
261 fn try_full_scan_expired_items(&mut self, current_time: SystemTime) {
262 if let Some(full_scan_frequency) = self.full_scan_frequency {
263 let since = current_time
264 .duration_since(self.last_scan_time.unwrap_or(self.created_time))
265 .unwrap();
266
267 if since >= full_scan_frequency {
268 self.cache_table
269 .retain(|_, cache_entry| !cache_entry.is_expired(current_time));
270
271 self.last_scan_time = Some(current_time);
272 }
273 }
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn contains_key_expired_entry() {
283 // Arrange
284 let mut cache = MemoryCache::new();
285 let key: &'static str = "key";
286
287 // Act
288 cache.insert(key, 1, Some(Duration::default()));
289
290 // Assert
291 assert!(!cache.contains_key(&key));
292 }
293
294 #[test]
295 fn get_expired_entry() {
296 // Arrange
297 let mut cache = MemoryCache::new();
298 let key: &'static str = "key";
299
300 // Act
301 cache.insert(key, 1, Some(Duration::default()));
302
303 // Assert
304 assert_eq!(cache.get(&key), None);
305 }
306
307 #[test]
308 fn insert_return_old_value() {
309 // Arrange
310 let mut cache = MemoryCache::new();
311 let key: &'static str = "key";
312
313 // Act
314 let result_1 = cache.insert(key, 1, Some(Duration::default()));
315 let result_2 = cache.insert(key, 2, None);
316 let result_3 = cache.insert(key, 3, None);
317
318 // Assert
319 assert_eq!(result_1, None);
320 assert_eq!(result_2, None);
321 assert_eq!(result_3, Some(2));
322 }
323
324 #[test]
325 fn get_or_insert_expired_entry() {
326 // Arrange
327 let mut cache = MemoryCache::new();
328 let key: &'static str = "key";
329
330 // Act
331 cache.get_or_insert(key, || 1, Some(Duration::default()));
332 let value = cache.get_or_insert(key, || 2, None);
333
334 // Assert
335 assert_eq!(value, &2);
336 }
337
338 #[test]
339 fn remove_expired_entry() {
340 // Arrange
341 let mut cache = MemoryCache::new();
342 let key: &'static str = "key";
343
344 // Act
345 cache.insert(key, 1, Some(Duration::default()));
346 let value = cache.remove(&key);
347
348 // Assert
349 assert_eq!(value, None);
350 }
351
352 #[test]
353 fn update_last_scan_time() {
354 // Arrange
355 let scan_frequency = Duration::default();
356 let mut cache = MemoryCache::with_full_scan(scan_frequency);
357 let key: &'static str = "key";
358
359 // Act
360 cache.insert(key, 1, None);
361 let last_scan_time = cache.get_last_scan_time();
362
363 // Assert
364 assert!(last_scan_time.is_some())
365 }
366}