use std::collections::{HashMap, VecDeque}; use std::hash::Hash; /// A small, safe LRU cache. /// /// The most recently used key is stored at the back of `order`; when capacity is /// exceeded, the least recently used key is evicted from the front. #[derive(Debug, Clone)] pub struct LruCache { capacity: usize, map: HashMap, order: VecDeque, } impl LruCache where K: Eq + Hash + Clone, { /// Creates an empty cache with the given maximum capacity. pub fn new(capacity: usize) -> Self { Self { capacity, map: HashMap::new(), order: VecDeque::new(), } } pub fn capacity(&self) -> usize { self.capacity } pub fn len(&self) -> usize { self.map.len() } pub fn is_empty(&self) -> bool { self.map.is_empty() } pub fn contains_key(&self, key: &K) -> bool { self.map.contains_key(key) } /// Returns a value by key and marks it as recently used. pub fn get(&mut self, key: &K) -> Option<&V> { if !self.map.contains_key(key) { return None; } self.touch(key); self.map.get(key) } /// Inserts or updates a value. /// /// Returns the previous value when updating an existing key. pub fn put(&mut self, key: K, value: V) -> Option { if self.capacity == 0 { return None; } if self.map.contains_key(&key) { self.touch(&key); return self.map.insert(key, value); } if self.map.len() == self.capacity { if let Some(oldest) = self.order.pop_front() { self.map.remove(&oldest); } } self.order.push_back(key.clone()); self.map.insert(key, value) } /// Removes a key from the cache. pub fn remove(&mut self, key: &K) -> Option { self.order.retain(|existing| existing != key); self.map.remove(key) } pub fn clear(&mut self) { self.map.clear(); self.order.clear(); } fn touch(&mut self, key: &K) { self.order.retain(|existing| existing != key); self.order.push_back(key.clone()); } } #[cfg(test)] mod tests { use super::LruCache; #[test] fn evicts_least_recently_used_entry() { let mut cache = LruCache::new(2); cache.put("a", 1); cache.put("b", 2); cache.get(&"a"); cache.put("c", 3); assert_eq!(cache.get(&"a"), Some(&1)); assert_eq!(cache.get(&"b"), None); assert_eq!(cache.get(&"c"), Some(&3)); } #[test] fn updating_existing_key_preserves_capacity_and_refreshes_recency() { let mut cache = LruCache::new(2); cache.put(1, "one"); cache.put(2, "two"); assert_eq!(cache.put(1, "ONE"), Some("one")); cache.put(3, "three"); assert_eq!(cache.len(), 2); assert_eq!(cache.get(&1), Some(&"ONE")); assert_eq!(cache.get(&2), None); assert_eq!(cache.get(&3), Some(&"three")); } }