Skip to content

Commit 8ef4847

Browse files
committed
Make sure a windows drive letter segment always ends with a slash.
1 parent a9ca033 commit 8ef4847

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2508,6 +2508,7 @@ fn path_to_file_url_segments_windows(
25082508
}
25092509
let mut components = path.components();
25102510

2511+
let host_start = serialization.len() + 1;
25112512
let host_end;
25122513
let host_internal;
25132514
match components.next() {
@@ -2534,15 +2535,24 @@ fn path_to_file_url_segments_windows(
25342535
_ => return Err(()),
25352536
}
25362537

2538+
let mut path_only_has_prefix = true;
25372539
for component in components {
25382540
if component == Component::RootDir {
25392541
continue;
25402542
}
2543+
path_only_has_prefix = false;
25412544
// FIXME: somehow work with non-unicode?
25422545
let component = component.as_os_str().to_str().ok_or(())?;
25432546
serialization.push('/');
25442547
serialization.extend(percent_encode(component.as_bytes(), PATH_SEGMENT));
25452548
}
2549+
// A windows drive letter must end with a slash.
2550+
if serialization.len() > host_start
2551+
&& parser::is_windows_drive_letter(&serialization[host_start..])
2552+
&& path_only_has_prefix
2553+
{
2554+
serialization.push('/');
2555+
}
25462556
Ok((host_end, host_internal))
25472557
}
25482558

@@ -2567,6 +2577,14 @@ fn file_url_segments_to_pathbuf(
25672577
bytes.push(b'/');
25682578
bytes.extend(percent_decode(segment.as_bytes()));
25692579
}
2580+
// A windows drive letter must end with a slash.
2581+
if bytes.len() > 2 {
2582+
if matches!(bytes[bytes.len() -2], b'a'..=b'z' | b'A'..=b'Z')
2583+
&& matches!(bytes[bytes.len() - 1], b':' | b'|')
2584+
{
2585+
bytes.push(b'/');
2586+
}
2587+
}
25702588
let os_str = OsStr::from_bytes(&bytes);
25712589
let path = PathBuf::from(os_str);
25722590
debug_assert!(

src/parser.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1571,7 +1571,8 @@ fn is_normalized_windows_drive_letter(segment: &str) -> bool {
15711571

15721572
/// Wether the scheme is file:, the path has a single segment, and that segment
15731573
/// is a Windows drive letter
1574-
fn is_windows_drive_letter(segment: &str) -> bool {
1574+
#[inline]
1575+
pub fn is_windows_drive_letter(segment: &str) -> bool {
15751576
segment.len() == 2 && starts_with_windows_drive_letter(segment)
15761577
}
15771578

tests/unit.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,3 +564,29 @@ fn test_options_reuse() {
564564
assert_eq!(url.as_str(), "http://mozilla.org/sub/path");
565565
assert_eq!(*violations.borrow(), vec!(ExpectedDoubleSlash, Backslash));
566566
}
567+
568+
/// https://github.com/servo/rust-url/issues/505
569+
#[cfg(windows)]
570+
#[test]
571+
fn test_url_from_file_path() {
572+
use std::path::PathBuf;
573+
use url::Url;
574+
575+
let p = PathBuf::from("c:///");
576+
let u = Url::from_file_path(p).unwrap();
577+
let path = u.to_file_path().unwrap();
578+
assert_eq!("C:\\", path.to_str().unwrap());
579+
}
580+
581+
/// https://github.com/servo/rust-url/issues/505
582+
#[cfg(not(windows))]
583+
#[test]
584+
fn test_url_from_file_path() {
585+
use std::path::PathBuf;
586+
use url::Url;
587+
588+
let p = PathBuf::from("/c:/");
589+
let u = Url::from_file_path(p).unwrap();
590+
let path = u.to_file_path().unwrap();
591+
assert_eq!("/c:/", path.to_str().unwrap());
592+
}

0 commit comments

Comments
 (0)