Repository: servo/rust-url (rust)

Issue:
`Url::from_file_path()` incorrect handling of backslash on linux
Also affects `Url::from_directory_path()`.

Backslashes in paths get left as-is. They should be percent-encoded to `%5C`. Currently that backslash would change to a forward-slash if the output were to be stringified and re-parsed with `Url::parse()`. See the following roundtrip:
```rust
fn main() {
  let path = std::path::PathBuf::from("/a/b/c\\d");
  let url = url::Url::from_file_path(&path).unwrap();
  println!("{}", url.as_str()); // file:///a/b/c\d
  let url2 = url::Url::parse(url.as_str()).unwrap();
  println!("{}", url2.as_str()); // file:///a/b/c/d
  let path2 = url2.to_file_path().unwrap();
  println!("{}", path2.display()); // /a/b/c/d
  //                         Expected /a/b/c\d
}
```
Expected:
```rust
fn main() {
  let path = std::path::PathBuf::from("/a/b/c\\d");
  let url = url::Url::from_file_path(&path).unwrap();
  println!("{}", url.as_str()); // file:///a/b/c%5Cd
  let url2 = url::Url::parse(url.as_str()).unwrap();
  println!("{}", url2.as_str()); // file:///a/b/c%5Cd
  let path2 = url2.to_file_path().unwrap();
  println!("{}", path2.display()); // /a/b/c\d
}
```

On Windows they get swapped to forward-slashes, which is correct for Windows.



Pull request description:
Use SPECIAL_PATH_SEGMENT when encoding path in from_file_path
Fixes #901

Patch:
diff --git a/url/src/lib.rs b/url/src/lib.rs
index d498ee3..78980e8 100644
--- a/url/src/lib.rs
+++ b/url/src/lib.rs
@@ -146,7 +146,9 @@ pub use form_urlencoded;
 extern crate serde;
 
 use crate::host::HostInternal;
-use crate::parser::{to_u32, Context, Parser, SchemeType, PATH_SEGMENT, USERINFO};
+use crate::parser::{
+    to_u32, Context, Parser, SchemeType, PATH_SEGMENT, SPECIAL_PATH_SEGMENT, USERINFO,
+};
 use percent_encoding::{percent_decode, percent_encode, utf8_percent_encode};
 use std::borrow::Borrow;
 use std::cmp;
@@ -2817,7 +2819,7 @@ fn path_to_file_url_segments(
         serialization.push('/');
         serialization.extend(percent_encode(
             component.as_os_str().as_bytes(),
-            PATH_SEGMENT,
+            SPECIAL_PATH_SEGMENT,
         ));
     }
     if empty {


Test patch:
diff --git a/url/tests/unit.rs b/url/tests/unit.rs
index c27f237..f8069dc 100644
--- a/url/tests/unit.rs
+++ b/url/tests/unit.rs
@@ -146,6 +146,7 @@ fn new_file_paths() {
         assert_from_file_path!("/foo/bar");
         assert_from_file_path!("/foo/ba\0r", "/foo/ba%00r");
         assert_from_file_path!("/foo/ba%00r", "/foo/ba%2500r");
+        assert_from_file_path!("/foo/ba\\r", "/foo/ba%5Cr");
     }
 }