1use std::collections::BTreeSet;
5use std::collections::HashMap;
6use std::path::Path;
7use std::path::PathBuf;
8use std::sync::Arc;
9use std::sync::Mutex;
10use std::sync::RwLock;
11use std::sync::atomic::AtomicUsize;
12use std::sync::atomic::Ordering;
13use std::time::Duration;
14
15use notify::Event;
16use notify::EventKind;
17use notify::RecommendedWatcher;
18use notify::RecursiveMode;
19use notify::Watcher;
20use tokio::runtime::Handle;
21use tokio::sync::Mutex as AsyncMutex;
22use tokio::sync::Notify;
23use tokio::sync::mpsc;
24use tokio::time::Instant;
25use tokio::time::sleep_until;
26use tracing::warn;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct FileWatcherEvent {
31 pub paths: Vec<PathBuf>,
33}
34
35#[derive(Clone, Debug, Eq, Hash, PartialEq)]
36pub struct WatchPath {
38 pub path: PathBuf,
40 pub recursive: bool,
42}
43
44type SubscriberId = u64;
45
46#[derive(Default)]
47struct WatchState {
48 next_subscriber_id: SubscriberId,
49 path_ref_counts: HashMap<PathBuf, PathWatchCounts>,
50 subscribers: HashMap<SubscriberId, SubscriberState>,
51}
52
53struct SubscriberState {
54 watched_paths: HashMap<SubscriberWatchKey, SubscriberWatchState>,
55 tx: WatchSender,
56}
57
58#[derive(Clone, Debug, Eq, Hash, PartialEq)]
60struct SubscriberWatchKey {
61 requested: WatchPath,
64 matched: WatchPath,
68}
69
70struct SubscriberWatchState {
72 actual: WatchPath,
75 count: usize,
76 last_exists: bool,
79 fallback: bool,
82}
83
84#[derive(Clone)]
89struct SubscriberWatchRegistration {
90 key: SubscriberWatchKey,
92 actual: WatchPath,
94 fallback: bool,
96}
97
98pub struct Receiver {
100 inner: Arc<ReceiverInner>,
101}
102
103struct WatchSender {
104 inner: Arc<ReceiverInner>,
105}
106
107struct ReceiverInner {
108 changed_paths: AsyncMutex<BTreeSet<PathBuf>>,
109 notify: Notify,
110 sender_count: AtomicUsize,
111}
112
113impl Receiver {
114 pub async fn recv(&mut self) -> Option<FileWatcherEvent> {
117 loop {
118 let notified = self.inner.notify.notified();
119 {
120 let mut changed_paths = self.inner.changed_paths.lock().await;
121 if !changed_paths.is_empty() {
122 return Some(FileWatcherEvent {
123 paths: std::mem::take(&mut *changed_paths).into_iter().collect(),
124 });
125 }
126 if self.inner.sender_count.load(Ordering::Acquire) == 0 {
127 return None;
128 }
129 }
130 notified.await;
131 }
132 }
133}
134
135impl WatchSender {
136 async fn add_changed_paths(&self, paths: &[PathBuf]) {
137 if paths.is_empty() {
138 return;
139 }
140
141 let mut changed_paths = self.inner.changed_paths.lock().await;
142 let previous_len = changed_paths.len();
143 changed_paths.extend(paths.iter().cloned());
144 if changed_paths.len() != previous_len {
145 self.inner.notify.notify_one();
146 }
147 }
148}
149
150impl Clone for WatchSender {
151 fn clone(&self) -> Self {
152 self.inner.sender_count.fetch_add(1, Ordering::Relaxed);
153 Self {
154 inner: Arc::clone(&self.inner),
155 }
156 }
157}
158
159impl Drop for WatchSender {
160 fn drop(&mut self) {
161 if self.inner.sender_count.fetch_sub(1, Ordering::AcqRel) == 1 {
162 self.inner.notify.notify_waiters();
163 }
164 }
165}
166
167fn watch_channel() -> (WatchSender, Receiver) {
168 let inner = Arc::new(ReceiverInner {
169 changed_paths: AsyncMutex::new(BTreeSet::new()),
170 notify: Notify::new(),
171 sender_count: AtomicUsize::new(1),
172 });
173 (
174 WatchSender {
175 inner: Arc::clone(&inner),
176 },
177 Receiver { inner },
178 )
179}
180
181#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
182struct PathWatchCounts {
183 non_recursive: usize,
184 recursive: usize,
185}
186
187impl PathWatchCounts {
188 fn increment(&mut self, recursive: bool, amount: usize) {
189 if recursive {
190 self.recursive += amount;
191 } else {
192 self.non_recursive += amount;
193 }
194 }
195
196 fn decrement(&mut self, recursive: bool, amount: usize) {
197 if recursive {
198 self.recursive = self.recursive.saturating_sub(amount);
199 } else {
200 self.non_recursive = self.non_recursive.saturating_sub(amount);
201 }
202 }
203
204 fn effective_mode(self) -> Option<RecursiveMode> {
205 if self.recursive > 0 {
206 Some(RecursiveMode::Recursive)
207 } else if self.non_recursive > 0 {
208 Some(RecursiveMode::NonRecursive)
209 } else {
210 None
211 }
212 }
213
214 fn is_empty(self) -> bool {
215 self.non_recursive == 0 && self.recursive == 0
216 }
217}
218
219struct FileWatcherInner {
220 watcher: RecommendedWatcher,
221 watched_paths: HashMap<PathBuf, RecursiveMode>,
222}
223
224pub struct ThrottledWatchReceiver {
226 rx: Receiver,
227 interval: Duration,
228 next_allowed: Option<Instant>,
229}
230
231impl ThrottledWatchReceiver {
232 pub fn new(rx: Receiver, interval: Duration) -> Self {
234 Self {
235 rx,
236 interval,
237 next_allowed: None,
238 }
239 }
240
241 pub async fn recv(&mut self) -> Option<FileWatcherEvent> {
244 if let Some(next_allowed) = self.next_allowed {
245 sleep_until(next_allowed).await;
246 }
247
248 let event = self.rx.recv().await;
249 if event.is_some() {
250 self.next_allowed = Some(Instant::now() + self.interval);
251 }
252 event
253 }
254}
255
256pub struct DebouncedWatchReceiver {
259 rx: Receiver,
260 interval: Duration,
261 changed_paths: BTreeSet<PathBuf>,
262}
263
264impl DebouncedWatchReceiver {
265 pub fn new(rx: Receiver, interval: Duration) -> Self {
267 Self {
268 rx,
269 interval,
270 changed_paths: BTreeSet::new(),
271 }
272 }
273
274 pub async fn recv(&mut self) -> Option<FileWatcherEvent> {
276 while self.changed_paths.is_empty() {
277 self.changed_paths.extend(self.rx.recv().await?.paths);
278 }
279 let deadline = Instant::now() + self.interval;
280
281 loop {
282 tokio::select! {
283 event = self.rx.recv() => match event {
284 Some(event) => self.changed_paths.extend(event.paths),
285 None => break,
286 },
287 _ = sleep_until(deadline) => break,
288 }
289 }
290
291 Some(FileWatcherEvent {
292 paths: std::mem::take(&mut self.changed_paths)
293 .into_iter()
294 .collect(),
295 })
296 }
297}
298
299pub struct FileWatcherSubscriber {
301 id: SubscriberId,
302 file_watcher: Arc<FileWatcher>,
303}
304
305impl FileWatcherSubscriber {
306 pub fn register_paths(&self, watched_paths: Vec<WatchPath>) -> WatchRegistration {
309 let watched_paths = dedupe_watched_paths(watched_paths)
310 .into_iter()
311 .map(|requested| {
312 let (actual, matched, fallback) = actual_watch_path(&requested);
313 let key = SubscriberWatchKey { requested, matched };
314 SubscriberWatchRegistration {
315 key,
316 actual,
317 fallback,
318 }
319 })
320 .collect::<Vec<_>>();
321 self.file_watcher.register_paths(self.id, &watched_paths);
322
323 WatchRegistration {
324 file_watcher: Arc::downgrade(&self.file_watcher),
325 subscriber_id: self.id,
326 watched_paths: watched_paths
327 .iter()
328 .map(|watch| watch.key.clone())
329 .collect(),
330 }
331 }
332
333 #[cfg(test)]
334 pub(crate) fn register_path(&self, path: PathBuf, recursive: bool) -> WatchRegistration {
335 self.register_paths(vec![WatchPath { path, recursive }])
336 }
337}
338
339impl Drop for FileWatcherSubscriber {
340 fn drop(&mut self) {
341 self.file_watcher.remove_subscriber(self.id);
342 }
343}
344
345pub struct WatchRegistration {
347 file_watcher: std::sync::Weak<FileWatcher>,
348 subscriber_id: SubscriberId,
349 watched_paths: Vec<SubscriberWatchKey>,
350}
351
352impl Default for WatchRegistration {
353 fn default() -> Self {
354 Self {
355 file_watcher: std::sync::Weak::new(),
356 subscriber_id: 0,
357 watched_paths: Vec::new(),
358 }
359 }
360}
361
362impl Drop for WatchRegistration {
363 fn drop(&mut self) {
364 if let Some(file_watcher) = self.file_watcher.upgrade() {
365 file_watcher.unregister_paths(self.subscriber_id, &self.watched_paths);
366 }
367 }
368}
369
370pub struct FileWatcher {
372 inner: Option<Arc<Mutex<FileWatcherInner>>>,
373 state: Arc<RwLock<WatchState>>,
374}
375
376impl FileWatcher {
377 pub fn new() -> notify::Result<Self> {
380 let (raw_tx, raw_rx) = mpsc::unbounded_channel();
381 let raw_tx_clone = raw_tx;
382 let watcher = notify::recommended_watcher(move |res| {
383 let _ = raw_tx_clone.send(res);
384 })?;
385 let inner = FileWatcherInner {
386 watcher,
387 watched_paths: HashMap::new(),
388 };
389 let state = Arc::new(RwLock::new(WatchState::default()));
390 let file_watcher = Self {
391 inner: Some(Arc::new(Mutex::new(inner))),
392 state,
393 };
394 file_watcher.spawn_event_loop(raw_rx);
395 Ok(file_watcher)
396 }
397
398 pub fn noop() -> Self {
401 Self {
402 inner: None,
403 state: Arc::new(RwLock::new(WatchState::default())),
404 }
405 }
406
407 pub fn add_subscriber(self: &Arc<Self>) -> (FileWatcherSubscriber, Receiver) {
410 let (tx, rx) = watch_channel();
411 let mut state = self
412 .state
413 .write()
414 .unwrap_or_else(std::sync::PoisonError::into_inner);
415 let subscriber_id = state.next_subscriber_id;
416 state.next_subscriber_id += 1;
417 state.subscribers.insert(
418 subscriber_id,
419 SubscriberState {
420 watched_paths: HashMap::new(),
421 tx,
422 },
423 );
424
425 let subscriber = FileWatcherSubscriber {
426 id: subscriber_id,
427 file_watcher: self.clone(),
428 };
429 (subscriber, rx)
430 }
431
432 fn register_paths(
433 &self,
434 subscriber_id: SubscriberId,
435 watched_paths: &[SubscriberWatchRegistration],
436 ) {
437 let mut state = self
438 .state
439 .write()
440 .unwrap_or_else(std::sync::PoisonError::into_inner);
441 let mut inner_guard: Option<std::sync::MutexGuard<'_, FileWatcherInner>> = None;
442
443 for registration in watched_paths {
444 let actual = {
445 let Some(subscriber) = state.subscribers.get_mut(&subscriber_id) else {
446 return;
447 };
448 match subscriber.watched_paths.entry(registration.key.clone()) {
449 std::collections::hash_map::Entry::Occupied(mut entry) => {
450 entry.get_mut().count += 1;
451 entry.get().actual.clone()
452 }
453 std::collections::hash_map::Entry::Vacant(entry) => {
454 entry.insert(SubscriberWatchState {
455 actual: registration.actual.clone(),
456 count: 1,
457 last_exists: registration.key.matched.path.exists(),
458 fallback: registration.fallback,
459 });
460 registration.actual.clone()
461 }
462 }
463 };
464
465 let counts = state
466 .path_ref_counts
467 .entry(actual.path.clone())
468 .or_default();
469 let previous_mode = counts.effective_mode();
470 counts.increment(actual.recursive, 1);
471 let next_mode = counts.effective_mode();
472 if previous_mode != next_mode {
473 self.reconfigure_watch(&actual.path, next_mode, &mut inner_guard);
474 }
475 }
476 }
477
478 fn unregister_paths(&self, subscriber_id: SubscriberId, watched_paths: &[SubscriberWatchKey]) {
479 let mut state = self
480 .state
481 .write()
482 .unwrap_or_else(std::sync::PoisonError::into_inner);
483 let mut inner_guard: Option<std::sync::MutexGuard<'_, FileWatcherInner>> = None;
484
485 for subscriber_watch in watched_paths {
486 let actual = {
487 let Some(subscriber) = state.subscribers.get_mut(&subscriber_id) else {
488 return;
489 };
490 let Some(subscriber_watch_state) =
491 subscriber.watched_paths.get_mut(subscriber_watch)
492 else {
493 continue;
494 };
495 let actual = subscriber_watch_state.actual.clone();
496 subscriber_watch_state.count = subscriber_watch_state.count.saturating_sub(1);
497 if subscriber_watch_state.count == 0 {
498 subscriber.watched_paths.remove(subscriber_watch);
499 }
500 actual
501 };
502
503 let Some(counts) = state.path_ref_counts.get_mut(&actual.path) else {
504 continue;
505 };
506 let previous_mode = counts.effective_mode();
507 counts.decrement(actual.recursive, 1);
508 let next_mode = counts.effective_mode();
509 if counts.is_empty() {
510 state.path_ref_counts.remove(&actual.path);
511 }
512 if previous_mode != next_mode {
513 self.reconfigure_watch(&actual.path, next_mode, &mut inner_guard);
514 }
515 }
516 }
517
518 fn remove_subscriber(&self, subscriber_id: SubscriberId) {
519 let mut state = self
520 .state
521 .write()
522 .unwrap_or_else(std::sync::PoisonError::into_inner);
523 let Some(subscriber) = state.subscribers.remove(&subscriber_id) else {
524 return;
525 };
526
527 let mut inner_guard: Option<std::sync::MutexGuard<'_, FileWatcherInner>> = None;
528 for (_subscriber_watch, subscriber_watch_state) in subscriber.watched_paths {
529 let Some(path_counts) = state
530 .path_ref_counts
531 .get_mut(&subscriber_watch_state.actual.path)
532 else {
533 continue;
534 };
535 let previous_mode = path_counts.effective_mode();
536 path_counts.decrement(
537 subscriber_watch_state.actual.recursive,
538 subscriber_watch_state.count,
539 );
540 let next_mode = path_counts.effective_mode();
541 if path_counts.is_empty() {
542 state
543 .path_ref_counts
544 .remove(&subscriber_watch_state.actual.path);
545 }
546 if previous_mode != next_mode {
547 self.reconfigure_watch(
548 &subscriber_watch_state.actual.path,
549 next_mode,
550 &mut inner_guard,
551 );
552 }
553 }
554 }
555
556 fn reconfigure_watch<'a>(
557 &'a self,
558 path: &Path,
559 next_mode: Option<RecursiveMode>,
560 inner_guard: &mut Option<std::sync::MutexGuard<'a, FileWatcherInner>>,
561 ) {
562 Self::reconfigure_watch_inner(self.inner.as_ref(), path, next_mode, inner_guard);
563 }
564
565 fn reconfigure_watch_inner<'a>(
566 inner: Option<&'a Arc<Mutex<FileWatcherInner>>>,
567 path: &Path,
568 next_mode: Option<RecursiveMode>,
569 inner_guard: &mut Option<std::sync::MutexGuard<'a, FileWatcherInner>>,
570 ) {
571 let Some(inner) = inner else {
572 return;
573 };
574 if inner_guard.is_none() {
575 let guard = inner
576 .lock()
577 .unwrap_or_else(std::sync::PoisonError::into_inner);
578 *inner_guard = Some(guard);
579 }
580 let Some(guard) = inner_guard.as_mut() else {
581 return;
582 };
583
584 let existing_mode = guard.watched_paths.get(path).copied();
585 if existing_mode == next_mode {
586 return;
587 }
588
589 if existing_mode.is_some() {
590 if let Err(err) = guard.watcher.unwatch(path) {
591 warn!("failed to unwatch {}: {err}", path.display());
592 }
593 guard.watched_paths.remove(path);
594 }
595
596 let Some(next_mode) = next_mode else {
597 return;
598 };
599 if !path.exists() {
600 return;
601 }
602
603 if let Err(err) = guard.watcher.watch(path, next_mode) {
604 warn!("failed to watch {}: {err}", path.display());
605 return;
606 }
607 guard.watched_paths.insert(path.to_path_buf(), next_mode);
608 }
609
610 fn apply_actual_watch_move<'a>(
611 path_ref_counts: &mut HashMap<PathBuf, PathWatchCounts>,
612 old_actual: WatchPath,
613 new_actual: WatchPath,
614 count: usize,
615 inner: Option<&'a Arc<Mutex<FileWatcherInner>>>,
616 inner_guard: &mut Option<std::sync::MutexGuard<'a, FileWatcherInner>>,
617 ) {
618 if old_actual == new_actual {
619 return;
620 }
621
622 if let Some(counts) = path_ref_counts.get_mut(&old_actual.path) {
623 let previous_mode = counts.effective_mode();
624 counts.decrement(old_actual.recursive, count);
625 let next_mode = counts.effective_mode();
626 if counts.is_empty() {
627 path_ref_counts.remove(&old_actual.path);
628 }
629 if previous_mode != next_mode {
630 Self::reconfigure_watch_inner(inner, &old_actual.path, next_mode, inner_guard);
631 }
632 }
633
634 let counts = path_ref_counts.entry(new_actual.path.clone()).or_default();
635 let previous_mode = counts.effective_mode();
636 counts.increment(new_actual.recursive, count);
637 let next_mode = counts.effective_mode();
638 if previous_mode != next_mode {
639 Self::reconfigure_watch_inner(inner, &new_actual.path, next_mode, inner_guard);
640 }
641 }
642
643 fn spawn_event_loop(&self, mut raw_rx: mpsc::UnboundedReceiver<notify::Result<Event>>) {
646 if let Ok(handle) = Handle::try_current() {
647 let state = Arc::clone(&self.state);
648 let inner = self.inner.as_ref().map(Arc::downgrade);
649 handle.spawn(async move {
650 loop {
651 match raw_rx.recv().await {
652 Some(Ok(event)) => {
653 if !is_mutating_event(&event) {
654 continue;
655 }
656 if event.paths.is_empty() {
657 continue;
658 }
659 let inner = inner.as_ref().and_then(std::sync::Weak::upgrade);
660 Self::notify_subscribers(&state, inner.as_ref(), &event.paths).await;
661 }
662 Some(Err(err)) => {
663 warn!("file watcher error: {err}");
664 }
665 None => break,
666 }
667 }
668 });
669 } else {
670 warn!("file watcher loop skipped: no Tokio runtime available");
671 }
672 }
673
674 async fn notify_subscribers(
675 state: &RwLock<WatchState>,
676 inner: Option<&Arc<Mutex<FileWatcherInner>>>,
677 event_paths: &[PathBuf],
678 ) {
679 let subscribers_to_notify: Vec<(WatchSender, Vec<PathBuf>)> = {
680 let mut state = state
681 .write()
682 .unwrap_or_else(std::sync::PoisonError::into_inner);
683 let mut actual_watch_moves = Vec::new();
684 let mut subscribers_to_notify = Vec::new();
685
686 for subscriber in state.subscribers.values_mut() {
687 let mut changed_paths = Vec::new();
688 for event_path in event_paths {
689 for (subscriber_watch, subscriber_watch_state) in &mut subscriber.watched_paths
690 {
691 if let Some(path) = changed_path_for_event(
692 subscriber_watch,
693 subscriber_watch_state,
694 event_path,
695 ) {
696 changed_paths.push(path);
697 }
698
699 let (new_actual, _new_matched, fallback) =
700 actual_watch_path(&subscriber_watch.requested);
701 subscriber_watch_state.fallback |= fallback;
702 if subscriber_watch_state.actual != new_actual {
703 let old_actual = subscriber_watch_state.actual.clone();
704 let count = subscriber_watch_state.count;
705 subscriber_watch_state.actual = new_actual.clone();
706 actual_watch_moves.push((old_actual, new_actual, count));
707 }
708 }
709 }
710 if !changed_paths.is_empty() {
711 subscribers_to_notify.push((subscriber.tx.clone(), changed_paths));
712 }
713 }
714
715 let mut inner_guard: Option<std::sync::MutexGuard<'_, FileWatcherInner>> = None;
716 for (old_actual, new_actual, count) in actual_watch_moves {
717 Self::apply_actual_watch_move(
718 &mut state.path_ref_counts,
719 old_actual,
720 new_actual,
721 count,
722 inner,
723 &mut inner_guard,
724 );
725 }
726
727 subscribers_to_notify
728 };
729
730 for (subscriber, changed_paths) in subscribers_to_notify {
731 subscriber.add_changed_paths(&changed_paths).await;
732 }
733 }
734
735 #[cfg(test)]
736 pub(crate) async fn send_paths_for_test(&self, paths: Vec<PathBuf>) {
737 Self::notify_subscribers(&self.state, self.inner.as_ref(), &paths).await;
738 }
739
740 #[cfg(test)]
741 pub(crate) fn spawn_event_loop_for_test(
742 &self,
743 raw_rx: mpsc::UnboundedReceiver<notify::Result<Event>>,
744 ) {
745 self.spawn_event_loop(raw_rx);
746 }
747
748 #[cfg(test)]
749 pub(crate) fn watch_counts_for_test(&self, path: &Path) -> Option<(usize, usize)> {
750 let state = self
751 .state
752 .read()
753 .unwrap_or_else(std::sync::PoisonError::into_inner);
754 state
755 .path_ref_counts
756 .get(path)
757 .map(|counts| (counts.non_recursive, counts.recursive))
758 }
759}
760
761fn is_mutating_event(event: &Event) -> bool {
762 matches!(
763 event.kind,
764 EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_)
765 )
766}
767
768fn dedupe_watched_paths(mut watched_paths: Vec<WatchPath>) -> Vec<WatchPath> {
769 watched_paths.sort_unstable_by(|a, b| {
770 a.path
771 .as_os_str()
772 .cmp(b.path.as_os_str())
773 .then(a.recursive.cmp(&b.recursive))
774 });
775 watched_paths.dedup();
776 watched_paths
777}
778
779fn actual_watch_path(requested: &WatchPath) -> (WatchPath, WatchPath, bool) {
786 if requested.path.exists() {
787 let matched_path = requested
788 .path
789 .canonicalize()
790 .unwrap_or_else(|_| requested.path.clone());
791 let actual = requested.clone();
792 let matched = WatchPath {
793 path: matched_path,
794 recursive: requested.recursive,
795 };
796 return (actual, matched, false);
797 }
798
799 let requested_parent = requested.path.parent();
800 let mut ancestor = requested_parent;
801 while let Some(path) = ancestor {
802 if path.is_dir() {
803 let actual_path = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
804 let matched_path = requested
805 .path
806 .strip_prefix(path)
807 .map(|suffix| actual_path.join(suffix))
808 .unwrap_or_else(|_| requested.path.clone());
809 let actual = WatchPath {
810 path: path.to_path_buf(),
811 recursive: false,
812 };
813 let matched = WatchPath {
814 path: matched_path,
815 recursive: requested.recursive,
816 };
817 return (actual, matched, true);
818 }
819 ancestor = path.parent();
820 }
821
822 (requested.clone(), requested.clone(), false)
823}
824
825fn changed_path_for_event(
831 subscriber_watch: &SubscriberWatchKey,
832 subscriber_watch_state: &mut SubscriberWatchState,
833 event_path: &Path,
834) -> Option<PathBuf> {
835 if let Some(path) = changed_path_for_matched_path(
836 subscriber_watch,
837 subscriber_watch_state,
838 &subscriber_watch.matched,
839 event_path,
840 ) {
841 return Some(path);
842 }
843 if subscriber_watch.matched.path == subscriber_watch.requested.path {
844 return None;
845 }
846 changed_path_for_matched_path(
847 subscriber_watch,
848 subscriber_watch_state,
849 &subscriber_watch.requested,
850 event_path,
851 )
852}
853
854fn changed_path_for_matched_path(
857 subscriber_watch: &SubscriberWatchKey,
858 subscriber_watch_state: &mut SubscriberWatchState,
859 matched: &WatchPath,
860 event_path: &Path,
861) -> Option<PathBuf> {
862 let requested = &subscriber_watch.requested;
863 if event_path == matched.path {
864 subscriber_watch_state.last_exists = matched.path.exists();
865 return Some(requested.path.clone());
866 }
867 if matched.path.starts_with(event_path) {
868 let now_exists = matched.path.exists();
869 if subscriber_watch_state.fallback {
870 let should_notify = now_exists || subscriber_watch_state.last_exists;
871 subscriber_watch_state.last_exists = now_exists;
872 return should_notify.then(|| requested.path.clone());
873 }
874 if subscriber_watch_state.actual.path != matched.path {
875 let should_notify = now_exists || subscriber_watch_state.last_exists;
876 subscriber_watch_state.last_exists = now_exists;
877 return should_notify.then(|| requested.path.clone());
878 }
879 subscriber_watch_state.last_exists = now_exists;
880 return Some(event_path.to_path_buf());
881 }
882 if !event_path.starts_with(&matched.path) {
883 return None;
884 }
885 if !(matched.recursive || event_path.parent() == Some(matched.path.as_path())) {
886 return None;
887 }
888 subscriber_watch_state.last_exists = matched.path.exists();
889 Some(
890 event_path
891 .strip_prefix(&matched.path)
892 .map(|suffix| requested.path.join(suffix))
893 .unwrap_or_else(|_| event_path.to_path_buf()),
894 )
895}
896
897#[cfg(test)]
898#[path = "file_watcher_tests.rs"]
899mod tests;