1use std::collections::HashMap;
28use std::hash::Hash;
29use std::sync::{Arc, RwLock};
30
31#[derive(Debug, Clone, Default)]
33pub struct MemoryStoreConfig {
34 pub max_entries: usize,
36 pub track_stats: bool,
38}
39
40impl MemoryStoreConfig {
41 pub fn with_max_entries(max_entries: usize) -> Self {
43 Self {
44 max_entries,
45 ..Default::default()
46 }
47 }
48
49 pub fn with_stats(mut self) -> Self {
51 self.track_stats = true;
52 self
53 }
54}
55
56#[derive(Debug, Clone, Default)]
58pub struct MemoryStoreStats {
59 pub hits: u64,
61 pub misses: u64,
63 pub insertions: u64,
65 pub removals: u64,
67 pub evictions: u64,
69}
70
71impl MemoryStoreStats {
72 pub fn hit_rate(&self) -> f64 {
74 let total = self.hits + self.misses;
75 if total == 0 {
76 0.0
77 } else {
78 self.hits as f64 / total as f64
79 }
80 }
81}
82
83#[derive(Debug)]
94pub struct MemoryStore<V, K = String>
95where
96 K: Eq + Hash + Clone,
97 V: Clone,
98{
99 data: Arc<RwLock<HashMap<K, V>>>,
100 config: MemoryStoreConfig,
101 stats: Arc<RwLock<MemoryStoreStats>>,
102}
103
104impl<V, K> Clone for MemoryStore<V, K>
105where
106 K: Eq + Hash + Clone,
107 V: Clone,
108{
109 fn clone(&self) -> Self {
110 Self {
111 data: Arc::clone(&self.data),
112 config: self.config.clone(),
113 stats: Arc::clone(&self.stats),
114 }
115 }
116}
117
118impl<V> Default for MemoryStore<V, String>
119where
120 V: Clone,
121{
122 fn default() -> Self {
123 Self::new(MemoryStoreConfig::default())
124 }
125}
126
127impl<V, K> MemoryStore<V, K>
128where
129 K: Eq + Hash + Clone,
130 V: Clone,
131{
132 pub fn new(config: MemoryStoreConfig) -> Self {
134 Self {
135 data: Arc::new(RwLock::new(HashMap::new())),
136 config,
137 stats: Arc::new(RwLock::new(MemoryStoreStats::default())),
138 }
139 }
140
141 pub fn insert(&self, key: K, value: V) {
146 let mut data = self.data.write().unwrap();
147
148 if self.config.max_entries > 0 && data.len() >= self.config.max_entries {
150 if let Some(first_key) = data.keys().next().cloned() {
152 data.remove(&first_key);
153 if self.config.track_stats {
154 self.stats.write().unwrap().evictions += 1;
155 }
156 }
157 }
158
159 data.insert(key, value);
160
161 if self.config.track_stats {
162 self.stats.write().unwrap().insertions += 1;
163 }
164 }
165
166 pub fn get(&self, key: &K) -> Option<V> {
170 let data = self.data.read().unwrap();
171 let result = data.get(key).cloned();
172
173 if self.config.track_stats {
174 let mut stats = self.stats.write().unwrap();
175 if result.is_some() {
176 stats.hits += 1;
177 } else {
178 stats.misses += 1;
179 }
180 }
181
182 result
183 }
184
185 pub fn contains(&self, key: &K) -> bool {
187 self.data.read().unwrap().contains_key(key)
188 }
189
190 pub fn remove(&self, key: &K) -> Option<V> {
194 let result = self.data.write().unwrap().remove(key);
195
196 if self.config.track_stats && result.is_some() {
197 self.stats.write().unwrap().removals += 1;
198 }
199
200 result
201 }
202
203 pub fn clear(&self) {
205 self.data.write().unwrap().clear();
206 }
207
208 pub fn len(&self) -> usize {
210 self.data.read().unwrap().len()
211 }
212
213 pub fn is_empty(&self) -> bool {
215 self.data.read().unwrap().is_empty()
216 }
217
218 pub fn keys(&self) -> Vec<K> {
220 self.data.read().unwrap().keys().cloned().collect()
221 }
222
223 pub fn stats(&self) -> MemoryStoreStats {
227 self.stats.read().unwrap().clone()
228 }
229
230 pub fn reset_stats(&self) {
232 *self.stats.write().unwrap() = MemoryStoreStats::default();
233 }
234
235 pub fn get_or_insert_with<F>(&self, key: K, factory: F) -> V
241 where
242 F: FnOnce() -> V,
243 {
244 if let Some(value) = self.get(&key) {
246 return value;
247 }
248
249 let mut data = self.data.write().unwrap();
251
252 if let Some(value) = data.get(&key) {
254 if self.config.track_stats {
255 self.stats.write().unwrap().hits += 1;
256 }
257 return value.clone();
258 }
259
260 let value = factory();
262
263 if self.config.max_entries > 0 && data.len() >= self.config.max_entries {
265 if let Some(first_key) = data.keys().next().cloned() {
266 data.remove(&first_key);
267 if self.config.track_stats {
268 self.stats.write().unwrap().evictions += 1;
269 }
270 }
271 }
272
273 data.insert(key, value.clone());
274
275 if self.config.track_stats {
276 let mut stats = self.stats.write().unwrap();
277 stats.misses += 1;
278 stats.insertions += 1;
279 }
280
281 value
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn test_basic_operations() {
291 let store: MemoryStore<String> = MemoryStore::default();
292
293 store.insert("key1".to_string(), "value1".to_string());
294 assert_eq!(store.get(&"key1".to_string()), Some("value1".to_string()));
295 assert!(store.contains(&"key1".to_string()));
296 assert_eq!(store.len(), 1);
297
298 store.remove(&"key1".to_string());
299 assert!(!store.contains(&"key1".to_string()));
300 assert!(store.is_empty());
301 }
302
303 #[test]
304 fn test_max_entries() {
305 let config = MemoryStoreConfig::with_max_entries(2).with_stats();
306 let store: MemoryStore<i32> = MemoryStore::new(config);
307
308 store.insert("a".to_string(), 1);
309 store.insert("b".to_string(), 2);
310 assert_eq!(store.len(), 2);
311
312 store.insert("c".to_string(), 3);
314 assert_eq!(store.len(), 2);
315 assert_eq!(store.stats().evictions, 1);
316 }
317
318 #[test]
319 fn test_stats_tracking() {
320 let config = MemoryStoreConfig::default().with_stats();
321 let store: MemoryStore<String> = MemoryStore::new(config);
322
323 store.insert("key1".to_string(), "value1".to_string());
324 assert_eq!(store.stats().insertions, 1);
325
326 store.get(&"key1".to_string());
327 assert_eq!(store.stats().hits, 1);
328
329 store.get(&"nonexistent".to_string());
330 assert_eq!(store.stats().misses, 1);
331
332 let rate = store.stats().hit_rate();
333 assert!((rate - 0.5).abs() < 0.01);
334 }
335
336 #[test]
337 fn test_get_or_insert_with() {
338 let store: MemoryStore<i32> = MemoryStore::default();
339
340 let value = store.get_or_insert_with("key1".to_string(), || 42);
341 assert_eq!(value, 42);
342
343 let value = store.get_or_insert_with("key1".to_string(), || 100);
345 assert_eq!(value, 42);
346 }
347
348 #[test]
349 fn test_clear() {
350 let store: MemoryStore<String> = MemoryStore::default();
351
352 store.insert("a".to_string(), "1".to_string());
353 store.insert("b".to_string(), "2".to_string());
354 assert_eq!(store.len(), 2);
355
356 store.clear();
357 assert!(store.is_empty());
358 }
359
360 #[test]
361 fn test_keys() {
362 let store: MemoryStore<i32> = MemoryStore::default();
363
364 store.insert("a".to_string(), 1);
365 store.insert("b".to_string(), 2);
366
367 let mut keys = store.keys();
368 keys.sort();
369 assert_eq!(keys, vec!["a".to_string(), "b".to_string()]);
370 }
371
372 #[test]
373 fn test_clone_shares_data() {
374 let store1: MemoryStore<String> = MemoryStore::default();
375 store1.insert("key".to_string(), "value".to_string());
376
377 let store2 = store1.clone();
378 assert_eq!(store2.get(&"key".to_string()), Some("value".to_string()));
379
380 store2.insert("key2".to_string(), "value2".to_string());
382 assert_eq!(store1.get(&"key2".to_string()), Some("value2".to_string()));
383 }
384
385 #[test]
386 fn test_custom_key_type() {
387 let store: MemoryStore<String, i32> = MemoryStore::new(MemoryStoreConfig::default());
388
389 store.insert(1, "one".to_string());
390 store.insert(2, "two".to_string());
391
392 assert_eq!(store.get(&1), Some("one".to_string()));
393 assert_eq!(store.get(&2), Some("two".to_string()));
394 }
395}