sqry_core/query/cache/
mod.rs1mod ast_parse_cache;
4mod budget;
5mod result_cache;
6mod types;
7
8pub use ast_parse_cache::AstParseCache;
9pub use budget::{BudgetConfig, BudgetStats, CacheBudgetController, ClampAction, ClampReason};
10pub use result_cache::ResultCache;
11pub use types::{CacheKey, CacheStats};
12
13use std::collections::HashMap;
14use std::collections::hash_map::DefaultHasher;
15use std::hash::{BuildHasher, Hasher};
16use std::path::{Path, PathBuf};
17use std::time::SystemTime;
18
19#[derive(Debug, Clone)]
21pub struct FileMetadata {
22 pub path: PathBuf,
24 pub modified_time: SystemTime,
26 pub size: u64,
28}
29
30#[must_use]
35pub fn compute_file_set_hash<S>(file_metadata: &HashMap<PathBuf, FileMetadata, S>) -> u64
36where
37 S: BuildHasher,
38{
39 let mut hasher = DefaultHasher::new();
40
41 let mut paths: Vec<_> = file_metadata.keys().collect();
43 paths.sort();
44
45 for path in paths {
46 let meta = &file_metadata[path];
47
48 hasher.write(path.to_string_lossy().as_bytes());
51
52 if let Ok(duration) = meta.modified_time.duration_since(std::time::UNIX_EPOCH) {
54 hasher.write(&duration.as_millis().to_le_bytes());
55 }
56
57 hasher.write(&meta.size.to_le_bytes());
58 }
59
60 hasher.finish()
61}
62
63#[must_use]
67pub fn compute_root_path_hash(root: &Path) -> u64 {
68 let mut hasher = DefaultHasher::new();
69 hasher.write(root.to_string_lossy().as_bytes());
71 hasher.finish()
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use std::time::SystemTime;
78
79 #[test]
80 fn file_set_hash_deterministic() {
81 let mut metadata = HashMap::new();
82 metadata.insert(
83 PathBuf::from("file1.rs"),
84 FileMetadata {
85 path: PathBuf::from("file1.rs"),
86 modified_time: SystemTime::now(),
87 size: 100,
88 },
89 );
90
91 let hash1 = compute_file_set_hash(&metadata);
92 let hash2 = compute_file_set_hash(&metadata);
93
94 assert_eq!(hash1, hash2, "Hash should be deterministic");
95 }
96
97 #[test]
98 fn root_path_hash_deterministic() {
99 let path = Path::new("/home/user/project");
100 let hash1 = compute_root_path_hash(path);
101 let hash2 = compute_root_path_hash(path);
102
103 assert_eq!(hash1, hash2, "Hash should be deterministic");
104 }
105
106 #[test]
107 fn file_set_hash_changes_on_modification() {
108 let time1 = SystemTime::now();
109 let mut metadata1 = HashMap::new();
110 metadata1.insert(
111 PathBuf::from("file1.rs"),
112 FileMetadata {
113 path: PathBuf::from("file1.rs"),
114 modified_time: time1,
115 size: 100,
116 },
117 );
118
119 let mut metadata2 = HashMap::new();
120 metadata2.insert(
121 PathBuf::from("file1.rs"),
122 FileMetadata {
123 path: PathBuf::from("file1.rs"),
124 modified_time: time1,
125 size: 200, },
127 );
128
129 let hash1 = compute_file_set_hash(&metadata1);
130 let hash2 = compute_file_set_hash(&metadata2);
131
132 assert_ne!(hash1, hash2, "Hash should change when file changes");
133 }
134}