rusty_files/storage/
cache.rs1use 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}