Skip to content

Commit 87d3895

Browse files
committed
Make sure a windows drive letter segment always ends with a slash.
1 parent 3c71cac commit 87d3895

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
@@ -2336,6 +2336,7 @@ fn path_to_file_url_segments_windows(
23362336
}
23372337
let mut components = path.components();
23382338

2339+
let host_start = serialization.len() + 1;
23392340
let host_end;
23402341
let host_internal;
23412342
match components.next() {
@@ -2362,15 +2363,24 @@ fn path_to_file_url_segments_windows(
23622363
_ => return Err(()),
23632364
}
23642365

2366+
let mut path_only_has_prefix = true;
23652367
for component in components {
23662368
if component == Component::RootDir {
23672369
continue;
23682370
}
2371+
path_only_has_prefix = false;
23692372
// FIXME: somehow work with non-unicode?
23702373
let component = component.as_os_str().to_str().ok_or(())?;
23712374
serialization.push('/');
23722375
serialization.extend(percent_encode(component.as_bytes(), PATH_SEGMENT));
23732376
}
2377+
// A windows drive letter must end with a slash.
2378+
if serialization.len() > host_start
2379+
&& parser::is_windows_drive_letter(&serialization[host_start..])
2380+
&& path_only_has_prefix
2381+
{
2382+
serialization.push('/');
2383+
}
23742384
Ok((host_end, host_internal))
23752385
}
23762386

@@ -2395,6 +2405,14 @@ fn file_url_segments_to_pathbuf(
23952405
bytes.push(b'/');
23962406
bytes.extend(percent_decode(segment.as_bytes()));
23972407
}
2408+
// A windows drive letter must end with a slash.
2409+
if bytes.len() > 2 {
2410+
if matches!(bytes[bytes.len() -2], b'a'..=b'z' | b'A'..=b'Z')
2411+
&& matches!(bytes[bytes.len() - 1], b':' | b'|')
2412+
{
2413+
bytes.push(b'/');
2414+
}
2415+
}
23982416
let os_str = OsStr::from_bytes(&bytes);
23992417
let path = PathBuf::from(os_str);
24002418
debug_assert!(

src/parser.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1393,7 +1393,8 @@ pub fn to_u32(i: usize) -> ParseResult<u32> {
13931393

13941394
/// Wether the scheme is file:, the path has a single segment, and that segment
13951395
/// is a Windows drive letter
1396-
fn is_windows_drive_letter(segment: &str) -> bool {
1396+
#[inline]
1397+
pub fn is_windows_drive_letter(segment: &str) -> bool {
13971398
segment.len() == 2 && starts_with_windows_drive_letter(segment)
13981399
}
13991400

tests/unit.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,3 +549,29 @@ fn test_options_reuse() {
549549
assert_eq!(url.as_str(), "http://mozilla.org/sub/path");
550550
assert_eq!(*violations.borrow(), vec!(ExpectedDoubleSlash, Backslash));
551551
}
552+
553+
/// https://github.com/servo/rust-url/issues/505
554+
#[cfg(windows)]
555+
#[test]
556+
fn test_url_from_file_path() {
557+
use std::path::PathBuf;
558+
use url::Url;
559+
560+
let p = PathBuf::from("c:///");
561+
let u = Url::from_file_path(p).unwrap();
562+
let path = u.to_file_path().unwrap();
563+
assert_eq!("C:\\", path.to_str().unwrap());
564+
}
565+
566+
/// https://github.com/servo/rust-url/issues/505
567+
#[cfg(not(windows))]
568+
#[test]
569+
fn test_url_from_file_path() {
570+
use std::path::PathBuf;
571+
use url::Url;
572+
573+
let p = PathBuf::from("/c:/");
574+
let u = Url::from_file_path(p).unwrap();
575+
let path = u.to_file_path().unwrap();
576+
assert_eq!("/c:/", path.to_str().unwrap());
577+
}

0 commit comments

Comments
 (0)