@@ -14,7 +14,7 @@ use std::env;
1414use std:: ffi:: OsString ;
1515use std:: os:: raw:: c_void;
1616use std:: os:: windows:: ffi:: { OsStrExt , OsStringExt } ;
17- use std:: path:: { Path , PathBuf } ;
17+ use std:: path:: { Component , Path , PathBuf , Prefix } ;
1818use std:: ptr;
1919use std:: slice;
2020use std:: sync:: { Arc , Mutex } ;
@@ -540,6 +540,36 @@ impl ReadDirectoryChangesWatcher {
540540 "Input watch path is neither a file nor a directory." ,
541541 ) ) ;
542542 }
543+ let pb = pb. canonicalize ( ) . map_err ( |e| Error :: io ( e) . add_path ( pb) ) ?;
544+ let pb = {
545+ let mut comps = pb. components ( ) ;
546+ match comps. next ( ) {
547+ Some ( Component :: Prefix ( p) ) => match p. kind ( ) {
548+ Prefix :: VerbatimDisk ( drive) => {
549+ // \\?\C:\ -> C:\
550+ let mut out = PathBuf :: from ( format ! ( "{}:" , drive as char ) ) ;
551+ for c in comps {
552+ out. push ( c. as_os_str ( ) ) ;
553+ }
554+ out
555+ }
556+ Prefix :: VerbatimUNC ( server, share) => {
557+ // \\?\UNC\server\share\ -> \\server\share\
558+ let mut root = OsString :: from ( r"\\" ) ;
559+ root. push ( server) ;
560+ root. push ( "\\ " ) ;
561+ root. push ( share) ;
562+ let mut out = PathBuf :: from ( root) ;
563+ for c in comps {
564+ out. push ( c. as_os_str ( ) ) ;
565+ }
566+ out
567+ }
568+ _ => pb,
569+ } ,
570+ _ => pb,
571+ }
572+ } ;
543573 self . send_action_require_ack ( Action :: Watch ( pb. clone ( ) , recursive_mode) , & pb)
544574 }
545575
@@ -600,3 +630,64 @@ impl Drop for ReadDirectoryChangesWatcher {
600630unsafe impl Send for ReadDirectoryChangesWatcher { }
601631// Because all public methods are `&mut self` it's also perfectly safe to share references.
602632unsafe impl Sync for ReadDirectoryChangesWatcher { }
633+
634+ /// https://github.com/notify-rs/notify/issues/687
635+ #[ test]
636+ fn test_mixed_slashes_are_normalized ( ) {
637+ use crate :: { Event , EventKind , ReadDirectoryChangesWatcher , RecursiveMode , Result , Watcher } ;
638+ use std:: fs:: { self , File } ;
639+ use std:: path:: Path ;
640+ use std:: sync:: { Arc , Mutex } ;
641+ use tempfile:: TempDir ;
642+
643+ // Create a temporary directory
644+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
645+ let subfolder_path = temp_dir. path ( ) . join ( "subfolder" ) ;
646+ fs:: create_dir_all ( & subfolder_path) . unwrap ( ) ; // Create the subfolder
647+
648+ let file_path = subfolder_path. join ( "dep.txt" ) ; // Place dep.txt inside the subfolder
649+
650+ // Create the file
651+ File :: create ( & file_path) . unwrap ( ) ;
652+
653+ // Use a Vec wrapped in Arc<Mutex<>> to collect events
654+ let events = Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ;
655+ let events_clone = events. clone ( ) ;
656+
657+ // Create the event handler closure
658+ let mut watcher = ReadDirectoryChangesWatcher :: new (
659+ move |event : Result < Event > | {
660+ if let Ok ( e) = event {
661+ events_clone. lock ( ) . unwrap ( ) . push ( e) ;
662+ }
663+ } ,
664+ Default :: default ( ) ,
665+ )
666+ . unwrap ( ) ;
667+
668+ // Replace backslashes with forward slashes in the temp_dir path
669+ let temp_dir_path = temp_dir. path ( ) . to_string_lossy ( ) . replace ( '\\' , "/" ) ;
670+ watcher
671+ . watch ( Path :: new ( & temp_dir_path) , RecursiveMode :: Recursive )
672+ . unwrap ( ) ;
673+
674+ // Modify the file to trigger the event
675+ std:: fs:: write ( & file_path, "New content" ) . unwrap ( ) ;
676+
677+ // Check the events received
678+ let events_guard = events. lock ( ) . unwrap ( ) ;
679+ assert ! ( !events_guard. is_empty( ) , "No events received" ) ;
680+
681+ // Check the last event for mixed slashes
682+ let last_event = events_guard. last ( ) . unwrap ( ) ;
683+ assert_eq ! ( last_event. kind, EventKind :: Modify ( ModifyKind :: Any ) ) ;
684+
685+ assert ! (
686+ last_event. paths. iter( ) . any( |p| {
687+ let path_str = p. to_string_lossy( ) ;
688+ path_str. contains( '\\' ) && !path_str. contains( '/' ) && !path_str. starts_with( "\\ \\ ?\\ " )
689+ } ) ,
690+ "Path \" {}\" should only contain Windows slashes and not start with \" \\ \\ ?\\ \" " ,
691+ last_event. paths. last( ) . unwrap( ) . display( )
692+ ) ;
693+ }
0 commit comments