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}