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::{Error, ErrorKind, RecursiveMode, TargetMode, WatchMode, Watcher, test::*};
828
829 fn watcher() -> (TestWatcher<PollWatcher>, Receiver) {
830 poll_watcher_channel()
831 }
832
833 #[test]
834 fn poll_watcher_is_send_and_sync() {
835 fn check<T: Send + Sync>() {}
836 check::<PollWatcher>();
837 }
838
839 #[test]
840 fn create_file() {
841 let tmpdir = testdir();
842 let (mut watcher, rx) = watcher();
843 watcher.watch_recursively(&tmpdir);
844 watcher.watcher.wait_next_scan().expect("wait next scan");
845
846 let path = tmpdir.path().join("entry");
847 std::fs::File::create_new(&path).expect("Unable to create");
848
849 rx.sleep_until_parent_contains(&path);
850 rx.sleep_until_exists(&path);
851
852 rx.wait_unordered_exact([
853 expected(&path).create_file(),
854 expected(tmpdir.path()).modify_meta_mtime().optional(),
855 ]);
856 }
857
858 #[test]
859 fn create_self_file() {
860 let tmpdir = testdir();
861 let (mut watcher, rx) = watcher();
862
863 let path = tmpdir.path().join("entry");
864
865 watcher.watch_nonrecursively(&path);
866 watcher.watcher.wait_next_scan().expect("wait next scan");
867
868 std::fs::File::create_new(&path).expect("create");
869
870 rx.sleep_until_exists(&path);
871 rx.wait_ordered_exact([expected(&path).create_file()]);
872 }
873
874 #[test]
875 fn create_self_file_no_track() {
876 let tmpdir = testdir();
877 let (mut watcher, _) = watcher();
878
879 let path = tmpdir.path().join("entry");
880
881 let result = watcher.watcher.watch(
882 &path,
883 WatchMode {
884 recursive_mode: RecursiveMode::NonRecursive,
885 target_mode: TargetMode::NoTrack,
886 },
887 );
888 assert!(matches!(
889 result,
890 Err(Error {
891 paths: _,
892 kind: ErrorKind::PathNotFound
893 })
894 ));
895 }
896
897 #[test]
898 fn create_self_file_nested() {
899 let tmpdir = testdir();
900 let (mut watcher, rx) = watcher();
901
902 let path = tmpdir.path().join("entry/nested");
903
904 watcher.watch_nonrecursively(&path);
905 watcher.watcher.wait_next_scan().expect("wait next scan");
906
907 std::fs::create_dir_all(path.parent().unwrap()).expect("create");
908 std::fs::File::create_new(&path).expect("create");
909
910 rx.wait_ordered_exact([expected(&path).create_file()]);
911 }
912
913 #[test]
914 fn create_dir() {
915 let tmpdir = testdir();
916 let (mut watcher, rx) = watcher();
917 watcher.watch_recursively(&tmpdir);
918 watcher.watcher.wait_next_scan().expect("wait next scan");
919
920 let path = tmpdir.path().join("entry");
921 std::fs::create_dir(&path).expect("Unable to create");
922
923 rx.sleep_until_parent_contains(&path);
924 rx.sleep_until_exists(&path);
925
926 rx.wait_unordered_exact([
927 expected(&path).create_folder(),
928 expected(tmpdir.path()).modify_meta_mtime().optional(),
929 ]);
930 }
931
932 #[test]
933 fn modify_file() {
934 let tmpdir = testdir();
935 let (mut watcher, rx) = watcher();
936 let path = tmpdir.path().join("entry");
937 std::fs::File::create_new(&path).expect("Unable to create");
938
939 rx.sleep_until_parent_contains(&path);
940
941 watcher.watch_recursively(&tmpdir);
942 watcher.watcher.wait_next_scan().expect("wait next scan");
943 std::fs::write(&path, b"123").expect("Unable to write");
944
945 assert!(
946 rx.sleep_until(|| std::fs::read_to_string(&path).is_ok_and(|content| content == "123")),
947 "the file wasn't modified"
948 );
949 rx.wait_unordered_exact([expected(&path).modify().multiple()]);
950 }
951
952 #[test]
953 fn rename_file() {
954 let tmpdir = testdir();
955 let (mut watcher, rx) = watcher();
956 let path = tmpdir.path().join("entry");
957 let new_path = tmpdir.path().join("new_entry");
958 std::fs::File::create_new(&path).expect("Unable to create");
959
960 rx.sleep_until_parent_contains(&path);
961
962 watcher.watch_recursively(&tmpdir);
963
964 watcher.watcher.wait_next_scan().expect("wait next scan");
965 std::fs::rename(&path, &new_path).expect("Unable to remove");
966
967 rx.sleep_while_exists(&path);
968 rx.sleep_until_exists(&new_path);
969
970 rx.sleep_while_parent_contains(&path);
971 rx.sleep_until_parent_contains(&new_path);
972
973 rx.wait_unordered_exact([
974 expected(&path).remove_file(),
975 expected(&new_path).create_file(),
976 expected(tmpdir.path()).modify_meta_mtime().optional(),
977 ]);
978 }
979
980 #[test]
981 fn rename_self_file() {
982 let tmpdir = testdir();
983 let (mut watcher, rx) = watcher();
984
985 let path = tmpdir.path().join("entry");
986 std::fs::File::create_new(&path).expect("create");
987
988 watcher.watch_nonrecursively(&path);
989 watcher.watcher.wait_next_scan().expect("wait next scan");
990 let new_path = tmpdir.path().join("renamed");
991
992 std::fs::rename(&path, &new_path).expect("rename");
993
994 rx.sleep_while_exists(&path);
995 rx.sleep_until_exists(&new_path);
996
997 rx.wait_unordered_exact([expected(&path).remove_file()])
998 .ensure_no_tail();
999
1000 std::fs::rename(&new_path, &path).expect("rename2");
1001 watcher.watcher.wait_next_scan().expect("wait next scan");
1002
1003 rx.sleep_while_exists(&new_path);
1004 rx.sleep_until_exists(&path);
1005
1006 rx.wait_unordered_exact([expected(&path).create_file()])
1007 .ensure_no_tail();
1008 }
1009
1010 #[test]
1011 fn rename_self_file_no_track() {
1012 let tmpdir = testdir();
1013 let (mut watcher, rx) = watcher();
1014
1015 let path = tmpdir.path().join("entry");
1016 std::fs::File::create_new(&path).expect("create");
1017
1018 watcher.watch(
1019 &path,
1020 WatchMode {
1021 recursive_mode: RecursiveMode::NonRecursive,
1022 target_mode: TargetMode::NoTrack,
1023 },
1024 );
1025 watcher.watcher.wait_next_scan().expect("wait next scan");
1026
1027 let new_path = tmpdir.path().join("renamed");
1028
1029 std::fs::rename(&path, &new_path).expect("rename");
1030
1031 rx.sleep_while_exists(&path);
1032 rx.sleep_until_exists(&new_path);
1033
1034 #[cfg(target_family = "wasm")]
1035 sleep(Duration::from_millis(100));
1036
1037 rx.wait_unordered_exact([
1038 expected(&path).modify_data_any().optional(),
1039 expected(&path).remove_file(),
1040 ])
1041 .ensure_no_tail();
1042
1043 let result = watcher.watcher.watch(
1044 &path,
1045 WatchMode {
1046 recursive_mode: RecursiveMode::NonRecursive,
1047 target_mode: TargetMode::NoTrack,
1048 },
1049 );
1050 assert!(matches!(
1051 result,
1052 Err(Error {
1053 paths: _,
1054 kind: ErrorKind::PathNotFound
1055 })
1056 ));
1057 }
1058
1059 #[test]
1060 fn delete_file() {
1061 let tmpdir = testdir();
1062 let (mut watcher, rx) = watcher();
1063 let path = tmpdir.path().join("entry");
1064 std::fs::File::create_new(&path).expect("Unable to create");
1065
1066 rx.sleep_until_parent_contains(&path);
1067
1068 watcher.watch_recursively(&tmpdir);
1069 watcher.watcher.wait_next_scan().expect("wait next scan");
1070
1071 std::fs::remove_file(&path).expect("Unable to remove");
1072
1073 rx.sleep_while_exists(&path);
1074 rx.sleep_while_parent_contains(&path);
1075
1076 rx.wait_unordered_exact([
1077 expected(&path).modify_data_any().optional(),
1078 expected(&path).remove_file(),
1079 expected(tmpdir.path()).modify_meta_mtime().optional(),
1080 ]);
1081 }
1082
1083 #[test]
1084 fn delete_self_file() {
1085 let tmpdir = testdir();
1086 let (mut watcher, rx) = watcher();
1087 let path = tmpdir.path().join("entry");
1088 std::fs::File::create_new(&path).expect("Unable to create");
1089
1090 watcher.watch_nonrecursively(&path);
1091 watcher.watcher.wait_next_scan().expect("wait next scan");
1092
1093 std::fs::remove_file(&path).expect("Unable to remove");
1094
1095 rx.sleep_while_exists(&path);
1096 rx.wait_ordered_exact([
1097 expected(&path).modify_data_any().optional(),
1098 expected(&path).remove_file(),
1099 ]);
1100
1101 std::fs::write(&path, "").expect("write");
1102
1103 rx.sleep_until_exists(&path);
1104 rx.wait_ordered_exact([expected(&path).create_file()]);
1105 }
1106
1107 #[test]
1108 fn delete_self_file_no_track() {
1109 let tmpdir = testdir();
1110 let (mut watcher, rx) = watcher();
1111 let path = tmpdir.path().join("entry");
1112 std::fs::File::create_new(&path).expect("Unable to create");
1113
1114 watcher.watch(
1115 &path,
1116 WatchMode {
1117 recursive_mode: RecursiveMode::NonRecursive,
1118 target_mode: TargetMode::NoTrack,
1119 },
1120 );
1121 watcher.watcher.wait_next_scan().expect("wait next scan");
1122
1123 std::fs::remove_file(&path).expect("Unable to remove");
1124
1125 rx.sleep_while_exists(&path);
1126 rx.wait_ordered_exact([
1127 expected(&path).modify_data_any().optional(),
1128 expected(&path).remove_file(),
1129 ]);
1130
1131 #[cfg(target_family = "wasm")]
1132 sleep(Duration::from_millis(100));
1133
1134 std::fs::write(&path, "").expect("write");
1135
1136 rx.ensure_empty_with_wait();
1137 }
1138
1139 #[test]
1140 fn create_write_overwrite() {
1141 let tmpdir = testdir();
1142 let (mut watcher, rx) = watcher();
1143 let overwritten_file = tmpdir.path().join("overwritten_file");
1144 let overwriting_file = tmpdir.path().join("overwriting_file");
1145 std::fs::write(&overwritten_file, "123").expect("write1");
1146
1147 rx.sleep_until_parent_contains(&overwritten_file);
1148 rx.sleep_until_exists(&overwritten_file);
1149
1150 watcher.watch_nonrecursively(&tmpdir);
1151 watcher.watcher.wait_next_scan().expect("wait next scan");
1152
1153 std::fs::File::create(&overwriting_file).expect("create");
1154 std::fs::write(&overwriting_file, "321").expect("write2");
1155 std::fs::rename(&overwriting_file, &overwritten_file).expect("rename");
1156
1157 rx.sleep_while_exists(&overwriting_file);
1158 rx.sleep_while_parent_contains(&overwriting_file);
1159
1160 assert!(
1161 rx.sleep_until(
1162 || std::fs::read_to_string(&overwritten_file).is_ok_and(|cnt| cnt == "321")
1163 ),
1164 "file {overwritten_file:?} was not replaced"
1165 );
1166
1167 rx.wait_unordered([expected(&overwritten_file).modify()]);
1168 }
1169}