1#![allow(missing_docs)]
2use crate::{
9 BoundSender, Config, ErrorKind, Receiver, Sender, TargetMode, WatchMode, bounded, unbounded,
10};
11use crate::{Error, EventHandler, RecursiveMode, Result, Watcher};
12use crate::{WatcherKind, event::*};
13use std::alloc;
14use std::cell::RefCell;
15use std::collections::HashMap;
16#[cfg(test)]
17use std::collections::HashSet;
18use std::env;
19use std::ffi::OsString;
20use std::os::raw::c_void;
21use std::os::windows::ffi::{OsStrExt, OsStringExt};
22use std::path::{Path, PathBuf};
23use std::ptr;
24use std::rc::Rc;
25use std::slice;
26use std::sync::{Arc, Mutex};
27use std::thread;
28use windows_sys::Win32::Foundation::{
29 CloseHandle, ERROR_ACCESS_DENIED, ERROR_OPERATION_ABORTED, ERROR_SUCCESS, HANDLE,
30 INVALID_HANDLE_VALUE, WAIT_OBJECT_0,
31};
32use windows_sys::Win32::Storage::FileSystem::{
33 CreateFileW, FILE_ACTION_ADDED, FILE_ACTION_MODIFIED, FILE_ACTION_REMOVED,
34 FILE_ACTION_RENAMED_NEW_NAME, FILE_ACTION_RENAMED_OLD_NAME, FILE_FLAG_BACKUP_SEMANTICS,
35 FILE_FLAG_OVERLAPPED, FILE_LIST_DIRECTORY, FILE_NOTIFY_CHANGE_ATTRIBUTES,
36 FILE_NOTIFY_CHANGE_CREATION, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_NOTIFY_CHANGE_FILE_NAME,
37 FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_NOTIFY_CHANGE_SECURITY, FILE_NOTIFY_CHANGE_SIZE,
38 FILE_NOTIFY_INFORMATION, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
39 ReadDirectoryChangesW,
40};
41use windows_sys::Win32::System::IO::{CancelIo, OVERLAPPED};
42use windows_sys::Win32::System::Threading::{
43 CreateSemaphoreW, INFINITE, ReleaseSemaphore, WaitForSingleObjectEx,
44};
45
46const BUF_SIZE: u32 = 16384;
47
48fn windows_namespace_prefix_len(path: &[u16]) -> usize {
49 let is_separator = |ch: u16| ch == '/' as u16 || ch == '\\' as u16;
50
51 if path.len() >= 4
52 && is_separator(path[0])
53 && is_separator(path[1])
54 && (path[2] == '?' as u16 || path[2] == '.' as u16)
55 && is_separator(path[3])
56 {
57 4
58 } else {
59 0
60 }
61}
62
63fn normalize_path_separators(path: PathBuf) -> PathBuf {
64 let separator = '\\' as u16;
65 let mut encoded_path: Vec<u16> = path.into_os_string().encode_wide().collect();
66 let prefix_len = windows_namespace_prefix_len(&encoded_path);
67
68 for ch in encoded_path.iter_mut().skip(prefix_len) {
69 if *ch == '/' as u16 || *ch == '\\' as u16 {
70 *ch = separator;
71 }
72 }
73
74 PathBuf::from(OsString::from_wide(&encoded_path))
75}
76
77#[derive(Clone)]
78struct ReadData {
79 dir: PathBuf, watches: Rc<RefCell<HashMap<PathBuf, WatchMode>>>,
81 complete_sem: HANDLE,
82 is_recursive: bool,
83}
84
85struct ReadDirectoryRequest {
86 event_handler: Arc<Mutex<dyn EventHandler>>,
87 buffer: [u8; BUF_SIZE as usize],
88 handle: HANDLE,
89 data: ReadData,
90 action_tx: Sender<Action>,
91}
92
93impl ReadDirectoryRequest {
94 fn unwatch_raw(&self) {
95 let result = self
96 .action_tx
97 .send(Action::UnwatchRaw(self.data.dir.clone()));
98 if let Err(e) = result {
99 tracing::error!(?e, "failed to send UnwatchRaw action");
100 }
101 }
102}
103
104enum Action {
105 Watch(PathBuf, WatchMode),
106 Unwatch(PathBuf),
107 UnwatchRaw(PathBuf),
108 Stop,
109 Configure(Config, BoundSender<Result<bool>>),
110 #[cfg(test)]
111 GetWatchHandles(BoundSender<HashSet<PathBuf>>),
112}
113
114struct WatchState {
115 dir_handle: HANDLE,
116 complete_sem: HANDLE,
117}
118
119struct ReadDirectoryChangesServer {
120 tx: Sender<Action>,
121 rx: Receiver<Action>,
122 event_handler: Arc<Mutex<dyn EventHandler>>,
123 cmd_tx: Sender<Result<PathBuf>>,
124 watches: Rc<RefCell<HashMap<PathBuf, WatchMode>>>,
125 watch_handles: HashMap<PathBuf, (WatchState, bool)>,
126 wakeup_sem: HANDLE,
127}
128
129impl ReadDirectoryChangesServer {
130 fn start(
131 event_handler: Arc<Mutex<dyn EventHandler>>,
132 cmd_tx: Sender<Result<PathBuf>>,
133 wakeup_sem: HANDLE,
134 ) -> Sender<Action> {
135 let (action_tx, action_rx) = unbounded();
136 let sem_temp = wakeup_sem as u64;
138 let result = thread::Builder::new()
139 .name("notify-rs windows loop".to_string())
140 .spawn({
141 let tx = action_tx.clone();
142 move || {
143 let wakeup_sem = sem_temp as HANDLE;
144 let server = ReadDirectoryChangesServer {
145 tx,
146 rx: action_rx,
147 event_handler,
148 cmd_tx,
149 watches: Rc::new(RefCell::new(HashMap::new())),
150 watch_handles: HashMap::new(),
151 wakeup_sem,
152 };
153 server.run();
154 }
155 });
156 if let Err(e) = result {
157 tracing::error!(?e, "failed to spawn ReadDirectoryChangesWatcher thread");
158 }
159 action_tx
160 }
161
162 fn run(mut self) {
163 loop {
164 let mut stopped = false;
166
167 while let Ok(action) = self.rx.try_recv() {
168 match action {
169 Action::Watch(path, watch_mode) => {
170 let res = self.add_watch(path, watch_mode);
171 let result = self.cmd_tx.send(res);
172 if let Err(e) = result {
173 tracing::error!(?e, "failed to send Watch result");
174 }
175 }
176 Action::Unwatch(path) => self.remove_watch(&path),
177 Action::UnwatchRaw(path) => self.remove_watch_raw(&path),
178 Action::Stop => {
179 stopped = true;
180 for (ws, _) in self.watch_handles.values() {
181 stop_watch(ws);
182 }
183 break;
184 }
185 Action::Configure(config, tx) => {
186 Self::configure_raw_mode(config, &tx);
187 }
188 #[cfg(test)]
189 Action::GetWatchHandles(tx) => {
190 let handles = self.watch_handles.keys().cloned().collect();
191 tx.send(handles).unwrap();
192 }
193 }
194 }
195
196 if stopped {
197 break;
198 }
199
200 unsafe {
201 WaitForSingleObjectEx(self.wakeup_sem, 100, 1);
203 }
204 }
205
206 unsafe {
208 CloseHandle(self.wakeup_sem);
209 }
210 }
211
212 #[tracing::instrument(level = "trace", skip(self))]
213 fn add_watch(&mut self, path: PathBuf, watch_mode: WatchMode) -> Result<PathBuf> {
214 let existing_watch_mode = self.watches.borrow().get(&path).copied();
215 if let Some(existing) = existing_watch_mode {
216 let need_upgrade_to_recursive = match existing.recursive_mode {
217 RecursiveMode::Recursive => false,
218 RecursiveMode::NonRecursive => {
219 watch_mode.recursive_mode == RecursiveMode::Recursive
220 }
221 };
222 let need_to_watch_parent_newly = match existing.target_mode {
223 TargetMode::TrackPath => false,
224 TargetMode::NoTrack => watch_mode.target_mode == TargetMode::TrackPath,
225 };
226 tracing::trace!(
227 ?need_upgrade_to_recursive,
228 ?need_to_watch_parent_newly,
229 "upgrading existing watch for path: {}",
230 path.display()
231 );
232 if need_to_watch_parent_newly && let Some(parent) = path.parent() {
233 self.add_watch_raw(parent.to_path_buf(), false, false)?;
234 }
235 if !need_upgrade_to_recursive {
236 return Ok(path);
237 }
238 } else if watch_mode.target_mode == TargetMode::TrackPath
239 && let Some(parent) = path.parent()
240 {
241 self.add_watch_raw(parent.to_path_buf(), false, false)?;
242 }
243
244 let metadata = match path.metadata().map_err(Error::io_watch) {
245 Ok(meta) => {
246 if !meta.is_dir() && !meta.is_file() {
248 return Err(Error::generic(
249 "Input watch path is neither a file nor a directory.",
250 )
251 .add_path(path));
252 }
253 meta
254 }
255 Err(err) => {
256 if watch_mode.target_mode == TargetMode::TrackPath
257 && matches!(err.kind, ErrorKind::PathNotFound)
258 {
259 self.watches.borrow_mut().insert(path.clone(), watch_mode);
260 return Ok(path);
261 }
262 return Err(err);
263 }
264 };
265
266 let (watching_file, dir_target) = {
267 if metadata.is_dir() {
268 (false, path.clone())
269 } else {
270 (true, path.parent().unwrap().to_path_buf())
272 }
273 };
274
275 self.add_watch_raw(
276 dir_target,
277 watch_mode.recursive_mode.is_recursive(),
278 watching_file,
279 )?;
280
281 let upgraded_watch_mode = if let Some(mut existing) = existing_watch_mode {
282 existing.upgrade_with(watch_mode);
283 existing
284 } else {
285 watch_mode
286 };
287 self.watches
288 .borrow_mut()
289 .insert(path.clone(), upgraded_watch_mode);
290
291 Ok(path)
292 }
293
294 #[tracing::instrument(level = "trace", skip(self))]
295 fn add_watch_raw(
296 &mut self,
297 path: PathBuf,
298 is_recursive: bool,
299 watching_file: bool,
300 ) -> Result<()> {
301 if let Some((ws, was_recursive)) = self.watch_handles.get(&path) {
302 let need_upgrade_to_recursive = !*was_recursive && is_recursive;
303 if !need_upgrade_to_recursive {
304 tracing::trace!(
305 "watch handle already exists and no need to upgrade: {}",
306 path.display()
307 );
308 return Ok(());
309 }
310 tracing::trace!("upgrading watch handle to recursive: {}", path.display());
311 stop_watch(ws);
312 }
313
314 let encoded_path: Vec<u16> = path.as_os_str().encode_wide().chain(Some(0)).collect();
315 let handle;
316 unsafe {
317 handle = CreateFileW(
318 encoded_path.as_ptr(),
319 FILE_LIST_DIRECTORY,
320 FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
321 ptr::null_mut(),
322 OPEN_EXISTING,
323 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
324 ptr::null_mut(),
325 );
326
327 if handle == INVALID_HANDLE_VALUE {
328 return Err(if watching_file {
329 Error::generic(
330 "You attempted to watch a single file, but parent \
331 directory could not be opened.",
332 )
333 .add_path(path)
334 } else {
335 Error::path_not_found().add_path(path)
337 });
338 }
339 }
340 let semaphore = unsafe { CreateSemaphoreW(ptr::null_mut(), 0, 1, ptr::null_mut()) };
342 if semaphore.is_null() || semaphore == INVALID_HANDLE_VALUE {
343 unsafe {
344 CloseHandle(handle);
345 }
346 return Err(Error::generic("Failed to create semaphore for watch.").add_path(path));
347 }
348 let rd = ReadData {
349 dir: path.clone(),
350 watches: Rc::clone(&self.watches),
351 complete_sem: semaphore,
352 is_recursive,
353 };
354 let ws = WatchState {
355 dir_handle: handle,
356 complete_sem: semaphore,
357 };
358 self.watch_handles.insert(path, (ws, is_recursive));
359 start_read(
360 &rd,
361 Arc::clone(&self.event_handler),
362 handle,
363 self.tx.clone(),
364 );
365 Ok(())
366 }
367
368 #[tracing::instrument(level = "trace", skip(self))]
369 fn remove_watch(&mut self, path: &Path) {
370 if self.watches.borrow_mut().remove(path).is_some() {
371 self.remove_watch_raw(path);
372 }
373 }
374
375 #[tracing::instrument(level = "trace", skip(self))]
376 fn remove_watch_raw(&mut self, path: &Path) {
377 if let Some((ws, _)) = self.watch_handles.remove(path) {
378 stop_watch(&ws);
379 } else if let Some(parent_path) = path.parent()
380 && self.watches.borrow().get(parent_path).is_none()
381 && self
382 .watches
383 .borrow()
384 .keys()
385 .filter(|p| p.starts_with(parent_path))
386 .count()
387 == 0
388 && let Some((ws, _)) = self.watch_handles.remove(parent_path)
389 {
390 stop_watch(&ws);
393 }
394 }
395
396 fn configure_raw_mode(_config: Config, tx: &BoundSender<Result<bool>>) {
397 tx.send(Ok(false))
398 .expect("configuration channel disconnect");
399 }
400}
401
402fn stop_watch(ws: &WatchState) {
403 tracing::trace!("removing ReadDirectoryChangesW watch");
404 unsafe {
405 let cio = CancelIo(ws.dir_handle);
406 let ch = CloseHandle(ws.dir_handle);
407 if cio != 0 && ch != 0 {
409 while WaitForSingleObjectEx(ws.complete_sem, INFINITE, 1) != WAIT_OBJECT_0 {
410 }
412 }
413 CloseHandle(ws.complete_sem);
414 }
415}
416
417fn start_read(
418 rd: &ReadData,
419 event_handler: Arc<Mutex<dyn EventHandler>>,
420 handle: HANDLE,
421 action_tx: Sender<Action>,
422) {
423 tracing::trace!("starting ReadDirectoryChangesW watch: {}", rd.dir.display());
424
425 let request = Box::new(ReadDirectoryRequest {
426 event_handler,
427 handle,
428 buffer: [0u8; BUF_SIZE as usize],
429 data: rd.clone(),
430 action_tx,
431 });
432
433 let flags = FILE_NOTIFY_CHANGE_FILE_NAME
434 | FILE_NOTIFY_CHANGE_DIR_NAME
435 | FILE_NOTIFY_CHANGE_ATTRIBUTES
436 | FILE_NOTIFY_CHANGE_SIZE
437 | FILE_NOTIFY_CHANGE_LAST_WRITE
438 | FILE_NOTIFY_CHANGE_CREATION
439 | FILE_NOTIFY_CHANGE_SECURITY;
440
441 let monitor_subdir = i32::from(request.data.is_recursive);
442
443 unsafe {
444 #[expect(clippy::cast_ptr_alignment)]
445 let overlapped =
446 alloc::alloc_zeroed(alloc::Layout::new::<OVERLAPPED>()).cast::<OVERLAPPED>();
447 let request = Box::leak(request);
451 (*overlapped).hEvent = std::ptr::from_mut(request).cast();
452
453 let ret = ReadDirectoryChangesW(
456 handle,
457 request.buffer.as_mut_ptr().cast::<c_void>(),
458 BUF_SIZE,
459 monitor_subdir,
460 flags,
461 std::ptr::from_mut::<u32>(&mut 0u32), overlapped,
463 Some(handle_event),
464 );
465
466 if ret == 0 {
467 let _overlapped = Box::from_raw(overlapped);
472 let request = Box::from_raw(request);
473 ReleaseSemaphore(request.data.complete_sem, 1, ptr::null_mut());
474 }
475 }
476}
477
478#[expect(clippy::too_many_lines)]
479unsafe extern "system" fn handle_event(
480 error_code: u32,
481 _bytes_written: u32,
482 overlapped: *mut OVERLAPPED,
483) {
484 let overlapped: Box<OVERLAPPED> = unsafe { Box::from_raw(overlapped) };
485 let request: Box<ReadDirectoryRequest> = unsafe { Box::from_raw(overlapped.hEvent.cast()) };
486
487 let release_semaphore =
488 || unsafe { ReleaseSemaphore(request.data.complete_sem, 1, ptr::null_mut()) };
489
490 fn emit_event(event_handler: &Mutex<dyn EventHandler>, res: Result<Event>) {
491 if let Ok(mut guard) = event_handler.lock() {
492 let f: &mut dyn EventHandler = &mut *guard;
493 f.handle_event(res);
494 }
495 }
496 let event_handler = |res| emit_event(&request.event_handler, res);
497
498 if error_code != ERROR_SUCCESS {
499 tracing::trace!(
500 path = ?request.data.dir,
501 is_recursive = request.data.is_recursive,
502 "ReadDirectoryChangesW handle_event called with error code {error_code}",
503 );
504 }
505
506 match error_code {
507 ERROR_OPERATION_ABORTED => {
508 release_semaphore();
510 return;
511 }
512 ERROR_ACCESS_DENIED => {
513 let dir = request.data.dir.clone();
514 if !dir.exists() {
517 tracing::debug!(
518 path = ?request.data.dir,
519 is_recursive = request.data.is_recursive,
520 "ReadDirectoryChangesW handle_event: ERROR_ACCESS_DENIED event and directory no longer exists",
521 );
522 if request
523 .data
524 .watches
525 .borrow()
526 .get(&dir)
527 .is_some_and(|mode| mode.target_mode == TargetMode::NoTrack)
528 {
529 let ev = Event::new(EventKind::Remove(RemoveKind::Any)).add_path(dir);
530 event_handler(Ok(ev));
531 }
532 request.unwatch_raw();
533 release_semaphore();
534 return;
535 }
536 }
537 ERROR_SUCCESS => {
538 }
540 _ => {
541 tracing::error!(
543 "unknown error in ReadDirectoryChangesW for directory {}: {}",
544 request.data.dir.display(),
545 error_code
546 );
547 request.unwatch_raw();
548 release_semaphore();
549 return;
550 }
551 }
552
553 start_read(
555 &request.data,
556 Arc::clone(&request.event_handler),
557 request.handle,
558 request.action_tx,
559 );
560
561 let mut remove_paths = vec![];
562
563 let mut cur_offset: *const u8 = request.buffer.as_ptr();
567 let mut cur_entry =
571 unsafe { ptr::read_unaligned(cur_offset.cast::<FILE_NOTIFY_INFORMATION>()) };
572 loop {
573 let len = cur_entry.FileNameLength as usize / 2;
575 let encoded_path: &[u16] = unsafe {
576 slice::from_raw_parts(
577 cur_offset
578 .add(std::mem::offset_of!(FILE_NOTIFY_INFORMATION, FileName))
579 .cast(),
580 len,
581 )
582 };
583 let path = normalize_path_separators(
585 request
586 .data
587 .dir
588 .join(PathBuf::from(OsString::from_wide(encoded_path))),
589 );
590
591 let skip = !(request
594 .data
595 .watches
596 .borrow()
597 .contains_key(&request.data.dir)
598 || request.data.watches.borrow().contains_key(&path));
599
600 tracing::trace!(
601 handle_path = ?request.data.dir,
602 is_recursive = request.data.is_recursive,
603 ?path,
604 skip,
605 action = cur_entry.Action,
606 "ReadDirectoryChangesW handle_event called",
607 );
608
609 if !skip {
610 let newe = Event::new(EventKind::Any).add_path(path.clone());
611
612 match cur_entry.Action {
613 FILE_ACTION_RENAMED_OLD_NAME => {
614 remove_paths.push(path.clone());
615 let kind = EventKind::Modify(ModifyKind::Name(RenameMode::From));
616 let ev = newe.set_kind(kind);
617 event_handler(Ok(ev));
618 }
619 FILE_ACTION_RENAMED_NEW_NAME => {
620 let kind = EventKind::Modify(ModifyKind::Name(RenameMode::To));
621 let ev = newe.set_kind(kind);
622 event_handler(Ok(ev));
623 }
624 FILE_ACTION_ADDED => {
625 let kind = EventKind::Create(CreateKind::Any);
626 let ev = newe.set_kind(kind);
627 event_handler(Ok(ev));
628 }
629 FILE_ACTION_REMOVED => {
630 remove_paths.push(path.clone());
631 let kind = EventKind::Remove(RemoveKind::Any);
632 let ev = newe.set_kind(kind);
633 event_handler(Ok(ev));
634 }
635 FILE_ACTION_MODIFIED => {
636 let kind = EventKind::Modify(ModifyKind::Any);
637 let ev = newe.set_kind(kind);
638 event_handler(Ok(ev));
639 }
640 _ => (),
641 }
642 }
643
644 if cur_entry.NextEntryOffset == 0 {
645 break;
646 }
647 cur_offset = unsafe { cur_offset.add(cur_entry.NextEntryOffset as usize) };
648 cur_entry = unsafe { ptr::read_unaligned(cur_offset.cast::<FILE_NOTIFY_INFORMATION>()) };
649 }
650
651 tracing::trace!(
652 ?remove_paths,
653 "processing ReadDirectoryChangesW watch changes",
654 );
655
656 for path in remove_paths {
657 let is_no_track = {
658 request
659 .data
660 .watches
661 .borrow()
662 .get(&path)
663 .is_some_and(|mode| mode.target_mode == TargetMode::NoTrack)
664 };
665 if is_no_track {
666 request.data.watches.borrow_mut().remove(&path);
667 }
668 }
669}
670
671#[derive(Debug)]
673pub struct ReadDirectoryChangesWatcher {
674 tx: Sender<Action>,
675 cmd_rx: Receiver<Result<PathBuf>>,
676 wakeup_sem: HANDLE,
677}
678
679impl ReadDirectoryChangesWatcher {
680 pub fn create(
681 event_handler: Arc<Mutex<dyn EventHandler>>,
682 ) -> Result<ReadDirectoryChangesWatcher> {
683 let (cmd_tx, cmd_rx) = unbounded();
684
685 let wakeup_sem = unsafe { CreateSemaphoreW(ptr::null_mut(), 0, 1, ptr::null_mut()) };
686 if wakeup_sem.is_null() || wakeup_sem == INVALID_HANDLE_VALUE {
687 return Err(Error::generic("Failed to create wakeup semaphore."));
688 }
689
690 let action_tx = ReadDirectoryChangesServer::start(event_handler, cmd_tx, wakeup_sem);
691
692 Ok(ReadDirectoryChangesWatcher {
693 tx: action_tx,
694 cmd_rx,
695 wakeup_sem,
696 })
697 }
698
699 fn wakeup_server(&mut self) {
700 unsafe {
704 ReleaseSemaphore(self.wakeup_sem, 1, ptr::null_mut());
705 }
706 }
707
708 fn send_action_require_ack(&mut self, action: Action, pb: &Path) -> Result<()> {
709 self.tx
710 .send(action)
711 .map_err(|_| Error::generic("Error sending to internal channel"))?;
712
713 self.wakeup_server();
715
716 let ack_pb = self
717 .cmd_rx
718 .recv()
719 .map_err(|_| Error::generic("Error receiving from command channel"))??;
720
721 if pb == ack_pb.as_path() {
722 Ok(())
723 } else {
724 Err(Error::generic(&format!(
725 "Expected ack for {} but got \
726 ack for {}",
727 pb.display(),
728 ack_pb.display()
729 )))
730 }
731 }
732
733 fn watch_inner(&mut self, path: &Path, watch_mode: WatchMode) -> Result<()> {
734 let pb = if path.is_absolute() {
735 path.to_owned()
736 } else {
737 let p = env::current_dir().map_err(Error::io)?;
738 p.join(path)
739 };
740 self.send_action_require_ack(Action::Watch(pb.clone(), watch_mode), &pb)
741 }
742
743 fn unwatch_inner(&mut self, path: &Path) -> Result<()> {
744 let pb = if path.is_absolute() {
745 path.to_owned()
746 } else {
747 let p = env::current_dir().map_err(Error::io)?;
748 p.join(path)
749 };
750 let res = self
751 .tx
752 .send(Action::Unwatch(pb))
753 .map_err(|_| Error::generic("Error sending to internal channel"));
754 self.wakeup_server();
755 res
756 }
757}
758
759impl Watcher for ReadDirectoryChangesWatcher {
760 #[tracing::instrument(level = "debug", skip(event_handler))]
761 #[expect(clippy::used_underscore_binding)]
762 fn new<F: EventHandler>(event_handler: F, _config: Config) -> Result<Self> {
763 let event_handler = Arc::new(Mutex::new(event_handler));
764 Self::create(event_handler)
765 }
766
767 #[tracing::instrument(level = "debug", skip(self))]
768 fn watch(&mut self, path: &Path, watch_mode: WatchMode) -> Result<()> {
769 self.watch_inner(path, watch_mode)
770 }
771
772 #[tracing::instrument(level = "debug", skip(self))]
773 fn unwatch(&mut self, path: &Path) -> Result<()> {
774 self.unwatch_inner(path)
775 }
776
777 #[tracing::instrument(level = "debug", skip(self))]
778 fn configure(&mut self, config: Config) -> Result<bool> {
779 let (tx, rx) = bounded(1);
780 self.tx.send(Action::Configure(config, tx))?;
781 rx.recv()?
782 }
783
784 fn kind() -> crate::WatcherKind {
785 WatcherKind::ReadDirectoryChangesWatcher
786 }
787
788 #[cfg(test)]
789 fn get_watch_handles(&self) -> HashSet<PathBuf> {
790 let (tx, rx) = bounded(1);
791 self.tx.send(Action::GetWatchHandles(tx)).unwrap();
792 rx.recv().unwrap()
793 }
794}
795
796impl Drop for ReadDirectoryChangesWatcher {
797 fn drop(&mut self) {
798 let result = self.tx.send(Action::Stop);
799 if let Err(e) = result {
800 tracing::error!(?e, "failed to send Stop action");
801 }
802 self.wakeup_server();
804 }
805}
806
807unsafe impl Send for ReadDirectoryChangesWatcher {}
810unsafe impl Sync for ReadDirectoryChangesWatcher {}
812
813#[cfg(test)]
814pub mod tests {
815 use crate::{
816 Error, ErrorKind, ReadDirectoryChangesWatcher, RecursiveMode, TargetMode, WatchMode,
817 Watcher, test::*, windows::normalize_path_separators,
818 };
819
820 use std::{
821 collections::HashSet, ffi::OsString, os::windows::ffi::OsStringExt, path::PathBuf,
822 time::Duration,
823 };
824
825 fn watcher() -> (TestWatcher<ReadDirectoryChangesWatcher>, Receiver) {
826 channel()
827 }
828
829 #[test]
830 fn trash_dir() -> std::result::Result<(), Box<dyn std::error::Error>> {
831 let dir = testdir();
832 let child_dir = dir.path().join("child");
833 std::fs::create_dir(&child_dir)?;
834
835 let mut watcher = crate::recommended_watcher(|_| {
836 })?;
838 watcher.watch(&child_dir, WatchMode::non_recursive())?;
839 assert_eq!(
840 watcher.get_watch_handles(),
841 HashSet::from([dir.to_path_buf(), child_dir.clone()])
842 );
843
844 trash::delete(&child_dir)?;
845
846 watcher.watch(dir.path(), WatchMode::non_recursive())?;
847 assert_eq!(
848 watcher.get_watch_handles(),
849 HashSet::from([dir.parent_path_buf(), dir.to_path_buf()])
850 );
851
852 Ok(())
853 }
854
855 #[test]
856 fn watcher_is_send_and_sync() {
857 fn check<T: Send + Sync>() {}
858 check::<ReadDirectoryChangesWatcher>();
859 }
860
861 #[test]
862 fn normalize_joined_event_path_for_posix_watch_path() {
863 let dir = PathBuf::from("G:/Feature");
864 let raw_event_name: Vec<u16> = "22.mp4".encode_utf16().collect();
865 let relative = PathBuf::from(OsString::from_wide(&raw_event_name));
866 let path = normalize_path_separators(dir.join(relative));
867
868 assert_eq!(path, PathBuf::from(r"G:\Feature\22.mp4"));
869 }
870
871 #[test]
872 fn normalize_path_separators_keeps_windows_namespace_prefix() {
873 let path = PathBuf::from(r"\\?\C:/very/long/file");
874 let normalized = normalize_path_separators(path);
875 assert_eq!(normalized, PathBuf::from(r"\\?\C:\very\long\file"));
876 }
877
878 #[test]
879 fn create_file_normalized() {
880 let tmpdir = testdir();
881 let (mut watcher, rx) = watcher();
882 let tmpdir_without_prefix =
883 PathBuf::from(tmpdir.path().to_str().unwrap().replace("\\\\?\\", ""));
884 let tmpdir_normalized =
885 PathBuf::from(tmpdir_without_prefix.to_str().unwrap().replace('\\', "/"));
886 watcher.watch_recursively(&tmpdir_normalized);
887
888 let path = tmpdir_without_prefix.join("entry");
889 std::fs::File::create_new(&path).expect("create");
890
891 let event = rx.recv();
892 assert_eq!(event.paths.len(), 1);
893 assert_eq!(event.paths[0], path);
894 assert_eq!(event.paths[0].to_str().unwrap(), path.to_str().unwrap());
895 }
896
897 #[test]
898 fn create_file() {
899 let tmpdir = testdir();
900 let (mut watcher, rx) = watcher();
901 watcher.watch_recursively(&tmpdir);
902
903 let path = tmpdir.path().join("entry");
904 std::fs::File::create_new(&path).expect("create");
905
906 rx.wait_ordered_exact([expected(&path).create_any()])
907 .ensure_no_tail();
908 assert_eq!(
909 watcher.get_watch_handles(),
910 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
911 );
912 }
913
914 #[test]
915 fn create_self_file() {
916 let tmpdir = testdir();
917 let (mut watcher, rx) = watcher();
918
919 let path = tmpdir.path().join("entry");
920
921 watcher.watch_nonrecursively(&path);
922
923 std::fs::File::create_new(&path).expect("create");
924
925 rx.wait_ordered_exact([expected(&path).create_any()])
926 .ensure_no_tail();
927 assert_eq!(
928 watcher.get_watch_handles(),
929 HashSet::from([tmpdir.to_path_buf()])
930 );
931 }
932
933 #[test]
934 fn create_self_file_no_track() {
935 let tmpdir = testdir();
936 let (mut watcher, _) = watcher();
937
938 let path = tmpdir.path().join("entry");
939
940 let result = watcher.watcher.watch(
941 &path,
942 WatchMode {
943 recursive_mode: RecursiveMode::NonRecursive,
944 target_mode: TargetMode::NoTrack,
945 },
946 );
947 assert!(matches!(
948 result,
949 Err(Error {
950 paths: _,
951 kind: ErrorKind::PathNotFound
952 })
953 ));
954 }
955
956 #[test]
957 #[ignore = "TODO: not implemented"]
958 fn create_self_file_nested() {
959 let tmpdir = testdir();
960 let (mut watcher, rx) = watcher();
961
962 let path = tmpdir.path().join("entry/nested");
963
964 watcher.watch_nonrecursively(&path);
965
966 std::fs::create_dir_all(path.parent().unwrap()).expect("create");
967 std::fs::File::create_new(&path).expect("create");
968
969 rx.wait_ordered_exact([expected(&path).create_any()])
970 .ensure_no_tail();
971 assert_eq!(
972 watcher.get_watch_handles(),
973 HashSet::from([tmpdir.to_path_buf()])
974 );
975 }
976
977 #[test]
978 fn write_file() {
979 let tmpdir = testdir();
980 let (mut watcher, rx) = watcher();
981
982 let path = tmpdir.path().join("entry");
983 std::fs::File::create_new(&path).expect("create");
984
985 watcher.watch_recursively(&tmpdir);
986 std::fs::write(&path, b"123").expect("write");
987
988 rx.wait_ordered_exact([
989 expected(tmpdir.path()).modify_any(),
990 expected(&path).modify_any().multiple(),
991 ])
992 .ensure_no_tail();
993 assert_eq!(
994 watcher.get_watch_handles(),
995 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
996 );
997 }
998
999 #[test]
1000 fn chmod_file() {
1001 let tmpdir = testdir();
1002 let (mut watcher, rx) = watcher();
1003
1004 let path = tmpdir.path().join("entry");
1005 let file = std::fs::File::create_new(&path).expect("create");
1006 let mut permissions = file.metadata().expect("metadata").permissions();
1007 permissions.set_readonly(true);
1008
1009 watcher.watch_recursively(&tmpdir);
1010 file.set_permissions(permissions).expect("set_permissions");
1011
1012 rx.wait_ordered_exact([
1013 expected(tmpdir.path()).modify_any(),
1014 expected(&path).modify_any(),
1015 ])
1016 .ensure_no_tail();
1017 assert_eq!(
1018 watcher.get_watch_handles(),
1019 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1020 );
1021 }
1022
1023 #[test]
1024 fn rename_file() {
1025 let tmpdir = testdir();
1026 let (mut watcher, rx) = watcher();
1027
1028 let path = tmpdir.path().join("entry");
1029 std::fs::File::create_new(&path).expect("create");
1030
1031 watcher.watch_recursively(&tmpdir);
1032 let new_path = tmpdir.path().join("renamed");
1033
1034 std::fs::rename(&path, &new_path).expect("rename");
1035
1036 rx.wait_ordered_exact([
1037 expected(tmpdir.path()).modify_any(),
1038 expected(&path).rename_from(),
1039 expected(&new_path).rename_to(),
1040 expected(tmpdir.path()).modify_any(),
1041 ])
1042 .ensure_no_tail();
1043 assert_eq!(
1044 watcher.get_watch_handles(),
1045 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1046 );
1047 }
1048
1049 #[test]
1050 fn rename_self_file() {
1051 let tmpdir = testdir();
1052 let (mut watcher, rx) = watcher();
1053
1054 let path = tmpdir.path().join("entry");
1055 std::fs::File::create_new(&path).expect("create");
1056
1057 watcher.watch_nonrecursively(&path);
1058 let new_path = tmpdir.path().join("renamed");
1059
1060 std::fs::rename(&path, &new_path).expect("rename");
1061
1062 rx.wait_ordered_exact([expected(&path).rename_from()])
1063 .ensure_no_tail();
1064 assert_eq!(
1065 watcher.get_watch_handles(),
1066 HashSet::from([tmpdir.to_path_buf()])
1067 );
1068
1069 std::fs::rename(&new_path, &path).expect("rename2");
1070
1071 rx.wait_ordered_exact([expected(&path).rename_to(), expected(&path).modify_any()])
1072 .ensure_no_tail();
1073 assert_eq!(
1074 watcher.get_watch_handles(),
1075 HashSet::from([tmpdir.to_path_buf()])
1076 );
1077 }
1078
1079 #[test]
1080 fn rename_self_file_no_track() {
1081 let tmpdir = testdir();
1082 let (mut watcher, rx) = watcher();
1083
1084 let path = tmpdir.path().join("entry");
1085 std::fs::File::create_new(&path).expect("create");
1086
1087 watcher.watch(
1088 &path,
1089 WatchMode {
1090 recursive_mode: RecursiveMode::NonRecursive,
1091 target_mode: TargetMode::NoTrack,
1092 },
1093 );
1094
1095 let new_path = tmpdir.path().join("renamed");
1096
1097 std::fs::rename(&path, &new_path).expect("rename");
1098
1099 rx.wait_ordered_exact([expected(&path).rename_from()])
1100 .ensure_no_tail();
1101 assert_eq!(
1102 watcher.get_watch_handles(),
1103 HashSet::from([tmpdir.to_path_buf()])
1104 );
1105
1106 let result = watcher.watcher.watch(
1107 &path,
1108 WatchMode {
1109 recursive_mode: RecursiveMode::NonRecursive,
1110 target_mode: TargetMode::NoTrack,
1111 },
1112 );
1113 assert!(matches!(
1114 result,
1115 Err(Error {
1116 paths: _,
1117 kind: ErrorKind::PathNotFound
1118 })
1119 ));
1120 }
1121
1122 #[test]
1123 fn delete_file() {
1124 let tmpdir = testdir();
1125 let (mut watcher, rx) = watcher();
1126 let file = tmpdir.path().join("file");
1127 std::fs::write(&file, "").expect("write");
1128
1129 watcher.watch_nonrecursively(&tmpdir);
1130
1131 std::fs::remove_file(&file).expect("remove");
1132
1133 rx.wait_ordered_exact([
1134 expected(tmpdir.path()).modify_any(),
1135 expected(&file).remove_any(),
1136 ])
1137 .ensure_no_tail();
1138 assert_eq!(
1139 watcher.get_watch_handles(),
1140 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1141 );
1142 }
1143
1144 #[test]
1145 fn delete_self_file() {
1146 let tmpdir = testdir();
1147 let (mut watcher, rx) = watcher();
1148 let file = tmpdir.path().join("file");
1149 std::fs::write(&file, "").expect("write");
1150
1151 watcher.watch_nonrecursively(&file);
1152
1153 std::fs::remove_file(&file).expect("remove");
1154
1155 rx.wait_ordered_exact([expected(&file).remove_any()]);
1156 assert_eq!(
1157 watcher.get_watch_handles(),
1158 HashSet::from([tmpdir.to_path_buf()])
1159 );
1160
1161 std::fs::write(&file, "").expect("write");
1162
1163 rx.wait_ordered_exact([expected(&file).create_any()]);
1164 assert_eq!(
1165 watcher.get_watch_handles(),
1166 HashSet::from([tmpdir.to_path_buf()])
1167 );
1168 }
1169
1170 #[test]
1171 fn delete_self_file_no_track() {
1172 let tmpdir = testdir();
1173 let (mut watcher, rx) = watcher();
1174 let file = tmpdir.path().join("file");
1175 std::fs::write(&file, "").expect("write");
1176
1177 watcher.watch(
1178 &file,
1179 WatchMode {
1180 recursive_mode: RecursiveMode::NonRecursive,
1181 target_mode: TargetMode::NoTrack,
1182 },
1183 );
1184
1185 std::fs::remove_file(&file).expect("remove");
1186
1187 rx.wait_ordered_exact([expected(&file).remove_any()]);
1188 }
1195
1196 #[test]
1197 fn create_write_overwrite() {
1198 let tmpdir = testdir();
1199 let (mut watcher, rx) = watcher();
1200 let overwritten_file = tmpdir.path().join("overwritten_file");
1201 let overwriting_file = tmpdir.path().join("overwriting_file");
1202 std::fs::write(&overwritten_file, "123").expect("write1");
1203
1204 watcher.watch_nonrecursively(&tmpdir);
1205
1206 std::fs::File::create(&overwriting_file).expect("create");
1207 std::fs::write(&overwriting_file, "321").expect("write2");
1208 std::fs::rename(&overwriting_file, &overwritten_file).expect("rename");
1209
1210 rx.wait_ordered_exact([
1211 expected(tmpdir.path()).modify_any(),
1212 expected(&overwriting_file).create_any(),
1213 expected(tmpdir.path()).modify_any(),
1214 expected(&overwriting_file).modify_any().multiple(),
1215 expected(&overwritten_file).remove_any(),
1216 expected(tmpdir.path()).modify_any().optional(),
1217 expected(&overwriting_file).rename_from(),
1218 expected(&overwritten_file).rename_to(),
1219 expected(tmpdir.path()).modify_any().optional(),
1220 ])
1221 .ensure_no_tail();
1222 assert_eq!(
1223 watcher.get_watch_handles(),
1224 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1225 );
1226 }
1227
1228 #[test]
1229 fn create_self_write_overwrite() {
1230 let tmpdir = testdir();
1231 let (mut watcher, rx) = watcher();
1232 let overwritten_file = tmpdir.path().join("overwritten_file");
1233 let overwriting_file = tmpdir.path().join("overwriting_file");
1234 std::fs::write(&overwritten_file, "123").expect("write1");
1235
1236 watcher.watch_nonrecursively(&overwritten_file);
1237
1238 std::fs::File::create(&overwriting_file).expect("create");
1239 std::fs::write(&overwriting_file, "321").expect("write2");
1240 std::fs::rename(&overwriting_file, &overwritten_file).expect("rename");
1241
1242 rx.wait_ordered_exact([
1243 expected(&overwritten_file).remove_any(),
1244 expected(&overwritten_file).rename_to(),
1245 ])
1246 .ensure_no_tail();
1247 assert_eq!(
1248 watcher.get_watch_handles(),
1249 HashSet::from([tmpdir.to_path_buf()])
1250 );
1251 }
1252
1253 #[test]
1254 fn create_self_write_overwrite_no_track() {
1255 let tmpdir = testdir();
1256 let (mut watcher, rx) = watcher();
1257 let overwritten_file = tmpdir.path().join("overwritten_file");
1258 let overwriting_file = tmpdir.path().join("overwriting_file");
1259 std::fs::write(&overwritten_file, "123").expect("write1");
1260
1261 watcher.watch(
1262 &overwritten_file,
1263 WatchMode {
1264 recursive_mode: RecursiveMode::NonRecursive,
1265 target_mode: TargetMode::NoTrack,
1266 },
1267 );
1268
1269 std::fs::File::create(&overwriting_file).expect("create");
1270 std::fs::write(&overwriting_file, "321").expect("write2");
1271 std::fs::rename(&overwriting_file, &overwritten_file).expect("rename");
1272
1273 rx.wait_ordered_exact([expected(&overwritten_file).remove_any()])
1274 .ensure_no_tail();
1275 assert_eq!(
1276 watcher.get_watch_handles(),
1277 HashSet::from([tmpdir.to_path_buf()]) );
1279 }
1280
1281 #[test]
1282 fn create_dir() {
1283 let tmpdir = testdir();
1284 let (mut watcher, rx) = watcher();
1285 watcher.watch_recursively(&tmpdir);
1286
1287 let path = tmpdir.path().join("entry");
1288 std::fs::create_dir(&path).expect("create");
1289
1290 rx.wait_ordered_exact([expected(&path).create_any()])
1291 .ensure_no_tail();
1292 assert_eq!(
1293 watcher.get_watch_handles(),
1294 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1295 );
1296 }
1297
1298 #[test]
1299 fn chmod_dir() {
1300 let tmpdir = testdir();
1301 let (mut watcher, rx) = watcher();
1302
1303 let path = tmpdir.path().join("entry");
1304 std::fs::create_dir(&path).expect("create_dir");
1305 let mut permissions = std::fs::metadata(&path).expect("metadata").permissions();
1306 permissions.set_readonly(true);
1307
1308 watcher.watch_recursively(&tmpdir);
1309 std::fs::set_permissions(&path, permissions).expect("set_permissions");
1310
1311 rx.wait_ordered_exact([
1312 expected(tmpdir.path()).modify_any(),
1313 expected(&path).modify_any(),
1314 ])
1315 .ensure_no_tail();
1316 assert_eq!(
1317 watcher.get_watch_handles(),
1318 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1319 );
1320 }
1321
1322 #[test]
1323 fn rename_dir() {
1324 let tmpdir = testdir();
1325 let (mut watcher, rx) = watcher();
1326
1327 let path = tmpdir.path().join("entry");
1328 let new_path = tmpdir.path().join("new_path");
1329 std::fs::create_dir(&path).expect("create_dir");
1330
1331 watcher.watch_recursively(&tmpdir);
1332
1333 std::fs::rename(&path, &new_path).expect("rename");
1334
1335 rx.wait_ordered_exact([
1336 expected(tmpdir.path()).modify_any(),
1337 expected(&path).rename_from(),
1338 expected(&new_path).rename_to(),
1339 expected(tmpdir.path()).modify_any(),
1340 ])
1341 .ensure_no_tail();
1342 assert_eq!(
1343 watcher.get_watch_handles(),
1344 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1345 );
1346 }
1347
1348 #[test]
1349 fn delete_dir() {
1350 let tmpdir = testdir();
1351 let (mut watcher, rx) = watcher();
1352
1353 let path = tmpdir.path().join("entry");
1354 std::fs::create_dir(&path).expect("create_dir");
1355
1356 watcher.watch_recursively(&tmpdir);
1357 std::fs::remove_dir(&path).expect("remove");
1358
1359 rx.wait_ordered_exact([
1360 expected(tmpdir.path()).modify_any(),
1361 expected(&path).remove_any(),
1362 ])
1363 .ensure_no_tail();
1364 assert_eq!(
1365 watcher.get_watch_handles(),
1366 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1367 );
1368 }
1369
1370 #[test]
1371 fn delete_self_dir() {
1372 let tmpdir = testdir();
1373 let (mut watcher, rx) = watcher();
1374
1375 let path = tmpdir.path().join("entry");
1376 std::fs::create_dir(&path).expect("create_dir");
1377
1378 watcher.watch_recursively(&path);
1379 std::fs::remove_dir(&path).expect("remove");
1380
1381 rx.wait_ordered_exact([expected(&path).remove_any()])
1382 .ensure_no_tail();
1383 assert_eq!(
1384 watcher.get_watch_handles(),
1385 HashSet::from([tmpdir.to_path_buf()])
1386 );
1387
1388 std::fs::create_dir(&path).expect("create_dir2");
1389
1390 rx.wait_ordered_exact([expected(&path).create_any()])
1391 .ensure_no_tail();
1392 assert_eq!(
1393 watcher.get_watch_handles(),
1394 HashSet::from([tmpdir.to_path_buf()])
1395 );
1396 }
1397
1398 #[test]
1399 fn delete_self_dir_no_track() {
1400 let tmpdir = testdir();
1401 let (mut watcher, rx) = watcher();
1402
1403 let path = tmpdir.path().join("entry");
1404 std::fs::create_dir(&path).expect("create_dir");
1405
1406 watcher
1407 .watcher
1408 .watch(
1409 &path,
1410 WatchMode {
1411 recursive_mode: RecursiveMode::Recursive,
1412 target_mode: TargetMode::NoTrack,
1413 },
1414 )
1415 .expect("watch");
1416 std::fs::remove_dir(&path).expect("remove");
1417
1418 rx.wait_ordered_exact([expected(&path).remove_any()])
1419 .ensure_no_tail();
1420 assert_eq!(watcher.get_watch_handles(), HashSet::from([]));
1421
1422 std::fs::create_dir(&path).expect("create_dir2");
1423
1424 rx.ensure_empty_with_wait();
1425 }
1426
1427 #[test]
1428 fn rename_dir_twice() {
1429 let tmpdir = testdir();
1430 let (mut watcher, rx) = watcher();
1431
1432 let path = tmpdir.path().join("entry");
1433 let new_path = tmpdir.path().join("new_path");
1434 let new_path2 = tmpdir.path().join("new_path2");
1435 std::fs::create_dir(&path).expect("create_dir");
1436
1437 watcher.watch_recursively(&tmpdir);
1438 std::fs::rename(&path, &new_path).expect("rename");
1439 std::fs::rename(&new_path, &new_path2).expect("rename2");
1440
1441 rx.wait_ordered_exact([
1442 expected(tmpdir.path()).modify_any(),
1443 expected(&path).rename_from(),
1444 expected(&new_path).rename_to(),
1445 expected(tmpdir.path()).modify_any(),
1446 expected(&new_path).rename_from(),
1447 expected(&new_path2).rename_to(),
1448 expected(tmpdir.path()).modify_any(),
1449 ])
1450 .ensure_no_tail();
1451 assert_eq!(
1452 watcher.get_watch_handles(),
1453 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1454 );
1455 }
1456
1457 #[test]
1458 fn move_out_of_watched_dir() {
1459 let tmpdir = testdir();
1460 let subdir = tmpdir.path().join("subdir");
1461 let (mut watcher, rx) = watcher();
1462
1463 let path = subdir.join("entry");
1464 std::fs::create_dir_all(&subdir).expect("create_dir_all");
1465 std::fs::File::create_new(&path).expect("create");
1466
1467 watcher.watch_recursively(&subdir);
1468 let new_path = tmpdir.path().join("entry");
1469
1470 std::fs::rename(&path, &new_path).expect("rename");
1471
1472 rx.wait_ordered_exact([expected(&subdir).modify_any(), expected(path).remove_any()])
1473 .ensure_no_tail();
1474 assert_eq!(
1475 watcher.get_watch_handles(),
1476 HashSet::from([tmpdir.to_path_buf(), subdir])
1477 );
1478 }
1479
1480 #[test]
1481 fn create_write_write_rename_write_remove() {
1482 let tmpdir = testdir();
1483 let (mut watcher, rx) = watcher();
1484
1485 let file1 = tmpdir.path().join("entry");
1486 let file2 = tmpdir.path().join("entry2");
1487 std::fs::File::create_new(&file2).expect("create file2");
1488 let new_path = tmpdir.path().join("renamed");
1489
1490 watcher.watch_recursively(&tmpdir);
1491 std::fs::write(&file1, "123").expect("write 1");
1492 std::fs::write(&file2, "321").expect("write 2");
1493 std::fs::rename(&file1, &new_path).expect("rename");
1494 std::fs::write(&new_path, b"1").expect("write 3");
1495 std::fs::remove_file(&new_path).expect("remove");
1496
1497 rx.wait_ordered_exact([
1498 expected(tmpdir.path()).modify_any(),
1499 expected(&file1).create_any(),
1500 expected(&file1).modify_any().multiple(),
1501 expected(tmpdir.path()).modify_any(),
1502 expected(&file2).modify_any().multiple(),
1503 expected(&file1).rename_from(),
1504 expected(&new_path).rename_to(),
1505 expected(tmpdir.path()).modify_any(),
1506 expected(&new_path).modify_any().multiple(),
1507 expected(&new_path).remove_any(),
1508 ]);
1509 assert_eq!(
1510 watcher.get_watch_handles(),
1511 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1512 );
1513 }
1514
1515 #[test]
1516 fn rename_twice() {
1517 let tmpdir = testdir();
1518 let (mut watcher, rx) = watcher();
1519
1520 let path = tmpdir.path().join("entry");
1521 std::fs::File::create_new(&path).expect("create");
1522
1523 watcher.watch_recursively(&tmpdir);
1524 let new_path1 = tmpdir.path().join("renamed1");
1525 let new_path2 = tmpdir.path().join("renamed2");
1526
1527 std::fs::rename(&path, &new_path1).expect("rename1");
1528 std::fs::rename(&new_path1, &new_path2).expect("rename2");
1529
1530 rx.wait_ordered_exact([
1531 expected(tmpdir.path()).modify_any(),
1532 expected(&path).rename_from(),
1533 expected(&new_path1).rename_to(),
1534 expected(tmpdir.path()).modify_any(),
1535 expected(&new_path1).rename_from(),
1536 expected(&new_path2).rename_to(),
1537 expected(tmpdir.path()).modify_any(),
1538 ])
1539 .ensure_no_tail();
1540 assert_eq!(
1541 watcher.get_watch_handles(),
1542 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1543 );
1544 }
1545
1546 #[test]
1547 fn set_file_mtime() {
1548 let tmpdir = testdir();
1549 let (mut watcher, rx) = watcher();
1550
1551 let path = tmpdir.path().join("entry");
1552 let file = std::fs::File::create_new(&path).expect("create");
1553
1554 watcher.watch_recursively(&tmpdir);
1555
1556 file.set_modified(
1557 std::time::SystemTime::now()
1558 .checked_sub(Duration::from_secs(60 * 60))
1559 .expect("time"),
1560 )
1561 .expect("set_time");
1562
1563 rx.wait_ordered_exact([
1564 expected(tmpdir.path()).modify_any(),
1565 expected(&path).modify_any(),
1566 ])
1567 .ensure_no_tail();
1568 }
1569
1570 #[test]
1571 fn write_file_non_recursive_watch() {
1572 let tmpdir = testdir();
1573 let (mut watcher, rx) = watcher();
1574
1575 let path = tmpdir.path().join("entry");
1576 std::fs::File::create_new(&path).expect("create");
1577
1578 watcher.watch_nonrecursively(&path);
1579
1580 std::fs::write(&path, b"123").expect("write");
1581
1582 rx.wait_ordered_exact([expected(&path).modify_any().multiple()])
1583 .ensure_no_tail();
1584 assert_eq!(
1585 watcher.get_watch_handles(),
1586 HashSet::from([tmpdir.to_path_buf()])
1587 );
1588 }
1589
1590 #[test]
1591 fn write_to_a_hardlink_pointed_to_the_file_in_the_watched_dir_doesnt_trigger_an_event() {
1592 let tmpdir = testdir();
1593 let (mut watcher, mut rx) = watcher();
1594
1595 let subdir = tmpdir.path().join("subdir");
1596 let subdir2 = tmpdir.path().join("subdir2");
1597 let file = subdir.join("file");
1598 let hardlink = subdir2.join("hardlink");
1599
1600 std::fs::create_dir(&subdir).expect("create");
1601 std::fs::create_dir(&subdir2).expect("create");
1602 std::fs::write(&file, "").expect("file");
1603 std::fs::hard_link(&file, &hardlink).expect("hardlink");
1604
1605 watcher.watch_nonrecursively(&file);
1606
1607 std::fs::write(&hardlink, "123123").expect("write to the hard link");
1608
1609 let events = rx.iter().collect::<Vec<_>>();
1610 assert!(events.is_empty(), "unexpected events: {events:#?}");
1611 assert_eq!(watcher.get_watch_handles(), HashSet::from([subdir]));
1612 }
1613
1614 #[test]
1615 fn recursive_creation() {
1616 let tmpdir = testdir();
1617 let nested1 = tmpdir.path().join("1");
1618 let nested2 = tmpdir.path().join("1/2");
1619 let nested3 = tmpdir.path().join("1/2/3");
1620 let nested4 = tmpdir.path().join("1/2/3/4");
1621 let nested5 = tmpdir.path().join("1/2/3/4/5");
1622 let nested6 = tmpdir.path().join("1/2/3/4/5/6");
1623 let nested7 = tmpdir.path().join("1/2/3/4/5/6/7");
1624 let nested8 = tmpdir.path().join("1/2/3/4/5/6/7/8");
1625 let nested9 = tmpdir.path().join("1/2/3/4/5/6/7/8/9");
1626
1627 let (mut watcher, rx) = watcher();
1628
1629 watcher.watch_recursively(&tmpdir);
1630
1631 std::fs::create_dir_all(&nested9).expect("create_dir_all");
1632 rx.wait_ordered_exact([
1633 expected(&nested1).create_any(),
1634 expected(&nested2).create_any(),
1635 expected(&nested3).create_any(),
1636 expected(&nested4).create_any(),
1637 expected(&nested5).create_any(),
1638 expected(&nested6).create_any(),
1639 expected(&nested7).create_any(),
1640 expected(&nested8).create_any(),
1641 expected(&nested9).create_any(),
1642 ])
1643 .ensure_no_tail();
1644 assert_eq!(
1645 watcher.get_watch_handles(),
1646 HashSet::from([tmpdir.parent_path_buf(), tmpdir.to_path_buf()])
1647 );
1648 }
1649
1650 #[test]
1651 fn upgrade_to_recursive() {
1652 let tmpdir = testdir();
1653 let (mut watcher, rx) = watcher();
1654
1655 let path = tmpdir.path().join("upgrade");
1656 let deep = tmpdir.path().join("upgrade/deep");
1657 let file = tmpdir.path().join("upgrade/deep/file");
1658 std::fs::create_dir_all(&deep).expect("create_dir");
1659
1660 watcher.watch_nonrecursively(&path);
1661 std::fs::File::create_new(&file).expect("create");
1662 std::fs::remove_file(&file).expect("delete");
1663
1664 rx.wait_ordered_exact([expected(&path).modify_any()])
1665 .ensure_no_tail();
1666
1667 watcher.watch_recursively(&path);
1668 std::fs::File::create_new(&file).expect("create");
1669
1670 rx.wait_ordered_exact([expected(&deep).modify_any(), expected(&file).create_any()])
1671 .ensure_no_tail();
1672 assert_eq!(
1673 watcher.get_watch_handles(),
1674 HashSet::from([tmpdir.to_path_buf(), path])
1675 );
1676 }
1677}