Skip to content

Commit b80f46c

Browse files
committed
On Windows, Unix slashes of watched paths get normalized to Windows slashes.
Fixes #687
1 parent 539bc95 commit b80f46c

File tree

1 file changed

+92
-1
lines changed

1 file changed

+92
-1
lines changed

notify/src/windows.rs

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::env;
1414
use std::ffi::OsString;
1515
use std::os::raw::c_void;
1616
use std::os::windows::ffi::{OsStrExt, OsStringExt};
17-
use std::path::{Path, PathBuf};
17+
use std::path::{Component, Path, PathBuf, Prefix};
1818
use std::ptr;
1919
use std::slice;
2020
use 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 {
600630
unsafe impl Send for ReadDirectoryChangesWatcher {}
601631
// Because all public methods are `&mut self` it's also perfectly safe to share references.
602632
unsafe 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

Comments
 (0)