Explorar o código

Merge pull request #84 from alexcrichton/update

Bump rustc-serialize and use std::path
Simon Sapin %!s(int64=11) %!d(string=hai) anos
pai
achega
8b63a3f2eb
Modificáronse 3 ficheiros con 188 adicións e 4 borrados
  1. 1 1
      Cargo.toml
  2. 101 3
      src/lib.rs
  3. 86 0
      src/tests.rs

+ 1 - 1
Cargo.toml

@@ -21,5 +21,5 @@ version = "0.2"
 optional = true
 
 [dependencies]
-rustc-serialize = "0.2"
+rustc-serialize = "0.3"
 matches = "0.1"

+ 101 - 3
src/lib.rs

@@ -119,7 +119,7 @@ assert!(css_url.serialize() == "http://servo.github.io/rust-url/main.css".to_str
 */
 
 
-#![feature(core, std_misc, collections, old_path)]
+#![feature(core, std_misc, collections, old_path, path, os)]
 
 extern crate "rustc-serialize" as rustc_serialize;
 
@@ -129,6 +129,7 @@ extern crate matches;
 use std::fmt::{self, Formatter};
 use std::hash;
 use std::old_path as path;
+use std::path as new_path;
 
 pub use host::{Host, Ipv6Address};
 pub use parser::{ErrorHandler, ParseResult, ParseError};
@@ -479,7 +480,7 @@ impl Url {
     ///
     /// This returns `Err` if the given path is not absolute
     /// or, with a Windows path, if the prefix is not a disk prefix (e.g. `C:`).
-    pub fn from_file_path<T: ToUrlPath>(path: &T) -> Result<Url, ()> {
+    pub fn from_file_path<T: ToUrlPath + ?Sized>(path: &T) -> Result<Url, ()> {
         let path = try!(path.to_url_path());
         Ok(Url::from_path_common(path))
     }
@@ -501,7 +502,7 @@ impl Url {
     ///   as the base URL is `file:///var/index.html`, which might not be what was intended.
     ///
     /// (Note that `Path::new` removes any trailing slash.)
-    pub fn from_directory_path<T: ToUrlPath>(path: &T) -> Result<Url, ()> {
+    pub fn from_directory_path<T: ToUrlPath + ?Sized>(path: &T) -> Result<Url, ()> {
         let mut path = try!(path.to_url_path());
         // Add an empty path component (i.e. a trailing slash in serialization)
         // so that the entire path is used as a base URL.
@@ -928,6 +929,51 @@ pub trait ToUrlPath {
 }
 
 
+impl ToUrlPath for new_path::Path {
+    #[cfg(unix)]
+    fn to_url_path(&self) -> Result<Vec<String>, ()> {
+        use std::os::unix::prelude::*;
+        if !self.is_absolute() {
+            return Err(())
+        }
+        // skip the root component
+        Ok(self.components().skip(1).map(|c| {
+            percent_encode(c.as_os_str().as_bytes(), DEFAULT_ENCODE_SET)
+        }).collect())
+    }
+
+    #[cfg(windows)]
+    fn to_url_path(&self) -> Result<Vec<String>, ()> {
+        if !self.is_absolute() {
+            return Err(())
+        }
+        let mut components = self.components();
+        let disk = match components.next() {
+            Some(new_path::Component::Prefix {
+                parsed: new_path::Prefix::Disk(byte), ..
+            }) => byte,
+
+            // FIXME: do something with UNC and other prefixes?
+            _ => return Err(())
+        };
+
+        // Start with the prefix, e.g. "C:"
+        let mut path = vec![format!("{}:", disk as char)];
+
+        for component in components {
+            if component == new_path::Component::RootDir { continue }
+            // FIXME: somehow work with non-unicode?
+            let part = match component.as_os_str().to_str() {
+                Some(s) => s,
+                None => return Err(()),
+            };
+            path.push(percent_encode(part.as_bytes(), DEFAULT_ENCODE_SET));
+        }
+        Ok(path)
+    }
+}
+
+
 impl ToUrlPath for path::posix::Path {
     fn to_url_path(&self) -> Result<Vec<String>, ()> {
         if !self.is_absolute() {
@@ -963,6 +1009,58 @@ pub trait FromUrlPath {
 }
 
 
+impl FromUrlPath for new_path::PathBuf {
+    #[cfg(unix)]
+    fn from_url_path(path: &[String]) -> Result<new_path::PathBuf, ()> {
+        use std::ffi::OsStr;
+        use std::os::unix::prelude::*;
+        use std::path::PathBuf;
+
+        if path.is_empty() {
+            return Ok(PathBuf::new("/"))
+        }
+        let mut bytes = Vec::new();
+        for path_part in path.iter() {
+            bytes.push(b'/');
+            percent_decode_to(path_part.as_bytes(), &mut bytes);
+        }
+        let os_str = <OsStr as OsStrExt>::from_bytes(&bytes);
+        let path = PathBuf::new(&os_str);
+        debug_assert!(path.is_absolute(),
+                      "to_file_path() failed to produce an absolute Path");
+        Ok(path)
+    }
+
+    #[cfg(windows)]
+    fn from_url_path(path: &[String]) -> Result<new_path::PathBuf, ()> {
+        use std::path::PathBuf;
+
+        if path.is_empty() {
+            return Err(())
+        }
+        let prefix = &*path[0];
+        if prefix.len() != 2 || !parser::starts_with_ascii_alpha(prefix)
+                || prefix.as_bytes()[1] != b':' {
+            return Err(())
+        }
+        let mut string = prefix.to_string();
+        for path_part in path[1..].iter() {
+            string.push('\\');
+
+            // Currently non-unicode windows paths cannot be represented
+            match String::from_utf8(percent_decode(path_part.as_bytes())) {
+                Ok(s) => string.push_str(&s),
+                Err(..) => return Err(()),
+            }
+        }
+        let path = PathBuf::new(&string);
+        debug_assert!(path.is_absolute(),
+                      "to_file_path() failed to produce an absolute Path");
+        Ok(path)
+    }
+}
+
+
 impl FromUrlPath for path::posix::Path {
     fn from_url_path(path: &[String]) -> Result<path::posix::Path, ()> {
         if path.is_empty() {

+ 86 - 0
src/tests.rs

@@ -259,3 +259,89 @@ fn directory_paths() {
     assert_eq!(url.path(), Some(&[
         "C:".to_string(), "foo".to_string(), "bar".to_string(), "".to_string()][..]));
 }
+
+#[test]
+fn new_file_paths() {
+    use std::path::{Path, PathBuf};
+    if cfg!(unix) {
+        assert_eq!(Url::from_file_path(Path::new("relative")), Err(()));
+        assert_eq!(Url::from_file_path(Path::new("../relative")), Err(()));
+    } else {
+        assert_eq!(Url::from_file_path(Path::new("relative")), Err(()));
+        assert_eq!(Url::from_file_path(Path::new(r"..\relative")), Err(()));
+        assert_eq!(Url::from_file_path(Path::new(r"\drive-relative")), Err(()));
+        assert_eq!(Url::from_file_path(Path::new(r"\\ucn\")), Err(()));
+    }
+
+    if cfg!(unix) {
+        let mut url = Url::from_file_path(Path::new("/foo/bar")).unwrap();
+        assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
+        assert_eq!(url.path(), Some(&["foo".to_string(), "bar".to_string()][..]));
+        assert!(url.to_file_path() == Ok(PathBuf::new("/foo/bar")));
+
+        url.path_mut().unwrap()[1] = "ba\0r".to_string();
+        url.to_file_path::<PathBuf>().is_ok();
+
+        url.path_mut().unwrap()[1] = "ba%00r".to_string();
+        url.to_file_path::<PathBuf>().is_ok();
+    }
+}
+
+#[test]
+#[cfg(unix)]
+fn new_path_bad_utf8() {
+    use std::ffi::OsStr;
+    use std::os::unix::prelude::*;
+    use std::path::{Path, PathBuf};
+
+    let url = Url::from_file_path(Path::new("/foo/ba%80r")).unwrap();
+    let os_str = <OsStr as OsStrExt>::from_bytes(b"/foo/ba\x80r");
+    assert_eq!(url.to_file_path(), Ok(PathBuf::new(&os_str)));
+}
+
+#[test]
+#[cfg(windows)]
+fn new_path_windows_fun() {
+    use std::path::{Path, PathBuf};
+    let mut url = Url::from_file_path(Path::new(r"C:\foo\bar")).unwrap();
+    assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
+    assert_eq!(url.path(), Some(&["C:".to_string(), "foo".to_string(), "bar".to_string()][..]));
+    assert_eq!(url.to_file_path::<PathBuf>(),
+               Ok(PathBuf::new(r"C:\foo\bar")));
+
+    url.path_mut().unwrap()[2] = "ba\0r".to_string();
+    assert!(url.to_file_path::<PathBuf>().is_ok());
+
+    url.path_mut().unwrap()[2] = "ba%00r".to_string();
+    assert!(url.to_file_path::<PathBuf>().is_ok());
+
+    // Invalid UTF-8
+    url.path_mut().unwrap()[2] = "ba%80r".to_string();
+    assert!(url.to_file_path::<PathBuf>().is_err());
+}
+
+
+#[test]
+fn new_directory_paths() {
+    use std::path::Path;
+
+    if cfg!(unix) {
+        assert_eq!(Url::from_directory_path(Path::new("relative")), Err(()));
+        assert_eq!(Url::from_directory_path(Path::new("../relative")), Err(()));
+
+        let url = Url::from_directory_path(Path::new("/foo/bar")).unwrap();
+        assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
+        assert_eq!(url.path(), Some(&["foo".to_string(), "bar".to_string(),
+                                      "".to_string()][..]));
+    } else {
+        assert_eq!(Url::from_directory_path(Path::new("relative")), Err(()));
+        assert_eq!(Url::from_directory_path(Path::new(r"..\relative")), Err(()));
+        assert_eq!(Url::from_directory_path(Path::new(r"\drive-relative")), Err(()));
+        assert_eq!(Url::from_directory_path(Path::new(r"\\ucn\")), Err(()));
+
+        let url = Url::from_directory_path(Path::new(r"C:\foo\bar")).unwrap();
+        assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
+        assert_eq!(url.path(), Some(&["C:".to_string(), "foo".to_string(),
+                                      "bar".to_string(), "".to_string()][..]));
+    }
+}