supa_mdx_lint/utils/
lru.rs
1use std::{
2 borrow::Borrow,
3 collections::{HashMap, VecDeque},
4 hash::Hash,
5};
6
7pub(crate) struct LruCache<K, V>
8where
9 K: Eq + Hash + Clone,
10{
11 capacity: usize,
12 inner: HashMap<K, V>,
13 usage_queue: VecDeque<K>,
14}
15
16impl<K, V> std::fmt::Debug for LruCache<K, V>
17where
18 K: std::fmt::Debug + Eq + Hash + Clone,
19 V: std::fmt::Debug,
20{
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 f.debug_struct("LruCache")
23 .field("capacity", &self.capacity)
24 .field("inner", &self.inner)
25 .field("usage_queue", &self.usage_queue)
26 .finish()
27 }
28}
29
30impl<K, V> Default for LruCache<K, V>
31where
32 K: Eq + Hash + Clone,
33{
34 fn default() -> Self {
35 Self {
36 capacity: 10,
37 inner: HashMap::default(),
38 usage_queue: VecDeque::default(),
39 }
40 }
41}
42
43impl<K, V> LruCache<K, V>
44where
45 K: Eq + Hash + Clone,
46{
47 pub(crate) fn contains_key<Q>(&self, key: &Q) -> bool
48 where
49 K: Borrow<Q>,
50 Q: Hash + Eq + ?Sized,
51 {
52 self.inner.contains_key(key)
53 }
54
55 pub(crate) fn insert(&mut self, key: K, value: V) -> Option<V> {
56 if self.inner.contains_key(&key) {
57 self.usage_queue.retain(|k| k != &key);
58 } else if self.inner.len().saturating_add(1) > self.capacity {
59 if let Some(lru_key) = self.usage_queue.pop_front() {
60 self.inner.remove(&lru_key);
61 }
62 }
63
64 self.usage_queue.push_back(key.clone());
65 self.inner.insert(key, value)
66 }
67
68 pub(crate) fn get(&mut self, key: &K) -> Option<&V> {
69 if self.inner.contains_key(key) {
70 self.usage_queue.retain(|k| k != key);
71 self.usage_queue.push_back(key.clone());
72 }
73 self.inner.get(key)
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn test_lru_cache_basic() {
83 let mut cache = LruCache::<String, i32>::default();
84
85 assert_eq!(cache.insert("a".to_string(), 1), None);
87 assert_eq!(cache.insert("b".to_string(), 2), None);
88 assert_eq!(cache.insert("c".to_string(), 3), None);
89
90 assert!(cache.contains_key("a"));
92 assert!(cache.contains_key("b"));
93 assert!(cache.contains_key("c"));
94
95 assert_eq!(cache.insert("b".to_string(), 22), Some(2));
97 assert!(cache.contains_key("b"));
98 assert_eq!(cache.get(&"b".to_string()), Some(&22));
99 }
100
101 #[test]
102 fn test_lru_cache_eviction() {
103 let mut cache = LruCache::<String, i32>::default();
104 cache.capacity = 3;
105
106 cache.insert("a".to_string(), 1);
108 cache.insert("b".to_string(), 2);
109 cache.insert("c".to_string(), 3);
110
111 assert!(cache.contains_key("a"));
113 assert!(cache.contains_key("b"));
114 assert!(cache.contains_key("c"));
115
116 cache.insert("d".to_string(), 4);
118 assert!(!cache.contains_key("a"));
119 assert!(cache.contains_key("b"));
120 assert!(cache.contains_key("c"));
121 assert!(cache.contains_key("d"));
122
123 cache.insert("b".to_string(), 22);
126
127 cache.insert("e".to_string(), 5);
129 assert!(!cache.contains_key("a"));
130 assert!(cache.contains_key("b"));
131 assert!(!cache.contains_key("c"));
132 assert!(cache.contains_key("d"));
133 assert!(cache.contains_key("e"));
134 }
135}