rusty_files/storage/
cache.rs

1use crate::core::types::FileEntry;
2use parking_lot::RwLock;
3use std::collections::{HashMap, VecDeque};
4use std::path::PathBuf;
5
6pub struct LruCache {
7    capacity: usize,
8    cache: RwLock<LruCacheInner>,
9}
10
11struct LruCacheInner {
12    map: HashMap<PathBuf, FileEntry>,
13    order: VecDeque<PathBuf>,
14}
15
16impl LruCache {
17    pub fn new(capacity: usize) -> Self {
18        Self {
19            capacity,
20            cache: RwLock::new(LruCacheInner {
21                map: HashMap::with_capacity(capacity),
22                order: VecDeque::with_capacity(capacity),
23            }),
24        }
25    }
26
27    pub fn get(&self, path: &PathBuf) -> Option<FileEntry> {
28        let mut cache = self.cache.write();
29
30        if let Some(entry) = cache.map.get(path).cloned() {
31            if let Some(pos) = cache.order.iter().position(|p| p == path) {
32                cache.order.remove(pos);
33            }
34            cache.order.push_back(path.clone());
35            Some(entry)
36        } else {
37            None
38        }
39    }
40
41    pub fn insert(&self, path: PathBuf, entry: FileEntry) {
42        let mut cache = self.cache.write();
43
44        if cache.map.contains_key(&path) {
45            if let Some(pos) = cache.order.iter().position(|p| p == &path) {
46                cache.order.remove(pos);
47            }
48        } else if cache.map.len() >= self.capacity {
49            if let Some(old_path) = cache.order.pop_front() {
50                cache.map.remove(&old_path);
51            }
52        }
53
54        cache.map.insert(path.clone(), entry);
55        cache.order.push_back(path);
56    }
57
58    pub fn remove(&self, path: &PathBuf) -> Option<FileEntry> {
59        let mut cache = self.cache.write();
60
61        if let Some(pos) = cache.order.iter().position(|p| p == path) {
62            cache.order.remove(pos);
63        }
64
65        cache.map.remove(path)
66    }
67
68    pub fn clear(&self) {
69        let mut cache = self.cache.write();
70        cache.map.clear();
71        cache.order.clear();
72    }
73
74    pub fn len(&self) -> usize {
75        self.cache.read().map.len()
76    }
77
78    pub fn is_empty(&self) -> bool {
79        self.cache.read().map.is_empty()
80    }
81
82    pub fn contains(&self, path: &PathBuf) -> bool {
83        self.cache.read().map.contains_key(path)
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90    use crate::core::types::FileEntry;
91
92    #[test]
93    fn test_lru_cache_basic_operations() {
94        let cache = LruCache::new(2);
95        let path1 = PathBuf::from("/test/file1.txt");
96        let path2 = PathBuf::from("/test/file2.txt");
97
98        let entry1 = FileEntry::new(path1.clone());
99        let entry2 = FileEntry::new(path2.clone());
100
101        cache.insert(path1.clone(), entry1.clone());
102        assert_eq!(cache.len(), 1);
103
104        cache.insert(path2.clone(), entry2.clone());
105        assert_eq!(cache.len(), 2);
106
107        assert!(cache.contains(&path1));
108        assert!(cache.contains(&path2));
109    }
110
111    #[test]
112    fn test_lru_cache_eviction() {
113        let cache = LruCache::new(2);
114        let path1 = PathBuf::from("/test/file1.txt");
115        let path2 = PathBuf::from("/test/file2.txt");
116        let path3 = PathBuf::from("/test/file3.txt");
117
118        cache.insert(path1.clone(), FileEntry::new(path1.clone()));
119        cache.insert(path2.clone(), FileEntry::new(path2.clone()));
120        cache.insert(path3.clone(), FileEntry::new(path3.clone()));
121
122        assert_eq!(cache.len(), 2);
123        assert!(!cache.contains(&path1));
124        assert!(cache.contains(&path2));
125        assert!(cache.contains(&path3));
126    }
127
128    #[test]
129    fn test_lru_cache_get_updates_order() {
130        let cache = LruCache::new(2);
131        let path1 = PathBuf::from("/test/file1.txt");
132        let path2 = PathBuf::from("/test/file2.txt");
133        let path3 = PathBuf::from("/test/file3.txt");
134
135        cache.insert(path1.clone(), FileEntry::new(path1.clone()));
136        cache.insert(path2.clone(), FileEntry::new(path2.clone()));
137
138        cache.get(&path1);
139
140        cache.insert(path3.clone(), FileEntry::new(path3.clone()));
141
142        assert!(cache.contains(&path1));
143        assert!(!cache.contains(&path2));
144        assert!(cache.contains(&path3));
145    }
146}