ferrous_forge/rust_version/
cache.rs

1//! Simple in-memory cache with TTL support
2
3use std::collections::HashMap;
4use std::time::{Duration, Instant};
5
6/// Cache entry with value and insertion time
7struct CacheEntry<V> {
8    value: V,
9    inserted_at: Instant,
10}
11
12/// Simple cache with time-to-live (TTL) support
13pub struct Cache<K, V> {
14    entries: HashMap<K, CacheEntry<V>>,
15    ttl: Duration,
16}
17
18impl<K, V> Cache<K, V>
19where
20    K: Eq + std::hash::Hash,
21    V: Clone,
22{
23    /// Create a new cache with the specified TTL
24    pub fn new(ttl: Duration) -> Self {
25        Self {
26            entries: HashMap::new(),
27            ttl,
28        }
29    }
30
31    /// Get a value from the cache
32    pub fn get(&self, key: &K) -> Option<V> {
33        self.entries.get(key).and_then(|entry| {
34            if entry.inserted_at.elapsed() < self.ttl {
35                Some(entry.value.clone())
36            } else {
37                None
38            }
39        })
40    }
41
42    /// Insert a value into the cache
43    pub fn insert(&mut self, key: K, value: V) {
44        self.entries.insert(
45            key,
46            CacheEntry {
47                value,
48                inserted_at: Instant::now(),
49            },
50        );
51    }
52
53    /// Clear the cache
54    pub fn clear(&mut self) {
55        self.entries.clear();
56    }
57
58    /// Remove expired entries
59    pub fn cleanup(&mut self) {
60        let now = Instant::now();
61        self.entries
62            .retain(|_, entry| now.duration_since(entry.inserted_at) < self.ttl);
63    }
64
65    /// Invalidate a specific entry
66    pub fn invalidate(&mut self, key: &K) {
67        self.entries.remove(key);
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use std::thread;
75
76    #[test]
77    fn test_cache_basic() {
78        let mut cache = Cache::new(Duration::from_secs(60));
79
80        cache.insert("key1", "value1");
81        assert_eq!(cache.get(&"key1"), Some("value1"));
82        assert_eq!(cache.get(&"key2"), None);
83    }
84
85    #[test]
86    fn test_cache_expiration() {
87        let mut cache = Cache::new(Duration::from_millis(50));
88
89        cache.insert("key1", "value1");
90        assert_eq!(cache.get(&"key1"), Some("value1"));
91
92        thread::sleep(Duration::from_millis(60));
93        assert_eq!(cache.get(&"key1"), None);
94    }
95
96    #[test]
97    fn test_cache_cleanup() {
98        let mut cache = Cache::new(Duration::from_millis(50));
99
100        cache.insert("key1", "value1");
101        cache.insert("key2", "value2");
102
103        thread::sleep(Duration::from_millis(60));
104
105        cache.insert("key3", "value3");
106        cache.cleanup();
107
108        assert_eq!(cache.entries.len(), 1);
109        assert_eq!(cache.get(&"key3"), Some("value3"));
110    }
111}