Skip to main content

notify/
windows.rs

1#![allow(missing_docs)]
2//! Watcher implementation for Windows' directory management APIs
3//!
4//! For more information see the [ReadDirectoryChangesW reference][ref].
5//!
6//! [ref]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363950(v=vs.85).aspx
7
8use 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, // directory that is being watched
80    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, /* is_recursive */ 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        // it is, in fact, ok to send the semaphore across threads
137        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            // process all available actions first
165            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                // wait with alertable flag so that the completion routine fires
202                WaitForSingleObjectEx(self.wakeup_sem, 100, 1);
203            }
204        }
205
206        // we have to clean this up, since the watcher may be long gone
207        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                // path must be either a file or directory
247                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                // emulate file watching by watching the parent directory
271                (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                    // TODO: Call GetLastError for better error info?
336                    Error::path_not_found().add_path(path)
337                });
338            }
339        }
340        // every watcher gets its own semaphore to signal completion
341        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            // if the parent path is not watched, the watch handle is used for the files under it
391            // if no files under it are watched anymore, we can stop the watch on the parent path
392            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        // have to wait for it, otherwise we leak the memory allocated for there read request
408        if cio != 0 && ch != 0 {
409            while WaitForSingleObjectEx(ws.complete_sem, INFINITE, 1) != WAIT_OBJECT_0 {
410                // drain the apc queue, fix for https://github.com/notify-rs/notify/issues/287#issuecomment-801465550
411            }
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        // When using callback based async requests, we are allowed to use the hEvent member
448        // for our own purposes
449
450        let request = Box::leak(request);
451        (*overlapped).hEvent = std::ptr::from_mut(request).cast();
452
453        // This is using an asynchronous call with a completion routine for receiving notifications
454        // An I/O completion port would probably be more performant
455        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), // not used for async reqs
462            overlapped,
463            Some(handle_event),
464        );
465
466        if ret == 0 {
467            // error reading. retransmute request memory to allow drop.
468            // Because of the error, ownership of the `overlapped` alloc was not passed
469            // over to `ReadDirectoryChangesW`.
470            // So we can claim ownership back.
471            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            // received when dir is unwatched or watcher is shutdown; return and let overlapped/request get drop-cleaned
509            release_semaphore();
510            return;
511        }
512        ERROR_ACCESS_DENIED => {
513            let dir = request.data.dir.clone();
514            // This could happen when the watched directory is deleted or trashed, first check if it's the case.
515            // If so, unwatch the directory and return, otherwise, continue to handle the event.
516            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            // Success, continue to handle the event
539        }
540        _ => {
541            // Some unidentified error occurred, log and unwatch the directory, then return.
542            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    // Get the next request queued up as soon as possible
554    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    // The FILE_NOTIFY_INFORMATION struct has a variable length due to the variable length
564    // string as its last member. Each struct contains an offset for getting the next entry in
565    // the buffer.
566    let mut cur_offset: *const u8 = request.buffer.as_ptr();
567    // In Wine, FILE_NOTIFY_INFORMATION structs are packed placed in the buffer;
568    // they are aligned to 16bit (WCHAR) boundary instead of 32bit required by FILE_NOTIFY_INFORMATION.
569    // Hence, we need to use `read_unaligned` here to avoid UB.
570    let mut cur_entry =
571        unsafe { ptr::read_unaligned(cur_offset.cast::<FILE_NOTIFY_INFORMATION>()) };
572    loop {
573        // filename length is size in bytes, so / 2
574        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        // prepend root to get a full path
584        let path = normalize_path_separators(
585            request
586                .data
587                .dir
588                .join(PathBuf::from(OsString::from_wide(encoded_path))),
589        );
590
591        // if we are watching a single file, ignore the event unless the path is exactly
592        // the watched file
593        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/// Watcher implementation based on ReadDirectoryChanges
672#[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        // breaks the server out of its wait state.  right now this is really just an optimization,
701        // so that if you add a watch you don't block for 100ms in watch() while the
702        // server sleeps.
703        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        // wake 'em up, we don't want to wait around for the ack
714        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        // better wake it up
803        self.wakeup_server();
804    }
805}
806
807// `ReadDirectoryChangesWatcher` is not Send/Sync because of the semaphore Handle.
808// As said elsewhere it's perfectly safe to send it across threads.
809unsafe impl Send for ReadDirectoryChangesWatcher {}
810// Because all public methods are `&mut self` it's also perfectly safe to share references.
811unsafe 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            // Do something with the event
837        })?;
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        // TODO: can remove from watch, but currently not removed
1189        // assert_eq!(watcher.get_watch_handles(), HashSet::from([]));
1190
1191        // std::fs::write(&file, "").expect("write");
1192
1193        // rx.ensure_empty_with_wait();
1194    }
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()]) // TODO: can remove from watch, but currently not removed
1278        );
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}