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.retain(|_, entry| {
62            now.duration_since(entry.inserted_at) < self.ttl
63        });
64    }
65    
66    /// Invalidate a specific entry
67    pub fn invalidate(&mut self, key: &K) {
68        self.entries.remove(key);
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use std::thread;
76    
77    #[test]
78    fn test_cache_basic() {
79        let mut cache = Cache::new(Duration::from_secs(60));
80        
81        cache.insert("key1", "value1");
82        assert_eq!(cache.get(&"key1"), Some("value1"));
83        assert_eq!(cache.get(&"key2"), None);
84    }
85    
86    #[test]
87    fn test_cache_expiration() {
88        let mut cache = Cache::new(Duration::from_millis(50));
89        
90        cache.insert("key1", "value1");
91        assert_eq!(cache.get(&"key1"), Some("value1"));
92        
93        thread::sleep(Duration::from_millis(60));
94        assert_eq!(cache.get(&"key1"), None);
95    }
96    
97    #[test]
98    fn test_cache_cleanup() {
99        let mut cache = Cache::new(Duration::from_millis(50));
100        
101        cache.insert("key1", "value1");
102        cache.insert("key2", "value2");
103        
104        thread::sleep(Duration::from_millis(60));
105        
106        cache.insert("key3", "value3");
107        cache.cleanup();
108        
109        assert_eq!(cache.entries.len(), 1);
110        assert_eq!(cache.get(&"key3"), Some("value3"));
111    }
112}