1use crate::{
7 Config, Error, EventHandler, PathsMut, Receiver, Result, Sender, WatchMode, Watcher,
8 poll::data::WatchData, unbounded,
9};
10use std::{
11 path::{Path, PathBuf},
12 sync::mpsc,
13 thread,
14 time::Duration,
15};
16
17pub type ScanEvent = crate::Result<PathBuf>;
19
20pub trait ScanEventHandler: Send + 'static {
25 fn handle_event(&mut self, event: ScanEvent);
27}
28
29impl<F> ScanEventHandler for F
30where
31 F: FnMut(ScanEvent) + Send + 'static,
32{
33 fn handle_event(&mut self, event: ScanEvent) {
34 (self)(event);
35 }
36}
37
38#[cfg(feature = "crossbeam-channel")]
39impl ScanEventHandler for crossbeam_channel::Sender<ScanEvent> {
40 fn handle_event(&mut self, event: ScanEvent) {
41 let result = self.send(event);
42 if let Err(e) = result {
43 tracing::error!(?e, "failed to send scan event result");
44 }
45 }
46}
47
48#[cfg(feature = "flume")]
49impl ScanEventHandler for flume::Sender<ScanEvent> {
50 fn handle_event(&mut self, event: ScanEvent) {
51 let result = self.send(event);
52 if let Err(e) = result {
53 tracing::error!(?e, "failed to send scan event result");
54 }
55 }
56}
57
58impl ScanEventHandler for std::sync::mpsc::Sender<ScanEvent> {
59 fn handle_event(&mut self, event: ScanEvent) {
60 let result = self.send(event);
61 if let Err(e) = result {
62 tracing::error!(?e, "failed to send scan event result");
63 }
64 }
65}
66
67impl ScanEventHandler for () {
68 fn handle_event(&mut self, _event: ScanEvent) {}
69}
70
71use data::DataBuilder;
72mod data {
73 use crate::{
74 Error, EventHandler, Result, WatchMode,
75 consolidating_path_trie::ConsolidatingPathTrie,
76 event::{CreateKind, DataChange, Event, EventKind, MetadataKind, ModifyKind, RemoveKind},
77 };
78 use rustc_hash::FxBuildHasher;
79 use std::{
80 cell::RefCell,
81 collections::{HashMap, hash_map::RandomState},
82 fmt::{self, Debug},
83 fs::{File, FileType, Metadata},
84 hash::{BuildHasher, Hasher},
85 io::{self, Read},
86 path::{Path, PathBuf},
87 time::{Instant, SystemTime},
88 };
89 use walkdir::WalkDir;
90
91 use super::ScanEventHandler;
92
93 pub(super) struct DataBuilder {
95 emitter: EventEmitter,
96 scan_emitter: Option<Box<RefCell<dyn ScanEventHandler>>>,
97
98 build_hasher: Option<RandomState>,
101
102 now: Instant,
104 }
105
106 impl DataBuilder {
107 pub(super) fn new<F, G>(
108 event_handler: F,
109 compare_content: bool,
110 scan_emitter: Option<G>,
111 ) -> Self
112 where
113 F: EventHandler,
114 G: ScanEventHandler,
115 {
116 let scan_emitter = match scan_emitter {
117 None => None,
118 Some(v) => {
119 let intermediate: Box<RefCell<dyn ScanEventHandler>> =
121 Box::new(RefCell::new(v));
122 Some(intermediate)
123 }
124 };
125 Self {
126 emitter: EventEmitter::new(event_handler),
127 scan_emitter,
128 build_hasher: compare_content.then(RandomState::default),
129 now: Instant::now(),
130 }
131 }
132
133 pub(super) fn update_timestamp(&mut self) {
135 self.now = Instant::now();
136 }
137
138 fn build_path_data(&self, meta_path: &MetaPath) -> PathData {
140 PathData::new(self, meta_path)
141 }
142 }
143
144 impl Debug for DataBuilder {
145 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146 f.debug_struct("DataBuilder")
147 .field("build_hasher", &self.build_hasher)
148 .field("now", &self.now)
149 .finish_non_exhaustive()
150 }
151 }
152
153 type SingleWatchHandlerMap = HashMap<PathBuf, bool, FxBuildHasher>;
154
155 #[derive(Debug)]
156 struct WatchHandlers {
157 current: SingleWatchHandlerMap,
158 next: SingleWatchHandlerMap,
159 is_stale: bool,
160 }
161
162 impl WatchHandlers {
163 fn new() -> Self {
164 Self {
165 current: HashMap::default(),
166 next: HashMap::default(),
167 is_stale: false,
168 }
169 }
170
171 fn recalculate(&mut self, watches: &HashMap<PathBuf, WatchMode, FxBuildHasher>) {
173 self.next.clear();
174 self.is_stale = true;
175
176 let mut trie = ConsolidatingPathTrie::new(false, 0);
177 for (path, mode) in watches {
178 if mode.recursive_mode == crate::RecursiveMode::Recursive {
179 trie.insert(path);
180 }
181 }
182 for (path, mode) in watches {
184 if mode.recursive_mode != crate::RecursiveMode::Recursive {
185 self.next.insert(path.clone(), false);
186 }
187 }
188 for path in trie.values() {
190 self.next.insert(path, true);
191 }
192 }
193
194 fn use_handlers(&mut self) -> (&SingleWatchHandlerMap, Option<SingleWatchHandlerMap>) {
195 if self.is_stale {
196 let old_next = std::mem::take(&mut self.next);
197 let old_current = std::mem::replace(&mut self.current, old_next);
198 self.is_stale = false;
199 return (&self.current, Some(old_current));
200 }
201 (&self.current, None)
202 }
203 }
204
205 #[derive(Debug)]
206 pub(super) struct WatchData {
207 follow_symlinks: bool,
209
210 watches: HashMap<PathBuf, WatchMode, FxBuildHasher>,
212 watch_handlers: WatchHandlers,
213 all_path_data: HashMap<PathBuf, PathData, FxBuildHasher>,
214 }
215
216 impl WatchData {
217 pub fn new(follow_symlinks: bool) -> Self {
219 Self {
220 follow_symlinks,
221 watches: HashMap::default(),
222 watch_handlers: WatchHandlers::new(),
223 all_path_data: HashMap::default(),
224 }
225 }
226
227 pub fn add_watch(&mut self, path: PathBuf, mode: WatchMode) -> Result<()> {
228 if mode.target_mode == crate::TargetMode::NoTrack && !path.exists() {
229 return Err(crate::Error::path_not_found().add_path(path));
230 }
231
232 self.watches.insert(path, mode);
233 self.watch_handlers.recalculate(&self.watches);
234 Ok(())
235 }
236
237 pub fn add_watch_multiple(&mut self, paths: Vec<(PathBuf, WatchMode)>) -> Result<()> {
238 for (path, mode) in paths {
239 if mode.target_mode == crate::TargetMode::NoTrack && !path.exists() {
240 return Err(crate::Error::path_not_found().add_path(path));
241 }
242
243 self.watches.insert(path, mode);
244 }
245 self.watch_handlers.recalculate(&self.watches);
246 Ok(())
247 }
248
249 pub fn remove_watch(&mut self, path: &Path) -> Result<()> {
250 self.watches.remove(path).ok_or(Error::watch_not_found())?;
251 self.watch_handlers.recalculate(&self.watches);
252 Ok(())
253 }
254
255 pub(super) fn rescan(&mut self, data_builder: &DataBuilder) {
261 let (watch_handlers, old_watch_handlers) = self.watch_handlers.use_handlers();
262
263 for (path, new_path_data) in
265 Self::scan_all_path_data(data_builder, watch_handlers, self.follow_symlinks)
266 {
267 let event_kind = if let Some(old_path_data) = self.all_path_data.get_mut(&path) {
268 let event_kind =
269 PathData::compare_to_kind(Some(&*old_path_data), Some(&new_path_data));
270 *old_path_data = new_path_data;
271 event_kind
272 } else {
273 let event_kind = PathData::compare_to_kind(None, Some(&new_path_data));
274 self.all_path_data.insert(path.clone(), new_path_data);
275 event_kind
276 };
277
278 let is_initial = old_watch_handlers
279 .as_ref()
280 .is_some_and(|old_watch_handlers| {
281 !old_watch_handlers.contains_key(&path)
282 && !path.ancestors().skip(1).any(|ancestor| {
283 old_watch_handlers
284 .get(ancestor)
285 .is_some_and(|is_recursive| *is_recursive)
286 })
287 });
288 if is_initial {
289 if let Some(ref emitter) = data_builder.scan_emitter {
291 emitter.borrow_mut().handle_event(Ok(path.clone()));
292 }
293 } else if let Some(event_kind) = event_kind {
294 let event = Event::new(event_kind).add_path(path);
295 data_builder.emitter.emit_ok(event);
296 }
297 }
298
299 let mut disappeared_paths = Vec::new();
301 for (path, path_data) in &self.all_path_data {
302 if path_data.last_check < data_builder.now {
303 disappeared_paths.push(path.clone());
304 }
305 }
306
307 for path in disappeared_paths {
309 let old_path_data = self.all_path_data.remove(&path);
310
311 if let Some(event_kind) = PathData::compare_to_kind(old_path_data.as_ref(), None) {
312 let event = Event::new(event_kind).add_path(path);
313 data_builder.emitter.emit_ok(event);
314 }
315 }
316 }
317
318 fn scan_all_path_data(
324 data_builder: &DataBuilder,
325 watch_handlers: &HashMap<PathBuf, bool, FxBuildHasher>,
326 follow_symlinks: bool,
327 ) -> impl Iterator<Item = (PathBuf, PathData)> {
328 tracing::trace!("rescanning");
329
330 watch_handlers.iter().flat_map(move |(path, is_recursive)| {
331 tracing::trace!(?path, is_recursive, "scanning watch handler");
332
333 WalkDir::new(path)
338 .follow_links(follow_symlinks)
339 .max_depth(if *is_recursive { usize::MAX } else { 1 })
340 .into_iter()
341 .filter_map(|entry_res| match entry_res {
342 Ok(entry) => Some(entry),
343 Err(err) => {
344 tracing::warn!("walkdir error scanning {err:?}");
345
346 if let Some(io_error) = err.io_error() {
347 if io_error.kind() == io::ErrorKind::NotFound {
348 return None;
349 }
350 let new_io_error = io::Error::new(io_error.kind(), err.to_string());
352 data_builder.emitter.emit_io_err(new_io_error, err.path());
353 } else {
354 let crate_err =
355 Error::new(crate::ErrorKind::Generic(err.to_string()));
356 data_builder.emitter.emit(Err(crate_err));
357 }
358 None
359 }
360 })
361 .filter_map(move |entry| match entry.metadata() {
362 Ok(metadata) => {
363 let path = entry.into_path();
364 let meta_path = MetaPath::from_parts_unchecked(path, metadata);
365 let data_path = data_builder.build_path_data(&meta_path);
366
367 Some((meta_path.into_path(), data_path))
368 }
369 Err(err) => {
370 if let Some(io_error) = err.io_error()
371 && io_error.kind() == io::ErrorKind::NotFound
372 {
373 return None;
374 }
375
376 let path = entry.into_path();
378 data_builder.emitter.emit_io_err(err, Some(path));
379
380 None
381 }
382 })
383 })
384 }
385 }
386
387 #[derive(Debug, Clone)]
391 struct PathData {
392 mtime: SystemTime,
394
395 file_type: FileType,
396
397 hash: Option<u64>,
400
401 last_check: Instant,
403 }
404
405 impl PathData {
406 fn new(data_builder: &DataBuilder, meta_path: &MetaPath) -> PathData {
408 let metadata = meta_path.metadata();
409
410 PathData {
411 mtime: metadata.modified().unwrap_or(SystemTime::UNIX_EPOCH),
412 file_type: metadata.file_type(),
413 hash: data_builder
414 .build_hasher
415 .as_ref()
416 .filter(|_| metadata.is_file())
417 .and_then(|build_hasher| {
418 Self::get_content_hash(build_hasher, meta_path.path()).ok()
419 }),
420
421 last_check: data_builder.now,
422 }
423 }
424
425 fn get_content_hash(build_hasher: &RandomState, path: &Path) -> io::Result<u64> {
427 let mut hasher = build_hasher.build_hasher();
428 let mut file = File::open(path)?;
429 let mut buf = [0; 512];
430
431 loop {
432 let n = match file.read(&mut buf) {
433 Ok(0) => break,
434 Ok(len) => len,
435 Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
436 Err(e) => return Err(e),
437 };
438
439 hasher.write(&buf[..n]);
440 }
441
442 Ok(hasher.finish())
443 }
444
445 fn get_create_kind(&self) -> CreateKind {
447 #[expect(clippy::filetype_is_file)]
448 if self.file_type.is_dir() {
449 CreateKind::Folder
450 } else if self.file_type.is_file() {
451 CreateKind::File
452 } else {
453 CreateKind::Any
454 }
455 }
456
457 fn get_remove_kind(&self) -> RemoveKind {
459 #[expect(clippy::filetype_is_file)]
460 if self.file_type.is_dir() {
461 RemoveKind::Folder
462 } else if self.file_type.is_file() {
463 RemoveKind::File
464 } else {
465 RemoveKind::Any
466 }
467 }
468
469 fn compare_to_kind(old: Option<&PathData>, new: Option<&PathData>) -> Option<EventKind> {
471 match (old, new) {
472 (Some(old), Some(new)) => {
473 if new.mtime > old.mtime {
474 Some(EventKind::Modify(ModifyKind::Metadata(
475 MetadataKind::WriteTime,
476 )))
477 } else if new.hash != old.hash {
478 Some(EventKind::Modify(ModifyKind::Data(DataChange::Any)))
479 } else {
480 None
481 }
482 }
483 (None, Some(new)) => Some(EventKind::Create(new.get_create_kind())),
484 (Some(old), None) => Some(EventKind::Remove(old.get_remove_kind())),
485 (None, None) => None,
486 }
487 }
488 }
489
490 #[derive(Debug)]
496 pub(super) struct MetaPath {
497 path: PathBuf,
498 metadata: Metadata,
499 }
500
501 impl MetaPath {
502 fn from_parts_unchecked(path: PathBuf, metadata: Metadata) -> Self {
508 Self { path, metadata }
509 }
510
511 fn path(&self) -> &Path {
512 &self.path
513 }
514
515 fn metadata(&self) -> &Metadata {
516 &self.metadata
517 }
518
519 fn into_path(self) -> PathBuf {
520 self.path
521 }
522 }
523
524 struct EventEmitter(
526 Box<RefCell<dyn EventHandler>>,
529 );
530
531 impl EventEmitter {
532 fn new<F: EventHandler>(event_handler: F) -> Self {
533 Self(Box::new(RefCell::new(event_handler)))
534 }
535
536 fn emit(&self, event: crate::Result<Event>) {
538 self.0.borrow_mut().handle_event(event);
539 }
540
541 fn emit_ok(&self, event: Event) {
543 self.emit(Ok(event));
544 }
545
546 fn emit_io_err<E, P>(&self, err: E, path: Option<P>)
548 where
549 E: Into<io::Error>,
550 P: Into<PathBuf>,
551 {
552 let e = crate::Error::io(err.into());
553 if let Some(path) = path {
554 self.emit(Err(e.add_path(path.into())));
555 } else {
556 self.emit(Err(e));
557 }
558 }
559 }
560}
561
562enum EventLoopMsg {
563 AddWatch(PathBuf, WatchMode, Sender<Result<()>>),
564 AddWatchMultiple(Vec<(PathBuf, WatchMode)>, Sender<Result<()>>),
565 RemoveWatch(PathBuf, Sender<Result<()>>),
566 #[cfg(test)]
567 WaitNextScan(Sender<Result<()>>),
568 Poll,
570 Shutdown,
571}
572
573struct PollPathsMut<'a> {
574 inner: &'a mut PollWatcher,
575 add_paths: Vec<(PathBuf, WatchMode)>,
576}
577impl<'a> PollPathsMut<'a> {
578 fn new(watcher: &'a mut PollWatcher) -> Self {
579 Self {
580 inner: watcher,
581 add_paths: Vec::new(),
582 }
583 }
584}
585impl PathsMut for PollPathsMut<'_> {
586 #[tracing::instrument(level = "debug", skip(self))]
587 fn add(&mut self, path: &Path, watch_mode: WatchMode) -> Result<()> {
588 self.add_paths.push((path.to_owned(), watch_mode));
589 Ok(())
590 }
591
592 #[tracing::instrument(level = "debug", skip(self))]
593 fn remove(&mut self, path: &Path) -> Result<()> {
594 self.inner.unwatch_inner(path)
595 }
596
597 #[tracing::instrument(level = "debug", skip(self))]
598 fn commit(self: Box<Self>) -> Result<()> {
599 let paths = self.add_paths;
600 self.inner.watch_multiple_inner(paths)
601 }
602}
603
604#[derive(Debug)]
611pub struct PollWatcher {
612 delay: Option<Duration>,
613 follow_symlinks: bool,
614
615 event_loop_tx: Sender<EventLoopMsg>,
616}
617
618impl PollWatcher {
619 pub fn new<F: EventHandler>(event_handler: F, config: Config) -> crate::Result<PollWatcher> {
621 Ok(Self::with_opt::<_, ()>(event_handler, config, None))
622 }
623
624 pub fn poll(&self) -> crate::Result<()> {
626 self.event_loop_tx
627 .send(EventLoopMsg::Poll)
628 .map_err(|_| Error::generic("failed to send poll message"))?;
629 Ok(())
630 }
631
632 #[cfg(test)]
633 pub(crate) fn wait_next_scan(&self) -> crate::Result<()> {
634 let (tx, rx) = unbounded();
635 self.event_loop_tx
636 .send(EventLoopMsg::WaitNextScan(tx))
637 .map_err(|_| Error::generic("failed to send WaitNextScan message"))?;
638 rx.recv().unwrap()
639 }
640
641 #[cfg(test)]
643 pub(crate) fn poll_sender(&self) -> Sender<()> {
644 let inner_tx = self.event_loop_tx.clone();
645 let (tx, rx) = unbounded();
646 thread::Builder::new()
647 .name("notify-rs poll loop".to_string())
648 .spawn(move || {
649 for () in &rx {
650 if let Err(err) = inner_tx.send(EventLoopMsg::Poll) {
651 tracing::error!(?err, "failed to send poll message");
652 }
653 }
654 })
655 .unwrap();
656 tx
657 }
658
659 pub fn with_initial_scan<F: EventHandler, G: ScanEventHandler>(
663 event_handler: F,
664 config: Config,
665 scan_callback: G,
666 ) -> crate::Result<PollWatcher> {
667 Ok(Self::with_opt(event_handler, config, Some(scan_callback)))
668 }
669
670 fn with_opt<F: EventHandler, G: ScanEventHandler>(
672 event_handler: F,
673 config: Config,
674 scan_callback: Option<G>,
675 ) -> PollWatcher {
676 let (tx, rx) = unbounded();
677
678 let poll_watcher = PollWatcher {
679 delay: config.poll_interval(),
680 follow_symlinks: config.follow_symlinks(),
681
682 event_loop_tx: tx,
683 };
684
685 let data_builder =
686 DataBuilder::new(event_handler, config.compare_contents(), scan_callback);
687 poll_watcher.run(rx, data_builder);
688
689 poll_watcher
690 }
691
692 fn run(&self, rx: Receiver<EventLoopMsg>, mut data_builder: DataBuilder) {
693 let delay = self.delay;
694 let follow_symlinks = self.follow_symlinks;
695
696 let result = thread::Builder::new()
697 .name("notify-rs poll loop".to_string())
698 .spawn(move || {
699 let mut watch_data = WatchData::new(follow_symlinks);
700
701 loop {
702 data_builder.update_timestamp();
703 watch_data.rescan(&data_builder);
704
705 let result = if let Some(delay) = delay {
707 rx.recv_timeout(delay).or_else(|e| match e {
708 mpsc::RecvTimeoutError::Timeout => Ok(EventLoopMsg::Poll),
709 mpsc::RecvTimeoutError::Disconnected => Err(mpsc::RecvError),
710 })
711 } else {
712 rx.recv()
713 };
714 match result {
715 Ok(EventLoopMsg::AddWatch(path, mode, resp_tx)) => {
716 let result = resp_tx.send(watch_data.add_watch(path, mode));
717 if let Err(e) = result {
718 tracing::error!(?e, "failed to send AddWatch response");
719 }
720 }
721 Ok(EventLoopMsg::AddWatchMultiple(paths, resp_tx)) => {
722 let result = resp_tx.send(watch_data.add_watch_multiple(paths));
723 if let Err(e) = result {
724 tracing::error!(?e, "failed to send AddWatchMultiple response");
725 }
726 }
727 Ok(EventLoopMsg::RemoveWatch(path, resp_tx)) => {
728 let result = resp_tx.send(watch_data.remove_watch(&path));
729 if let Err(e) = result {
730 tracing::error!(?e, "failed to send RemoveWatch response");
731 }
732 }
733 Ok(EventLoopMsg::Poll) => {
734 }
736 #[cfg(test)]
737 Ok(EventLoopMsg::WaitNextScan(resp_tx)) => {
738 let result = resp_tx.send(Ok(()));
739 if let Err(e) = result {
740 tracing::error!(?e, "failed to send WaitNextScan response");
741 }
742 }
743 Ok(EventLoopMsg::Shutdown) => {
744 break;
745 }
746 Err(e) => {
747 tracing::error!(?e, "failed to receive poll message");
748 }
749 }
750 }
751 });
752 if let Err(e) = result {
753 tracing::error!(?e, "failed to start poll watcher thread");
754 }
755 }
756
757 fn watch_inner(&self, path: &Path, watch_mode: WatchMode) -> crate::Result<()> {
759 let (tx, rx) = unbounded();
760 self.event_loop_tx
761 .send(EventLoopMsg::AddWatch(path.to_path_buf(), watch_mode, tx))?;
762 rx.recv().unwrap()
763 }
764
765 fn watch_multiple_inner(&self, paths: Vec<(PathBuf, WatchMode)>) -> crate::Result<()> {
766 let (tx, rx) = unbounded();
767 self.event_loop_tx
768 .send(EventLoopMsg::AddWatchMultiple(paths, tx))?;
769 rx.recv().unwrap()
770 }
771
772 fn unwatch_inner(&self, path: &Path) -> crate::Result<()> {
776 let (tx, rx) = unbounded();
777 self.event_loop_tx
778 .send(EventLoopMsg::RemoveWatch(path.to_path_buf(), tx))?;
779 rx.recv().unwrap()
780 }
781}
782
783impl Watcher for PollWatcher {
784 #[tracing::instrument(level = "debug", skip(event_handler))]
786 fn new<F: EventHandler>(event_handler: F, config: Config) -> crate::Result<Self> {
787 Self::new(event_handler, config)
788 }
789
790 #[tracing::instrument(level = "debug", skip(self))]
791 fn watch(&mut self, path: &Path, watch_mode: WatchMode) -> crate::Result<()> {
792 self.watch_inner(path, watch_mode)
793 }
794
795 #[tracing::instrument(level = "debug", skip(self))]
796 fn paths_mut<'me>(&'me mut self) -> Box<dyn PathsMut + 'me> {
797 Box::new(PollPathsMut::new(self))
798 }
799
800 #[tracing::instrument(level = "debug", skip(self))]
801 fn unwatch(&mut self, path: &Path) -> crate::Result<()> {
802 self.unwatch_inner(path)
803 }
804
805 fn kind() -> crate::WatcherKind {
806 crate::WatcherKind::PollWatcher
807 }
808}
809
810impl Drop for PollWatcher {
811 fn drop(&mut self) {
812 let result = self.event_loop_tx.send(EventLoopMsg::Shutdown);
813 if let Err(e) = result {
814 tracing::error!(?e, "failed to send shutdown message to poll watcher thread");
815 }
816 }
817}
818
819#[cfg(test)]
820mod tests {
821 #[cfg(target_family = "wasm")]
822 use std::thread::sleep;
823 #[cfg(target_family = "wasm")]
824 use std::time::Duration;
825
826 use super::PollWatcher;
827 use crate::{
828 Error, ErrorKind, RecursiveMode, TargetMode, WatchMode, Watcher, event::EventKind, test::*,
829 };
830
831 fn watcher() -> (TestWatcher<PollWatcher>, Receiver) {
832 poll_watcher_channel()
833 }
834
835 #[test]
836 fn poll_watcher_is_send_and_sync() {
837 fn check<T: Send + Sync>() {}
838 check::<PollWatcher>();
839 }
840
841 #[test]
842 fn create_file() {
843 let tmpdir = testdir();
844 let (mut watcher, rx) = watcher();
845 watcher.watch_recursively(&tmpdir);
846 watcher.watcher.wait_next_scan().expect("wait next scan");
847
848 let path = tmpdir.path().join("entry");
849 std::fs::File::create_new(&path).expect("Unable to create");
850
851 rx.sleep_until_parent_contains(&path);
852 rx.sleep_until_exists(&path);
853
854 rx.wait_unordered_exact([
855 expected(&path).create_file(),
856 expected(tmpdir.path()).modify_meta_mtime().optional(),
857 ]);
858 }
859
860 #[test]
861 fn create_self_file() {
862 let tmpdir = testdir();
863 let (mut watcher, rx) = watcher();
864
865 let path = tmpdir.path().join("entry");
866
867 watcher.watch_nonrecursively(&path);
868 watcher.watcher.wait_next_scan().expect("wait next scan");
869
870 std::fs::File::create_new(&path).expect("create");
871
872 rx.sleep_until_exists(&path);
873 rx.wait_ordered_exact([expected(&path).create_file()]);
874 }
875
876 #[test]
877 fn create_self_file_no_track() {
878 let tmpdir = testdir();
879 let (mut watcher, _) = watcher();
880
881 let path = tmpdir.path().join("entry");
882
883 let result = watcher.watcher.watch(
884 &path,
885 WatchMode {
886 recursive_mode: RecursiveMode::NonRecursive,
887 target_mode: TargetMode::NoTrack,
888 },
889 );
890 assert!(matches!(
891 result,
892 Err(Error {
893 paths: _,
894 kind: ErrorKind::PathNotFound
895 })
896 ));
897 }
898
899 #[test]
900 fn create_self_file_nested() {
901 let tmpdir = testdir();
902 let (mut watcher, rx) = watcher();
903
904 let path = tmpdir.path().join("entry/nested");
905
906 watcher.watch_nonrecursively(&path);
907 watcher.watcher.wait_next_scan().expect("wait next scan");
908
909 std::fs::create_dir_all(path.parent().unwrap()).expect("create");
910 std::fs::File::create_new(&path).expect("create");
911
912 rx.wait_ordered_exact([expected(&path).create_file()]);
913 }
914
915 #[test]
916 fn create_dir() {
917 let tmpdir = testdir();
918 let (mut watcher, rx) = watcher();
919 watcher.watch_recursively(&tmpdir);
920 watcher.watcher.wait_next_scan().expect("wait next scan");
921
922 let path = tmpdir.path().join("entry");
923 std::fs::create_dir(&path).expect("Unable to create");
924
925 rx.sleep_until_parent_contains(&path);
926 rx.sleep_until_exists(&path);
927
928 rx.wait_unordered_exact([
929 expected(&path).create_folder(),
930 expected(tmpdir.path()).modify_meta_mtime().optional(),
931 ]);
932 }
933
934 #[test]
935 fn modify_file() {
936 let tmpdir = testdir();
937 let (mut watcher, rx) = watcher();
938 let path = tmpdir.path().join("entry");
939 std::fs::File::create_new(&path).expect("Unable to create");
940
941 rx.sleep_until_parent_contains(&path);
942
943 watcher.watch_recursively(&tmpdir);
944 watcher.watcher.wait_next_scan().expect("wait next scan");
945 std::fs::write(&path, b"123").expect("Unable to write");
946
947 assert!(
948 rx.sleep_until(|| std::fs::read_to_string(&path).is_ok_and(|content| content == "123")),
949 "the file wasn't modified"
950 );
951 rx.wait_unordered_exact([expected(&path).modify().multiple()]);
952 }
953
954 #[test]
955 fn rename_file() {
956 let tmpdir = testdir();
957 let (mut watcher, rx) = watcher();
958 let path = tmpdir.path().join("entry");
959 let new_path = tmpdir.path().join("new_entry");
960 std::fs::File::create_new(&path).expect("Unable to create");
961
962 rx.sleep_until_parent_contains(&path);
963
964 watcher.watch_recursively(&tmpdir);
965
966 watcher.watcher.wait_next_scan().expect("wait next scan");
967 std::fs::rename(&path, &new_path).expect("Unable to remove");
968
969 rx.sleep_while_exists(&path);
970 rx.sleep_until_exists(&new_path);
971
972 rx.sleep_while_parent_contains(&path);
973 rx.sleep_until_parent_contains(&new_path);
974
975 rx.wait_unordered_exact([
976 expected(&path).remove_file(),
977 expected(&new_path).create_file(),
978 expected(tmpdir.path()).modify_meta_mtime().optional(),
979 ]);
980 }
981
982 #[test]
983 fn rename_self_file() {
984 let tmpdir = testdir();
985 let (mut watcher, rx) = watcher();
986
987 let path = tmpdir.path().join("entry");
988 std::fs::File::create_new(&path).expect("create");
989
990 watcher.watch_nonrecursively(&path);
991 watcher.watcher.wait_next_scan().expect("wait next scan");
992 let new_path = tmpdir.path().join("renamed");
993
994 std::fs::rename(&path, &new_path).expect("rename");
995
996 rx.sleep_while_exists(&path);
997 rx.sleep_until_exists(&new_path);
998
999 rx.wait_unordered_exact([expected(&path).remove_file()])
1000 .ensure_no_tail();
1001
1002 std::fs::rename(&new_path, &path).expect("rename2");
1003 watcher.watcher.wait_next_scan().expect("wait next scan");
1004
1005 rx.sleep_while_exists(&new_path);
1006 rx.sleep_until_exists(&path);
1007
1008 rx.wait_unordered_exact([expected(&path).create_file()])
1009 .ensure_no_tail();
1010 }
1011
1012 #[test]
1013 fn rename_self_file_no_track() {
1014 let tmpdir = testdir();
1015 let (mut watcher, rx) = watcher();
1016
1017 let path = tmpdir.path().join("entry");
1018 std::fs::File::create_new(&path).expect("create");
1019
1020 watcher.watch(
1021 &path,
1022 WatchMode {
1023 recursive_mode: RecursiveMode::NonRecursive,
1024 target_mode: TargetMode::NoTrack,
1025 },
1026 );
1027 watcher.watcher.wait_next_scan().expect("wait next scan");
1028
1029 let new_path = tmpdir.path().join("renamed");
1030
1031 std::fs::rename(&path, &new_path).expect("rename");
1032
1033 rx.sleep_while_exists(&path);
1034 rx.sleep_until_exists(&new_path);
1035
1036 #[cfg(target_family = "wasm")]
1037 sleep(Duration::from_millis(100));
1038
1039 rx.wait_unordered_exact([
1040 expected(&path).modify_data_any().optional(),
1041 expected(&path).remove_file(),
1042 ])
1043 .ensure_no_tail();
1044
1045 let result = watcher.watcher.watch(
1046 &path,
1047 WatchMode {
1048 recursive_mode: RecursiveMode::NonRecursive,
1049 target_mode: TargetMode::NoTrack,
1050 },
1051 );
1052 assert!(matches!(
1053 result,
1054 Err(Error {
1055 paths: _,
1056 kind: ErrorKind::PathNotFound
1057 })
1058 ));
1059 }
1060
1061 #[test]
1062 fn delete_file() {
1063 let tmpdir = testdir();
1064 let (mut watcher, rx) = watcher();
1065 let path = tmpdir.path().join("entry");
1066 std::fs::File::create_new(&path).expect("Unable to create");
1067
1068 rx.sleep_until_parent_contains(&path);
1069
1070 watcher.watch_recursively(&tmpdir);
1071 watcher.watcher.wait_next_scan().expect("wait next scan");
1072
1073 std::fs::remove_file(&path).expect("Unable to remove");
1074
1075 rx.sleep_while_exists(&path);
1076 rx.sleep_while_parent_contains(&path);
1077
1078 rx.wait_unordered_exact([
1079 expected(&path).modify_data_any().optional(),
1080 expected(&path).remove_file(),
1081 expected(tmpdir.path()).modify_meta_mtime().optional(),
1082 ]);
1083 }
1084
1085 #[test]
1086 fn delete_self_file() {
1087 let tmpdir = testdir();
1088 let (mut watcher, rx) = watcher();
1089 let path = tmpdir.path().join("entry");
1090 std::fs::File::create_new(&path).expect("Unable to create");
1091
1092 watcher.watch_nonrecursively(&path);
1093 watcher.watcher.wait_next_scan().expect("wait next scan");
1094
1095 std::fs::remove_file(&path).expect("Unable to remove");
1096
1097 rx.sleep_while_exists(&path);
1098 rx.wait_ordered_exact([
1099 expected(&path).modify_data_any().optional(),
1100 expected(&path).remove_file(),
1101 ]);
1102
1103 std::fs::write(&path, "").expect("write");
1104
1105 rx.sleep_until_exists(&path);
1106 rx.wait_ordered_exact([expected(&path).create_file()]);
1107 }
1108
1109 #[test]
1110 fn delete_self_file_no_track() {
1111 let tmpdir = testdir();
1112 let (mut watcher, rx) = watcher();
1113 let path = tmpdir.path().join("entry");
1114 std::fs::File::create_new(&path).expect("Unable to create");
1115
1116 watcher.watch(
1117 &path,
1118 WatchMode {
1119 recursive_mode: RecursiveMode::NonRecursive,
1120 target_mode: TargetMode::NoTrack,
1121 },
1122 );
1123 watcher.watcher.wait_next_scan().expect("wait next scan");
1124
1125 std::fs::remove_file(&path).expect("Unable to remove");
1126
1127 rx.sleep_while_exists(&path);
1128 rx.wait_ordered_exact([
1129 expected(&path).modify_data_any().optional(),
1130 expected(&path).remove_file(),
1131 ]);
1132
1133 #[cfg(target_family = "wasm")]
1134 sleep(Duration::from_millis(100));
1135
1136 std::fs::write(&path, "").expect("write");
1137
1138 rx.ensure_empty_with_wait();
1139 }
1140
1141 #[test]
1142 fn create_write_overwrite() {
1143 let tmpdir = testdir();
1144 let (mut watcher, rx) = watcher();
1145 let overwritten_file = tmpdir.path().join("overwritten_file");
1146 let overwriting_file = tmpdir.path().join("overwriting_file");
1147 std::fs::write(&overwritten_file, "123").expect("write1");
1148
1149 rx.sleep_until_parent_contains(&overwritten_file);
1150 rx.sleep_until_exists(&overwritten_file);
1151
1152 watcher.watch_nonrecursively(&tmpdir);
1153 watcher.watcher.wait_next_scan().expect("wait next scan");
1154
1155 std::fs::File::create(&overwriting_file).expect("create");
1156 std::fs::write(&overwriting_file, "321").expect("write2");
1157 std::fs::rename(&overwriting_file, &overwritten_file).expect("rename");
1158
1159 rx.sleep_while_exists(&overwriting_file);
1160 rx.sleep_while_parent_contains(&overwriting_file);
1161
1162 assert!(
1163 rx.sleep_until(
1164 || std::fs::read_to_string(&overwritten_file).is_ok_and(|cnt| cnt == "321")
1165 ),
1166 "file {overwritten_file:?} was not replaced"
1167 );
1168
1169 rx.wait_unordered([expected(&overwritten_file).modify()]);
1170 }
1171
1172 fn assert_track_path_continues_after_recreating_file_in_nested_directory(
1173 upgrade_from_no_track: bool,
1174 ) {
1175 let tmpdir = testdir();
1176 let (mut watcher, mut rx) = watcher();
1177 let nested_dir = tmpdir.path().join("nested");
1178 let watched_file = nested_dir.join("watched");
1179 let moved_file = tmpdir.path().join("moved");
1180 std::fs::create_dir(&nested_dir).expect("create nested dir");
1181 std::fs::write(&watched_file, "initial").expect("write watched file");
1182
1183 watcher.watch_nonrecursively(&tmpdir);
1184 if upgrade_from_no_track {
1185 watcher.watch(
1186 &watched_file,
1187 WatchMode {
1188 recursive_mode: RecursiveMode::NonRecursive,
1189 target_mode: TargetMode::NoTrack,
1190 },
1191 );
1192 }
1193 watcher.watch_nonrecursively(&watched_file);
1194
1195 std::fs::rename(&watched_file, &moved_file).expect("move watched file");
1196 std::fs::copy(&moved_file, &watched_file).expect("recreate watched file");
1197 std::fs::remove_file(&moved_file).expect("remove moved file");
1198
1199 watcher.watcher.poll().expect("scan replacement");
1201 watcher.watcher.wait_next_scan().expect("wait for scan");
1202 for _ in rx.iter() {}
1203
1204 std::fs::write(&watched_file, "updated").expect("update watched file");
1205 watcher.watcher.poll().expect("scan update");
1206 watcher.watcher.wait_next_scan().expect("wait for scan");
1207 let received_change = rx.iter().any(|event| {
1208 event.paths.iter().any(|path| path == &watched_file)
1209 && matches!(event.kind, EventKind::Create(_) | EventKind::Modify(_))
1210 });
1211
1212 assert!(
1213 received_change,
1214 "expected a change event after recreating the watched file"
1215 );
1216 }
1217
1218 #[test]
1219 fn track_path_continues_after_recreating_file_in_nested_directory() {
1220 assert_track_path_continues_after_recreating_file_in_nested_directory(false);
1221 }
1222
1223 #[test]
1224 fn track_path_upgrade_continues_after_recreating_file_in_nested_directory() {
1225 assert_track_path_continues_after_recreating_file_in_nested_directory(true);
1226 }
1227}