1use crate::{Config, Error, EventHandler, Receiver, Sender, WatchMode, Watcher, unbounded};
7use std::{
8 collections::HashMap,
9 path::{Path, PathBuf},
10 sync::{
11 Arc, Mutex,
12 atomic::{AtomicBool, Ordering},
13 },
14 thread,
15 time::Duration,
16};
17
18pub type ScanEvent = crate::Result<PathBuf>;
20
21pub trait ScanEventHandler: Send + 'static {
26 fn handle_event(&mut self, event: ScanEvent);
28}
29
30impl<F> ScanEventHandler for F
31where
32 F: FnMut(ScanEvent) + Send + 'static,
33{
34 fn handle_event(&mut self, event: ScanEvent) {
35 (self)(event);
36 }
37}
38
39#[cfg(feature = "crossbeam-channel")]
40impl ScanEventHandler for crossbeam_channel::Sender<ScanEvent> {
41 fn handle_event(&mut self, event: ScanEvent) {
42 let _ = self.send(event);
43 }
44}
45
46#[cfg(feature = "flume")]
47impl ScanEventHandler for flume::Sender<ScanEvent> {
48 fn handle_event(&mut self, event: ScanEvent) {
49 let _ = self.send(event);
50 }
51}
52
53impl ScanEventHandler for std::sync::mpsc::Sender<ScanEvent> {
54 fn handle_event(&mut self, event: ScanEvent) {
55 let _ = self.send(event);
56 }
57}
58
59impl ScanEventHandler for () {
60 fn handle_event(&mut self, _event: ScanEvent) {}
61}
62
63use data::{DataBuilder, WatchData};
64mod data {
65 use crate::{
66 EventHandler,
67 event::{CreateKind, DataChange, Event, EventKind, MetadataKind, ModifyKind, RemoveKind},
68 };
69 use std::{
70 cell::RefCell,
71 collections::{HashMap, hash_map::RandomState},
72 fmt::{self, Debug},
73 fs::{self, File, Metadata},
74 hash::{BuildHasher, Hasher},
75 io::{self, Read},
76 path::{Path, PathBuf},
77 time::Instant,
78 };
79 use walkdir::WalkDir;
80
81 use super::ScanEventHandler;
82
83 fn system_time_to_seconds(time: std::time::SystemTime) -> i64 {
84 match time.duration_since(std::time::SystemTime::UNIX_EPOCH) {
85 Ok(d) => d.as_secs() as i64,
86 Err(e) => -(e.duration().as_secs() as i64),
87 }
88 }
89
90 pub(super) struct DataBuilder {
92 emitter: EventEmitter,
93 scan_emitter: Option<Box<RefCell<dyn ScanEventHandler>>>,
94
95 build_hasher: Option<RandomState>,
98
99 now: Instant,
101 }
102
103 impl DataBuilder {
104 pub(super) fn new<F, G>(
105 event_handler: F,
106 compare_content: bool,
107 scan_emitter: Option<G>,
108 ) -> Self
109 where
110 F: EventHandler,
111 G: ScanEventHandler,
112 {
113 let scan_emitter = match scan_emitter {
114 None => None,
115 Some(v) => {
116 let intermediate: Box<RefCell<dyn ScanEventHandler>> =
118 Box::new(RefCell::new(v));
119 Some(intermediate)
120 }
121 };
122 Self {
123 emitter: EventEmitter::new(event_handler),
124 scan_emitter,
125 build_hasher: compare_content.then(RandomState::default),
126 now: Instant::now(),
127 }
128 }
129
130 pub(super) fn update_timestamp(&mut self) {
132 self.now = Instant::now();
133 }
134
135 pub(super) fn build_watch_data(
140 &self,
141 root: PathBuf,
142 is_recursive: bool,
143 follow_symlinks: bool,
144 ) -> Option<WatchData> {
145 WatchData::new(self, root, is_recursive, follow_symlinks)
146 }
147
148 fn build_path_data(&self, meta_path: &MetaPath) -> PathData {
150 PathData::new(self, meta_path)
151 }
152 }
153
154 impl Debug for DataBuilder {
155 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156 f.debug_struct("DataBuilder")
157 .field("build_hasher", &self.build_hasher)
158 .field("now", &self.now)
159 .finish()
160 }
161 }
162
163 #[derive(Debug)]
164 pub(super) struct WatchData {
165 root: PathBuf,
167 is_recursive: bool,
168 follow_symlinks: bool,
169
170 all_path_data: HashMap<PathBuf, PathData>,
172 }
173
174 impl WatchData {
175 fn new(
181 data_builder: &DataBuilder,
182 root: PathBuf,
183 is_recursive: bool,
184 follow_symlinks: bool,
185 ) -> Option<Self> {
186 if let Err(e) = fs::metadata(&root) {
205 data_builder.emitter.emit_io_err(e, Some(&root));
206 return None;
207 }
208
209 let all_path_data = Self::scan_all_path_data(
210 data_builder,
211 root.clone(),
212 is_recursive,
213 follow_symlinks,
214 true,
215 )
216 .collect();
217
218 Some(Self {
219 root,
220 is_recursive,
221 follow_symlinks,
222 all_path_data,
223 })
224 }
225
226 pub(super) fn rescan(&mut self, data_builder: &mut DataBuilder) {
232 for (path, new_path_data) in Self::scan_all_path_data(
234 data_builder,
235 self.root.clone(),
236 self.is_recursive,
237 self.follow_symlinks,
238 false,
239 ) {
240 let old_path_data = self
241 .all_path_data
242 .insert(path.clone(), new_path_data.clone());
243
244 let event =
246 PathData::compare_to_event(path, old_path_data.as_ref(), Some(&new_path_data));
247 if let Some(event) = event {
248 data_builder.emitter.emit_ok(event);
249 }
250 }
251
252 let mut disappeared_paths = Vec::new();
254 for (path, path_data) in self.all_path_data.iter() {
255 if path_data.last_check < data_builder.now {
256 disappeared_paths.push(path.clone());
257 }
258 }
259
260 for path in disappeared_paths {
262 let old_path_data = self.all_path_data.remove(&path);
263
264 let event = PathData::compare_to_event(path, old_path_data.as_ref(), None);
266 if let Some(event) = event {
267 data_builder.emitter.emit_ok(event);
268 }
269 }
270 }
271
272 fn scan_all_path_data(
278 data_builder: &'_ DataBuilder,
279 root: PathBuf,
280 is_recursive: bool,
281 follow_symlinks: bool,
282 is_initial: bool,
284 ) -> impl Iterator<Item = (PathBuf, PathData)> + '_ {
285 log::trace!("rescanning {root:?}");
286 WalkDir::new(root)
291 .follow_links(follow_symlinks)
292 .max_depth(Self::dir_scan_depth(is_recursive))
293 .into_iter()
294 .filter_map(|entry_res| match entry_res {
295 Ok(entry) => Some(entry),
296 Err(err) => {
297 log::warn!("walkdir error scanning {err:?}");
298
299 if let Some(io_error) = err.io_error() {
300 let new_io_error = io::Error::new(io_error.kind(), err.to_string());
302 data_builder.emitter.emit_io_err(new_io_error, err.path());
303 } else {
304 let crate_err =
305 crate::Error::new(crate::ErrorKind::Generic(err.to_string()));
306 data_builder.emitter.emit(Err(crate_err));
307 }
308 None
309 }
310 })
311 .filter_map(move |entry| match entry.metadata() {
312 Ok(metadata) => {
313 let path = entry.into_path();
314 if is_initial {
315 if let Some(ref emitter) = data_builder.scan_emitter {
317 emitter.borrow_mut().handle_event(Ok(path.clone()));
318 }
319 }
320 let meta_path = MetaPath::from_parts_unchecked(path, metadata);
321 let data_path = data_builder.build_path_data(&meta_path);
322
323 Some((meta_path.into_path(), data_path))
324 }
325 Err(e) => {
326 let path = entry.into_path();
328 data_builder.emitter.emit_io_err(e, Some(path));
329
330 None
331 }
332 })
333 }
334
335 fn dir_scan_depth(is_recursive: bool) -> usize {
336 if is_recursive { usize::MAX } else { 1 }
337 }
338 }
339
340 #[derive(Debug, Clone)]
344 struct PathData {
345 mtime: i64,
347
348 hash: Option<u64>,
351
352 last_check: Instant,
354 }
355
356 impl PathData {
357 fn new(data_builder: &DataBuilder, meta_path: &MetaPath) -> PathData {
359 let metadata = meta_path.metadata();
360
361 PathData {
362 mtime: metadata.modified().map_or(0, system_time_to_seconds),
363 hash: data_builder
364 .build_hasher
365 .as_ref()
366 .filter(|_| metadata.is_file())
367 .and_then(|build_hasher| {
368 Self::get_content_hash(build_hasher, meta_path.path()).ok()
369 }),
370
371 last_check: data_builder.now,
372 }
373 }
374
375 fn get_content_hash(build_hasher: &RandomState, path: &Path) -> io::Result<u64> {
377 let mut hasher = build_hasher.build_hasher();
378 let mut file = File::open(path)?;
379 let mut buf = [0; 512];
380
381 loop {
382 let n = match file.read(&mut buf) {
383 Ok(0) => break,
384 Ok(len) => len,
385 Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
386 Err(e) => return Err(e),
387 };
388
389 hasher.write(&buf[..n]);
390 }
391
392 Ok(hasher.finish())
393 }
394
395 fn compare_to_event<P>(
397 path: P,
398 old: Option<&PathData>,
399 new: Option<&PathData>,
400 ) -> Option<Event>
401 where
402 P: Into<PathBuf>,
403 {
404 match (old, new) {
405 (Some(old), Some(new)) => {
406 if new.mtime > old.mtime {
407 Some(EventKind::Modify(ModifyKind::Metadata(
408 MetadataKind::WriteTime,
409 )))
410 } else if new.hash != old.hash {
411 Some(EventKind::Modify(ModifyKind::Data(DataChange::Any)))
412 } else {
413 None
414 }
415 }
416 (None, Some(_new)) => Some(EventKind::Create(CreateKind::Any)),
417 (Some(_old), None) => Some(EventKind::Remove(RemoveKind::Any)),
418 (None, None) => None,
419 }
420 .map(|event_kind| Event::new(event_kind).add_path(path.into()))
421 }
422 }
423
424 #[derive(Debug)]
430 pub(super) struct MetaPath {
431 path: PathBuf,
432 metadata: Metadata,
433 }
434
435 impl MetaPath {
436 fn from_parts_unchecked(path: PathBuf, metadata: Metadata) -> Self {
442 Self { path, metadata }
443 }
444
445 fn path(&self) -> &Path {
446 &self.path
447 }
448
449 fn metadata(&self) -> &Metadata {
450 &self.metadata
451 }
452
453 fn into_path(self) -> PathBuf {
454 self.path
455 }
456 }
457
458 struct EventEmitter(
460 Box<RefCell<dyn EventHandler>>,
463 );
464
465 impl EventEmitter {
466 fn new<F: EventHandler>(event_handler: F) -> Self {
467 Self(Box::new(RefCell::new(event_handler)))
468 }
469
470 fn emit(&self, event: crate::Result<Event>) {
472 self.0.borrow_mut().handle_event(event);
473 }
474
475 fn emit_ok(&self, event: Event) {
477 self.emit(Ok(event))
478 }
479
480 fn emit_io_err<E, P>(&self, err: E, path: Option<P>)
482 where
483 E: Into<io::Error>,
484 P: Into<PathBuf>,
485 {
486 let e = crate::Error::io(err.into());
487 if let Some(path) = path {
488 self.emit(Err(e.add_path(path.into())));
489 } else {
490 self.emit(Err(e));
491 }
492 }
493 }
494}
495
496#[derive(Debug)]
503pub struct PollWatcher {
504 watches: Arc<Mutex<HashMap<PathBuf, WatchData>>>,
505 data_builder: Arc<Mutex<DataBuilder>>,
506 want_to_stop: Arc<AtomicBool>,
507 message_channel: Sender<()>,
510 delay: Option<Duration>,
511 follow_sylinks: bool,
512}
513
514impl PollWatcher {
515 pub fn new<F: EventHandler>(event_handler: F, config: Config) -> crate::Result<PollWatcher> {
517 Self::with_opt::<_, ()>(event_handler, config, None)
518 }
519
520 pub fn poll(&self) -> crate::Result<()> {
522 self.message_channel
523 .send(())
524 .map_err(|_| Error::generic("failed to send poll message"))?;
525 Ok(())
526 }
527
528 #[cfg(test)]
530 pub(crate) fn poll_sender(&self) -> Sender<()> {
531 self.message_channel.clone()
532 }
533
534 pub fn with_initial_scan<F: EventHandler, G: ScanEventHandler>(
538 event_handler: F,
539 config: Config,
540 scan_callback: G,
541 ) -> crate::Result<PollWatcher> {
542 Self::with_opt(event_handler, config, Some(scan_callback))
543 }
544
545 fn with_opt<F: EventHandler, G: ScanEventHandler>(
547 event_handler: F,
548 config: Config,
549 scan_callback: Option<G>,
550 ) -> crate::Result<PollWatcher> {
551 let data_builder =
552 DataBuilder::new(event_handler, config.compare_contents(), scan_callback);
553
554 let (tx, rx) = unbounded();
555
556 let poll_watcher = PollWatcher {
557 watches: Default::default(),
558 data_builder: Arc::new(Mutex::new(data_builder)),
559 want_to_stop: Arc::new(AtomicBool::new(false)),
560 delay: config.poll_interval(),
561 follow_sylinks: config.follow_symlinks(),
562 message_channel: tx,
563 };
564
565 poll_watcher.run(rx);
566
567 Ok(poll_watcher)
568 }
569
570 fn run(&self, rx: Receiver<()>) {
571 let watches = Arc::clone(&self.watches);
572 let data_builder = Arc::clone(&self.data_builder);
573 let want_to_stop = Arc::clone(&self.want_to_stop);
574 let delay = self.delay;
575
576 let _ = thread::Builder::new()
577 .name("notify-rs poll loop".to_string())
578 .spawn(move || {
579 loop {
580 if want_to_stop.load(Ordering::SeqCst) {
581 break;
582 }
583
584 if let (Ok(mut watches), Ok(mut data_builder)) =
589 (watches.lock(), data_builder.lock())
590 {
591 data_builder.update_timestamp();
592
593 let vals = watches.values_mut();
594 for watch_data in vals {
595 watch_data.rescan(&mut data_builder);
596 }
597 }
598 if let Some(delay) = delay {
600 let _ = rx.recv_timeout(delay);
601 } else {
602 let _ = rx.recv();
603 }
604 }
605 });
606 }
607
608 fn watch_inner(&mut self, path: &Path, watch_mode: WatchMode) {
613 if let (Ok(mut watches), Ok(mut data_builder)) =
617 (self.watches.lock(), self.data_builder.lock())
618 {
619 data_builder.update_timestamp();
620
621 let watch_data = data_builder.build_watch_data(
622 path.to_path_buf(),
623 watch_mode.recursive_mode.is_recursive(),
624 self.follow_sylinks,
625 );
626
627 if let Some(watch_data) = watch_data {
629 watches.insert(path.to_path_buf(), watch_data);
630 }
631 }
632 }
633
634 fn unwatch_inner(&mut self, path: &Path) -> crate::Result<()> {
638 self.watches
640 .lock()
641 .unwrap()
642 .remove(path)
643 .map(|_| ())
644 .ok_or_else(crate::Error::watch_not_found)
645 }
646}
647
648impl Watcher for PollWatcher {
649 fn new<F: EventHandler>(event_handler: F, config: Config) -> crate::Result<Self> {
651 Self::new(event_handler, config)
652 }
653
654 fn watch(&mut self, path: &Path, watch_mode: WatchMode) -> crate::Result<()> {
655 self.watch_inner(path, watch_mode);
656
657 Ok(())
658 }
659
660 fn unwatch(&mut self, path: &Path) -> crate::Result<()> {
661 self.unwatch_inner(path)
662 }
663
664 fn kind() -> crate::WatcherKind {
665 crate::WatcherKind::PollWatcher
666 }
667}
668
669impl Drop for PollWatcher {
670 fn drop(&mut self) {
671 self.want_to_stop.store(true, Ordering::Relaxed);
672 }
673}
674
675#[cfg(test)]
676mod tests {
677 use super::PollWatcher;
678 use crate::{Error, ErrorKind, RecursiveMode, TargetMode, WatchMode, Watcher, test::*};
679
680 fn watcher() -> (TestWatcher<PollWatcher>, Receiver) {
681 poll_watcher_channel()
682 }
683
684 #[test]
685 fn poll_watcher_is_send_and_sync() {
686 fn check<T: Send + Sync>() {}
687 check::<PollWatcher>();
688 }
689
690 #[test]
691 fn create_file() {
692 let tmpdir = testdir();
693 let (mut watcher, mut rx) = watcher();
694 watcher.watch_recursively(&tmpdir);
695
696 let path = tmpdir.path().join("entry");
697 std::fs::File::create_new(&path).expect("Unable to create");
698
699 rx.sleep_until_exists(&path);
700 rx.wait_ordered_exact([expected(&path).create_any()]);
701 }
702
703 #[test]
704 #[ignore = "not implemented"]
705 fn create_self_file() {
706 let tmpdir = testdir();
707 let (mut watcher, mut rx) = watcher();
708
709 let path = tmpdir.path().join("entry");
710
711 watcher.watch_nonrecursively(&path);
712
713 std::fs::File::create_new(&path).expect("create");
714
715 rx.sleep_until_exists(&path);
716 rx.wait_ordered_exact([expected(&path).create_any()]);
717 }
718
719 #[test]
720 #[ignore = "not implemented"]
721 fn create_self_file_no_track() {
722 let tmpdir = testdir();
723 let (mut watcher, _) = watcher();
724
725 let path = tmpdir.path().join("entry");
726
727 let result = watcher.watcher.watch(
728 &path,
729 WatchMode {
730 recursive_mode: RecursiveMode::NonRecursive,
731 target_mode: TargetMode::NoTrack,
732 },
733 );
734 assert!(matches!(
735 result,
736 Err(Error {
737 paths: _,
738 kind: ErrorKind::PathNotFound
739 })
740 ));
741 }
742
743 #[test]
744 #[ignore = "TODO: not implemented"]
745 fn create_self_file_nested() {
746 let tmpdir = testdir();
747 let (mut watcher, mut rx) = watcher();
748
749 let path = tmpdir.path().join("entry/nested");
750
751 watcher.watch_nonrecursively(&path);
752
753 std::fs::create_dir_all(path.parent().unwrap()).expect("create");
754 std::fs::File::create_new(&path).expect("create");
755
756 rx.wait_ordered_exact([expected(&path).create_file()]);
757 }
758
759 #[test]
760 fn create_dir() {
761 let tmpdir = testdir();
762 let (mut watcher, mut rx) = watcher();
763 watcher.watch_recursively(&tmpdir);
764
765 let path = tmpdir.path().join("entry");
766 std::fs::create_dir(&path).expect("Unable to create");
767
768 rx.sleep_until_exists(&path);
769 rx.wait_ordered_exact([expected(&path).create_any()]);
770 }
771
772 #[test]
773 fn modify_file() {
774 let tmpdir = testdir();
775 let (mut watcher, mut rx) = watcher();
776 let path = tmpdir.path().join("entry");
777 std::fs::File::create_new(&path).expect("Unable to create");
778
779 watcher.watch_recursively(&tmpdir);
780 std::fs::write(&path, b"123").expect("Unable to write");
781
782 assert!(
783 rx.sleep_until(|| std::fs::read_to_string(&path).is_ok_and(|content| content == "123")),
784 "the file wasn't modified"
785 );
786 rx.wait_ordered_exact([expected(&path).modify_data_any()]);
787 }
788
789 #[test]
790 fn rename_file() {
791 let tmpdir = testdir();
792 let (mut watcher, mut rx) = watcher();
793 let path = tmpdir.path().join("entry");
794 let new_path = tmpdir.path().join("new_entry");
795 std::fs::File::create_new(&path).expect("Unable to create");
796
797 watcher.watch_recursively(&tmpdir);
798 std::fs::rename(&path, &new_path).expect("Unable to remove");
799
800 rx.sleep_while_exists(&path);
801 rx.sleep_until_exists(&new_path);
802
803 rx.wait_unordered_exact([
804 expected(&path).remove_any(),
805 expected(&new_path).create_any(),
806 ]);
807 }
808
809 #[test]
810 #[ignore = "TODO: not implemented"]
811 fn rename_self_file() {
812 let tmpdir = testdir();
813 let (mut watcher, mut rx) = watcher();
814
815 let path = tmpdir.path().join("entry");
816 std::fs::File::create_new(&path).expect("create");
817
818 watcher.watch_nonrecursively(&path);
819 let new_path = tmpdir.path().join("renamed");
820
821 std::fs::rename(&path, &new_path).expect("rename");
822
823 rx.sleep_while_exists(&path);
824 rx.sleep_until_exists(&new_path);
825
826 rx.wait_unordered_exact([expected(&path).remove_any()])
827 .ensure_no_tail();
828
829 std::fs::rename(&new_path, &path).expect("rename2");
830
831 rx.sleep_until_exists(&new_path);
832 rx.sleep_while_exists(&path);
833
834 rx.wait_unordered_exact([expected(&path).create_any()])
835 .ensure_no_tail();
836 }
837
838 #[test]
839 #[ignore = "TODO: not implemented"]
840 fn rename_self_file_no_track() {
841 let tmpdir = testdir();
842 let (mut watcher, mut rx) = watcher();
843
844 let path = tmpdir.path().join("entry");
845 std::fs::File::create_new(&path).expect("create");
846
847 watcher.watch(
848 &path,
849 WatchMode {
850 recursive_mode: RecursiveMode::NonRecursive,
851 target_mode: TargetMode::NoTrack,
852 },
853 );
854
855 let new_path = tmpdir.path().join("renamed");
856
857 std::fs::rename(&path, &new_path).expect("rename");
858
859 rx.sleep_while_exists(&path);
860 rx.sleep_until_exists(&new_path);
861
862 rx.wait_unordered_exact([expected(&path).remove_any()])
863 .ensure_no_tail();
864
865 let result = watcher.watcher.watch(
866 &path,
867 WatchMode {
868 recursive_mode: RecursiveMode::NonRecursive,
869 target_mode: TargetMode::NoTrack,
870 },
871 );
872 assert!(matches!(
873 result,
874 Err(Error {
875 paths: _,
876 kind: ErrorKind::PathNotFound
877 })
878 ));
879 }
880
881 #[test]
882 fn delete_file() {
883 let tmpdir = testdir();
884 let (mut watcher, mut rx) = watcher();
885 let path = tmpdir.path().join("entry");
886 std::fs::File::create_new(&path).expect("Unable to create");
887
888 watcher.watch_recursively(&tmpdir);
889 std::fs::remove_file(&path).expect("Unable to remove");
890
891 rx.sleep_while_exists(&path);
892 rx.wait_ordered_exact([expected(&path).remove_any()]);
893 }
894
895 #[test]
896 #[ignore = "TODO: not implemented"]
897 fn delete_self_file() {
898 let tmpdir = testdir();
899 let (mut watcher, mut rx) = watcher();
900 let path = tmpdir.path().join("entry");
901 std::fs::File::create_new(&path).expect("Unable to create");
902
903 watcher.watch_nonrecursively(&path);
904
905 std::fs::remove_file(&path).expect("Unable to remove");
906
907 rx.sleep_while_exists(&path);
908 rx.wait_ordered_exact([expected(&path).remove_any()]);
909
910 std::fs::write(&path, "").expect("write");
911
912 rx.sleep_until_exists(&path);
913 rx.wait_ordered_exact([expected(&path).create_file()]);
914 }
915
916 #[test]
917 #[ignore = "TODO: not implemented"]
918 fn delete_self_file_no_track() {
919 let tmpdir = testdir();
920 let (mut watcher, mut rx) = watcher();
921 let path = tmpdir.path().join("entry");
922 std::fs::File::create_new(&path).expect("Unable to create");
923
924 watcher.watch(
925 &path,
926 WatchMode {
927 recursive_mode: RecursiveMode::NonRecursive,
928 target_mode: TargetMode::NoTrack,
929 },
930 );
931
932 std::fs::remove_file(&path).expect("Unable to remove");
933
934 rx.sleep_while_exists(&path);
935 rx.wait_ordered_exact([expected(&path).remove_any()]);
936
937 std::fs::write(&path, "").expect("write");
938
939 rx.ensure_empty_with_wait();
940 }
941
942 #[test]
943 fn create_write_overwrite() {
944 let tmpdir = testdir();
945 let (mut watcher, mut rx) = watcher();
946 let overwritten_file = tmpdir.path().join("overwritten_file");
947 let overwriting_file = tmpdir.path().join("overwriting_file");
948 std::fs::write(&overwritten_file, "123").expect("write1");
949
950 watcher.watch_nonrecursively(&tmpdir);
951
952 std::fs::File::create(&overwriting_file).expect("create");
953 std::fs::write(&overwriting_file, "321").expect("write2");
954 std::fs::rename(&overwriting_file, &overwritten_file).expect("rename");
955
956 rx.sleep_while_exists(&overwriting_file);
957 assert!(
958 rx.sleep_until(
959 || std::fs::read_to_string(&overwritten_file).is_ok_and(|cnt| cnt == "321")
960 ),
961 "file {overwritten_file:?} was not replaced"
962 );
963
964 rx.wait_unordered([expected(&overwritten_file).modify_data_any()]);
965 }
966}