context_creator/core/
semantic_cache.rs1use crate::core::semantic::analyzer::AnalysisResult;
7use dashmap::DashMap;
8use std::path::{Path, PathBuf};
9use std::sync::Arc;
10
11#[derive(Debug, Clone, Hash, PartialEq, Eq)]
13struct CacheKey {
14 path: PathBuf,
15 content_hash: u64,
16}
17
18pub struct SemanticCache {
20 cache: DashMap<CacheKey, Arc<AnalysisResult>>,
21}
22
23impl SemanticCache {
24 pub fn new() -> Self {
26 SemanticCache {
27 cache: DashMap::new(),
28 }
29 }
30
31 pub fn get(&self, path: &Path, content_hash: u64) -> Option<Arc<AnalysisResult>> {
33 let key = CacheKey {
34 path: path.to_path_buf(),
35 content_hash,
36 };
37 self.cache.get(&key).map(|entry| entry.clone())
38 }
39
40 pub fn insert(&self, path: &Path, content_hash: u64, result: AnalysisResult) {
42 let key = CacheKey {
43 path: path.to_path_buf(),
44 content_hash,
45 };
46 self.cache.insert(key, Arc::new(result));
47 }
48
49 pub fn stats(&self) -> CacheStats {
51 CacheStats {
52 entries: self.cache.len(),
53 }
54 }
55
56 pub fn clear(&self) {
58 self.cache.clear();
59 }
60}
61
62impl Default for SemanticCache {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68#[derive(Debug, Clone)]
70pub struct CacheStats {
71 pub entries: usize,
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use crate::core::semantic::analyzer::Import;
78
79 #[test]
80 fn test_cache_hit_returns_same_result() {
81 let cache = SemanticCache::new();
82 let path = PathBuf::from("/test/file.rs");
83 let content_hash = 12345u64;
84
85 let result = AnalysisResult {
86 imports: vec![Import {
87 module: "std::collections".to_string(),
88 items: vec!["HashMap".to_string()],
89 is_relative: false,
90 line: 1,
91 }],
92 function_calls: vec![],
93 type_references: vec![],
94 exported_functions: vec![],
95 errors: vec![],
96 };
97
98 cache.insert(&path, content_hash, result);
100
101 let cached = cache.get(&path, content_hash).unwrap();
103 assert_eq!(cached.imports.len(), 1);
104 assert_eq!(cached.imports[0].module, "std::collections");
105 }
106
107 #[test]
108 fn test_cache_miss_on_different_hash() {
109 let cache = SemanticCache::new();
110 let path = PathBuf::from("/test/file.rs");
111
112 let result = AnalysisResult::default();
113 cache.insert(&path, 12345, result);
114
115 assert!(cache.get(&path, 67890).is_none());
117 }
118
119 #[test]
120 fn test_cache_miss_on_different_path() {
121 let cache = SemanticCache::new();
122 let path1 = PathBuf::from("/test/file1.rs");
123 let path2 = PathBuf::from("/test/file2.rs");
124
125 let result = AnalysisResult::default();
126 cache.insert(&path1, 12345, result);
127
128 assert!(cache.get(&path2, 12345).is_none());
130 }
131
132 #[test]
133 fn test_cache_stats() {
134 let cache = SemanticCache::new();
135 assert_eq!(cache.stats().entries, 0);
136
137 cache.insert(&PathBuf::from("/test1.rs"), 111, AnalysisResult::default());
138 cache.insert(&PathBuf::from("/test2.rs"), 222, AnalysisResult::default());
139
140 assert_eq!(cache.stats().entries, 2);
141 }
142
143 #[test]
144 fn test_cache_clear() {
145 let cache = SemanticCache::new();
146
147 cache.insert(&PathBuf::from("/test.rs"), 123, AnalysisResult::default());
148 assert_eq!(cache.stats().entries, 1);
149
150 cache.clear();
151 assert_eq!(cache.stats().entries, 0);
152 }
153}