1use super::file_operations::{DirectoryOperationEvent, FileOperationEvent};
18use std::collections::HashMap;
19use std::path::PathBuf;
20use std::sync::Arc;
21use std::time::SystemTime;
22use tokio::sync::RwLock;
23
24#[derive(Debug, Clone)]
29pub struct FileSystemMonitor {
30 file_hashes: Arc<RwLock<HashMap<PathBuf, String>>>,
32}
33
34impl FileSystemMonitor {
35 pub fn new() -> Self {
37 Self {
38 file_hashes: Arc::new(RwLock::new(HashMap::new())),
39 }
40 }
41
42 pub fn emit_file_created(&self, path: PathBuf, size: u64) -> FileOperationEvent {
53 FileOperationEvent::Created {
54 path,
55 size,
56 timestamp: SystemTime::now(),
57 }
58 }
59
60 pub fn emit_file_modified(
72 &self,
73 path: PathBuf,
74 old_hash: String,
75 new_hash: String,
76 ) -> FileOperationEvent {
77 FileOperationEvent::Modified {
78 path,
79 old_hash,
80 new_hash,
81 timestamp: SystemTime::now(),
82 }
83 }
84
85 pub fn emit_file_deleted(&self, path: PathBuf) -> FileOperationEvent {
95 FileOperationEvent::Deleted {
96 path,
97 timestamp: SystemTime::now(),
98 }
99 }
100
101 pub fn emit_file_renamed(&self, old_path: PathBuf, new_path: PathBuf) -> FileOperationEvent {
112 FileOperationEvent::Renamed {
113 old_path,
114 new_path,
115 timestamp: SystemTime::now(),
116 }
117 }
118
119 pub fn emit_file_moved(&self, old_path: PathBuf, new_path: PathBuf) -> FileOperationEvent {
130 FileOperationEvent::Moved {
131 old_path,
132 new_path,
133 timestamp: SystemTime::now(),
134 }
135 }
136
137 pub fn emit_file_read(&self, path: PathBuf) -> FileOperationEvent {
147 FileOperationEvent::Read {
148 path,
149 timestamp: SystemTime::now(),
150 }
151 }
152
153 pub fn emit_directory_created(&self, path: PathBuf) -> DirectoryOperationEvent {
163 DirectoryOperationEvent::Created {
164 path,
165 timestamp: SystemTime::now(),
166 }
167 }
168
169 pub fn emit_directory_deleted(&self, path: PathBuf) -> DirectoryOperationEvent {
179 DirectoryOperationEvent::Deleted {
180 path,
181 timestamp: SystemTime::now(),
182 }
183 }
184
185 pub async fn track_file_hash(&self, path: PathBuf, hash: String) {
192 let mut hashes = self.file_hashes.write().await;
193 hashes.insert(path, hash);
194 }
195
196 pub async fn get_file_hash(&self, path: &PathBuf) -> Option<String> {
206 let hashes = self.file_hashes.read().await;
207 hashes.get(path).cloned()
208 }
209
210 pub async fn remove_file_hash(&self, path: &PathBuf) {
216 let mut hashes = self.file_hashes.write().await;
217 hashes.remove(path);
218 }
219
220 pub async fn clear_file_hashes(&self) {
222 let mut hashes = self.file_hashes.write().await;
223 hashes.clear();
224 }
225}
226
227impl Default for FileSystemMonitor {
228 fn default() -> Self {
229 Self::new()
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn test_emit_file_created() {
239 let monitor = FileSystemMonitor::new();
240 let path = PathBuf::from("/path/to/file.rs");
241 let event = monitor.emit_file_created(path.clone(), 1024);
242
243 match event {
244 FileOperationEvent::Created { path: p, size, .. } => {
245 assert_eq!(p, path);
246 assert_eq!(size, 1024);
247 }
248 _ => panic!("Expected Created event"),
249 }
250 }
251
252 #[test]
253 fn test_emit_file_modified() {
254 let monitor = FileSystemMonitor::new();
255 let path = PathBuf::from("/path/to/file.rs");
256 let event =
257 monitor.emit_file_modified(path.clone(), "abc123".to_string(), "def456".to_string());
258
259 match event {
260 FileOperationEvent::Modified {
261 path: p,
262 old_hash,
263 new_hash,
264 ..
265 } => {
266 assert_eq!(p, path);
267 assert_eq!(old_hash, "abc123");
268 assert_eq!(new_hash, "def456");
269 }
270 _ => panic!("Expected Modified event"),
271 }
272 }
273
274 #[test]
275 fn test_emit_file_deleted() {
276 let monitor = FileSystemMonitor::new();
277 let path = PathBuf::from("/path/to/file.rs");
278 let event = monitor.emit_file_deleted(path.clone());
279
280 match event {
281 FileOperationEvent::Deleted { path: p, .. } => {
282 assert_eq!(p, path);
283 }
284 _ => panic!("Expected Deleted event"),
285 }
286 }
287
288 #[test]
289 fn test_emit_file_renamed() {
290 let monitor = FileSystemMonitor::new();
291 let old_path = PathBuf::from("/path/to/old.rs");
292 let new_path = PathBuf::from("/path/to/new.rs");
293 let event = monitor.emit_file_renamed(old_path.clone(), new_path.clone());
294
295 match event {
296 FileOperationEvent::Renamed {
297 old_path: op,
298 new_path: np,
299 ..
300 } => {
301 assert_eq!(op, old_path);
302 assert_eq!(np, new_path);
303 }
304 _ => panic!("Expected Renamed event"),
305 }
306 }
307
308 #[test]
309 fn test_emit_file_moved() {
310 let monitor = FileSystemMonitor::new();
311 let old_path = PathBuf::from("/old/path/file.rs");
312 let new_path = PathBuf::from("/new/path/file.rs");
313 let event = monitor.emit_file_moved(old_path.clone(), new_path.clone());
314
315 match event {
316 FileOperationEvent::Moved {
317 old_path: op,
318 new_path: np,
319 ..
320 } => {
321 assert_eq!(op, old_path);
322 assert_eq!(np, new_path);
323 }
324 _ => panic!("Expected Moved event"),
325 }
326 }
327
328 #[test]
329 fn test_emit_file_read() {
330 let monitor = FileSystemMonitor::new();
331 let path = PathBuf::from("/path/to/file.rs");
332 let event = monitor.emit_file_read(path.clone());
333
334 match event {
335 FileOperationEvent::Read { path: p, .. } => {
336 assert_eq!(p, path);
337 }
338 _ => panic!("Expected Read event"),
339 }
340 }
341
342 #[test]
343 fn test_emit_directory_created() {
344 let monitor = FileSystemMonitor::new();
345 let path = PathBuf::from("/path/to/dir");
346 let event = monitor.emit_directory_created(path.clone());
347
348 match event {
349 DirectoryOperationEvent::Created { path: p, .. } => {
350 assert_eq!(p, path);
351 }
352 _ => panic!("Expected Created event"),
353 }
354 }
355
356 #[test]
357 fn test_emit_directory_deleted() {
358 let monitor = FileSystemMonitor::new();
359 let path = PathBuf::from("/path/to/dir");
360 let event = monitor.emit_directory_deleted(path.clone());
361
362 match event {
363 DirectoryOperationEvent::Deleted { path: p, .. } => {
364 assert_eq!(p, path);
365 }
366 _ => panic!("Expected Deleted event"),
367 }
368 }
369
370 #[tokio::test]
371 async fn test_track_file_hash() {
372 let monitor = FileSystemMonitor::new();
373 let path = PathBuf::from("/path/to/file.rs");
374 let hash = "abc123".to_string();
375
376 monitor.track_file_hash(path.clone(), hash.clone()).await;
377 let tracked = monitor.get_file_hash(&path).await;
378
379 assert_eq!(tracked, Some(hash));
380 }
381
382 #[tokio::test]
383 async fn test_remove_file_hash() {
384 let monitor = FileSystemMonitor::new();
385 let path = PathBuf::from("/path/to/file.rs");
386 let hash = "abc123".to_string();
387
388 monitor.track_file_hash(path.clone(), hash).await;
389 monitor.remove_file_hash(&path).await;
390 let tracked = monitor.get_file_hash(&path).await;
391
392 assert_eq!(tracked, None);
393 }
394
395 #[tokio::test]
396 async fn test_clear_file_hashes() {
397 let monitor = FileSystemMonitor::new();
398 let path1 = PathBuf::from("/path/to/file1.rs");
399 let path2 = PathBuf::from("/path/to/file2.rs");
400
401 monitor
402 .track_file_hash(path1.clone(), "hash1".to_string())
403 .await;
404 monitor
405 .track_file_hash(path2.clone(), "hash2".to_string())
406 .await;
407
408 monitor.clear_file_hashes().await;
409
410 assert_eq!(monitor.get_file_hash(&path1).await, None);
411 assert_eq!(monitor.get_file_hash(&path2).await, None);
412 }
413
414 #[test]
415 fn test_file_system_monitor_default() {
416 let monitor = FileSystemMonitor::default();
417 let path = PathBuf::from("/path/to/file.rs");
418 let event = monitor.emit_file_created(path.clone(), 512);
419
420 match event {
421 FileOperationEvent::Created { size, .. } => {
422 assert_eq!(size, 512);
423 }
424 _ => panic!("Expected Created event"),
425 }
426 }
427}