Skip to content

Commit

Permalink
Make sure a windows drive letter segment always ends with a slash.
Browse files Browse the repository at this point in the history
  • Loading branch information
o0Ignition0o committed Aug 5, 2019
1 parent 3c71cac commit 87d3895
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 1 deletion.
18 changes: 18 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2336,6 +2336,7 @@ fn path_to_file_url_segments_windows(
}
let mut components = path.components();

let host_start = serialization.len() + 1;
let host_end;
let host_internal;
match components.next() {
Expand All @@ -2362,15 +2363,24 @@ fn path_to_file_url_segments_windows(
_ => return Err(()),
}

let mut path_only_has_prefix = true;
for component in components {
if component == Component::RootDir {
continue;
}
path_only_has_prefix = false;
// FIXME: somehow work with non-unicode?
let component = component.as_os_str().to_str().ok_or(())?;
serialization.push('/');
serialization.extend(percent_encode(component.as_bytes(), PATH_SEGMENT));
}
// A windows drive letter must end with a slash.
if serialization.len() > host_start
&& parser::is_windows_drive_letter(&serialization[host_start..])
&& path_only_has_prefix
{
serialization.push('/');
}
Ok((host_end, host_internal))
}

Expand All @@ -2395,6 +2405,14 @@ fn file_url_segments_to_pathbuf(
bytes.push(b'/');
bytes.extend(percent_decode(segment.as_bytes()));
}
// A windows drive letter must end with a slash.
if bytes.len() > 2 {
if matches!(bytes[bytes.len() -2], b'a'..=b'z' | b'A'..=b'Z')
&& matches!(bytes[bytes.len() - 1], b':' | b'|')
{
bytes.push(b'/');
}
}
let os_str = OsStr::from_bytes(&bytes);
let path = PathBuf::from(os_str);
debug_assert!(
Expand Down
3 changes: 2 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1393,7 +1393,8 @@ pub fn to_u32(i: usize) -> ParseResult<u32> {

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

Expand Down
26 changes: 26 additions & 0 deletions tests/unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,3 +549,29 @@ fn test_options_reuse() {
assert_eq!(url.as_str(), "http://mozilla.org/sub/path");
assert_eq!(*violations.borrow(), vec!(ExpectedDoubleSlash, Backslash));
}

/// https://github.com/servo/rust-url/issues/505
#[cfg(windows)]
#[test]
fn test_url_from_file_path() {
use std::path::PathBuf;
use url::Url;

let p = PathBuf::from("c:///");
let u = Url::from_file_path(p).unwrap();
let path = u.to_file_path().unwrap();
assert_eq!("C:\\", path.to_str().unwrap());
}

/// https://github.com/servo/rust-url/issues/505
#[cfg(not(windows))]
#[test]
fn test_url_from_file_path() {
use std::path::PathBuf;
use url::Url;

let p = PathBuf::from("/c:/");
let u = Url::from_file_path(p).unwrap();
let path = u.to_file_path().unwrap();
assert_eq!("/c:/", path.to_str().unwrap());
}

0 comments on commit 87d3895

Please # to comment.