oxihuman_core/
path_cache.rs1#![allow(dead_code)]
4
5use std::collections::HashMap;
6
7#[allow(dead_code)]
9pub struct PathCache {
10 cache: HashMap<String, String>,
11 hits: u64,
12 misses: u64,
13 max_size: usize,
14}
15
16#[allow(dead_code)]
17impl PathCache {
18 pub fn new(max_size: usize) -> Self {
19 Self {
20 cache: HashMap::new(),
21 hits: 0,
22 misses: 0,
23 max_size: max_size.max(1),
24 }
25 }
26 pub fn get(&mut self, key: &str) -> Option<&str> {
27 if self.cache.contains_key(key) {
28 self.hits += 1;
29 self.cache.get(key).map(|s| s.as_str())
30 } else {
31 self.misses += 1;
32 None
33 }
34 }
35 pub fn insert(&mut self, key: &str, resolved: &str) {
36 if self.cache.len() >= self.max_size && !self.cache.contains_key(key) {
37 if let Some(k) = self.cache.keys().next().cloned() {
38 self.cache.remove(&k);
39 }
40 }
41 self.cache.insert(key.to_string(), resolved.to_string());
42 }
43 pub fn invalidate(&mut self, key: &str) -> bool {
44 self.cache.remove(key).is_some()
45 }
46 pub fn contains(&self, key: &str) -> bool {
47 self.cache.contains_key(key)
48 }
49 pub fn len(&self) -> usize {
50 self.cache.len()
51 }
52 pub fn is_empty(&self) -> bool {
53 self.cache.is_empty()
54 }
55 pub fn hits(&self) -> u64 {
56 self.hits
57 }
58 pub fn misses(&self) -> u64 {
59 self.misses
60 }
61 pub fn hit_rate(&self) -> f32 {
62 let t = self.hits + self.misses;
63 if t == 0 {
64 0.0
65 } else {
66 self.hits as f32 / t as f32
67 }
68 }
69 pub fn clear(&mut self) {
70 self.cache.clear();
71 }
72 pub fn max_size(&self) -> usize {
73 self.max_size
74 }
75}
76
77#[allow(dead_code)]
78pub fn new_path_cache(max_size: usize) -> PathCache {
79 PathCache::new(max_size)
80}
81#[allow(dead_code)]
82pub fn pc_get<'a>(c: &'a mut PathCache, key: &str) -> Option<&'a str> {
83 c.get(key)
84}
85#[allow(dead_code)]
86pub fn pc_insert(c: &mut PathCache, key: &str, resolved: &str) {
87 c.insert(key, resolved);
88}
89#[allow(dead_code)]
90pub fn pc_invalidate(c: &mut PathCache, key: &str) -> bool {
91 c.invalidate(key)
92}
93#[allow(dead_code)]
94pub fn pc_contains(c: &PathCache, key: &str) -> bool {
95 c.contains(key)
96}
97#[allow(dead_code)]
98pub fn pc_len(c: &PathCache) -> usize {
99 c.len()
100}
101#[allow(dead_code)]
102pub fn pc_is_empty(c: &PathCache) -> bool {
103 c.is_empty()
104}
105#[allow(dead_code)]
106pub fn pc_hit_rate(c: &PathCache) -> f32 {
107 c.hit_rate()
108}
109#[allow(dead_code)]
110pub fn pc_clear(c: &mut PathCache) {
111 c.clear();
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 #[test]
118 fn test_insert_get() {
119 let mut c = new_path_cache(16);
120 pc_insert(&mut c, "/a", "/resolved/a");
121 assert_eq!(pc_get(&mut c, "/a"), Some("/resolved/a"));
122 }
123 #[test]
124 fn test_miss() {
125 let mut c = new_path_cache(16);
126 assert_eq!(pc_get(&mut c, "/nope"), None);
127 }
128 #[test]
129 fn test_invalidate() {
130 let mut c = new_path_cache(16);
131 pc_insert(&mut c, "/k", "/v");
132 assert!(pc_invalidate(&mut c, "/k"));
133 assert!(!pc_contains(&c, "/k"));
134 }
135 #[test]
136 fn test_hit_rate() {
137 let mut c = new_path_cache(16);
138 pc_insert(&mut c, "/k", "/v");
139 pc_get(&mut c, "/k");
140 pc_get(&mut c, "/missing");
141 let r = pc_hit_rate(&c);
142 assert!((0.0..=1.0).contains(&r));
143 }
144 #[test]
145 fn test_len() {
146 let mut c = new_path_cache(16);
147 pc_insert(&mut c, "/a", "/ra");
148 pc_insert(&mut c, "/b", "/rb");
149 assert_eq!(pc_len(&c), 2);
150 }
151 #[test]
152 fn test_max_size_respected() {
153 let mut c = new_path_cache(2);
154 pc_insert(&mut c, "/a", "/ra");
155 pc_insert(&mut c, "/b", "/rb");
156 pc_insert(&mut c, "/c", "/rc");
157 assert!(pc_len(&c) <= 2);
158 }
159 #[test]
160 fn test_clear() {
161 let mut c = new_path_cache(16);
162 pc_insert(&mut c, "/a", "/ra");
163 pc_clear(&mut c);
164 assert!(pc_is_empty(&c));
165 }
166 #[test]
167 fn test_contains() {
168 let mut c = new_path_cache(16);
169 pc_insert(&mut c, "/x", "/rx");
170 assert!(pc_contains(&c, "/x"));
171 assert!(!pc_contains(&c, "/y"));
172 }
173 #[test]
174 fn test_is_empty() {
175 let c = new_path_cache(8);
176 assert!(pc_is_empty(&c));
177 }
178 #[test]
179 fn test_max_size_getter() {
180 let c = new_path_cache(32);
181 assert_eq!(c.max_size(), 32);
182 }
183}