clnrm_core/cache/
memory_cache.rs1use super::cache_trait::{Cache, CacheStats};
7use super::hash;
8use crate::error::{CleanroomError, Result};
9use chrono::Utc;
10use std::collections::HashMap;
11use std::path::Path;
12use std::sync::{Arc, Mutex};
13use tracing::debug;
14
15#[derive(Debug, Clone)]
41pub struct MemoryCache {
42 hashes: Arc<Mutex<HashMap<String, String>>>,
44}
45
46impl MemoryCache {
47 pub fn new() -> Self {
49 Self {
50 hashes: Arc::new(Mutex::new(HashMap::new())),
51 }
52 }
53
54 pub fn len(&self) -> usize {
58 self.hashes.lock().map(|h| h.len()).unwrap_or_else(|e| {
59 debug!("Failed to acquire cache lock in len(): {}", e);
62 0
63 })
64 }
65
66 pub fn is_empty(&self) -> bool {
68 self.len() == 0
69 }
70}
71
72impl Cache for MemoryCache {
73 fn has_changed(&self, file_path: &Path, rendered_content: &str) -> Result<bool> {
74 let file_key = file_path
75 .to_str()
76 .ok_or_else(|| CleanroomError::validation_error("Invalid file path encoding"))?
77 .to_string();
78
79 let current_hash = hash::hash_content(rendered_content)?;
81
82 let hashes = self.hashes.lock().map_err(|e| {
84 CleanroomError::internal_error(format!("Failed to acquire cache lock: {}", e))
85 })?;
86
87 match hashes.get(&file_key) {
88 Some(cached_hash) if cached_hash == ¤t_hash => {
89 debug!("Memory cache hit: {} (unchanged)", file_key);
90 Ok(false)
91 }
92 Some(_) => {
93 debug!("Memory cache miss: {} (changed)", file_key);
94 Ok(true)
95 }
96 None => {
97 debug!("Memory cache miss: {} (new file)", file_key);
98 Ok(true)
99 }
100 }
101 }
102
103 fn update(&self, file_path: &Path, rendered_content: &str) -> Result<()> {
104 let file_key = file_path
105 .to_str()
106 .ok_or_else(|| CleanroomError::validation_error("Invalid file path encoding"))?
107 .to_string();
108
109 let hash = hash::hash_content(rendered_content)?;
110
111 let mut hashes = self.hashes.lock().map_err(|e| {
112 CleanroomError::internal_error(format!("Failed to acquire cache lock: {}", e))
113 })?;
114
115 hashes.insert(file_key.clone(), hash);
116 debug!("Memory cache updated: {}", file_key);
117
118 Ok(())
119 }
120
121 fn remove(&self, file_path: &Path) -> Result<()> {
122 let file_key = file_path
123 .to_str()
124 .ok_or_else(|| CleanroomError::validation_error("Invalid file path encoding"))?
125 .to_string();
126
127 let mut hashes = self.hashes.lock().map_err(|e| {
128 CleanroomError::internal_error(format!("Failed to acquire cache lock: {}", e))
129 })?;
130
131 if hashes.remove(&file_key).is_some() {
132 debug!("Removed from memory cache: {}", file_key);
133 }
134
135 Ok(())
136 }
137
138 fn save(&self) -> Result<()> {
139 Ok(())
141 }
142
143 fn stats(&self) -> Result<CacheStats> {
144 let hashes = self.hashes.lock().map_err(|e| {
145 CleanroomError::internal_error(format!("Failed to acquire cache lock: {}", e))
146 })?;
147
148 Ok(CacheStats {
149 total_files: hashes.len(),
150 last_updated: Utc::now(),
151 cache_path: None,
152 })
153 }
154
155 fn clear(&self) -> Result<()> {
156 let mut hashes = self.hashes.lock().map_err(|e| {
157 CleanroomError::internal_error(format!("Failed to acquire cache lock: {}", e))
158 })?;
159
160 hashes.clear();
161 debug!("Memory cache cleared");
162
163 Ok(())
164 }
165}
166
167impl Default for MemoryCache {
168 fn default() -> Self {
169 Self::new()
170 }
171}