1use crate::{
8 error::{Error, Result},
9 IgnoreRules, MatchResult,
10};
11pub use notify::{
12 Config, Event, EventHandler, PollWatcher, RecommendedWatcher, RecursiveMode, Watcher,
13};
14use std::fs::Metadata;
15use std::path::PathBuf;
16use std::time::Duration;
17use xvc_logging::watch;
18
19use crossbeam_channel::{bounded, Receiver, Sender};
20use log::{debug, warn};
21
22#[derive(Debug, Clone)]
25pub enum PathEvent {
26 Create {
28 path: PathBuf,
30 metadata: Metadata,
32 },
33 Update {
35 path: PathBuf,
37 metadata: Metadata,
39 },
40 Delete {
42 path: PathBuf,
44 },
45}
46
47struct PathEventHandler {
49 sender: Sender<Option<PathEvent>>,
50 ignore_rules: IgnoreRules,
51}
52
53impl EventHandler for PathEventHandler {
54 fn handle_event(&mut self, event: notify::Result<Event>) {
55 watch!(event);
56 if let Ok(event) = event {
57 match event.kind {
58 notify::EventKind::Create(_) => self.create_event(event.paths[0].clone()),
59 notify::EventKind::Modify(mk) => match mk {
60 notify::event::ModifyKind::Any => todo!(),
61 notify::event::ModifyKind::Data(_) => self.write_event(event.paths[0].clone()),
62 notify::event::ModifyKind::Metadata(_) => {
63 self.write_event(event.paths[0].clone())
64 }
65 notify::event::ModifyKind::Name(rk) => match rk {
66 notify::event::RenameMode::Any => {}
67 notify::event::RenameMode::To => self.create_event(event.paths[0].clone()),
68 notify::event::RenameMode::From => {
69 self.remove_event(event.paths[0].clone())
70 }
71 notify::event::RenameMode::Both => {
72 self.rename_event(event.paths[0].clone(), event.paths[1].clone())
73 }
74 notify::event::RenameMode::Other => {}
75 },
76 notify::event::ModifyKind::Other => {}
77 },
78 notify::EventKind::Remove(_) => self.remove_event(event.paths[0].clone()),
79 notify::EventKind::Any => {}
80 notify::EventKind::Access(_) => {}
81 notify::EventKind::Other => {}
82 }
83 } else {
84 debug!("{:?}", event);
85 }
86 }
87}
88
89impl PathEventHandler {
90 fn write_event(&mut self, path: PathBuf) {
91 match self.ignore_rules.check(&path) {
92 MatchResult::Whitelist | MatchResult::NoMatch => {
93 if let Ok(metadata) = path.metadata() {
94 self.sender
95 .send(Some(PathEvent::Create {
96 path: path.clone(),
97 metadata,
98 }))
99 .unwrap_or_else(|e| {
100 Error::from(e).warn();
101 });
102 } else {
103 debug!("Error in metadata for {}", path.to_string_lossy());
104 }
105 }
106 MatchResult::Ignore => {
107 debug!("FS Notification Ignored: {}", path.to_string_lossy());
108 }
109 }
110 }
111
112 fn create_event(&mut self, path: PathBuf) {
113 match self.ignore_rules.check(&path) {
114 MatchResult::Whitelist | MatchResult::NoMatch => {
115 if let Ok(metadata) = path.metadata() {
116 self.sender
117 .send(Some(PathEvent::Create {
118 path: path.clone(),
119 metadata,
120 }))
121 .unwrap_or_else(|e| {
122 Error::from(e).warn();
123 });
124 } else {
125 debug!("Error in metadata for {}", path.to_string_lossy());
126 }
127 }
128 MatchResult::Ignore => {
129 debug!("FS Notification Ignored: {}", path.to_string_lossy());
130 }
131 }
132 }
133
134 fn remove_event(&mut self, path: PathBuf) {
135 match self.ignore_rules.check(&path) {
136 MatchResult::Whitelist | MatchResult::NoMatch => {
137 self.sender
138 .send(Some(PathEvent::Delete { path }))
139 .unwrap_or_else(|e| warn!("{}", e));
140 }
141 MatchResult::Ignore => {
142 debug!("FS Notification Ignored: {}", path.to_string_lossy());
143 }
144 }
145 }
146
147 fn rename_event(&mut self, from: PathBuf, to: PathBuf) {
148 self.remove_event(from);
149 self.create_event(to);
150 }
151}
152
153pub fn make_watcher(
159 ignore_rules: IgnoreRules,
160) -> Result<(RecommendedWatcher, Receiver<Option<PathEvent>>)> {
161 let (sender, receiver) = bounded(10000);
162 let root = ignore_rules.root.clone();
163 let mut watcher = notify::recommended_watcher(PathEventHandler {
164 ignore_rules,
165 sender,
166 })?;
167
168 watcher.watch(&root, RecursiveMode::Recursive)?;
169 watch!(watcher);
170 Ok((watcher, receiver))
171}
172
173pub fn make_polling_watcher(
181 ignore_rules: IgnoreRules,
182) -> Result<(PollWatcher, Receiver<Option<PathEvent>>)> {
183 let (sender, receiver) = bounded(10000);
184 let root = ignore_rules.root.clone();
185 let mut watcher = notify::poll::PollWatcher::new(
186 PathEventHandler {
187 ignore_rules,
188 sender,
189 },
190 Config::default().with_poll_interval(Duration::from_secs(2)),
191 )?;
192
193 watcher.watch(&root, RecursiveMode::Recursive)?;
194 watch!(watcher);
195 Ok((watcher, receiver))
196}