Răsfoiți Sursa

Merge branch 'master' into crates.io

Conflicts:
	Cargo.toml
Simon Sapin 12 ani în urmă
părinte
comite
94e637886a
12 a modificat fișierele cu 447 adăugiri și 332 ștergeri
  1. 5 1
      .travis.yml
  2. 7 2
      Cargo.toml
  3. 1 1
      README.md
  4. 108 0
      src/encoding.rs
  5. 60 56
      src/form_urlencoded.rs
  6. 4 4
      src/format.rs
  7. 23 26
      src/host.rs
  8. 85 78
      src/lib.rs
  9. 125 130
      src/parser.rs
  10. 1 1
      src/punycode.rs
  11. 7 8
      src/tests.rs
  12. 21 25
      src/urlutils.rs

+ 5 - 1
.travis.yml

@@ -1,9 +1,13 @@
 language: rust
 language: rust
 
 
+script:
+  - cargo test --features query_encoding
+  - cargo test
+
 after_success: |
 after_success: |
   [ $TRAVIS_BRANCH = master ] &&
   [ $TRAVIS_BRANCH = master ] &&
   [ $TRAVIS_PULL_REQUEST = false ] &&
   [ $TRAVIS_PULL_REQUEST = false ] &&
-  cargo doc &&
+  cargo doc --features query_encoding &&
   echo '<meta http-equiv=refresh content=0;url=url/index.html>' > target/doc/index.html &&
   echo '<meta http-equiv=refresh content=0;url=url/index.html>' > target/doc/index.html &&
   cp github.png target/doc/ &&
   cp github.png target/doc/ &&
   sudo pip install ghp-import &&
   sudo pip install ghp-import &&

+ 7 - 2
Cargo.toml

@@ -11,6 +11,11 @@ readme = "README.md"
 keywords = ["url", "parser"]
 keywords = ["url", "parser"]
 license = "MIT/Apache-2.0"
 license = "MIT/Apache-2.0"
 
 
+[features]
 
 
-[dependencies]
-encoding = "0.2"
+query_encoding = ["encoding"]
+
+[dependencies.encoding]
+
+version = "0.2"
+optional = true

+ 1 - 1
README.md

@@ -9,7 +9,7 @@ This is a replacement for Rust’s “old” (as of July 2014) `url` crate.
 See [Rust bug #10707](https://github.com/mozilla/rust/issues/10707).
 See [Rust bug #10707](https://github.com/mozilla/rust/issues/10707).
 
 
 This builds with [Cargo](https://github.com/rust-lang/cargo),
 This builds with [Cargo](https://github.com/rust-lang/cargo),
-pulling in [rust-encoding](https://github.com/lifthrasiir/rust-encoding) as a depedency.
+pulling in [rust-encoding](https://github.com/lifthrasiir/rust-encoding) as a dependency.
 
 
 [Documentation](http://servo.github.io/rust-url/url/index.html)
 [Documentation](http://servo.github.io/rust-url/url/index.html)
 
 

+ 108 - 0
src/encoding.rs

@@ -0,0 +1,108 @@
+// Copyright 2013-2014 Simon Sapin.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+//! Abstraction that conditionally compiles either to rust-encoding,
+//! or to only support UTF-8.
+
+#[cfg(feature = "query_encoding")]
+extern crate encoding;
+
+#[cfg(feature = "query_encoding")]
+use self::encoding::types::{DecoderTrap, EncoderTrap};
+
+#[cfg(feature = "query_encoding")]
+use self::encoding::label::encoding_from_whatwg_label;
+
+#[cfg(feature = "query_encoding")]
+pub use self::encoding::types::EncodingRef;
+
+
+#[cfg(feature = "query_encoding")]
+pub struct EncodingOverride {
+    /// `None` means UTF-8.
+    encoding: Option<EncodingRef>
+}
+
+#[cfg(feature = "query_encoding")]
+impl EncodingOverride {
+    pub fn from_opt_encoding(encoding: Option<EncodingRef>) -> EncodingOverride {
+        encoding.map(EncodingOverride::from_encoding).unwrap_or_else(EncodingOverride::utf8)
+    }
+
+    pub fn from_encoding(encoding: EncodingRef) -> EncodingOverride {
+        EncodingOverride {
+            encoding: if encoding.name() == "utf-8" { None } else { Some(encoding) }
+        }
+    }
+
+    pub fn utf8() -> EncodingOverride {
+        EncodingOverride { encoding: None }
+    }
+
+    pub fn lookup(label: &[u8]) -> Option<EncodingOverride> {
+        ::std::str::from_utf8(label.as_slice())
+        .and_then(encoding_from_whatwg_label)
+        .map(EncodingOverride::from_encoding)
+    }
+
+    pub fn is_utf8(&self) -> bool {
+        self.encoding.is_none()
+    }
+
+    pub fn decode(&self, input: &[u8]) -> String {
+        match self.encoding {
+            Some(encoding) => encoding.decode(input, DecoderTrap::Replace).unwrap(),
+            None => String::from_utf8_lossy(input).into_string(),
+        }
+    }
+
+    // For UTF-8, we want to return the &[u8] bytes of the &str input strings without copying
+    // But for other encodings we have to allocate a new Vec<u8>.
+    // To return &[u8] in that case, the vector has to be kept somewhere
+    // that lives at least as long as the return value.
+    // Therefore, the caller provides a temporary Vec<u8> as scratch space.
+    //
+    // FIXME: Return std::borrow::Cow<'a, Vec<u8>, [u8]> instead.
+    pub fn encode<'a>(&self, input: &'a str, tmp: &'a mut Vec<u8>) -> &'a [u8] {
+        match self.encoding {
+            Some(encoding) => {
+                *tmp = encoding.encode(input.as_slice(), EncoderTrap::NcrEscape).unwrap();
+                tmp.as_slice()
+            },
+            None => input.as_bytes()  // UTF-8
+        }
+    }
+}
+
+
+#[cfg(not(feature = "query_encoding"))]
+pub struct EncodingOverride;
+
+#[cfg(not(feature = "query_encoding"))]
+impl EncodingOverride {
+    pub fn utf8() -> EncodingOverride {
+        EncodingOverride
+    }
+
+    pub fn lookup(_label: &[u8]) -> Option<EncodingOverride> {
+        None
+    }
+
+    pub fn is_utf8(&self) -> bool {
+        true
+    }
+
+    pub fn decode(&self, input: &[u8]) -> String {
+        String::from_utf8_lossy(input).into_string()
+    }
+
+    pub fn encode<'a>(&self, input: &'a str, _: &'a mut Vec<u8>) -> &'a [u8] {
+        input.as_bytes()
+    }
+}

+ 60 - 56
src/form_urlencoded.rs

@@ -13,52 +13,60 @@
 //! Converts between a string (such as an URL’s query string)
 //! Converts between a string (such as an URL’s query string)
 //! and a sequence of (name, value) pairs.
 //! and a sequence of (name, value) pairs.
 
 
-use std::str;
-
-use encoding;
-use encoding::EncodingRef;
-use encoding::all::UTF_8;
-use encoding::label::encoding_from_whatwg_label;
-
+use encoding::EncodingOverride;
 use percent_encoding::{percent_encode_to, percent_decode, FORM_URLENCODED_ENCODE_SET};
 use percent_encoding::{percent_encode_to, percent_decode, FORM_URLENCODED_ENCODE_SET};
 
 
 
 
-/// Convert a string in the `application/x-www-form-urlencoded` format
+/// Convert a byte string in the `application/x-www-form-urlencoded` format
 /// into a vector of (name, value) pairs.
 /// into a vector of (name, value) pairs.
+///
+/// Use `parse(input.as_bytes())` to parse a `&str` string.
 #[inline]
 #[inline]
-pub fn parse_str(input: &str) -> Vec<(String, String)> {
-    parse_bytes(input.as_bytes(), None, false, false).unwrap()
+pub fn parse(input: &[u8]) -> Vec<(String, String)> {
+    parse_internal(input, EncodingOverride::utf8(), false).unwrap()
 }
 }
 
 
 
 
 /// Convert a byte string in the `application/x-www-form-urlencoded` format
 /// Convert a byte string in the `application/x-www-form-urlencoded` format
 /// into a vector of (name, value) pairs.
 /// into a vector of (name, value) pairs.
 ///
 ///
+/// Use `parse(input.as_bytes())` to parse a `&str` string.
+///
+/// This function is only available if the `query_encoding` Cargo feature is enabled.
+///
 /// Arguments:
 /// Arguments:
 ///
 ///
 /// * `encoding_override`: The character encoding each name and values is decoded as
 /// * `encoding_override`: The character encoding each name and values is decoded as
 ///    after percent-decoding. Defaults to UTF-8.
 ///    after percent-decoding. Defaults to UTF-8.
 /// * `use_charset`: The *use _charset_ flag*. If in doubt, set to `false`.
 /// * `use_charset`: The *use _charset_ flag*. If in doubt, set to `false`.
-/// * `isindex`: The *isindex flag*. If in doubt, set to `false`.
-pub fn parse_bytes(input: &[u8], encoding_override: Option<EncodingRef>,
-                   mut use_charset: bool, mut isindex: bool) -> Option<Vec<(String, String)>> {
-    let mut encoding_override = encoding_override.unwrap_or(UTF_8 as EncodingRef);
+#[cfg(feature = "query_encoding")]
+#[inline]
+pub fn parse_with_encoding(input: &[u8], encoding_override: Option<::encoding::EncodingRef>,
+                           use_charset: bool)
+                           -> Option<Vec<(String, String)>> {
+    parse_internal(input, EncodingOverride::from_opt_encoding(encoding_override), use_charset)
+}
+
+
+fn parse_internal(input: &[u8], mut encoding_override: EncodingOverride, mut use_charset: bool)
+                  -> Option<Vec<(String, String)>> {
     let mut pairs = Vec::new();
     let mut pairs = Vec::new();
     for piece in input.split(|&b| b == b'&') {
     for piece in input.split(|&b| b == b'&') {
-        if piece.is_empty() {
-            if isindex {
-                pairs.push((Vec::new(), Vec::new()))
-            }
-        } else {
+        if !piece.is_empty() {
             let (name, value) = match piece.position_elem(&b'=') {
             let (name, value) = match piece.position_elem(&b'=') {
                 Some(position) => (piece.slice_to(position), piece.slice_from(position + 1)),
                 Some(position) => (piece.slice_to(position), piece.slice_from(position + 1)),
-                None => if isindex { ([].as_slice(), piece) } else { (piece, [].as_slice()) }
+                None => (piece, [].as_slice())
             };
             };
+
+            #[inline]
+            fn replace_plus(input: &[u8]) -> Vec<u8> {
+                input.iter().map(|&b| if b == b'+' { b' ' } else { b }).collect()
+            }
+
             let name = replace_plus(name);
             let name = replace_plus(name);
             let value = replace_plus(value);
             let value = replace_plus(value);
             if use_charset && name.as_slice() == b"_charset_" {
             if use_charset && name.as_slice() == b"_charset_" {
-                // Non-UTF8 here is ok, encoding_from_whatwg_label only matches in the ASCII range.
-                match encoding_from_whatwg_label(unsafe { str::raw::from_utf8(value.as_slice()) }) {
+                match EncodingOverride::lookup(value.as_slice()) {
                     Some(encoding) => encoding_override = encoding,
                     Some(encoding) => encoding_override = encoding,
                     None => (),
                     None => (),
                 }
                 }
@@ -66,27 +74,15 @@ pub fn parse_bytes(input: &[u8], encoding_override: Option<EncodingRef>,
             }
             }
             pairs.push((name, value));
             pairs.push((name, value));
         }
         }
-        isindex = false;
     }
     }
-    if encoding_override.name() != "utf-8" && !input.is_ascii() {
+    if !(encoding_override.is_utf8() || input.is_ascii()) {
         return None
         return None
     }
     }
 
 
-    #[inline]
-    fn replace_plus(input: &[u8]) -> Vec<u8> {
-        input.iter().map(|&b| if b == b'+' { b' ' } else { b }).collect()
-    }
-
-    #[inline]
-    fn decode(input: Vec<u8>, encoding_override: EncodingRef) -> String {
-        encoding_override.decode(
-            percent_decode(input.as_slice()).as_slice(),
-            encoding::DecodeReplace).unwrap()
-    }
-
-    Some(pairs.into_iter().map(
-        |(name, value)| (decode(name, encoding_override), decode(value, encoding_override))
-    ).collect())
+    Some(pairs.into_iter().map(|(name, value)| (
+        encoding_override.decode(percent_decode(name.as_slice()).as_slice()),
+        encoding_override.decode(percent_decode(value.as_slice()).as_slice())
+    )).collect())
 }
 }
 
 
 
 
@@ -94,37 +90,45 @@ pub fn parse_bytes(input: &[u8], encoding_override: Option<EncodingRef>,
 /// into a string in the `application/x-www-form-urlencoded` format.
 /// into a string in the `application/x-www-form-urlencoded` format.
 #[inline]
 #[inline]
 pub fn serialize_owned(pairs: &[(String, String)]) -> String {
 pub fn serialize_owned(pairs: &[(String, String)]) -> String {
-    serialize(pairs.iter().map(|&(ref n, ref v)| (n.as_slice(), v.as_slice())), None)
+    serialize(pairs.iter().map(|&(ref n, ref v)| (n.as_slice(), v.as_slice())))
 }
 }
 
 
 
 
 /// Convert an iterator of (name, value) pairs
 /// Convert an iterator of (name, value) pairs
 /// into a string in the `application/x-www-form-urlencoded` format.
 /// into a string in the `application/x-www-form-urlencoded` format.
+#[inline]
+pub fn serialize<'a, I>(pairs: I) -> String where I: Iterator<(&'a str, &'a str)> {
+    serialize_internal(pairs, EncodingOverride::utf8())
+}
+
+/// Convert an iterator of (name, value) pairs
+/// into a string in the `application/x-www-form-urlencoded` format.
+///
+/// This function is only available if the `query_encoding` Cargo feature is enabled.
 ///
 ///
 /// Arguments:
 /// Arguments:
 ///
 ///
 /// * `encoding_override`: The character encoding each name and values is encoded as
 /// * `encoding_override`: The character encoding each name and values is encoded as
 ///    before percent-encoding. Defaults to UTF-8.
 ///    before percent-encoding. Defaults to UTF-8.
-pub fn serialize<'a, I: Iterator<(&'a str, &'a str)>>(
-        mut pairs: I, encoding_override: Option<EncodingRef>)
-        -> String {
+#[cfg(feature = "query_encoding")]
+#[inline]
+pub fn serialize_with_encoding<'a, I>(pairs: I, encoding_override: Option<::encoding::EncodingRef>)
+                                      -> String
+                                      where I: Iterator<(&'a str, &'a str)> {
+    serialize_internal(pairs, EncodingOverride::from_opt_encoding(encoding_override))
+}
+
+fn serialize_internal<'a, I>(mut pairs: I, encoding_override: EncodingOverride) -> String
+                             where I: Iterator<(&'a str, &'a str)> {
     #[inline]
     #[inline]
     fn byte_serialize(input: &str, output: &mut String,
     fn byte_serialize(input: &str, output: &mut String,
-                      encoding_override: Option<EncodingRef>) {
-        let keep_alive;
-        let input = match encoding_override {
-            None => input.as_bytes(),  // "Encode" to UTF-8
-            Some(encoding) => {
-                keep_alive = encoding.encode(input, encoding::EncodeNcrEscape).unwrap();
-                keep_alive.as_slice()
-            }
-        };
-
-        for &byte in input.iter() {
+                      encoding_override: EncodingOverride) {
+        let tmp = &mut vec![];
+        for &byte in encoding_override.encode(input, tmp).iter() {
             if byte == b' ' {
             if byte == b' ' {
                 output.push_str("+")
                 output.push_str("+")
             } else {
             } else {
-                percent_encode_to([byte], FORM_URLENCODED_ENCODE_SET, output)
+                percent_encode_to(&[byte], FORM_URLENCODED_ENCODE_SET, output)
             }
             }
         }
         }
     }
     }
@@ -151,5 +155,5 @@ fn test_form_urlencoded() {
     ];
     ];
     let encoded = serialize_owned(pairs.as_slice());
     let encoded = serialize_owned(pairs.as_slice());
     assert_eq!(encoded.as_slice(), "foo=%C3%A9%26&bar=&foo=%23");
     assert_eq!(encoded.as_slice(), "foo=%C3%A9%26&bar=&foo=%23");
-    assert_eq!(parse_str(encoded.as_slice()), pairs.as_slice().to_vec());
+    assert_eq!(parse(encoded.as_bytes()), pairs.as_slice().to_vec());
 }
 }

+ 4 - 4
src/format.rs

@@ -12,7 +12,7 @@
 //!
 //!
 //! You can use `<formatter>.to_string()`, as the formatters implement `Show`.
 //! You can use `<formatter>.to_string()`, as the formatters implement `Show`.
 
 
-use std::fmt::{Show, Formatter, FormatError};
+use std::fmt::{mod, Show, Formatter};
 use super::Url;
 use super::Url;
 
 
 /// Formatter and serializer for URL path data.
 /// Formatter and serializer for URL path data.
@@ -22,7 +22,7 @@ pub struct PathFormatter<'a, T:'a> {
 }
 }
 
 
 impl<'a, T: Str + Show> Show for PathFormatter<'a, T> {
 impl<'a, T: Str + Show> Show for PathFormatter<'a, T> {
-    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
+    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
         if self.path.is_empty() {
         if self.path.is_empty() {
             formatter.write(b"/")
             formatter.write(b"/")
         } else {
         } else {
@@ -48,7 +48,7 @@ pub struct UserInfoFormatter<'a> {
 }
 }
 
 
 impl<'a> Show for UserInfoFormatter<'a> {
 impl<'a> Show for UserInfoFormatter<'a> {
-    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
+    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
         if !self.username.is_empty() || self.password.is_some() {
         if !self.username.is_empty() || self.password.is_some() {
             try!(formatter.write(self.username.as_bytes()));
             try!(formatter.write(self.username.as_bytes()));
             match self.password {
             match self.password {
@@ -71,7 +71,7 @@ pub struct UrlNoFragmentFormatter<'a> {
 }
 }
 
 
 impl<'a> Show for UrlNoFragmentFormatter<'a> {
 impl<'a> Show for UrlNoFragmentFormatter<'a> {
-    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
+    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
         try!(formatter.write(self.url.scheme.as_bytes()));
         try!(formatter.write(self.url.scheme.as_bytes()));
         try!(formatter.write(b":"));
         try!(formatter.write(b":"));
         try!(self.url.scheme_data.fmt(formatter));
         try!(self.url.scheme_data.fmt(formatter));

+ 23 - 26
src/host.rs

@@ -8,11 +8,8 @@
 
 
 use std::ascii::OwnedAsciiExt;
 use std::ascii::OwnedAsciiExt;
 use std::cmp;
 use std::cmp;
-use std::fmt::{Formatter, FormatError, Show};
-use parser::{
-    ParseResult,
-    InvalidIpv6Address, EmptyHost, NonAsciiDomainsNotSupportedYet, InvalidDomainCharacter,
-};
+use std::fmt::{mod, Formatter, Show};
+use parser::{ParseResult, ParseError};
 use percent_encoding::{from_hex, percent_decode};
 use percent_encoding::{from_hex, percent_decode};
 
 
 
 
@@ -61,25 +58,25 @@ impl Host {
     /// FIXME: Add IDNA support for non-ASCII domains.
     /// FIXME: Add IDNA support for non-ASCII domains.
     pub fn parse(input: &str) -> ParseResult<Host> {
     pub fn parse(input: &str) -> ParseResult<Host> {
         if input.len() == 0 {
         if input.len() == 0 {
-            Err(EmptyHost)
+            Err(ParseError::EmptyHost)
         } else if input.starts_with("[") {
         } else if input.starts_with("[") {
             if input.ends_with("]") {
             if input.ends_with("]") {
-                Ipv6Address::parse(input.slice(1, input.len() - 1)).map(Ipv6)
+                Ipv6Address::parse(input.slice(1, input.len() - 1)).map(Host::Ipv6)
             } else {
             } else {
-                Err(InvalidIpv6Address)
+                Err(ParseError::InvalidIpv6Address)
             }
             }
         } else {
         } else {
             let decoded = percent_decode(input.as_bytes());
             let decoded = percent_decode(input.as_bytes());
             let domain = String::from_utf8_lossy(decoded.as_slice());
             let domain = String::from_utf8_lossy(decoded.as_slice());
             // TODO: Remove this check and use IDNA "domain to ASCII"
             // TODO: Remove this check and use IDNA "domain to ASCII"
             if !domain.as_slice().is_ascii() {
             if !domain.as_slice().is_ascii() {
-                Err(NonAsciiDomainsNotSupportedYet)
+                Err(ParseError::NonAsciiDomainsNotSupportedYet)
             } else if domain.as_slice().find([
             } else if domain.as_slice().find([
                 '\0', '\t', '\n', '\r', ' ', '#', '%', '/', ':', '?', '@', '[', '\\', ']'
                 '\0', '\t', '\n', '\r', ' ', '#', '%', '/', ':', '?', '@', '[', '\\', ']'
             ].as_slice()).is_some() {
             ].as_slice()).is_some() {
-                Err(InvalidDomainCharacter)
+                Err(ParseError::InvalidDomainCharacter)
             } else {
             } else {
-                Ok(Domain(domain.into_string().into_ascii_lower()))
+                Ok(Host::Domain(domain.into_string().into_ascii_lower()))
             }
             }
         }
         }
     }
     }
@@ -94,10 +91,10 @@ impl Host {
 
 
 
 
 impl Show for Host {
 impl Show for Host {
-    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
+    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
         match *self {
         match *self {
-            Domain(ref domain) => domain.fmt(formatter),
-            Ipv6(ref address) => {
+            Host::Domain(ref domain) => domain.fmt(formatter),
+            Host::Ipv6(ref address) => {
                 try!(formatter.write(b"["));
                 try!(formatter.write(b"["));
                 try!(address.fmt(formatter));
                 try!(address.fmt(formatter));
                 formatter.write(b"]")
                 formatter.write(b"]")
@@ -119,7 +116,7 @@ impl Ipv6Address {
         let mut i = 0u;
         let mut i = 0u;
         if input[0] == b':' {
         if input[0] == b':' {
             if input[1] != b':' {
             if input[1] != b':' {
-                return Err(InvalidIpv6Address)
+                return Err(ParseError::InvalidIpv6Address)
             }
             }
             i = 2;
             i = 2;
             piece_pointer = 1;
             piece_pointer = 1;
@@ -128,11 +125,11 @@ impl Ipv6Address {
 
 
         while i < len {
         while i < len {
             if piece_pointer == 8 {
             if piece_pointer == 8 {
-                return Err(InvalidIpv6Address)
+                return Err(ParseError::InvalidIpv6Address)
             }
             }
             if input[i] == b':' {
             if input[i] == b':' {
                 if compress_pointer.is_some() {
                 if compress_pointer.is_some() {
-                    return Err(InvalidIpv6Address)
+                    return Err(ParseError::InvalidIpv6Address)
                 }
                 }
                 i += 1;
                 i += 1;
                 piece_pointer += 1;
                 piece_pointer += 1;
@@ -155,7 +152,7 @@ impl Ipv6Address {
                 match input[i] {
                 match input[i] {
                     b'.' => {
                     b'.' => {
                         if i == start {
                         if i == start {
-                            return Err(InvalidIpv6Address)
+                            return Err(ParseError::InvalidIpv6Address)
                         }
                         }
                         i = start;
                         i = start;
                         is_ip_v4 = true;
                         is_ip_v4 = true;
@@ -163,10 +160,10 @@ impl Ipv6Address {
                     b':' => {
                     b':' => {
                         i += 1;
                         i += 1;
                         if i == len {
                         if i == len {
-                            return Err(InvalidIpv6Address)
+                            return Err(ParseError::InvalidIpv6Address)
                         }
                         }
                     },
                     },
-                    _ => return Err(InvalidIpv6Address)
+                    _ => return Err(ParseError::InvalidIpv6Address)
                 }
                 }
             }
             }
             if is_ip_v4 {
             if is_ip_v4 {
@@ -178,7 +175,7 @@ impl Ipv6Address {
 
 
         if is_ip_v4 {
         if is_ip_v4 {
             if piece_pointer > 6 {
             if piece_pointer > 6 {
-                return Err(InvalidIpv6Address)
+                return Err(ParseError::InvalidIpv6Address)
             }
             }
             let mut dots_seen = 0u;
             let mut dots_seen = 0u;
             while i < len {
             while i < len {
@@ -191,11 +188,11 @@ impl Ipv6Address {
                     };
                     };
                     value = value * 10 + digit as u16;
                     value = value * 10 + digit as u16;
                     if value == 0 || value > 255 {
                     if value == 0 || value > 255 {
-                        return Err(InvalidIpv6Address)
+                        return Err(ParseError::InvalidIpv6Address)
                     }
                     }
                 }
                 }
                 if dots_seen < 3 && !(i < len && input[i] == b'.') {
                 if dots_seen < 3 && !(i < len && input[i] == b'.') {
-                    return Err(InvalidIpv6Address)
+                    return Err(ParseError::InvalidIpv6Address)
                 }
                 }
                 pieces[piece_pointer] = pieces[piece_pointer] * 0x100 + value;
                 pieces[piece_pointer] = pieces[piece_pointer] * 0x100 + value;
                 if dots_seen == 0 || dots_seen == 2 {
                 if dots_seen == 0 || dots_seen == 2 {
@@ -203,7 +200,7 @@ impl Ipv6Address {
                 }
                 }
                 i += 1;
                 i += 1;
                 if dots_seen == 3 && i < len {
                 if dots_seen == 3 && i < len {
-                    return Err(InvalidIpv6Address)
+                    return Err(ParseError::InvalidIpv6Address)
                 }
                 }
                 dots_seen += 1;
                 dots_seen += 1;
             }
             }
@@ -221,7 +218,7 @@ impl Ipv6Address {
                 }
                 }
             }
             }
             _ => if piece_pointer != 8 {
             _ => if piece_pointer != 8 {
-                return Err(InvalidIpv6Address)
+                return Err(ParseError::InvalidIpv6Address)
             }
             }
         }
         }
         Ok(Ipv6Address { pieces: pieces })
         Ok(Ipv6Address { pieces: pieces })
@@ -235,7 +232,7 @@ impl Ipv6Address {
 
 
 
 
 impl Show for Ipv6Address {
 impl Show for Ipv6Address {
-    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
+    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
         let (compress_start, compress_end) = longest_zero_sequence(&self.pieces);
         let (compress_start, compress_end) = longest_zero_sequence(&self.pieces);
         let mut i = 0;
         let mut i = 0;
         while i < 8 {
         while i < 8 {

+ 85 - 78
src/lib.rs

@@ -22,9 +22,6 @@ To use it in your project, add this to your `Cargo.toml` file:
 git = "https://github.com/servo/rust-url"
 git = "https://github.com/servo/rust-url"
 ```
 ```
 
 
-This will automatically pull in the
-[rust-encoding](https://github.com/lifthrasiir/rust-encoding) dependency.
-
 rust-url is a replacement of the [`url` crate](http://doc.rust-lang.org/url/index.html)
 rust-url is a replacement of the [`url` crate](http://doc.rust-lang.org/url/index.html)
 currently distributed with Rust.
 currently distributed with Rust.
 rust-url’s crate is also named `url`.
 rust-url’s crate is also named `url`.
@@ -34,21 +31,35 @@ but that means that you can not also use the old `url` in the same crate.
 If you’re not using Cargo, you’ll need to pass `--extern url=/path/to/liburl.rlib`
 If you’re not using Cargo, you’ll need to pass `--extern url=/path/to/liburl.rlib`
 explicitly to rustc.
 explicitly to rustc.
 
 
+Supporting encodings other than UTF-8 in query strings is an optional feature
+that requires [rust-encoding](https://github.com/lifthrasiir/rust-encoding)
+and is off by default.
+You can enable it with
+[Cargo’s *features* mechanism](http://doc.crates.io/manifest.html#the-[features]-section):
+
+```Cargo
+[dependencies.url]
+git = "https://github.com/servo/rust-url"
+features = ["query_encoding"]
+```
+
+… or by passing `--cfg 'feature="query_encoding"'` to rustc.
+
 
 
 # URL parsing and data structures
 # URL parsing and data structures
 
 
 First, URL parsing may fail for various reasons and therefore returns a `Result`.
 First, URL parsing may fail for various reasons and therefore returns a `Result`.
 
 
 ```
 ```
-use url::{Url, InvalidIpv6Address};
+use url::{Url, ParseError};
 
 
-assert!(Url::parse("http://[:::1]") == Err(InvalidIpv6Address))
+assert!(Url::parse("http://[:::1]") == Err(ParseError::InvalidIpv6Address))
 ```
 ```
 
 
 Let’s parse a valid URL and look at its components.
 Let’s parse a valid URL and look at its components.
 
 
 ```
 ```
-use url::{Url, RelativeSchemeData, NonRelativeSchemeData};
+use url::{Url, SchemeData};
 
 
 let issue_list_url = Url::parse(
 let issue_list_url = Url::parse(
     "https://github.com/rust-lang/rust/issues?labels=E-easy&state=open"
     "https://github.com/rust-lang/rust/issues?labels=E-easy&state=open"
@@ -64,8 +75,8 @@ assert!(issue_list_url.path() == Some(["rust-lang".to_string(),
 assert!(issue_list_url.query == Some("labels=E-easy&state=open".to_string()));
 assert!(issue_list_url.query == Some("labels=E-easy&state=open".to_string()));
 assert!(issue_list_url.fragment == None);
 assert!(issue_list_url.fragment == None);
 match issue_list_url.scheme_data {
 match issue_list_url.scheme_data {
-    RelativeSchemeData(..) => {},  // Expected
-    NonRelativeSchemeData(..) => panic!(),
+    SchemeData::Relative(..) => {},  // Expected
+    SchemeData::NonRelative(..) => panic!(),
 }
 }
 ```
 ```
 
 
@@ -75,12 +86,12 @@ Every other components has accessors because they only apply to URLs said to be
 “in a relative scheme”. `https` is a relative scheme, but `data` is not:
 “in a relative scheme”. `https` is a relative scheme, but `data` is not:
 
 
 ```
 ```
-use url::{Url, NonRelativeSchemeData};
+use url::{Url, SchemeData};
 
 
 let data_url = Url::parse("data:text/plain,Hello#").unwrap();
 let data_url = Url::parse("data:text/plain,Hello#").unwrap();
 
 
 assert!(data_url.scheme == "data".to_string());
 assert!(data_url.scheme == "data".to_string());
-assert!(data_url.scheme_data == NonRelativeSchemeData("text/plain,Hello".to_string()));
+assert!(data_url.scheme_data == SchemeData::NonRelative("text/plain,Hello".to_string()));
 assert!(data_url.non_relative_scheme_data() == Some("text/plain,Hello"));
 assert!(data_url.non_relative_scheme_data() == Some("text/plain,Hello"));
 assert!(data_url.query == None);
 assert!(data_url.query == None);
 assert!(data_url.fragment == Some("".to_string()));
 assert!(data_url.fragment == Some("".to_string()));
@@ -98,9 +109,9 @@ Many contexts allow URL *references* that can be relative to a *base URL*:
 Since parsed URL are absolute, giving a base is required:
 Since parsed URL are absolute, giving a base is required:
 
 
 ```
 ```
-use url::{Url, RelativeUrlWithoutBase};
+use url::{Url, ParseError};
 
 
-assert!(Url::parse("../main.css") == Err(RelativeUrlWithoutBase))
+assert!(Url::parse("../main.css") == Err(ParseError::RelativeUrlWithoutBase))
 ```
 ```
 
 
 `UrlParser` is a method-chaining API to provide various optional parameters
 `UrlParser` is a method-chaining API to provide various optional parameters
@@ -119,24 +130,14 @@ assert!(css_url.serialize() == "http://servo.github.io/rust-url/main.css".to_str
 
 
 #![feature(macro_rules, default_type_params)]
 #![feature(macro_rules, default_type_params)]
 
 
-extern crate encoding;
 extern crate serialize;
 extern crate serialize;
 
 
-use std::fmt::{Formatter, FormatError, Show};
+use std::fmt::{mod, Formatter, Show};
 use std::hash;
 use std::hash;
 use std::path;
 use std::path;
 
 
-use encoding::EncodingRef;
-
-pub use host::{Host, Domain, Ipv6, Ipv6Address};
-pub use parser::{
-    ErrorHandler, ParseResult, ParseError,
-    EmptyHost, InvalidScheme, InvalidPort, InvalidIpv6Address, InvalidDomainCharacter,
-    InvalidCharacter, InvalidBackslash, InvalidPercentEncoded, InvalidAtSymbolInUser,
-    ExpectedTwoSlashes, NonUrlCodePoint, RelativeUrlWithScheme, RelativeUrlWithoutBase,
-    RelativeUrlWithNonRelativeBase, NonAsciiDomainsNotSupportedYet,
-    CannotSetFileScheme, CannotSetJavascriptScheme, CannotSetNonRelativeScheme,
-};
+pub use host::{Host, Ipv6Address};
+pub use parser::{ErrorHandler, ParseResult, ParseError};
 
 
 #[deprecated = "Moved to the `percent_encoding` module"]
 #[deprecated = "Moved to the `percent_encoding` module"]
 pub use percent_encoding::{
 pub use percent_encoding::{
@@ -147,7 +148,9 @@ pub use percent_encoding::{
 };
 };
 
 
 use format::{PathFormatter, UserInfoFormatter, UrlNoFragmentFormatter};
 use format::{PathFormatter, UserInfoFormatter, UrlNoFragmentFormatter};
+use encoding::EncodingOverride;
 
 
+mod encoding;
 mod host;
 mod host;
 mod parser;
 mod parser;
 mod urlutils;
 mod urlutils;
@@ -195,19 +198,19 @@ pub struct Url {
 #[deriving(PartialEq, Eq, Clone)]
 #[deriving(PartialEq, Eq, Clone)]
 pub enum SchemeData {
 pub enum SchemeData {
     /// Components for URLs in a *relative* scheme such as HTTP.
     /// Components for URLs in a *relative* scheme such as HTTP.
-    RelativeSchemeData(UrlRelativeSchemeData),
+    Relative(RelativeSchemeData),
 
 
     /// No further structure is assumed for *non-relative* schemes such as `data` and `mailto`.
     /// No further structure is assumed for *non-relative* schemes such as `data` and `mailto`.
     ///
     ///
     /// This is a single percent-encoded string, whose interpretation depends on the scheme.
     /// This is a single percent-encoded string, whose interpretation depends on the scheme.
     ///
     ///
     /// Percent encoded strings are within the ASCII range.
     /// Percent encoded strings are within the ASCII range.
-    NonRelativeSchemeData(String),
+    NonRelative(String),
 }
 }
 
 
 /// Components for URLs in a *relative* scheme such as HTTP.
 /// Components for URLs in a *relative* scheme such as HTTP.
 #[deriving(PartialEq, Eq, Clone)]
 #[deriving(PartialEq, Eq, Clone)]
-pub struct UrlRelativeSchemeData {
+pub struct RelativeSchemeData {
     /// The username of the URL, as a possibly empty, pecent-encoded string.
     /// The username of the URL, as a possibly empty, pecent-encoded string.
     ///
     ///
     /// Percent encoded strings are within the ASCII range.
     /// Percent encoded strings are within the ASCII range.
@@ -255,7 +258,7 @@ impl<S: hash::Writer> hash::Hash<S> for Url {
 /// A set of optional parameters for URL parsing.
 /// A set of optional parameters for URL parsing.
 pub struct UrlParser<'a> {
 pub struct UrlParser<'a> {
     base_url: Option<&'a Url>,
     base_url: Option<&'a Url>,
-    query_encoding_override: Option<EncodingRef>,
+    query_encoding_override: EncodingOverride,
     error_handler: ErrorHandler,
     error_handler: ErrorHandler,
     scheme_type_mapper: fn(scheme: &str) -> SchemeType,
     scheme_type_mapper: fn(scheme: &str) -> SchemeType,
 }
 }
@@ -269,7 +272,7 @@ impl<'a> UrlParser<'a> {
         fn silent_handler(_reason: ParseError) -> ParseResult<()> { Ok(()) }
         fn silent_handler(_reason: ParseError) -> ParseResult<()> { Ok(()) }
         UrlParser {
         UrlParser {
             base_url: None,
             base_url: None,
-            query_encoding_override: None,
+            query_encoding_override: EncodingOverride::utf8(),
             error_handler: silent_handler,
             error_handler: silent_handler,
             scheme_type_mapper: whatwg_scheme_type_mapper,
             scheme_type_mapper: whatwg_scheme_type_mapper,
         }
         }
@@ -287,9 +290,13 @@ impl<'a> UrlParser<'a> {
     /// and return the `UrlParser`.
     /// and return the `UrlParser`.
     ///
     ///
     /// This legacy quirk is only relevant to HTML.
     /// This legacy quirk is only relevant to HTML.
+    ///
+    /// This method is only available if the `query_encoding` Cargo feature is enabled.
+    #[cfg(feature = "query_encoding")]
     #[inline]
     #[inline]
-    pub fn query_encoding_override<'b>(&'b mut self, value: EncodingRef) -> &'b mut UrlParser<'a> {
-        self.query_encoding_override = Some(value);
+    pub fn query_encoding_override<'b>(&'b mut self, value: encoding::EncodingRef)
+                                       -> &'b mut UrlParser<'a> {
+        self.query_encoding_override = EncodingOverride::from_encoding(value);
         self
         self
     }
     }
 
 
@@ -320,14 +327,14 @@ impl<'a> UrlParser<'a> {
     /// ```ignore
     /// ```ignore
     /// fn whatwg_scheme_type_mapper(scheme: &str) -> SchemeType {
     /// fn whatwg_scheme_type_mapper(scheme: &str) -> SchemeType {
     ///     match scheme {
     ///     match scheme {
-    ///         "file" => FileLikeRelativeScheme,
-    ///         "ftp" => RelativeScheme(21),
-    ///         "gopher" => RelativeScheme(70),
-    ///         "http" => RelativeScheme(80),
-    ///         "https" => RelativeScheme(443),
-    ///         "ws" => RelativeScheme(80),
-    ///         "wss" => RelativeScheme(443),
-    ///         _ => NonRelativeScheme,
+    ///         "file" => SchemeType::FileLike,
+    ///         "ftp" => SchemeType::Relative(21),
+    ///         "gopher" => SchemeType::Relative(70),
+    ///         "http" => SchemeType::Relative(80),
+    ///         "https" => SchemeType::Relative(443),
+    ///         "ws" => SchemeType::Relative(80),
+    ///         "wss" => SchemeType::Relative(443),
+    ///         _ => NonRelative,
     ///     }
     ///     }
     /// }
     /// }
     /// ```
     /// ```
@@ -413,7 +420,7 @@ pub enum SchemeType {
     /// (everything other than the scheme, query string, and fragment identifier)
     /// (everything other than the scheme, query string, and fragment identifier)
     /// is parsed as a single percent-encoded string of which no structure is assumed.
     /// is parsed as a single percent-encoded string of which no structure is assumed.
     /// That string may need to be parsed further, per a scheme-specific format.
     /// That string may need to be parsed further, per a scheme-specific format.
-    NonRelativeScheme,
+    NonRelative,
 
 
     /// Indicate that the scheme is *relative*, and what the default port number is.
     /// Indicate that the scheme is *relative*, and what the default port number is.
     ///
     ///
@@ -422,22 +429,22 @@ pub enum SchemeType {
     /// Relative URL references are supported, if a base URL was given.
     /// Relative URL references are supported, if a base URL was given.
     /// The string value indicates the default port number as a string of ASCII digits,
     /// The string value indicates the default port number as a string of ASCII digits,
     /// or the empty string to indicate no default port number.
     /// or the empty string to indicate no default port number.
-    RelativeScheme(u16),
+    Relative(u16),
 
 
     /// Indicate a *relative* scheme similar to the *file* scheme.
     /// Indicate a *relative* scheme similar to the *file* scheme.
     ///
     ///
     /// For example, you might want to have distinct `git+file` and `hg+file` URL schemes.
     /// For example, you might want to have distinct `git+file` and `hg+file` URL schemes.
     ///
     ///
-    /// This is like `RelativeScheme` except the host can be empty, there is no port number,
+    /// This is like `Relative` except the host can be empty, there is no port number,
     /// and path parsing has (platform-independent) quirks to support Windows filenames.
     /// and path parsing has (platform-independent) quirks to support Windows filenames.
-    FileLikeRelativeScheme,
+    FileLike,
 }
 }
 
 
 
 
 impl SchemeType {
 impl SchemeType {
     pub fn default_port(&self) -> Option<u16> {
     pub fn default_port(&self) -> Option<u16> {
         match self {
         match self {
-            &RelativeScheme(default_port) => Some(default_port),
+            &SchemeType::Relative(default_port) => Some(default_port),
             _ => None,
             _ => None,
         }
         }
     }
     }
@@ -446,14 +453,14 @@ impl SchemeType {
 /// http://url.spec.whatwg.org/#relative-scheme
 /// http://url.spec.whatwg.org/#relative-scheme
 pub fn whatwg_scheme_type_mapper(scheme: &str) -> SchemeType {
 pub fn whatwg_scheme_type_mapper(scheme: &str) -> SchemeType {
     match scheme {
     match scheme {
-        "file" => FileLikeRelativeScheme,
-        "ftp" => RelativeScheme(21),
-        "gopher" => RelativeScheme(70),
-        "http" => RelativeScheme(80),
-        "https" => RelativeScheme(443),
-        "ws" => RelativeScheme(80),
-        "wss" => RelativeScheme(443),
-        _ => NonRelativeScheme,
+        "file" => SchemeType::FileLike,
+        "ftp" => SchemeType::Relative(21),
+        "gopher" => SchemeType::Relative(70),
+        "http" => SchemeType::Relative(80),
+        "https" => SchemeType::Relative(443),
+        "ws" => SchemeType::Relative(80),
+        "wss" => SchemeType::Relative(443),
+        _ => SchemeType::NonRelative,
     }
     }
 }
 }
 
 
@@ -504,12 +511,12 @@ impl Url {
     fn from_path_common(path: Vec<String>) -> Url {
     fn from_path_common(path: Vec<String>) -> Url {
         Url {
         Url {
             scheme: "file".to_string(),
             scheme: "file".to_string(),
-            scheme_data: RelativeSchemeData(UrlRelativeSchemeData {
+            scheme_data: SchemeData::Relative(RelativeSchemeData {
                 username: "".to_string(),
                 username: "".to_string(),
                 password: None,
                 password: None,
                 port: None,
                 port: None,
                 default_port: None,
                 default_port: None,
-                host: Domain("".to_string()),
+                host: Host::Domain("".to_string()),
                 path: path,
                 path: path,
             }),
             }),
             query: None,
             query: None,
@@ -540,8 +547,8 @@ impl Url {
     #[inline]
     #[inline]
     pub fn to_file_path<T: FromUrlPath>(&self) -> Result<T, ()> {
     pub fn to_file_path<T: FromUrlPath>(&self) -> Result<T, ()> {
         match self.scheme_data {
         match self.scheme_data {
-            RelativeSchemeData(ref scheme_data) => scheme_data.to_file_path(),
-            NonRelativeSchemeData(..) => Err(()),
+            SchemeData::Relative(ref scheme_data) => scheme_data.to_file_path(),
+            SchemeData::NonRelative(..) => Err(()),
         }
         }
     }
     }
 
 
@@ -559,8 +566,8 @@ impl Url {
     #[inline]
     #[inline]
     pub fn non_relative_scheme_data<'a>(&'a self) -> Option<&'a str> {
     pub fn non_relative_scheme_data<'a>(&'a self) -> Option<&'a str> {
         match self.scheme_data {
         match self.scheme_data {
-            RelativeSchemeData(..) => None,
-            NonRelativeSchemeData(ref scheme_data) => Some(scheme_data.as_slice()),
+            SchemeData::Relative(..) => None,
+            SchemeData::NonRelative(ref scheme_data) => Some(scheme_data.as_slice()),
         }
         }
     }
     }
 
 
@@ -568,27 +575,27 @@ impl Url {
     #[inline]
     #[inline]
     pub fn non_relative_scheme_data_mut<'a>(&'a mut self) -> Option<&'a mut String> {
     pub fn non_relative_scheme_data_mut<'a>(&'a mut self) -> Option<&'a mut String> {
         match self.scheme_data {
         match self.scheme_data {
-            RelativeSchemeData(..) => None,
-            NonRelativeSchemeData(ref mut scheme_data) => Some(scheme_data),
+            SchemeData::Relative(..) => None,
+            SchemeData::NonRelative(ref mut scheme_data) => Some(scheme_data),
         }
         }
     }
     }
 
 
     /// If the URL is in a *relative scheme*, return the structured scheme data.
     /// If the URL is in a *relative scheme*, return the structured scheme data.
     #[inline]
     #[inline]
-    pub fn relative_scheme_data<'a>(&'a self) -> Option<&'a UrlRelativeSchemeData> {
+    pub fn relative_scheme_data<'a>(&'a self) -> Option<&'a RelativeSchemeData> {
         match self.scheme_data {
         match self.scheme_data {
-            RelativeSchemeData(ref scheme_data) => Some(scheme_data),
-            NonRelativeSchemeData(..) => None,
+            SchemeData::Relative(ref scheme_data) => Some(scheme_data),
+            SchemeData::NonRelative(..) => None,
         }
         }
     }
     }
 
 
     /// If the URL is in a *relative scheme*,
     /// If the URL is in a *relative scheme*,
     /// return a mutable reference to the structured scheme data.
     /// return a mutable reference to the structured scheme data.
     #[inline]
     #[inline]
-    pub fn relative_scheme_data_mut<'a>(&'a mut self) -> Option<&'a mut UrlRelativeSchemeData> {
+    pub fn relative_scheme_data_mut<'a>(&'a mut self) -> Option<&'a mut RelativeSchemeData> {
         match self.scheme_data {
         match self.scheme_data {
-            RelativeSchemeData(ref mut scheme_data) => Some(scheme_data),
-            NonRelativeSchemeData(..) => None,
+            SchemeData::Relative(ref mut scheme_data) => Some(scheme_data),
+            SchemeData::NonRelative(..) => None,
         }
         }
     }
     }
 
 
@@ -722,14 +729,14 @@ impl Url {
     /// and return a vector of (key, value) pairs.
     /// and return a vector of (key, value) pairs.
     #[inline]
     #[inline]
     pub fn query_pairs(&self) -> Option<Vec<(String, String)>> {
     pub fn query_pairs(&self) -> Option<Vec<(String, String)>> {
-        self.query.as_ref().map(|query| form_urlencoded::parse_str(query.as_slice()))
+        self.query.as_ref().map(|query| form_urlencoded::parse(query.as_bytes()))
     }
     }
 
 
     /// Serialize an iterator of (key, value) pairs as `application/x-www-form-urlencoded`
     /// Serialize an iterator of (key, value) pairs as `application/x-www-form-urlencoded`
     /// and set it as the URL’s query string.
     /// and set it as the URL’s query string.
     #[inline]
     #[inline]
     pub fn set_query_from_pairs<'a, I: Iterator<(&'a str, &'a str)>>(&mut self, pairs: I) {
     pub fn set_query_from_pairs<'a, I: Iterator<(&'a str, &'a str)>>(&mut self, pairs: I) {
-        self.query = Some(form_urlencoded::serialize(pairs, None));
+        self.query = Some(form_urlencoded::serialize(pairs));
     }
     }
 
 
     /// Percent-decode the URL’s query string, if any.
     /// Percent-decode the URL’s query string, if any.
@@ -769,7 +776,7 @@ impl<E, D: serialize::Decoder<E>> serialize::Decodable<D, E> for Url {
 
 
 
 
 impl Show for Url {
 impl Show for Url {
-    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
+    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
         try!(UrlNoFragmentFormatter{ url: self }.fmt(formatter));
         try!(UrlNoFragmentFormatter{ url: self }.fmt(formatter));
         match self.fragment {
         match self.fragment {
             None => (),
             None => (),
@@ -784,16 +791,16 @@ impl Show for Url {
 
 
 
 
 impl Show for SchemeData {
 impl Show for SchemeData {
-    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
+    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
         match *self {
         match *self {
-            RelativeSchemeData(ref scheme_data) => scheme_data.fmt(formatter),
-            NonRelativeSchemeData(ref scheme_data) => scheme_data.fmt(formatter),
+            SchemeData::Relative(ref scheme_data) => scheme_data.fmt(formatter),
+            SchemeData::NonRelative(ref scheme_data) => scheme_data.fmt(formatter),
         }
         }
     }
     }
 }
 }
 
 
 
 
-impl UrlRelativeSchemeData {
+impl RelativeSchemeData {
     /// Percent-decode the URL’s username.
     /// Percent-decode the URL’s username.
     ///
     ///
     /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
     /// This is “lossy”: invalid UTF-8 percent-encoded byte sequences
@@ -845,7 +852,7 @@ impl UrlRelativeSchemeData {
     #[inline]
     #[inline]
     pub fn domain<'a>(&'a self) -> Option<&'a str> {
     pub fn domain<'a>(&'a self) -> Option<&'a str> {
         match self.host {
         match self.host {
-            Domain(ref domain) => Some(domain.as_slice()),
+            Host::Domain(ref domain) => Some(domain.as_slice()),
             _ => None,
             _ => None,
         }
         }
     }
     }
@@ -854,7 +861,7 @@ impl UrlRelativeSchemeData {
     #[inline]
     #[inline]
     pub fn domain_mut<'a>(&'a mut self) -> Option<&'a mut String> {
     pub fn domain_mut<'a>(&'a mut self) -> Option<&'a mut String> {
         match self.host {
         match self.host {
-            Domain(ref mut domain) => Some(domain),
+            Host::Domain(ref mut domain) => Some(domain),
             _ => None,
             _ => None,
         }
         }
     }
     }
@@ -888,8 +895,8 @@ impl UrlRelativeSchemeData {
 }
 }
 
 
 
 
-impl Show for UrlRelativeSchemeData {
-    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
+impl Show for RelativeSchemeData {
+    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
         // Write the scheme-trailing double slashes.
         // Write the scheme-trailing double slashes.
         try!(formatter.write(b"//"));
         try!(formatter.write(b"//"));
 
 

+ 125 - 130
src/parser.rs

@@ -6,18 +6,12 @@
 // option. This file may not be copied, modified, or distributed
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 // except according to those terms.
 
 
-
 use std::ascii::AsciiExt;
 use std::ascii::AsciiExt;
-use std::fmt::{Formatter, FormatError, Show};
+use std::error::Error;
+use std::fmt::{mod, Formatter, Show};
 use std::str::CharRange;
 use std::str::CharRange;
 
 
-use encoding;
-
-use super::{
-    UrlParser, Url, RelativeSchemeData, NonRelativeSchemeData, Host, Domain,
-    SchemeType, FileLikeRelativeScheme, RelativeScheme, NonRelativeScheme,
-    UrlRelativeSchemeData,
-};
+use super::{UrlParser, Url, SchemeData, RelativeSchemeData, Host, SchemeType};
 use percent_encoding::{
 use percent_encoding::{
     utf8_percent_encode_to, percent_encode,
     utf8_percent_encode_to, percent_encode,
     SIMPLE_ENCODE_SET, DEFAULT_ENCODE_SET, USERINFO_ENCODE_SET, QUERY_ENCODE_SET
     SIMPLE_ENCODE_SET, DEFAULT_ENCODE_SET, USERINFO_ENCODE_SET, QUERY_ENCODE_SET
@@ -33,56 +27,59 @@ macro_rules! is_match(
 
 
 pub type ParseResult<T> = Result<T, ParseError>;
 pub type ParseResult<T> = Result<T, ParseError>;
 
 
-/// Errors that can occur during parsing.
-#[deriving(PartialEq, Eq, Clone)]
-pub enum ParseError {
-    EmptyHost,
-    InvalidScheme,
-    InvalidPort,
-    InvalidIpv6Address,
-    InvalidDomainCharacter,
-    InvalidCharacter,
-    InvalidBackslash,
-    InvalidPercentEncoded,
-    InvalidAtSymbolInUser,
-    ExpectedTwoSlashes,
-    ExpectedInitialSlash,
-    NonUrlCodePoint,
-    RelativeUrlWithScheme,
-    RelativeUrlWithoutBase,
-    RelativeUrlWithNonRelativeBase,
-    NonAsciiDomainsNotSupportedYet,
-    CannotSetFileScheme(&'static str),
-    CannotSetJavascriptScheme(&'static str),
-    CannotSetNonRelativeScheme(&'static str)
+
+macro_rules! simple_enum_error {
+    ($($name: ident => $description: expr,)+) => {
+        /// Errors that can occur during parsing.
+        #[deriving(PartialEq, Eq, Clone)]
+        pub enum ParseError {
+            $(
+                $name,
+            )+
+        }
+
+        impl Error for ParseError {
+            fn description(&self) -> &str {
+                match *self {
+                    $(
+                        ParseError::$name => $description,
+                    )+
+                }
+            }
+        }
+    }
+}
+
+simple_enum_error! {
+    EmptyHost => "empty host",
+    InvalidScheme => "invalid scheme",
+    InvalidPort => "invalid port number",
+    InvalidIpv6Address => "invalid IPv6 address",
+    InvalidDomainCharacter => "invalid domain character",
+    InvalidCharacter => "invalid character",
+    InvalidBackslash => "invalid backslash",
+    InvalidPercentEncoded => "invalid percent-encoded sequence",
+    InvalidAtSymbolInUser => "invalid @-symbol in user",
+    ExpectedTwoSlashes => "expected two slashes (//)",
+    ExpectedInitialSlash => "expected the input to start with a slash",
+    NonUrlCodePoint => "non URL code point",
+    RelativeUrlWithScheme => "relative URL with scheme",
+    RelativeUrlWithoutBase => "relative URL without a base",
+    RelativeUrlWithNonRelativeBase => "relative URL with a non-relative base",
+    NonAsciiDomainsNotSupportedYet => "non-ASCII domains are not supported yet",
+    CannotSetJavascriptFragment => "cannot set fragment on javascript: URL",
+    CannotSetPortWithFileLikeScheme => "cannot set port with file-like scheme",
+    CannotSetUsernameWithNonRelativeScheme => "cannot set username with non-relative scheme",
+    CannotSetPasswordWithNonRelativeScheme => "cannot set password with non-relative scheme",
+    CannotSetHostPortWithNonRelativeScheme => "cannot set host and port with non-relative scheme",
+    CannotSetHostWithNonRelativeScheme => "cannot set host with non-relative scheme",
+    CannotSetPortWithNonRelativeScheme => "cannot set port with non-relative scheme",
+    CannotSetPathWithNonRelativeScheme => "cannot set path with non-relative scheme",
 }
 }
 
 
 impl Show for ParseError {
 impl Show for ParseError {
-    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FormatError> {
-        match *self {
-            EmptyHost => "Empty host",
-            InvalidScheme => "Invalid scheme",
-            InvalidPort => "Invalid port number",
-            InvalidIpv6Address => "Invalid IPv6 address",
-            InvalidDomainCharacter => "Invalid domain character",
-            InvalidCharacter => "Invalid character",
-            InvalidBackslash => "Invalid backslash",
-            InvalidPercentEncoded => "Invalid percent-encoded sequence",
-            InvalidAtSymbolInUser => "Invalid @-symbol in user",
-            ExpectedTwoSlashes => "Expected two slashes (//)",
-            ExpectedInitialSlash => "Expected the input to start with a slash",
-            NonUrlCodePoint => "Non URL code point",
-            RelativeUrlWithScheme => "Relative URL with scheme",
-            RelativeUrlWithoutBase => "Relative URL without a base",
-            RelativeUrlWithNonRelativeBase => "Relative URL with a non-relative base",
-            NonAsciiDomainsNotSupportedYet => "Non Ascii domains are not support yet",
-            CannotSetFileScheme(ref part) =>
-                return write!(fmt, "Cannot set {} on file: URLs", part),
-            CannotSetJavascriptScheme(ref part) =>
-                return write!(fmt, "Cannot set {} on javascript: URLs", part),
-            CannotSetNonRelativeScheme(ref part) =>
-                return write!(fmt, "Cannot set {} on non-relative URLs", part),
-        }.fmt(fmt)
+    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
+        self.description().fmt(fmt)
     }
     }
 }
 }
 
 
@@ -97,59 +94,59 @@ pub type ErrorHandler = fn(reason: ParseError) -> ParseResult<()>;
 
 
 #[deriving(PartialEq, Eq)]
 #[deriving(PartialEq, Eq)]
 pub enum Context {
 pub enum Context {
-    UrlParserContext,
-    SetterContext,
+    UrlParser,
+    Setter,
 }
 }
 
 
 
 
 pub fn parse_url(input: &str, parser: &UrlParser) -> ParseResult<Url> {
 pub fn parse_url(input: &str, parser: &UrlParser) -> ParseResult<Url> {
     let input = input.trim_chars([' ', '\t', '\n', '\r', '\x0C'].as_slice());
     let input = input.trim_chars([' ', '\t', '\n', '\r', '\x0C'].as_slice());
-    let (scheme, remaining) = match parse_scheme(input, UrlParserContext) {
+    let (scheme, remaining) = match parse_scheme(input, Context::UrlParser) {
         Some((scheme, remaining)) => (scheme, remaining),
         Some((scheme, remaining)) => (scheme, remaining),
         // No-scheme state
         // No-scheme state
         None => return match parser.base_url {
         None => return match parser.base_url {
-            Some(&Url { ref scheme, scheme_data: RelativeSchemeData(ref base),
+            Some(&Url { ref scheme, scheme_data: SchemeData::Relative(ref base),
                         ref query, .. }) => {
                         ref query, .. }) => {
                 let scheme_type = parser.get_scheme_type(scheme.as_slice());
                 let scheme_type = parser.get_scheme_type(scheme.as_slice());
                 parse_relative_url(input, scheme.clone(), scheme_type, base, query, parser)
                 parse_relative_url(input, scheme.clone(), scheme_type, base, query, parser)
             },
             },
-            Some(_) => Err(RelativeUrlWithNonRelativeBase),
-            None => Err(RelativeUrlWithoutBase),
+            Some(_) => Err(ParseError::RelativeUrlWithNonRelativeBase),
+            None => Err(ParseError::RelativeUrlWithoutBase),
         },
         },
     };
     };
     let scheme_type = parser.get_scheme_type(scheme.as_slice());
     let scheme_type = parser.get_scheme_type(scheme.as_slice());
     match scheme_type {
     match scheme_type {
-        FileLikeRelativeScheme => {
+        SchemeType::FileLike => {
             // Relative state?
             // Relative state?
             match parser.base_url {
             match parser.base_url {
-                Some(&Url { scheme: ref base_scheme, scheme_data: RelativeSchemeData(ref base),
+                Some(&Url { scheme: ref base_scheme, scheme_data: SchemeData::Relative(ref base),
                             ref query, .. })
                             ref query, .. })
                 if scheme == *base_scheme => {
                 if scheme == *base_scheme => {
                     parse_relative_url(remaining, scheme, scheme_type, base, query, parser)
                     parse_relative_url(remaining, scheme, scheme_type, base, query, parser)
                 },
                 },
                 // FIXME: Should not have to use a made-up base URL.
                 // FIXME: Should not have to use a made-up base URL.
-                _ => parse_relative_url(remaining, scheme, scheme_type, &UrlRelativeSchemeData {
-                    username: String::new(), password: None, host: Domain(String::new()),
+                _ => parse_relative_url(remaining, scheme, scheme_type, &RelativeSchemeData {
+                    username: String::new(), password: None, host: Host::Domain(String::new()),
                     port: None, default_port: None, path: Vec::new()
                     port: None, default_port: None, path: Vec::new()
                 }, &None, parser)
                 }, &None, parser)
             }
             }
         },
         },
-        RelativeScheme(..) => {
+        SchemeType::Relative(..) => {
             match parser.base_url {
             match parser.base_url {
-                Some(&Url { scheme: ref base_scheme, scheme_data: RelativeSchemeData(ref base),
+                Some(&Url { scheme: ref base_scheme, scheme_data: SchemeData::Relative(ref base),
                             ref query, .. })
                             ref query, .. })
                 if scheme == *base_scheme && !remaining.starts_with("//") => {
                 if scheme == *base_scheme && !remaining.starts_with("//") => {
-                    try!(parser.parse_error(RelativeUrlWithScheme));
+                    try!(parser.parse_error(ParseError::RelativeUrlWithScheme));
                     parse_relative_url(remaining, scheme, scheme_type, base, query, parser)
                     parse_relative_url(remaining, scheme, scheme_type, base, query, parser)
                 },
                 },
                 _ => parse_absolute_url(scheme, scheme_type, remaining, parser),
                 _ => parse_absolute_url(scheme, scheme_type, remaining, parser),
             }
             }
         },
         },
-        NonRelativeScheme => {
+        SchemeType::NonRelative => {
             // Scheme data state
             // Scheme data state
             let (scheme_data, remaining) = try!(parse_scheme_data(remaining, parser));
             let (scheme_data, remaining) = try!(parse_scheme_data(remaining, parser));
             let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
             let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
-            Ok(Url { scheme: scheme, scheme_data: NonRelativeSchemeData(scheme_data),
+            Ok(Url { scheme: scheme, scheme_data: SchemeData::NonRelative(scheme_data),
                      query: query, fragment: fragment })
                      query: query, fragment: fragment })
         }
         }
     }
     }
@@ -172,8 +169,8 @@ pub fn parse_scheme<'a>(input: &'a str, context: Context) -> Option<(String, &'a
     }
     }
     // EOF before ':'
     // EOF before ':'
     match context {
     match context {
-        SetterContext => Some((input.to_ascii_lower(), "")),
-        UrlParserContext => None
+        Context::Setter => Some((input.to_ascii_lower(), "")),
+        Context::UrlParser => None
     }
     }
 }
 }
 
 
@@ -187,8 +184,8 @@ fn parse_absolute_url<'a>(scheme: String, scheme_type: SchemeType,
     // Host state
     // Host state
     let (host, port, default_port, remaining) = try!(parse_host(remaining, scheme_type, parser));
     let (host, port, default_port, remaining) = try!(parse_host(remaining, scheme_type, parser));
     let (path, remaining) = try!(parse_path_start(
     let (path, remaining) = try!(parse_path_start(
-        remaining, UrlParserContext, scheme_type, parser));
-    let scheme_data = RelativeSchemeData(UrlRelativeSchemeData {
+        remaining, Context::UrlParser, scheme_type, parser));
+    let scheme_data = SchemeData::Relative(RelativeSchemeData {
         username: username, password: password,
         username: username, password: password,
         host: host, port: port, default_port: default_port,
         host: host, port: port, default_port: default_port,
         path: path });
         path: path });
@@ -198,19 +195,21 @@ fn parse_absolute_url<'a>(scheme: String, scheme_type: SchemeType,
 
 
 
 
 fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeType,
 fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeType,
-                          base: &UrlRelativeSchemeData, base_query: &Option<String>,
+                          base: &RelativeSchemeData, base_query: &Option<String>,
                           parser: &UrlParser)
                           parser: &UrlParser)
                           -> ParseResult<Url> {
                           -> ParseResult<Url> {
     if input.is_empty() {
     if input.is_empty() {
-        return Ok(Url { scheme: scheme, scheme_data: RelativeSchemeData(base.clone()),
+        return Ok(Url { scheme: scheme, scheme_data: SchemeData::Relative(base.clone()),
                         query: base_query.clone(), fragment: None })
                         query: base_query.clone(), fragment: None })
     }
     }
     match input.char_at(0) {
     match input.char_at(0) {
         '/' | '\\' => {
         '/' | '\\' => {
             // Relative slash state
             // Relative slash state
             if input.len() > 1 && is_match!(input.char_at(1), '/' | '\\') {
             if input.len() > 1 && is_match!(input.char_at(1), '/' | '\\') {
-                if input.char_at(1) == '\\' { try!(parser.parse_error(InvalidBackslash)) }
-                if scheme_type == FileLikeRelativeScheme {
+                if input.char_at(1) == '\\' {
+                    try!(parser.parse_error(ParseError::InvalidBackslash))
+                }
+                if scheme_type == SchemeType::FileLike {
                     // File host state
                     // File host state
                     let remaining = input.slice_from(2);
                     let remaining = input.slice_from(2);
                     let (host, remaining) = if remaining.len() >= 2
                     let (host, remaining) = if remaining.len() >= 2
@@ -221,13 +220,13 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
                                          '/' | '\\' | '?' | '#'))
                                          '/' | '\\' | '?' | '#'))
                     {
                     {
                         // Windows drive letter quirk
                         // Windows drive letter quirk
-                        (Domain(String::new()), remaining)
+                        (Host::Domain(String::new()), remaining)
                     } else {
                     } else {
                         try!(parse_file_host(remaining, parser))
                         try!(parse_file_host(remaining, parser))
                     };
                     };
                     let (path, remaining) = try!(parse_path_start(
                     let (path, remaining) = try!(parse_path_start(
-                        remaining, UrlParserContext, scheme_type, parser));
-                    let scheme_data = RelativeSchemeData(UrlRelativeSchemeData {
+                        remaining, Context::UrlParser, scheme_type, parser));
+                    let scheme_data = SchemeData::Relative(RelativeSchemeData {
                         username: String::new(), password: None,
                         username: String::new(), password: None,
                         host: host, port: None, default_port: None, path: path
                         host: host, port: None, default_port: None, path: path
                     });
                     });
@@ -240,14 +239,14 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
             } else {
             } else {
                 // Relative path state
                 // Relative path state
                 let (path, remaining) = try!(parse_path(
                 let (path, remaining) = try!(parse_path(
-                    [], input.slice_from(1), UrlParserContext, scheme_type, parser));
-                let scheme_data = RelativeSchemeData(if scheme_type == FileLikeRelativeScheme {
-                    UrlRelativeSchemeData {
+                    &[], input.slice_from(1), Context::UrlParser, scheme_type, parser));
+                let scheme_data = SchemeData::Relative(if scheme_type == SchemeType::FileLike {
+                    RelativeSchemeData {
                         username: String::new(), password: None, host:
                         username: String::new(), password: None, host:
-                        Domain(String::new()), port: None, default_port: None, path: path
+                        Host::Domain(String::new()), port: None, default_port: None, path: path
                     }
                     }
                 } else {
                 } else {
-                    UrlRelativeSchemeData {
+                    RelativeSchemeData {
                         username: base.username.clone(),
                         username: base.username.clone(),
                         password: base.password.clone(),
                         password: base.password.clone(),
                         host: base.host.clone(),
                         host: base.host.clone(),
@@ -264,16 +263,16 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
         },
         },
         '?' => {
         '?' => {
             let (query, fragment) = try!(parse_query_and_fragment(input, parser));
             let (query, fragment) = try!(parse_query_and_fragment(input, parser));
-            Ok(Url { scheme: scheme, scheme_data: RelativeSchemeData(base.clone()),
+            Ok(Url { scheme: scheme, scheme_data: SchemeData::Relative(base.clone()),
                      query: query, fragment: fragment })
                      query: query, fragment: fragment })
         },
         },
         '#' => {
         '#' => {
             let fragment = Some(try!(parse_fragment(input.slice_from(1), parser)));
             let fragment = Some(try!(parse_fragment(input.slice_from(1), parser)));
-            Ok(Url { scheme: scheme, scheme_data: RelativeSchemeData(base.clone()),
+            Ok(Url { scheme: scheme, scheme_data: SchemeData::Relative(base.clone()),
                      query: base_query.clone(), fragment: fragment })
                      query: base_query.clone(), fragment: fragment })
         }
         }
         _ => {
         _ => {
-            let (scheme_data, remaining) = if scheme_type == FileLikeRelativeScheme
+            let (scheme_data, remaining) = if scheme_type == SchemeType::FileLike
                && input.len() >= 2
                && input.len() >= 2
                && starts_with_ascii_alpha(input)
                && starts_with_ascii_alpha(input)
                && is_match!(input.char_at(1), ':' | '|')
                && is_match!(input.char_at(1), ':' | '|')
@@ -282,10 +281,10 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
             {
             {
                 // Windows drive letter quirk
                 // Windows drive letter quirk
                 let (path, remaining) = try!(parse_path(
                 let (path, remaining) = try!(parse_path(
-                    [], input, UrlParserContext, scheme_type, parser));
-                 (RelativeSchemeData(UrlRelativeSchemeData {
+                    &[], input, Context::UrlParser, scheme_type, parser));
+                 (SchemeData::Relative(RelativeSchemeData {
                     username: String::new(), password: None,
                     username: String::new(), password: None,
-                    host: Domain(String::new()),
+                    host: Host::Domain(String::new()),
                     port: None,
                     port: None,
                     default_port: None,
                     default_port: None,
                     path: path
                     path: path
@@ -294,8 +293,8 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
                 let base_path = base.path.slice_to(base.path.len() - 1);
                 let base_path = base.path.slice_to(base.path.len() - 1);
                 // Relative path state
                 // Relative path state
                 let (path, remaining) = try!(parse_path(
                 let (path, remaining) = try!(parse_path(
-                    base_path, input, UrlParserContext, scheme_type, parser));
-                (RelativeSchemeData(UrlRelativeSchemeData {
+                    base_path, input, Context::UrlParser, scheme_type, parser));
+                (SchemeData::Relative(RelativeSchemeData {
                     username: base.username.clone(),
                     username: base.username.clone(),
                     password: base.password.clone(),
                     password: base.password.clone(),
                     host: base.host.clone(),
                     host: base.host.clone(),
@@ -315,7 +314,7 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
 fn skip_slashes<'a>(input: &'a str, parser: &UrlParser) -> ParseResult<&'a str> {
 fn skip_slashes<'a>(input: &'a str, parser: &UrlParser) -> ParseResult<&'a str> {
     let first_non_slash = input.find(|c| !is_match!(c, '/' | '\\')).unwrap_or(input.len());
     let first_non_slash = input.find(|c| !is_match!(c, '/' | '\\')).unwrap_or(input.len());
     if input.slice_to(first_non_slash) != "//" {
     if input.slice_to(first_non_slash) != "//" {
-        try!(parser.parse_error(ExpectedTwoSlashes));
+        try!(parser.parse_error(ParseError::ExpectedTwoSlashes));
     }
     }
     Ok(input.slice_from(first_non_slash))
     Ok(input.slice_from(first_non_slash))
 }
 }
@@ -327,7 +326,9 @@ fn parse_userinfo<'a>(input: &'a str, parser: &UrlParser)
     for (i, c) in input.char_indices() {
     for (i, c) in input.char_indices() {
         match c {
         match c {
             '@' => {
             '@' => {
-                if last_at.is_some() { try!(parser.parse_error(InvalidAtSymbolInUser)) }
+                if last_at.is_some() {
+                    try!(parser.parse_error(ParseError::InvalidAtSymbolInUser))
+                }
                 last_at = Some(i)
                 last_at = Some(i)
             },
             },
             '/' | '\\' | '?' | '#' => break,
             '/' | '\\' | '?' | '#' => break,
@@ -347,7 +348,7 @@ fn parse_userinfo<'a>(input: &'a str, parser: &UrlParser)
                 password = Some(try!(parse_password(input.slice_from(i + 1), parser)));
                 password = Some(try!(parse_password(input.slice_from(i + 1), parser)));
                 break
                 break
             },
             },
-            '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
+            '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
             _ => {
             _ => {
                 try!(check_url_code_point(input, i, c, parser));
                 try!(check_url_code_point(input, i, c, parser));
                 // The spec says to use the default encode set,
                 // The spec says to use the default encode set,
@@ -365,7 +366,7 @@ fn parse_password(input: &str, parser: &UrlParser) -> ParseResult<String> {
     let mut password = String::new();
     let mut password = String::new();
     for (i, c, next_i) in input.char_ranges() {
     for (i, c, next_i) in input.char_ranges() {
         match c {
         match c {
-            '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
+            '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
             _ => {
             _ => {
                 try!(check_url_code_point(input, i, c, parser));
                 try!(check_url_code_point(input, i, c, parser));
                 // The spec says to use the default encode set,
                 // The spec says to use the default encode set,
@@ -406,7 +407,7 @@ pub fn parse_hostname<'a>(input: &'a str, parser: &UrlParser)
                 end = i;
                 end = i;
                 break
                 break
             },
             },
-            '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
+            '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
             c => {
             c => {
                 match c {
                 match c {
                     '[' => inside_square_brackets = true,
                     '[' => inside_square_brackets = true,
@@ -432,7 +433,7 @@ pub fn parse_port<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParse
             '0'...'9' => {
             '0'...'9' => {
                 port = port * 10 + (c as u32 - '0' as u32);
                 port = port * 10 + (c as u32 - '0' as u32);
                 if port > ::std::u16::MAX as u32 {
                 if port > ::std::u16::MAX as u32 {
-                    return Err(InvalidPort)
+                    return Err(ParseError::InvalidPort)
                 }
                 }
                 has_any_digit = true;
                 has_any_digit = true;
             },
             },
@@ -440,8 +441,8 @@ pub fn parse_port<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParse
                 end = i;
                 end = i;
                 break
                 break
             },
             },
-            '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
-            _ => return Err(InvalidPort)
+            '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
+            _ => return Err(ParseError::InvalidPort)
         }
         }
     }
     }
     let default_port = scheme_type.default_port();
     let default_port = scheme_type.default_port();
@@ -462,12 +463,12 @@ fn parse_file_host<'a>(input: &'a str, parser: &UrlParser) -> ParseResult<(Host,
                 end = i;
                 end = i;
                 break
                 break
             },
             },
-            '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
+            '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
             _ => host_input.push(c)
             _ => host_input.push(c)
         }
         }
     }
     }
     let host = if host_input.is_empty() {
     let host = if host_input.is_empty() {
-        Domain(String::new())
+        Host::Domain(String::new())
     } else {
     } else {
         try!(Host::parse(host_input.as_slice()))
         try!(Host::parse(host_input.as_slice()))
     };
     };
@@ -479,13 +480,13 @@ pub fn parse_standalone_path(input: &str, parser: &UrlParser)
                              -> ParseResult<(Vec<String>, Option<String>, Option<String>)> {
                              -> ParseResult<(Vec<String>, Option<String>, Option<String>)> {
     if !input.starts_with("/") {
     if !input.starts_with("/") {
         if input.starts_with("\\") {
         if input.starts_with("\\") {
-            try!(parser.parse_error(InvalidBackslash));
+            try!(parser.parse_error(ParseError::InvalidBackslash));
         } else {
         } else {
-            return Err(ExpectedInitialSlash)
+            return Err(ParseError::ExpectedInitialSlash)
         }
         }
     }
     }
     let (path, remaining) = try!(parse_path(
     let (path, remaining) = try!(parse_path(
-        [], input.slice_from(1), UrlParserContext, RelativeScheme(0), parser));
+        &[], input.slice_from(1), Context::UrlParser, SchemeType::Relative(0), parser));
     let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
     let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
     Ok((path, query, fragment))
     Ok((path, query, fragment))
 }
 }
@@ -500,13 +501,13 @@ pub fn parse_path_start<'a>(input: &'a str, context: Context, scheme_type: Schem
         match input.char_at(0) {
         match input.char_at(0) {
             '/' => i = 1,
             '/' => i = 1,
             '\\' => {
             '\\' => {
-                try!(parser.parse_error(InvalidBackslash));
+                try!(parser.parse_error(ParseError::InvalidBackslash));
                 i = 1;
                 i = 1;
             },
             },
             _ => ()
             _ => ()
         }
         }
     }
     }
-    parse_path([], input.slice_from(i), context, scheme_type, parser)
+    parse_path(&[], input.slice_from(i), context, scheme_type, parser)
 }
 }
 
 
 
 
@@ -529,16 +530,16 @@ fn parse_path<'a>(base_path: &[String], input: &'a str, context: Context,
                     break
                     break
                 },
                 },
                 '\\' => {
                 '\\' => {
-                    try!(parser.parse_error(InvalidBackslash));
+                    try!(parser.parse_error(ParseError::InvalidBackslash));
                     ends_with_slash = true;
                     ends_with_slash = true;
                     end = i;
                     end = i;
                     break
                     break
                 },
                 },
-                '?' | '#' if context == UrlParserContext => {
+                '?' | '#' if context == Context::UrlParser => {
                     end = i;
                     end = i;
                     break
                     break
                 },
                 },
-                '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
+                '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
                 _ => {
                 _ => {
                     try!(check_url_code_point(input, i, c, parser));
                     try!(check_url_code_point(input, i, c, parser));
                     utf8_percent_encode_to(input.slice(i, next_i),
                     utf8_percent_encode_to(input.slice(i, next_i),
@@ -560,7 +561,7 @@ fn parse_path<'a>(base_path: &[String], input: &'a str, context: Context,
                 }
                 }
             },
             },
             _ => {
             _ => {
-                if scheme_type == FileLikeRelativeScheme
+                if scheme_type == SchemeType::FileLike
                    && path.is_empty()
                    && path.is_empty()
                    && path_part.len() == 2
                    && path_part.len() == 2
                    && starts_with_ascii_alpha(path_part.as_slice())
                    && starts_with_ascii_alpha(path_part.as_slice())
@@ -591,7 +592,7 @@ fn parse_scheme_data<'a>(input: &'a str, parser: &UrlParser)
                 end = i;
                 end = i;
                 break
                 break
             },
             },
-            '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
+            '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
             _ => {
             _ => {
                 try!(check_url_code_point(input, i, c, parser));
                 try!(check_url_code_point(input, i, c, parser));
                 utf8_percent_encode_to(input.slice(i, next_i),
                 utf8_percent_encode_to(input.slice(i, next_i),
@@ -612,7 +613,7 @@ fn parse_query_and_fragment(input: &str, parser: &UrlParser)
         '#' => Ok((None, Some(try!(parse_fragment(input.slice_from(1), parser))))),
         '#' => Ok((None, Some(try!(parse_fragment(input.slice_from(1), parser))))),
         '?' => {
         '?' => {
             let (query, remaining) = try!(parse_query(
             let (query, remaining) = try!(parse_query(
-                input.slice_from(1), UrlParserContext, parser));
+                input.slice_from(1), Context::UrlParser, parser));
             let fragment = match remaining {
             let fragment = match remaining {
                 Some(remaining) => Some(try!(parse_fragment(remaining, parser))),
                 Some(remaining) => Some(try!(parse_fragment(remaining, parser))),
                 None => None
                 None => None
@@ -631,26 +632,20 @@ pub fn parse_query<'a>(input: &'a str, context: Context, parser: &UrlParser)
     let mut remaining = None;
     let mut remaining = None;
     for (i, c) in input.char_indices() {
     for (i, c) in input.char_indices() {
         match c {
         match c {
-            '#' if context == UrlParserContext => {
+            '#' if context == Context::UrlParser => {
                 remaining = Some(input.slice_from(i + 1));
                 remaining = Some(input.slice_from(i + 1));
                 break
                 break
             },
             },
-            '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
+            '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
             _ => {
             _ => {
                 try!(check_url_code_point(input, i, c, parser));
                 try!(check_url_code_point(input, i, c, parser));
                 query.push(c);
                 query.push(c);
             }
             }
         }
         }
     }
     }
-    let encoded;
-    let query_bytes = match parser.query_encoding_override {
-        Some(encoding) => {
-            encoded = encoding.encode(query.as_slice(), encoding::EncodeReplace).unwrap();
-            encoded.as_slice()
-        },
-        None => query.as_bytes()  // UTF-8
-    };
-    ;
+
+    let tmp = &mut vec![];
+    let query_bytes = parser.query_encoding_override.encode(query.as_slice(), tmp);
     Ok((percent_encode(query_bytes.as_slice(), QUERY_ENCODE_SET), remaining))
     Ok((percent_encode(query_bytes.as_slice(), QUERY_ENCODE_SET), remaining))
 }
 }
 
 
@@ -659,7 +654,7 @@ pub fn parse_fragment<'a>(input: &'a str, parser: &UrlParser) -> ParseResult<Str
     let mut fragment = String::new();
     let mut fragment = String::new();
     for (i, c, next_i) in input.char_ranges() {
     for (i, c, next_i) in input.char_ranges() {
         match c {
         match c {
-            '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
+            '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
             _ => {
             _ => {
                 try!(check_url_code_point(input, i, c, parser));
                 try!(check_url_code_point(input, i, c, parser));
                 utf8_percent_encode_to(input.slice(i, next_i),
                 utf8_percent_encode_to(input.slice(i, next_i),
@@ -760,10 +755,10 @@ fn check_url_code_point(input: &str, i: uint, c: char, parser: &UrlParser)
                         -> ParseResult<()> {
                         -> ParseResult<()> {
     if c == '%' {
     if c == '%' {
         if !starts_with_2_hex(input.slice_from(i + 1)) {
         if !starts_with_2_hex(input.slice_from(i + 1)) {
-            try!(parser.parse_error(InvalidPercentEncoded));
+            try!(parser.parse_error(ParseError::InvalidPercentEncoded));
         }
         }
     } else if !is_url_code_point(c) {
     } else if !is_url_code_point(c) {
-        try!(parser.parse_error(NonUrlCodePoint));
+        try!(parser.parse_error(ParseError::NonUrlCodePoint));
     }
     }
     Ok(())
     Ok(())
 }
 }

+ 1 - 1
src/punycode.rs

@@ -240,7 +240,7 @@ mod tests {
     }
     }
 
 
     fn get_string<'a>(map: &'a JsonObject, key: &str) -> &'a str {
     fn get_string<'a>(map: &'a JsonObject, key: &str) -> &'a str {
-        match map.find(&key.to_string()) {
+        match map.get(&key.to_string()) {
             Some(&String(ref s)) => s.as_slice(),
             Some(&String(ref s)) => s.as_slice(),
             None => "",
             None => "",
             _ => panic!(),
             _ => panic!(),

+ 7 - 8
src/tests.rs

@@ -10,8 +10,7 @@
 use std::char;
 use std::char;
 use std::num::from_str_radix;
 use std::num::from_str_radix;
 use std::path;
 use std::path;
-use super::{UrlParser, Url, RelativeSchemeData, NonRelativeSchemeData, Domain};
-use super::UrlRelativeSchemeData;
+use super::{UrlParser, Url, SchemeData, RelativeSchemeData, Host};
 
 
 
 
 #[test]
 #[test]
@@ -70,7 +69,7 @@ fn url_parsing() {
 
 
         assert_eq!(Some(scheme), expected_scheme);
         assert_eq!(Some(scheme), expected_scheme);
         match scheme_data {
         match scheme_data {
-            RelativeSchemeData(UrlRelativeSchemeData {
+            SchemeData::Relative(RelativeSchemeData {
                 username, password, host, port, default_port: _, path,
                 username, password, host, port, default_port: _, path,
             }) => {
             }) => {
                 assert_eq!(username, expected_username);
                 assert_eq!(username, expected_username);
@@ -80,7 +79,7 @@ fn url_parsing() {
                 assert_eq!(port, expected_port);
                 assert_eq!(port, expected_port);
                 assert_eq!(Some(format!("/{}", path.connect("/"))), expected_path);
                 assert_eq!(Some(format!("/{}", path.connect("/"))), expected_path);
             },
             },
-            NonRelativeSchemeData(scheme_data) => {
+            SchemeData::NonRelative(scheme_data) => {
                 assert_eq!(Some(scheme_data), expected_path);
                 assert_eq!(Some(scheme_data), expected_path);
                 assert_eq!(String::new(), expected_username);
                 assert_eq!(String::new(), expected_username);
                 assert_eq!(None, expected_password);
                 assert_eq!(None, expected_password);
@@ -209,7 +208,7 @@ fn file_paths() {
     assert_eq!(Url::from_file_path(&path::windows::Path::new(r"\\ucn\")), Err(()));
     assert_eq!(Url::from_file_path(&path::windows::Path::new(r"\\ucn\")), Err(()));
 
 
     let mut url = Url::from_file_path(&path::posix::Path::new("/foo/bar")).unwrap();
     let mut url = Url::from_file_path(&path::posix::Path::new("/foo/bar")).unwrap();
-    assert_eq!(url.host(), Some(&Domain("".to_string())));
+    assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
     assert_eq!(url.path(), Some(["foo".to_string(), "bar".to_string()].as_slice()));
     assert_eq!(url.path(), Some(["foo".to_string(), "bar".to_string()].as_slice()));
     assert!(url.to_file_path() == Ok(path::posix::Path::new("/foo/bar")));
     assert!(url.to_file_path() == Ok(path::posix::Path::new("/foo/bar")));
 
 
@@ -225,7 +224,7 @@ fn file_paths() {
         /* note: byte string, invalid UTF-8 */ b"/foo/ba\x80r")));
         /* note: byte string, invalid UTF-8 */ b"/foo/ba\x80r")));
 
 
     let mut url = Url::from_file_path(&path::windows::Path::new(r"C:\foo\bar")).unwrap();
     let mut url = Url::from_file_path(&path::windows::Path::new(r"C:\foo\bar")).unwrap();
-    assert_eq!(url.host(), Some(&Domain("".to_string())));
+    assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
     assert_eq!(url.path(), Some(["C:".to_string(), "foo".to_string(), "bar".to_string()].as_slice()));
     assert_eq!(url.path(), Some(["C:".to_string(), "foo".to_string(), "bar".to_string()].as_slice()));
     assert!(url.to_file_path::<path::windows::Path>()
     assert!(url.to_file_path::<path::windows::Path>()
             == Ok(path::windows::Path::new(r"C:\foo\bar")));
             == Ok(path::windows::Path::new(r"C:\foo\bar")));
@@ -252,11 +251,11 @@ fn directory_paths() {
     assert_eq!(Url::from_directory_path(&path::windows::Path::new(r"\\ucn\")), Err(()));
     assert_eq!(Url::from_directory_path(&path::windows::Path::new(r"\\ucn\")), Err(()));
 
 
     let url = Url::from_directory_path(&path::posix::Path::new("/foo/bar")).unwrap();
     let url = Url::from_directory_path(&path::posix::Path::new("/foo/bar")).unwrap();
-    assert_eq!(url.host(), Some(&Domain("".to_string())));
+    assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
     assert_eq!(url.path(), Some(["foo".to_string(), "bar".to_string(), "".to_string()].as_slice()));
     assert_eq!(url.path(), Some(["foo".to_string(), "bar".to_string(), "".to_string()].as_slice()));
 
 
     let url = Url::from_directory_path(&path::windows::Path::new(r"C:\foo\bar")).unwrap();
     let url = Url::from_directory_path(&path::windows::Path::new(r"C:\foo\bar")).unwrap();
-    assert_eq!(url.host(), Some(&Domain("".to_string())));
+    assert_eq!(url.host(), Some(&Host::Domain("".to_string())));
     assert_eq!(url.path(), Some([
     assert_eq!(url.path(), Some([
         "C:".to_string(), "foo".to_string(), "bar".to_string(), "".to_string()].as_slice()));
         "C:".to_string(), "foo".to_string(), "bar".to_string(), "".to_string()].as_slice()));
 }
 }

+ 21 - 25
src/urlutils.rs

@@ -10,12 +10,8 @@
 //! These methods are not meant for use in Rust code,
 //! These methods are not meant for use in Rust code,
 //! only to help implement the JavaScript URLUtils API: http://url.spec.whatwg.org/#urlutils
 //! only to help implement the JavaScript URLUtils API: http://url.spec.whatwg.org/#urlutils
 
 
-use super::{Url, UrlParser, RelativeSchemeData, NonRelativeSchemeData, FileLikeRelativeScheme};
-use super::UrlRelativeSchemeData;
-use parser::{
-    ParseResult, InvalidScheme,
-    CannotSetFileScheme, CannotSetJavascriptScheme, CannotSetNonRelativeScheme,
-};
+use super::{Url, UrlParser, SchemeType, SchemeData, RelativeSchemeData};
+use parser::{ParseError, ParseResult, Context};
 use percent_encoding::{utf8_percent_encode_to, USERNAME_ENCODE_SET, PASSWORD_ENCODE_SET};
 use percent_encoding::{utf8_percent_encode_to, USERNAME_ENCODE_SET, PASSWORD_ENCODE_SET};
 
 
 
 
@@ -42,44 +38,44 @@ trait UrlUtils {
 impl<'a> UrlUtils for UrlUtilsWrapper<'a> {
 impl<'a> UrlUtils for UrlUtilsWrapper<'a> {
     /// `URLUtils.protocol` setter
     /// `URLUtils.protocol` setter
     fn set_scheme(&mut self, input: &str) -> ParseResult<()> {
     fn set_scheme(&mut self, input: &str) -> ParseResult<()> {
-        match ::parser::parse_scheme(input.as_slice(), ::parser::SetterContext) {
+        match ::parser::parse_scheme(input.as_slice(), Context::Setter) {
             Some((scheme, _)) => {
             Some((scheme, _)) => {
                 self.url.scheme = scheme;
                 self.url.scheme = scheme;
                 Ok(())
                 Ok(())
             },
             },
-            None => Err(InvalidScheme),
+            None => Err(ParseError::InvalidScheme),
         }
         }
     }
     }
 
 
     /// `URLUtils.username` setter
     /// `URLUtils.username` setter
     fn set_username(&mut self, input: &str) -> ParseResult<()> {
     fn set_username(&mut self, input: &str) -> ParseResult<()> {
         match self.url.scheme_data {
         match self.url.scheme_data {
-            RelativeSchemeData(UrlRelativeSchemeData { ref mut username, .. }) => {
+            SchemeData::Relative(RelativeSchemeData { ref mut username, .. }) => {
                 username.truncate(0);
                 username.truncate(0);
                 utf8_percent_encode_to(input, USERNAME_ENCODE_SET, username);
                 utf8_percent_encode_to(input, USERNAME_ENCODE_SET, username);
                 Ok(())
                 Ok(())
             },
             },
-            NonRelativeSchemeData(_) => Err(CannotSetNonRelativeScheme("username"))
+            SchemeData::NonRelative(_) => Err(ParseError::CannotSetUsernameWithNonRelativeScheme)
         }
         }
     }
     }
 
 
     /// `URLUtils.password` setter
     /// `URLUtils.password` setter
     fn set_password(&mut self, input: &str) -> ParseResult<()> {
     fn set_password(&mut self, input: &str) -> ParseResult<()> {
         match self.url.scheme_data {
         match self.url.scheme_data {
-            RelativeSchemeData(UrlRelativeSchemeData { ref mut password, .. }) => {
+            SchemeData::Relative(RelativeSchemeData { ref mut password, .. }) => {
                 let mut new_password = String::new();
                 let mut new_password = String::new();
                 utf8_percent_encode_to(input, PASSWORD_ENCODE_SET, &mut new_password);
                 utf8_percent_encode_to(input, PASSWORD_ENCODE_SET, &mut new_password);
                 *password = Some(new_password);
                 *password = Some(new_password);
                 Ok(())
                 Ok(())
             },
             },
-            NonRelativeSchemeData(_) => Err(CannotSetNonRelativeScheme("password"))
+            SchemeData::NonRelative(_) => Err(ParseError::CannotSetPasswordWithNonRelativeScheme)
         }
         }
     }
     }
 
 
     /// `URLUtils.host` setter
     /// `URLUtils.host` setter
     fn set_host_and_port(&mut self, input: &str) -> ParseResult<()> {
     fn set_host_and_port(&mut self, input: &str) -> ParseResult<()> {
         match self.url.scheme_data {
         match self.url.scheme_data {
-            RelativeSchemeData(UrlRelativeSchemeData {
+            SchemeData::Relative(RelativeSchemeData {
                 ref mut host, ref mut port, ref mut default_port, ..
                 ref mut host, ref mut port, ref mut default_port, ..
             }) => {
             }) => {
                 let scheme_type = self.parser.get_scheme_type(self.url.scheme.as_slice());
                 let scheme_type = self.parser.get_scheme_type(self.url.scheme.as_slice());
@@ -90,29 +86,29 @@ impl<'a> UrlUtils for UrlUtilsWrapper<'a> {
                 *default_port = new_default_port;
                 *default_port = new_default_port;
                 Ok(())
                 Ok(())
             },
             },
-            NonRelativeSchemeData(_) => Err(CannotSetNonRelativeScheme("host/port"))
+            SchemeData::NonRelative(_) => Err(ParseError::CannotSetHostPortWithNonRelativeScheme)
         }
         }
     }
     }
 
 
     /// `URLUtils.hostname` setter
     /// `URLUtils.hostname` setter
     fn set_host(&mut self, input: &str) -> ParseResult<()> {
     fn set_host(&mut self, input: &str) -> ParseResult<()> {
         match self.url.scheme_data {
         match self.url.scheme_data {
-            RelativeSchemeData(UrlRelativeSchemeData { ref mut host, .. }) => {
+            SchemeData::Relative(RelativeSchemeData { ref mut host, .. }) => {
                 let (new_host, _) = try!(::parser::parse_hostname(input, self.parser));
                 let (new_host, _) = try!(::parser::parse_hostname(input, self.parser));
                 *host = new_host;
                 *host = new_host;
                 Ok(())
                 Ok(())
             },
             },
-            NonRelativeSchemeData(_) => Err(CannotSetNonRelativeScheme("host"))
+            SchemeData::NonRelative(_) => Err(ParseError::CannotSetHostWithNonRelativeScheme)
         }
         }
     }
     }
 
 
     /// `URLUtils.port` setter
     /// `URLUtils.port` setter
     fn set_port(&mut self, input: &str) -> ParseResult<()> {
     fn set_port(&mut self, input: &str) -> ParseResult<()> {
         match self.url.scheme_data {
         match self.url.scheme_data {
-            RelativeSchemeData(UrlRelativeSchemeData { ref mut port, ref mut default_port, .. }) => {
+            SchemeData::Relative(RelativeSchemeData { ref mut port, ref mut default_port, .. }) => {
                 let scheme_type = self.parser.get_scheme_type(self.url.scheme.as_slice());
                 let scheme_type = self.parser.get_scheme_type(self.url.scheme.as_slice());
-                if scheme_type == FileLikeRelativeScheme {
-                    return Err(CannotSetFileScheme("port"));
+                if scheme_type == SchemeType::FileLike {
+                    return Err(ParseError::CannotSetPortWithFileLikeScheme);
                 }
                 }
                 let (new_port, new_default_port, _) = try!(::parser::parse_port(
                 let (new_port, new_default_port, _) = try!(::parser::parse_port(
                     input, scheme_type, self.parser));
                     input, scheme_type, self.parser));
@@ -120,21 +116,21 @@ impl<'a> UrlUtils for UrlUtilsWrapper<'a> {
                 *default_port = new_default_port;
                 *default_port = new_default_port;
                 Ok(())
                 Ok(())
             },
             },
-            NonRelativeSchemeData(_) => Err(CannotSetNonRelativeScheme("port"))
+            SchemeData::NonRelative(_) => Err(ParseError::CannotSetPortWithNonRelativeScheme)
         }
         }
     }
     }
 
 
     /// `URLUtils.pathname` setter
     /// `URLUtils.pathname` setter
     fn set_path(&mut self, input: &str) -> ParseResult<()> {
     fn set_path(&mut self, input: &str) -> ParseResult<()> {
         match self.url.scheme_data {
         match self.url.scheme_data {
-            RelativeSchemeData(UrlRelativeSchemeData { ref mut path, .. }) => {
+            SchemeData::Relative(RelativeSchemeData { ref mut path, .. }) => {
                 let scheme_type = self.parser.get_scheme_type(self.url.scheme.as_slice());
                 let scheme_type = self.parser.get_scheme_type(self.url.scheme.as_slice());
                 let (new_path, _) = try!(::parser::parse_path_start(
                 let (new_path, _) = try!(::parser::parse_path_start(
-                    input, ::parser::SetterContext, scheme_type, self.parser));
+                    input, Context::Setter, scheme_type, self.parser));
                 *path = new_path;
                 *path = new_path;
                 Ok(())
                 Ok(())
             },
             },
-            NonRelativeSchemeData(_) => Err(CannotSetNonRelativeScheme("path"))
+            SchemeData::NonRelative(_) => Err(ParseError::CannotSetPathWithNonRelativeScheme)
         }
         }
     }
     }
 
 
@@ -145,7 +141,7 @@ impl<'a> UrlUtils for UrlUtilsWrapper<'a> {
         } else {
         } else {
             let input = if input.starts_with("?") { input.slice_from(1) } else { input };
             let input = if input.starts_with("?") { input.slice_from(1) } else { input };
             let (new_query, _) = try!(::parser::parse_query(
             let (new_query, _) = try!(::parser::parse_query(
-                input, ::parser::SetterContext, self.parser));
+                input, Context::Setter, self.parser));
             Some(new_query)
             Some(new_query)
         };
         };
         Ok(())
         Ok(())
@@ -154,7 +150,7 @@ impl<'a> UrlUtils for UrlUtilsWrapper<'a> {
     /// `URLUtils.hash` setter
     /// `URLUtils.hash` setter
     fn set_fragment(&mut self, input: &str) -> ParseResult<()> {
     fn set_fragment(&mut self, input: &str) -> ParseResult<()> {
         if self.url.scheme.as_slice() == "javascript" {
         if self.url.scheme.as_slice() == "javascript" {
-            return Err(CannotSetJavascriptScheme("fragment"))
+            return Err(ParseError::CannotSetJavascriptFragment)
         }
         }
         self.url.fragment = if input.is_empty() {
         self.url.fragment = if input.is_empty() {
             None
             None