rust_filesearch/fs/
watch.rs1#[cfg(feature = "watch")]
2use crate::errors::{FsError, Result};
3#[cfg(feature = "watch")]
4use crate::models::WatchEvent;
5#[cfg(feature = "watch")]
6use notify::{Event, EventKind, RecursiveMode, Watcher};
7#[cfg(feature = "watch")]
8use std::path::Path;
9#[cfg(feature = "watch")]
10use std::sync::mpsc::channel;
11#[cfg(feature = "watch")]
12use std::time::Duration;
13
14#[cfg(feature = "watch")]
15pub struct FileWatcher {
16 events: Vec<String>,
17}
18
19#[cfg(feature = "watch")]
20impl FileWatcher {
21 pub fn new(events: Vec<String>) -> Self {
22 Self { events }
23 }
24
25 pub fn watch<F>(&self, path: &Path, mut callback: F) -> Result<()>
26 where
27 F: FnMut(WatchEvent),
28 {
29 let (tx, rx) = channel();
30
31 let mut watcher = notify::recommended_watcher(move |res: notify::Result<Event>| {
32 if let Ok(event) = res {
33 let _ = tx.send(event);
34 }
35 })
36 .map_err(|e| FsError::Watch(e.to_string()))?;
37
38 watcher
39 .watch(path, RecursiveMode::Recursive)
40 .map_err(|e| FsError::Watch(e.to_string()))?;
41
42 println!(
43 "Watching {} for changes... (Ctrl+C to stop)",
44 path.display()
45 );
46
47 loop {
48 match rx.recv_timeout(Duration::from_millis(100)) {
49 Ok(event) => {
50 if let Some(watch_event) = self.process_event(event) {
51 callback(watch_event);
52 }
53 }
54 Err(std::sync::mpsc::RecvTimeoutError::Timeout) => continue,
55 Err(_) => break,
56 }
57 }
58
59 Ok(())
60 }
61
62 fn process_event(&self, event: Event) -> Option<WatchEvent> {
63 let event_type = match event.kind {
64 EventKind::Create(_) => "create",
65 EventKind::Modify(_) => "modify",
66 EventKind::Remove(_) => "remove",
67 _ => return None,
68 };
69
70 if !self.events.is_empty() && !self.events.contains(&event_type.to_string()) {
72 return None;
73 }
74
75 let path = event.paths.first()?.clone();
77
78 let (mtime, size) = if let Ok(metadata) = std::fs::metadata(&path) {
80 let mtime = metadata.modified().ok().map(chrono::DateTime::from);
81 let size = Some(metadata.len());
82 (mtime, size)
83 } else {
84 (None, None)
85 };
86
87 Some(WatchEvent {
88 event: event_type.to_string(),
89 path,
90 mtime,
91 size,
92 })
93 }
94}
95
96#[cfg(not(feature = "watch"))]
97pub struct FileWatcher;
98
99#[cfg(not(feature = "watch"))]
100impl FileWatcher {
101 pub fn new(_events: Vec<String>) -> Self {
102 Self
103 }
104}