From c8a43acaa90b8fde2f12e36de7f74e1341c1ebf9 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 11 Nov 2024 07:03:30 +0000 Subject: [PATCH] Fixed PathExt.normalize() on Windows - Windows path prefixes and roots need to be handled in a particular way. Added logic to PathExt.normalize() to check that the number of segments being removed preserves these correctly. - Added Windows-specific tests for PathExt.normalize(). --- crates/rubedo/src/std.rs | 28 ++++++++++++++++++++++------ crates/rubedo/src/tests/std.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/crates/rubedo/src/std.rs b/crates/rubedo/src/std.rs index d942a79..1fd47d9 100644 --- a/crates/rubedo/src/std.rs +++ b/crates/rubedo/src/std.rs @@ -1337,12 +1337,18 @@ impl PathExt for Path { return cwd; } let mut segments: Vec = vec![]; + let mut had_prefix = false; + let mut had_root = false; for (i, component) in self.components().enumerate() { match component { - PathComponent::Prefix(_) | - PathComponent::RootDir => { - if i == 0 { + PathComponent::Prefix(prefix) => { + segments.push(prefix.as_os_str().to_os_string()); + had_prefix = true; + }, + PathComponent::RootDir => { + if had_prefix || i == 0 { segments.push(component.as_os_str().to_os_string()); + had_root = true; } }, PathComponent::CurDir | @@ -1355,8 +1361,11 @@ impl PathExt for Path { .as_mut() ); } - if component == PathComponent::ParentDir && segments.len() > 1 { - drop(segments.pop()); + if component == PathComponent::ParentDir { + // Only pop if we have segments beyond the root components + if segments.len() > usize::from(had_prefix).saturating_add(usize::from(had_root)) { + drop(segments.pop()); + } } }, PathComponent::Normal(_) => { @@ -1430,7 +1439,14 @@ impl PathExt for Path { }, } } - segments.iter().collect() + if cfg!(windows) { + segments.iter() + .collect::() + .to_str() + .map_or_else(PathBuf::new, |s| PathBuf::from(s.trim_start_matches('\\'))) + } else { + segments.iter().collect() + } } } diff --git a/crates/rubedo/src/tests/std.rs b/crates/rubedo/src/tests/std.rs index 73e5cb7..a4b245e 100644 --- a/crates/rubedo/src/tests/std.rs +++ b/crates/rubedo/src/tests/std.rs @@ -625,6 +625,38 @@ mod path_ext { path = PathBuf::from("tests/🥳.rs"); assert_eq!(path.normalize(), cwd.join("tests/🥳.rs")); + if cfg!(windows) { + path = PathBuf::from(r"C:\tests\std.rs"); + assert_eq!(path.normalize(), PathBuf::from(r"C:\tests\std.rs")); + + path = PathBuf::from(r"C:\tests\\std.rs"); + assert_eq!(path.normalize(), PathBuf::from(r"C:\tests\std.rs")); + + path = PathBuf::from(r"C:\tests\.\std.rs"); + assert_eq!(path.normalize(), PathBuf::from(r"C:\tests\std.rs")); + + path = PathBuf::from(r"C:\tests\..\std.rs"); + assert_eq!(path.normalize(), PathBuf::from(r"C:\std.rs")); + + path = PathBuf::from(r"C:\tests\..\..\std.rs"); + assert_eq!(path.normalize(), PathBuf::from(r"C:\std.rs")); + + path = PathBuf::from(r"\\SERVER\Share\tests\\std.rs"); + assert_eq!(path.normalize(), PathBuf::from(r"\\SERVER\Share\tests\std.rs")); + + path = PathBuf::from(r"\\SERVER\Share\tests\std.rs"); + assert_eq!(path.normalize(), PathBuf::from(r"\\SERVER\Share\tests\std.rs")); + + path = PathBuf::from(r"\\SERVER\Share\tests\.\std.rs"); + assert_eq!(path.normalize(), PathBuf::from(r"\\SERVER\Share\tests\std.rs")); + + path = PathBuf::from(r"\\SERVER\Share\tests\..\std.rs"); + assert_eq!(path.normalize(), PathBuf::from(r"\\SERVER\Share\std.rs")); + + path = PathBuf::from(r"\\SERVER\Share\tests\..\..\std.rs"); + assert_eq!(path.normalize(), PathBuf::from(r"\\SERVER\Share\std.rs")); + } + let path2: &Path = Path::new("/tests/std.rs"); assert_eq!(path2.normalize(), Path::new("/tests/std.rs")); assert_eq!(path2.normalize(), PathBuf::from("/tests/std.rs"));