Simon Sapin 7 ani în urmă
părinte
comite
c276a8237a
23 a modificat fișierele cu 1566 adăugiri și 931 ștergeri
  1. 15 19
      data-url/src/forgiving_base64.rs
  2. 46 30
      data-url/src/lib.rs
  3. 11 9
      data-url/src/mime.rs
  4. 17 18
      data-url/tests/wpt.rs
  5. 19 12
      idna/src/lib.rs
  6. 47 36
      idna/src/punycode.rs
  7. 90 61
      idna/src/uts46.rs
  8. 35 25
      idna/tests/punycode.rs
  9. 12 7
      idna/tests/unit.rs
  10. 78 49
      idna/tests/uts46.rs
  11. 37 47
      percent_encoding/lib.rs
  12. 27 16
      src/encoding.rs
  13. 84 42
      src/form_urlencoded.rs
  14. 125 69
      src/host.rs
  15. 292 151
      src/lib.rs
  16. 14 14
      src/origin.rs
  17. 275 157
      src/parser.rs
  18. 21 9
      src/path_segments.rs
  19. 19 10
      src/quirks.rs
  20. 37 32
      src/slicing.rs
  21. 32 20
      tests/data.rs
  22. 132 50
      tests/unit.rs
  23. 101 48
      url_serde/src/lib.rs

+ 15 - 19
data-url/src/forgiving_base64.rs

@@ -29,7 +29,7 @@ impl From<DecodeError<Impossible>> for InvalidBase64 {
     fn from(e: DecodeError<Impossible>) -> Self {
         match e {
             DecodeError::InvalidBase64(e) => e,
-            DecodeError::WriteError(e) => match e {}
+            DecodeError::WriteError(e) => match e {},
         }
     }
 }
@@ -46,14 +46,20 @@ pub fn decode_to_vec(input: &[u8]) -> Result<Vec<u8>, InvalidBase64> {
 }
 
 /// <https://infra.spec.whatwg.org/#forgiving-base64-decode>
-pub struct Decoder<F, E> where F: FnMut(&[u8]) -> Result<(), E> {
+pub struct Decoder<F, E>
+where
+    F: FnMut(&[u8]) -> Result<(), E>,
+{
     write_bytes: F,
     bit_buffer: u32,
     buffer_bit_length: u8,
     padding_symbols: u8,
 }
 
-impl<F, E> Decoder<F, E> where F: FnMut(&[u8]) -> Result<(), E> {
+impl<F, E> Decoder<F, E>
+where
+    F: FnMut(&[u8]) -> Result<(), E>,
+{
     pub fn new(write_bytes: F) -> Self {
         Self {
             write_bytes,
@@ -72,12 +78,12 @@ impl<F, E> Decoder<F, E> where F: FnMut(&[u8]) -> Result<(), E> {
 
                 // Remove ASCII whitespace
                 if matches!(byte, b' ' | b'\t' | b'\n' | b'\r' | b'\x0C') {
-                    continue
+                    continue;
                 }
 
                 if byte == b'=' {
                     self.padding_symbols = self.padding_symbols.saturating_add(1);
-                    continue
+                    continue;
                 }
 
                 Err(InvalidBase64Details::UnexpectedSymbol(byte))?
@@ -115,32 +121,22 @@ impl<F, E> Decoder<F, E> where F: FnMut(&[u8]) -> Result<(), E> {
             (12, 2) | (12, 0) => {
                 // A multiple of four of alphabet symbols, followed by two more symbols,
                 // optionally followed by two padding characters (which make a total multiple of four).
-                let byte_buffer = [
-                    (self.bit_buffer >> 4) as u8,
-                ];
+                let byte_buffer = [(self.bit_buffer >> 4) as u8];
                 (self.write_bytes)(&byte_buffer).map_err(DecodeError::WriteError)?;
             }
             (18, 1) | (18, 0) => {
                 // A multiple of four of alphabet symbols, followed by three more symbols,
                 // optionally followed by one padding character (which make a total multiple of four).
-                let byte_buffer = [
-                    (self.bit_buffer >> 10) as u8,
-                    (self.bit_buffer >> 2) as u8,
-                ];
+                let byte_buffer = [(self.bit_buffer >> 10) as u8, (self.bit_buffer >> 2) as u8];
                 (self.write_bytes)(&byte_buffer).map_err(DecodeError::WriteError)?;
             }
-            (6, _) => {
-                Err(InvalidBase64Details::LoneAlphabetSymbol)?
-            }
-            _ => {
-                Err(InvalidBase64Details::Padding)?
-            }
+            (6, _) => Err(InvalidBase64Details::LoneAlphabetSymbol)?,
+            _ => Err(InvalidBase64Details::Padding)?,
         }
         Ok(())
     }
 }
 
-
 /// Generated by `make_base64_decode_table.py` based on "Table 1: The Base 64 Alphabet"
 /// at <https://tools.ietf.org/html/rfc4648#section-4>
 ///

+ 46 - 30
data-url/src/lib.rs

@@ -15,14 +15,15 @@
 //! assert!(fragment.is_none());
 //! ```
 
-#[macro_use] extern crate matches;
+#[macro_use]
+extern crate matches;
 
 macro_rules! require {
     ($condition: expr) => {
         if !$condition {
-            return None
+            return None;
         }
-    }
+    };
 }
 
 pub mod forgiving_base64;
@@ -53,7 +54,11 @@ impl<'a> DataUrl<'a> {
 
         let (mime_type, base64) = parse_header(from_colon_to_comma);
 
-        Ok(DataUrl { mime_type, base64, encoded_body_plus_fragment })
+        Ok(DataUrl {
+            mime_type,
+            base64,
+            encoded_body_plus_fragment,
+        })
     }
 
     pub fn mime_type(&self) -> &mime::Mime {
@@ -62,9 +67,12 @@ impl<'a> DataUrl<'a> {
 
     /// Streaming-decode the data URL’s body to `write_body_bytes`,
     /// and return the URL’s fragment identifier if it has one.
-    pub fn decode<F, E>(&self, write_body_bytes: F)
-                        -> Result<Option<FragmentIdentifier<'a>>, forgiving_base64::DecodeError<E>>
-        where F: FnMut(&[u8]) -> Result<(), E>
+    pub fn decode<F, E>(
+        &self,
+        write_body_bytes: F,
+    ) -> Result<Option<FragmentIdentifier<'a>>, forgiving_base64::DecodeError<E>>
+    where
+        F: FnMut(&[u8]) -> Result<(), E>,
     {
         if self.base64 {
             decode_with_base64(self.encoded_body_plus_fragment, write_body_bytes)
@@ -75,9 +83,9 @@ impl<'a> DataUrl<'a> {
     }
 
     /// Return the decoded body, and the URL’s fragment identifier if it has one.
-    pub fn decode_to_vec(&self)
-        -> Result<(Vec<u8>, Option<FragmentIdentifier<'a>>), forgiving_base64::InvalidBase64>
-    {
+    pub fn decode_to_vec(
+        &self,
+    ) -> Result<(Vec<u8>, Option<FragmentIdentifier<'a>>), forgiving_base64::InvalidBase64> {
         let mut body = Vec::new();
         let fragment = self.decode(|bytes| Ok(body.extend_from_slice(bytes)))?;
         Ok((body, fragment))
@@ -100,7 +108,7 @@ impl<'a> FragmentIdentifier<'a> {
                     percent_encode(byte, &mut string)
                 }
                 // Printable ASCII
-                _ => string.push(byte as char)
+                _ => string.push(byte as char),
             }
         }
         string
@@ -125,7 +133,9 @@ fn pretend_parse_data_url(input: &str) -> Option<&str> {
     let mut bytes = left_trimmed.bytes();
     {
         // Ignore ASCII tabs or newlines like the URL parser would
-        let mut iter = bytes.by_ref().filter(|&byte| !matches!(byte, b'\t' | b'\n' | b'\r'));
+        let mut iter = bytes
+            .by_ref()
+            .filter(|&byte| !matches!(byte, b'\t' | b'\n' | b'\r'));
         require!(iter.next()?.to_ascii_lowercase() == b'd');
         require!(iter.next()?.to_ascii_lowercase() == b'a');
         require!(iter.next()?.to_ascii_lowercase() == b't');
@@ -142,10 +152,10 @@ fn pretend_parse_data_url(input: &str) -> Option<&str> {
 fn find_comma_before_fragment(after_colon: &str) -> Option<(&str, &str)> {
     for (i, byte) in after_colon.bytes().enumerate() {
         if byte == b',' {
-            return Some((&after_colon[..i], &after_colon[i + 1..]))
+            return Some((&after_colon[..i], &after_colon[i + 1..]));
         }
         if byte == b'#' {
-            break
+            break;
         }
     }
     None
@@ -187,18 +197,16 @@ fn parse_header(from_colon_to_comma: &str) -> (mime::Mime, bool) {
             }
 
             // Printable ASCII
-            _ => string.push(byte as char)
+            _ => string.push(byte as char),
         }
     }
 
     // FIXME: does Mime::from_str match the MIME Sniffing Standard’s parsing algorithm?
     // <https://mimesniff.spec.whatwg.org/#parse-a-mime-type>
-    let mime_type = string.parse().unwrap_or_else(|_| {
-        mime::Mime {
-            type_: String::from("text"),
-            subtype: String::from("plain"),
-            parameters: vec![(String::from("charset"), String::from("US-ASCII"))],
-        }
+    let mime_type = string.parse().unwrap_or_else(|_| mime::Mime {
+        type_: String::from("text"),
+        subtype: String::from("plain"),
+        parameters: vec![(String::from("charset"), String::from("US-ASCII"))],
     });
 
     (mime_type, base64)
@@ -209,7 +217,9 @@ fn remove_base64_suffix(s: &str) -> Option<&str> {
     let mut bytes = s.bytes();
     {
         // Ignore ASCII tabs or newlines like the URL parser would
-        let iter = bytes.by_ref().filter(|&byte| !matches!(byte, b'\t' | b'\n' | b'\r'));
+        let iter = bytes
+            .by_ref()
+            .filter(|&byte| !matches!(byte, b'\t' | b'\n' | b'\r'));
 
         // Search from the end
         let mut iter = iter.rev();
@@ -240,9 +250,12 @@ fn percent_encode(byte: u8, string: &mut String) {
 /// Anything that would have been UTF-8 percent-encoded by the URL parser
 /// would be percent-decoded here.
 /// We skip that round-trip and pass it through unchanged.
-fn decode_without_base64<F, E>(encoded_body_plus_fragment: &str, mut write_bytes: F)
-                               -> Result<Option<FragmentIdentifier>, E>
-    where F: FnMut(&[u8]) -> Result<(), E>
+fn decode_without_base64<F, E>(
+    encoded_body_plus_fragment: &str,
+    mut write_bytes: F,
+) -> Result<Option<FragmentIdentifier>, E>
+where
+    F: FnMut(&[u8]) -> Result<(), E>,
 {
     let bytes = encoded_body_plus_fragment.as_bytes();
     let mut slice_start = 0;
@@ -275,11 +288,11 @@ fn decode_without_base64<F, E>(encoded_body_plus_fragment: &str, mut write_bytes
                 b'#' => {
                     let fragment_start = i + 1;
                     let fragment = &encoded_body_plus_fragment[fragment_start..];
-                    return Ok(Some(FragmentIdentifier(fragment)))
+                    return Ok(Some(FragmentIdentifier(fragment)));
                 }
 
                 // Ignore over '\t' | '\n' | '\r'
-                _ => slice_start = i + 1
+                _ => slice_start = i + 1,
             }
         }
     }
@@ -290,9 +303,12 @@ fn decode_without_base64<F, E>(encoded_body_plus_fragment: &str, mut write_bytes
 /// `decode_without_base64()` composed with
 /// <https://infra.spec.whatwg.org/#isomorphic-decode> composed with
 /// <https://infra.spec.whatwg.org/#forgiving-base64-decode>.
-fn decode_with_base64<F, E>(encoded_body_plus_fragment: &str, write_bytes: F)
-                            -> Result<Option<FragmentIdentifier>, forgiving_base64::DecodeError<E>>
-    where F: FnMut(&[u8]) -> Result<(), E>
+fn decode_with_base64<F, E>(
+    encoded_body_plus_fragment: &str,
+    write_bytes: F,
+) -> Result<Option<FragmentIdentifier>, forgiving_base64::DecodeError<E>>
+where
+    F: FnMut(&[u8]) -> Result<(), E>,
 {
     let mut decoder = forgiving_base64::Decoder::new(write_bytes);
     let fragment = decode_without_base64(encoded_body_plus_fragment, |bytes| decoder.feed(bytes))?;

+ 11 - 9
data-url/src/mime.rs

@@ -7,14 +7,16 @@ pub struct Mime {
     pub type_: String,
     pub subtype: String,
     /// (name, value)
-    pub parameters: Vec<(String, String)>
+    pub parameters: Vec<(String, String)>,
 }
 
 impl Mime {
     pub fn get_parameter<P>(&self, name: &P) -> Option<&str>
-        where P: ?Sized + PartialEq<str>
+    where
+        P: ?Sized + PartialEq<str>,
     {
-        self.parameters.iter()
+        self.parameters
+            .iter()
             .find(|&&(ref n, _)| name == &**n)
             .map(|&(_, ref v)| &**v)
     }
@@ -67,11 +69,11 @@ fn parse_parameters(s: &str, parameters: &mut Vec<(String, String)>) {
         let piece = piece.trim_left_matches(ascii_whitespace);
         let (name, value) = split2(piece, '=');
         if name.is_empty() || !only_http_token_code_points(name) || contains(&parameters, name) {
-            continue
+            continue;
         }
         if let Some(value) = value {
             let value = if value.starts_with('"') {
-                let max_len = value.len().saturating_sub(2);  // without start or end quotes
+                let max_len = value.len().saturating_sub(2); // without start or end quotes
                 let mut unescaped_value = String::with_capacity(max_len);
                 let mut chars = value[1..].chars();
                 'until_closing_quote: loop {
@@ -79,7 +81,7 @@ fn parse_parameters(s: &str, parameters: &mut Vec<(String, String)>) {
                         match c {
                             '"' => break 'until_closing_quote,
                             '\\' => unescaped_value.push(chars.next().unwrap_or('\\')),
-                            _ => unescaped_value.push(c)
+                            _ => unescaped_value.push(c),
                         }
                     }
                     if let Some(piece) = semicolon_separated.next() {
@@ -88,17 +90,17 @@ fn parse_parameters(s: &str, parameters: &mut Vec<(String, String)>) {
                         unescaped_value.push(';');
                         chars = piece.chars()
                     } else {
-                        break
+                        break;
                     }
                 }
                 if !valid_value(&unescaped_value) {
-                    continue
+                    continue;
                 }
                 unescaped_value
             } else {
                 let value = value.trim_right_matches(ascii_whitespace);
                 if !valid_value(value) {
-                    continue
+                    continue;
                 }
                 value.to_owned()
             };

+ 17 - 18
data-url/tests/wpt.rs

@@ -1,6 +1,7 @@
 extern crate data_url;
 extern crate rustc_test;
-#[macro_use] extern crate serde;
+#[macro_use]
+extern crate serde;
 extern crate serde_json;
 
 fn run_data_url(input: String, expected_mime: Option<String>, expected_body: Option<Vec<u8>>) {
@@ -22,11 +23,10 @@ fn run_data_url(input: String, expected_mime: Option<String>, expected_body: Opt
 }
 
 fn collect_data_url<F>(add_test: &mut F)
-    where F: FnMut(String, bool, rustc_test::TestFn)
+where
+    F: FnMut(String, bool, rustc_test::TestFn),
 {
-    let known_failures = [
-        "data://test:test/,X",
-    ];
+    let known_failures = ["data://test:test/,X"];
 
     #[derive(Deserialize)]
     #[serde(untagged)]
@@ -47,7 +47,7 @@ fn collect_data_url<F>(add_test: &mut F)
             should_panic,
             rustc_test::TestFn::dyn_test_fn(move || {
                 run_data_url(input, expected_mime, expected_body)
-            })
+            }),
         );
     }
 }
@@ -62,9 +62,9 @@ fn run_base64(input: String, expected: Option<Vec<u8>>) {
     }
 }
 
-
 fn collect_base64<F>(add_test: &mut F)
-    where F: FnMut(String, bool, rustc_test::TestFn)
+where
+    F: FnMut(String, bool, rustc_test::TestFn),
 {
     let known_failures = [];
 
@@ -75,9 +75,7 @@ fn collect_base64<F>(add_test: &mut F)
         add_test(
             format!("base64 {:?}", input),
             should_panic,
-            rustc_test::TestFn::dyn_test_fn(move || {
-                run_base64(input, expected)
-            })
+            rustc_test::TestFn::dyn_test_fn(move || run_base64(input, expected)),
         );
     }
 }
@@ -92,9 +90,9 @@ fn run_mime(input: String, expected: Option<String>) {
     }
 }
 
-
 fn collect_mime<F>(add_test: &mut F)
-    where F: FnMut(String, bool, rustc_test::TestFn)
+where
+    F: FnMut(String, bool, rustc_test::TestFn),
 {
     let known_failures = [];
 
@@ -102,7 +100,10 @@ fn collect_mime<F>(add_test: &mut F)
     #[serde(untagged)]
     enum Entry {
         Comment(String),
-        TestCase { input: String, output: Option<String> }
+        TestCase {
+            input: String,
+            output: Option<String>,
+        },
     }
 
     let v: Vec<Entry> = serde_json::from_str(include_str!("mime-types.json")).unwrap();
@@ -115,7 +116,7 @@ fn collect_mime<F>(add_test: &mut F)
             Entry::TestCase { input, output } => (input, output),
             Entry::Comment(s) => {
                 last_comment = Some(s);
-                continue
+                continue;
             }
         };
 
@@ -127,9 +128,7 @@ fn collect_mime<F>(add_test: &mut F)
                 format!("MIME type {:?}", input)
             },
             should_panic,
-            rustc_test::TestFn::dyn_test_fn(move || {
-                run_mime(input, expected)
-            })
+            rustc_test::TestFn::dyn_test_fn(move || run_mime(input, expected)),
         );
     }
 }

+ 19 - 12
idna/src/lib.rs

@@ -32,7 +32,8 @@
 //! > that minimizes the impact of this transition for client software,
 //! > allowing client software to access domains that are valid under either system.
 
-#[macro_use] extern crate matches;
+#[macro_use]
+extern crate matches;
 extern crate unicode_bidi;
 extern crate unicode_normalization;
 
@@ -47,11 +48,14 @@ pub mod uts46;
 ///
 /// This process may fail.
 pub fn domain_to_ascii(domain: &str) -> Result<String, uts46::Errors> {
-    uts46::to_ascii(domain, uts46::Flags {
-        use_std3_ascii_rules: false,
-        transitional_processing: false,
-        verify_dns_length: false,
-    })
+    uts46::to_ascii(
+        domain,
+        uts46::Flags {
+            use_std3_ascii_rules: false,
+            transitional_processing: false,
+            verify_dns_length: false,
+        },
+    )
 }
 
 /// The [domain to Unicode](https://url.spec.whatwg.org/#concept-domain-to-unicode) algorithm.
@@ -63,11 +67,14 @@ pub fn domain_to_ascii(domain: &str) -> Result<String, uts46::Errors> {
 /// This may indicate [syntax violations](https://url.spec.whatwg.org/#syntax-violation)
 /// but always returns a string for the mapped domain.
 pub fn domain_to_unicode(domain: &str) -> (String, Result<(), uts46::Errors>) {
-    uts46::to_unicode(domain, uts46::Flags {
-        use_std3_ascii_rules: false,
+    uts46::to_unicode(
+        domain,
+        uts46::Flags {
+            use_std3_ascii_rules: false,
 
-        // Unused:
-        transitional_processing: false,
-        verify_dns_length: false,
-    })
+            // Unused:
+            transitional_processing: false,
+            verify_dns_length: false,
+        },
+    )
 }

+ 47 - 36
idna/src/punycode.rs

@@ -13,8 +13,8 @@
 //! `encode_str` and `decode_to_string` provide convenience wrappers
 //! that convert from and to Rust’s UTF-8 based `str` and `String` types.
 
-use std::u32;
 use std::char;
+use std::u32;
 
 // Bootstring parameters for Punycode
 static BASE: u32 = 36;
@@ -26,7 +26,6 @@ static INITIAL_BIAS: u32 = 72;
 static INITIAL_N: u32 = 0x80;
 static DELIMITER: char = '-';
 
-
 #[inline]
 fn adapt(mut delta: u32, num_points: u32, first_time: bool) -> u32 {
     delta /= if first_time { DAMP } else { 2 };
@@ -39,7 +38,6 @@ fn adapt(mut delta: u32, num_points: u32, first_time: bool) -> u32 {
     k + (((BASE - T_MIN + 1) * delta) / (delta + SKEW))
 }
 
-
 /// Convert Punycode to an Unicode `String`.
 ///
 /// This is a convenience wrapper around `decode`.
@@ -48,7 +46,6 @@ pub fn decode_to_string(input: &str) -> Option<String> {
     decode(input).map(|chars| chars.into_iter().collect())
 }
 
-
 /// Convert Punycode to Unicode.
 ///
 /// Return None on malformed input or overflow.
@@ -61,8 +58,12 @@ pub fn decode(input: &str) -> Option<Vec<char>> {
         None => (Vec::new(), input),
         Some(position) => (
             input[..position].chars().collect(),
-            if position > 0 { &input[position + 1..] } else { input }
-        )
+            if position > 0 {
+                &input[position + 1..]
+            } else {
+                input
+            },
+        ),
     };
     let mut code_point = INITIAL_N;
     let mut bias = INITIAL_BIAS;
@@ -80,35 +81,39 @@ pub fn decode(input: &str) -> Option<Vec<char>> {
         // which gets added to i.
         loop {
             let digit = match byte {
-                byte @ b'0' ..= b'9' => byte - b'0' + 26,
-                byte @ b'A' ..= b'Z' => byte - b'A',
-                byte @ b'a' ..= b'z' => byte - b'a',
-                _ => return None
+                byte @ b'0'..=b'9' => byte - b'0' + 26,
+                byte @ b'A'..=b'Z' => byte - b'A',
+                byte @ b'a'..=b'z' => byte - b'a',
+                _ => return None,
             } as u32;
             if digit > (u32::MAX - i) / weight {
-                return None  // Overflow
+                return None; // Overflow
             }
             i += digit * weight;
-            let t = if k <= bias { T_MIN }
-                    else if k >= bias + T_MAX { T_MAX }
-                    else { k - bias };
+            let t = if k <= bias {
+                T_MIN
+            } else if k >= bias + T_MAX {
+                T_MAX
+            } else {
+                k - bias
+            };
             if digit < t {
-                break
+                break;
             }
             if weight > u32::MAX / (BASE - t) {
-                return None  // Overflow
+                return None; // Overflow
             }
             weight *= BASE - t;
             k += BASE;
             byte = match iter.next() {
-                None => return None,  // End of input before the end of this delta
+                None => return None, // End of input before the end of this delta
                 Some(byte) => byte,
             };
         }
         let length = output.len() as u32;
         bias = adapt(i - previous_i, length + 1, previous_i == 0);
         if i / (length + 1) > u32::MAX - code_point {
-            return None  // Overflow
+            return None; // Overflow
         }
         // i was supposed to wrap around from length+1 to 0,
         // incrementing code_point each time.
@@ -116,7 +121,7 @@ pub fn decode(input: &str) -> Option<Vec<char>> {
         i %= length + 1;
         let c = match char::from_u32(code_point) {
             Some(c) => c,
-            None => return None
+            None => return None,
         };
         output.insert(i as usize, c);
         i += 1;
@@ -124,7 +129,6 @@ pub fn decode(input: &str) -> Option<Vec<char>> {
     Some(output)
 }
 
-
 /// Convert an Unicode `str` to Punycode.
 ///
 /// This is a convenience wrapper around `encode`.
@@ -133,16 +137,16 @@ pub fn encode_str(input: &str) -> Option<String> {
     encode(&input.chars().collect::<Vec<char>>())
 }
 
-
 /// Convert Unicode to Punycode.
 ///
 /// Return None on overflow, which can only happen on inputs that would take more than
 /// 63 encoded bytes, the DNS limit on domain name labels.
 pub fn encode(input: &[char]) -> Option<String> {
     // Handle "basic" (ASCII) code points. They are encoded as-is.
-    let output_bytes = input.iter().filter_map(|&c|
-        if c.is_ascii() { Some(c as u8) } else { None }
-    ).collect();
+    let output_bytes = input
+        .iter()
+        .filter_map(|&c| if c.is_ascii() { Some(c as u8) } else { None })
+        .collect();
     let mut output = unsafe { String::from_utf8_unchecked(output_bytes) };
     let basic_length = output.len() as u32;
     if basic_length > 0 {
@@ -156,10 +160,14 @@ pub fn encode(input: &[char]) -> Option<String> {
     while processed < input_length {
         // All code points < code_point have been handled already.
         // Find the next larger one.
-        let min_code_point = input.iter().map(|&c| c as u32)
-                                  .filter(|&c| c >= code_point).min().unwrap();
+        let min_code_point = input
+            .iter()
+            .map(|&c| c as u32)
+            .filter(|&c| c >= code_point)
+            .min()
+            .unwrap();
         if min_code_point - code_point > (u32::MAX - delta) / (processed + 1) {
-            return None  // Overflow
+            return None; // Overflow
         }
         // Increase delta to advance the decoder’s <code_point,i> state to <min_code_point,0>
         delta += (min_code_point - code_point) * (processed + 1);
@@ -169,7 +177,7 @@ pub fn encode(input: &[char]) -> Option<String> {
             if c < code_point {
                 delta += 1;
                 if delta == 0 {
-                    return None  // Overflow
+                    return None; // Overflow
                 }
             }
             if c == code_point {
@@ -177,11 +185,15 @@ pub fn encode(input: &[char]) -> Option<String> {
                 let mut q = delta;
                 let mut k = BASE;
                 loop {
-                    let t = if k <= bias { T_MIN }
-                            else if k >= bias + T_MAX { T_MAX }
-                            else { k - bias };
+                    let t = if k <= bias {
+                        T_MIN
+                    } else if k >= bias + T_MAX {
+                        T_MAX
+                    } else {
+                        k - bias
+                    };
                     if q < t {
-                        break
+                        break;
                     }
                     let value = t + ((q - t) % (BASE - t));
                     output.push(value_to_digit(value));
@@ -200,12 +212,11 @@ pub fn encode(input: &[char]) -> Option<String> {
     Some(output)
 }
 
-
 #[inline]
 fn value_to_digit(value: u32) -> char {
     match value {
-        0 ..= 25 => (value as u8 + 'a' as u8) as char,  // a..z
-        26 ..= 35 => (value as u8 - 26 + '0' as u8) as char,  // 0..9
-        _ => panic!()
+        0..=25 => (value as u8 + 'a' as u8) as char, // a..z
+        26..=35 => (value as u8 - 26 + '0' as u8) as char, // 0..9
+        _ => panic!(),
     }
 }

+ 90 - 61
idna/src/uts46.rs

@@ -11,17 +11,15 @@
 
 use self::Mapping::*;
 use punycode;
-use std::cmp::Ordering::{Equal, Less, Greater};
-use unicode_bidi::{BidiClass, bidi_class};
-use unicode_normalization::UnicodeNormalization;
+use std::cmp::Ordering::{Equal, Greater, Less};
+use unicode_bidi::{bidi_class, BidiClass};
 use unicode_normalization::char::is_combining_mark;
+use unicode_normalization::UnicodeNormalization;
 
 include!("uts46_mapping_table.rs");
 
-
 pub static PUNYCODE_PREFIX: &'static str = "xn--";
 
-
 #[derive(Debug)]
 struct StringTableSlice {
     // Store these as separate fields so the structure will have an
@@ -66,25 +64,27 @@ fn find_char(codepoint: char) -> &'static Mapping {
             Equal
         }
     });
-    r.ok().map(|i| {
-        const SINGLE_MARKER: u16 = 1 << 15;
+    r.ok()
+        .map(|i| {
+            const SINGLE_MARKER: u16 = 1 << 15;
 
-        let x = INDEX_TABLE[i];
-        let single = (x & SINGLE_MARKER) != 0;
-        let offset = !SINGLE_MARKER & x;
+            let x = INDEX_TABLE[i];
+            let single = (x & SINGLE_MARKER) != 0;
+            let offset = !SINGLE_MARKER & x;
 
-        if single {
-            &MAPPING_TABLE[offset as usize]
-        } else {
-            &MAPPING_TABLE[(offset + (codepoint as u16 - TABLE[i].from as u16)) as usize]
-        }
-    }).unwrap()
+            if single {
+                &MAPPING_TABLE[offset as usize]
+            } else {
+                &MAPPING_TABLE[(offset + (codepoint as u16 - TABLE[i].from as u16)) as usize]
+            }
+        })
+        .unwrap()
 }
 
 fn map_char(codepoint: char, flags: Flags, output: &mut String, errors: &mut Vec<Error>) {
     match *find_char(codepoint) {
         Mapping::Valid => output.push(codepoint),
-        Mapping::Ignored => {},
+        Mapping::Ignored => {}
         Mapping::Mapped(ref slice) => output.push_str(decode_slice(slice)),
         Mapping::Deviation(ref slice) => {
             if flags.transitional_processing {
@@ -133,16 +133,23 @@ fn passes_bidi(label: &str, is_bidi_domain: bool) -> bool {
             loop {
                 match chars.next() {
                     Some(c) => {
-                        if !matches!(bidi_class(c),
-                                     BidiClass::L | BidiClass::EN |
-                                     BidiClass::ES | BidiClass::CS |
-                                     BidiClass::ET | BidiClass::ON |
-                                     BidiClass::BN | BidiClass::NSM
-                                    ) {
+                        if !matches!(
+                            bidi_class(c),
+                            BidiClass::L
+                                | BidiClass::EN
+                                | BidiClass::ES
+                                | BidiClass::CS
+                                | BidiClass::ET
+                                | BidiClass::ON
+                                | BidiClass::BN
+                                | BidiClass::NSM
+                        ) {
                             return false;
                         }
-                    },
-                    None => { break; },
+                    }
+                    None => {
+                        break;
+                    }
                 }
             }
 
@@ -156,16 +163,18 @@ fn passes_bidi(label: &str, is_bidi_domain: bool) -> bool {
                         last_non_nsm = rev_chars.next();
                         continue;
                     }
-                    _ => { break; },
+                    _ => {
+                        break;
+                    }
                 }
             }
             match last_non_nsm {
-                Some(c) if bidi_class(c) == BidiClass::L
-                    || bidi_class(c) == BidiClass::EN => {},
-                Some(_) => { return false; },
+                Some(c) if bidi_class(c) == BidiClass::L || bidi_class(c) == BidiClass::EN => {}
+                Some(_) => {
+                    return false;
+                }
                 _ => {}
             }
-
         }
 
         // RTL label
@@ -186,33 +195,51 @@ fn passes_bidi(label: &str, is_bidi_domain: bool) -> bool {
                             found_an = true;
                         }
 
-                        if !matches!(char_class, BidiClass::R | BidiClass::AL |
-                                     BidiClass::AN | BidiClass::EN |
-                                     BidiClass::ES | BidiClass::CS |
-                                     BidiClass::ET | BidiClass::ON |
-                                     BidiClass::BN | BidiClass::NSM) {
+                        if !matches!(
+                            char_class,
+                            BidiClass::R
+                                | BidiClass::AL
+                                | BidiClass::AN
+                                | BidiClass::EN
+                                | BidiClass::ES
+                                | BidiClass::CS
+                                | BidiClass::ET
+                                | BidiClass::ON
+                                | BidiClass::BN
+                                | BidiClass::NSM
+                        ) {
                             return false;
                         }
-                    },
-                    None => { break; },
+                    }
+                    None => {
+                        break;
+                    }
                 }
             }
             // Rule 3
             let mut rev_chars = label.chars().rev();
             let mut last = rev_chars.next();
-            loop { // must end in L or EN followed by 0 or more NSM
+            loop {
+                // must end in L or EN followed by 0 or more NSM
                 match last {
                     Some(c) if bidi_class(c) == BidiClass::NSM => {
                         last = rev_chars.next();
                         continue;
                     }
-                    _ => { break; },
+                    _ => {
+                        break;
+                    }
                 }
             }
             match last {
-                Some(c) if matches!(bidi_class(c), BidiClass::R | BidiClass::AL |
-                                    BidiClass::EN | BidiClass::AN) => {},
-                _ => { return false; }
+                Some(c)
+                    if matches!(
+                        bidi_class(c),
+                        BidiClass::R | BidiClass::AL | BidiClass::EN | BidiClass::AN
+                    ) => {}
+                _ => {
+                    return false;
+                }
             }
 
             // Rule 4
@@ -245,7 +272,6 @@ fn validate(label: &str, is_bidi_domain: bool, flags: Flags, errors: &mut Vec<Er
     if first_char == None {
         // Empty string, pass
     }
-
     // V2: No U+002D HYPHEN-MINUS in both third and fourth positions.
     //
     // NOTE: Spec says that the label must not contain a HYPHEN-MINUS character in both the
@@ -258,7 +284,6 @@ fn validate(label: &str, is_bidi_domain: bool, flags: Flags, errors: &mut Vec<Er
     else if label.starts_with("-") || label.ends_with("-") {
         errors.push(Error::ValidityCriteria);
     }
-
     // V4: not contain a U+002E FULL STOP
     //
     // Here, label can't contain '.' since the input is from .split('.')
@@ -267,7 +292,6 @@ fn validate(label: &str, is_bidi_domain: bool, flags: Flags, errors: &mut Vec<Er
     else if is_combining_mark(first_char.unwrap()) {
         errors.push(Error::ValidityCriteria);
     }
-
     // V6: Check against Mapping Table
     else if label.chars().any(|c| match *find_char(c) {
         Mapping::Valid => false,
@@ -277,7 +301,6 @@ fn validate(label: &str, is_bidi_domain: bool, flags: Flags, errors: &mut Vec<Er
     }) {
         errors.push(Error::ValidityCriteria);
     }
-
     // V7: ContextJ rules
     //
     // TODO: Implement rules and add *CheckJoiners* flag.
@@ -285,8 +308,7 @@ fn validate(label: &str, is_bidi_domain: bool, flags: Flags, errors: &mut Vec<Er
     // V8: Bidi rules
     //
     // TODO: Add *CheckBidi* flag
-    else if !passes_bidi(label, is_bidi_domain)
-    {
+    else if !passes_bidi(label, is_bidi_domain) {
         errors.push(Error::ValidityCriteria);
     }
 }
@@ -303,18 +325,18 @@ fn processing(domain: &str, flags: Flags, errors: &mut Vec<Error>) -> String {
     // Find out if it's a Bidi Domain Name
     //
     // First, check for literal bidi chars
-    let mut is_bidi_domain = domain.chars().any(|c|
-        matches!(bidi_class(c), BidiClass::R | BidiClass::AL | BidiClass::AN)
-    );
+    let mut is_bidi_domain = domain
+        .chars()
+        .any(|c| matches!(bidi_class(c), BidiClass::R | BidiClass::AL | BidiClass::AN));
     if !is_bidi_domain {
         // Then check for punycode-encoded bidi chars
         for label in normalized.split('.') {
             if label.starts_with(PUNYCODE_PREFIX) {
                 match punycode::decode_to_string(&label[PUNYCODE_PREFIX.len()..]) {
                     Some(decoded_label) => {
-                        if decoded_label.chars().any(|c|
+                        if decoded_label.chars().any(|c| {
                             matches!(bidi_class(c), BidiClass::R | BidiClass::AL | BidiClass::AN)
-                        ) {
+                        }) {
                             is_bidi_domain = true;
                         }
                     }
@@ -336,11 +358,14 @@ fn processing(domain: &str, flags: Flags, errors: &mut Vec<Error>) -> String {
         if label.starts_with(PUNYCODE_PREFIX) {
             match punycode::decode_to_string(&label[PUNYCODE_PREFIX.len()..]) {
                 Some(decoded_label) => {
-                    let flags = Flags { transitional_processing: false, ..flags };
+                    let flags = Flags {
+                        transitional_processing: false,
+                        ..flags
+                    };
                     validate_full(&decoded_label, is_bidi_domain, flags, errors);
                     validated.push_str(&decoded_label)
                 }
-                None => errors.push(Error::PunycodeError)
+                None => errors.push(Error::PunycodeError),
             }
         } else {
             // `normalized` is already `NFC` so we can skip that check
@@ -353,9 +378,9 @@ fn processing(domain: &str, flags: Flags, errors: &mut Vec<Error>) -> String {
 
 #[derive(Copy, Clone)]
 pub struct Flags {
-   pub use_std3_ascii_rules: bool,
-   pub transitional_processing: bool,
-   pub verify_dns_length: bool,
+    pub use_std3_ascii_rules: bool,
+    pub transitional_processing: bool,
+    pub verify_dns_length: bool,
 }
 
 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
@@ -393,14 +418,18 @@ pub fn to_ascii(domain: &str, flags: Flags) -> Result<String, Errors> {
                 Some(x) => {
                     result.push_str(PUNYCODE_PREFIX);
                     result.push_str(&x);
-                },
-                None => errors.push(Error::PunycodeError)
+                }
+                None => errors.push(Error::PunycodeError),
             }
         }
     }
 
     if flags.verify_dns_length {
-        let domain = if result.ends_with(".") { &result[..result.len()-1]  } else { &*result };
+        let domain = if result.ends_with(".") {
+            &result[..result.len() - 1]
+        } else {
+            &*result
+        };
         if domain.len() < 1 || domain.split('.').any(|label| label.len() < 1) {
             errors.push(Error::TooShortForDns)
         }

+ 35 - 25
idna/tests/punycode.rs

@@ -15,19 +15,25 @@ fn one_test(decoded: &str, encoded: &str) {
         None => panic!("Decoding {} failed.", encoded),
         Some(result) => {
             let result = result.into_iter().collect::<String>();
-            assert!(result == decoded,
-                    format!("Incorrect decoding of \"{}\":\n   \"{}\"\n!= \"{}\"\n",
-                            encoded, result, decoded))
+            assert!(
+                result == decoded,
+                format!(
+                    "Incorrect decoding of \"{}\":\n   \"{}\"\n!= \"{}\"\n",
+                    encoded, result, decoded
+                )
+            )
         }
     }
 
     match encode_str(decoded) {
         None => panic!("Encoding {} failed.", decoded),
-        Some(result) => {
-            assert!(result == encoded,
-                    format!("Incorrect encoding of \"{}\":\n   \"{}\"\n!= \"{}\"\n",
-                            decoded, result, encoded))
-        }
+        Some(result) => assert!(
+            result == encoded,
+            format!(
+                "Incorrect encoding of \"{}\":\n   \"{}\"\n!= \"{}\"\n",
+                decoded, result, encoded
+            )
+        ),
     }
 }
 
@@ -41,25 +47,29 @@ fn get_string<'a>(map: &'a Object, key: &str) -> &'a str {
 
 pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
     match Json::from_str(include_str!("punycode_tests.json")) {
-        Ok(Json::Array(tests)) => for (i, test) in tests.into_iter().enumerate() {
-            match test {
-                Json::Object(o) => {
-                    let test_name = {
-                        let desc = get_string(&o, "description");
+        Ok(Json::Array(tests)) => {
+            for (i, test) in tests.into_iter().enumerate() {
+                match test {
+                    Json::Object(o) => {
+                        let test_name = {
+                            let desc = get_string(&o, "description");
                             if desc.is_empty() {
-                            format!("Punycode {}", i + 1)
-                        } else {
-                            format!("Punycode {}: {}", i + 1, desc)
-                        }
-                    };
-                    add_test(test_name, TestFn::dyn_test_fn(move || one_test(
-                        get_string(&o, "decoded"),
-                        get_string(&o, "encoded"),
-                    )))
+                                format!("Punycode {}", i + 1)
+                            } else {
+                                format!("Punycode {}: {}", i + 1, desc)
+                            }
+                        };
+                        add_test(
+                            test_name,
+                            TestFn::dyn_test_fn(move || {
+                                one_test(get_string(&o, "decoded"), get_string(&o, "encoded"))
+                            }),
+                        )
+                    }
+                    _ => panic!(),
                 }
-                _ => panic!(),
             }
-        },
-        other => panic!("{:?}", other)
+        }
+        other => panic!("{:?}", other),
     }
 }

+ 12 - 7
idna/tests/unit.rs

@@ -4,13 +4,15 @@ extern crate unicode_normalization;
 use idna::uts46;
 use unicode_normalization::char::is_combining_mark;
 
-
 fn _to_ascii(domain: &str) -> Result<String, uts46::Errors> {
-    uts46::to_ascii(domain, uts46::Flags {
-        transitional_processing: false,
-        use_std3_ascii_rules: true,
-        verify_dns_length: true,
-    })
+    uts46::to_ascii(
+        domain,
+        uts46::Flags {
+            transitional_processing: false,
+            use_std3_ascii_rules: true,
+            verify_dns_length: true,
+        },
+    )
 }
 
 #[test]
@@ -29,7 +31,10 @@ fn test_v8_bidi_rules() {
     assert_eq!(_to_ascii("אבּג").unwrap(), "xn--kdb3bdf");
     assert_eq!(_to_ascii("ابج").unwrap(), "xn--mgbcm");
     assert_eq!(_to_ascii("abc.ابج").unwrap(), "abc.xn--mgbcm");
-    assert_eq!(_to_ascii("אבּג.ابج").unwrap(), "xn--kdb3bdf.xn--mgbcm");
+    assert_eq!(
+        _to_ascii("אבּג.ابج").unwrap(),
+        "xn--kdb3bdf.xn--mgbcm"
+    );
 
     // Bidi domain names cannot start with digits
     assert!(_to_ascii("0a.\u{05D0}").is_err());

+ 78 - 49
idna/tests/uts46.rs

@@ -6,20 +6,20 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::char;
 use idna::uts46;
+use std::char;
 use test::TestFn;
 
 pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
     // http://www.unicode.org/Public/idna/latest/IdnaTest.txt
     for (i, line) in include_str!("IdnaTest.txt").lines().enumerate() {
         if line == "" || line.starts_with("#") {
-            continue
+            continue;
         }
         // Remove comments
         let mut line = match line.find("#") {
             Some(index) => &line[0..index],
-            None => line
+            None => line,
         };
 
         let mut expected_failure = false;
@@ -35,61 +35,87 @@ pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
         let source = unescape(original);
         let to_unicode = pieces.remove(0);
         let to_ascii = pieces.remove(0);
-        let nv8 = if pieces.len() > 0 { pieces.remove(0) } else { "" };
+        let nv8 = if pieces.len() > 0 {
+            pieces.remove(0)
+        } else {
+            ""
+        };
 
         if expected_failure {
             continue;
         }
 
         let test_name = format!("UTS #46 line {}", i + 1);
-        add_test(test_name, TestFn::dyn_test_fn(move || {
-            let result = uts46::to_ascii(&source, uts46::Flags {
-                use_std3_ascii_rules: true,
-                transitional_processing: test_type == "T",
-                verify_dns_length: true,
-            });
+        add_test(
+            test_name,
+            TestFn::dyn_test_fn(move || {
+                let result = uts46::to_ascii(
+                    &source,
+                    uts46::Flags {
+                        use_std3_ascii_rules: true,
+                        transitional_processing: test_type == "T",
+                        verify_dns_length: true,
+                    },
+                );
 
-            if to_ascii.starts_with("[") {
-                if to_ascii.starts_with("[C") {
-                    // http://unicode.org/reports/tr46/#Deviations
-                    // applications that perform IDNA2008 lookup are not required to check
-                    // for these contexts
-                    return;
-                }
-                if to_ascii == "[V2]" {
-                    // Everybody ignores V2
-                    // https://github.com/servo/rust-url/pull/240
-                    // https://github.com/whatwg/url/issues/53#issuecomment-181528158
-                    // http://www.unicode.org/review/pri317/
+                if to_ascii.starts_with("[") {
+                    if to_ascii.starts_with("[C") {
+                        // http://unicode.org/reports/tr46/#Deviations
+                        // applications that perform IDNA2008 lookup are not required to check
+                        // for these contexts
+                        return;
+                    }
+                    if to_ascii == "[V2]" {
+                        // Everybody ignores V2
+                        // https://github.com/servo/rust-url/pull/240
+                        // https://github.com/whatwg/url/issues/53#issuecomment-181528158
+                        // http://www.unicode.org/review/pri317/
+                        return;
+                    }
+                    let res = result.ok();
+                    assert!(
+                        res == None,
+                        "Expected error. result: {} | original: {} | source: {}",
+                        res.unwrap(),
+                        original,
+                        source
+                    );
                     return;
                 }
-                let res = result.ok();
-                assert!(res == None, "Expected error. result: {} | original: {} | source: {}",
-                        res.unwrap(), original, source);
-                return;
-            }
 
-            let to_ascii = if to_ascii.len() > 0 {
-                to_ascii.to_string()
-            } else {
-                if to_unicode.len() > 0 {
-                    to_unicode.to_string()
+                let to_ascii = if to_ascii.len() > 0 {
+                    to_ascii.to_string()
                 } else {
-                    source.clone()
-                }
-            };
+                    if to_unicode.len() > 0 {
+                        to_unicode.to_string()
+                    } else {
+                        source.clone()
+                    }
+                };
 
-            if nv8 == "NV8" {
-                // This result isn't valid under IDNA2008. Skip it
-                return;
-            }
+                if nv8 == "NV8" {
+                    // This result isn't valid under IDNA2008. Skip it
+                    return;
+                }
 
-            assert!(result.is_ok(), "Couldn't parse {} | original: {} | error: {:?}",
-                    source, original, result.err());
-            let output = result.ok().unwrap();
-            assert!(output == to_ascii, "result: {} | expected: {} | original: {} | source: {}",
-                    output, to_ascii, original, source);
-        }))
+                assert!(
+                    result.is_ok(),
+                    "Couldn't parse {} | original: {} | error: {:?}",
+                    source,
+                    original,
+                    result.err()
+                );
+                let output = result.ok().unwrap();
+                assert!(
+                    output == to_ascii,
+                    "result: {} | expected: {} | original: {} | source: {}",
+                    output,
+                    to_ascii,
+                    original,
+                    source
+                );
+            }),
+        )
     }
 }
 
@@ -99,7 +125,7 @@ fn unescape(input: &str) -> String {
     loop {
         match chars.next() {
             None => return output,
-            Some(c) =>
+            Some(c) => {
                 if c == '\\' {
                     match chars.next().unwrap() {
                         '\\' => output.push('\\'),
@@ -108,10 +134,12 @@ fn unescape(input: &str) -> String {
                             let c2 = chars.next().unwrap().to_digit(16).unwrap();
                             let c3 = chars.next().unwrap().to_digit(16).unwrap();
                             let c4 = chars.next().unwrap().to_digit(16).unwrap();
-                            match char::from_u32(((c1 * 16 + c2) * 16 + c3) * 16 + c4)
-                            {
+                            match char::from_u32(((c1 * 16 + c2) * 16 + c3) * 16 + c4) {
                                 Some(c) => output.push(c),
-                                None => { output.push_str(&format!("\\u{:X}{:X}{:X}{:X}",c1,c2,c3,c4)); }
+                                None => {
+                                    output
+                                        .push_str(&format!("\\u{:X}{:X}{:X}{:X}", c1, c2, c3, c4));
+                                }
                             };
                         }
                         _ => panic!("Invalid test data input"),
@@ -119,6 +147,7 @@ fn unescape(input: &str) -> String {
                 } else {
                     output.push(c);
                 }
+            }
         }
     }
 }

+ 37 - 47
percent_encoding/lib.rs

@@ -175,23 +175,23 @@ define_encode_set! {
 pub fn percent_encode_byte(byte: u8) -> &'static str {
     let index = usize::from(byte) * 3;
     &"\
-        %00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F\
-        %10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F\
-        %20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F\
-        %30%31%32%33%34%35%36%37%38%39%3A%3B%3C%3D%3E%3F\
-        %40%41%42%43%44%45%46%47%48%49%4A%4B%4C%4D%4E%4F\
-        %50%51%52%53%54%55%56%57%58%59%5A%5B%5C%5D%5E%5F\
-        %60%61%62%63%64%65%66%67%68%69%6A%6B%6C%6D%6E%6F\
-        %70%71%72%73%74%75%76%77%78%79%7A%7B%7C%7D%7E%7F\
-        %80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F\
-        %90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F\
-        %A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF\
-        %B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF\
-        %C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF\
-        %D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF\
-        %E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF\
-        %F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF\
-    "[index..index + 3]
+      %00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F\
+      %10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F\
+      %20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F\
+      %30%31%32%33%34%35%36%37%38%39%3A%3B%3C%3D%3E%3F\
+      %40%41%42%43%44%45%46%47%48%49%4A%4B%4C%4D%4E%4F\
+      %50%51%52%53%54%55%56%57%58%59%5A%5B%5C%5D%5E%5F\
+      %60%61%62%63%64%65%66%67%68%69%6A%6B%6C%6D%6E%6F\
+      %70%71%72%73%74%75%76%77%78%79%7A%7B%7C%7D%7E%7F\
+      %80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F\
+      %90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F\
+      %A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF\
+      %B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF\
+      %C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF\
+      %D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF\
+      %E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF\
+      %F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF\
+      "[index..index + 3]
 }
 
 /// Percent-encode the given bytes with the given encode set.
@@ -259,7 +259,7 @@ impl<'a, E: EncodeSet> Iterator for PercentEncode<'a, E> {
                         // 1 for first_byte + i for previous iterations of this loop
                         let (unchanged_slice, remaining) = self.bytes.split_at(1 + i);
                         self.bytes = remaining;
-                        return Some(unsafe { str::from_utf8_unchecked(unchanged_slice) })
+                        return Some(unsafe { str::from_utf8_unchecked(unchanged_slice) });
                     } else {
                         assert!(byte.is_ascii());
                     }
@@ -295,17 +295,15 @@ impl<'a, E: EncodeSet> From<PercentEncode<'a, E>> for Cow<'a, str> {
     fn from(mut iter: PercentEncode<'a, E>) -> Self {
         match iter.next() {
             None => "".into(),
-            Some(first) => {
-                match iter.next() {
-                    None => first.into(),
-                    Some(second) => {
-                        let mut string = first.to_owned();
-                        string.push_str(second);
-                        string.extend(iter);
-                        string.into()
-                    }
+            Some(first) => match iter.next() {
+                None => first.into(),
+                Some(second) => {
+                    let mut string = first.to_owned();
+                    string.push_str(second);
+                    string.extend(iter);
+                    string.into()
                 }
-            }
+            },
         }
     }
 }
@@ -327,7 +325,7 @@ impl<'a, E: EncodeSet> From<PercentEncode<'a, E>> for Cow<'a, str> {
 #[inline]
 pub fn percent_decode(input: &[u8]) -> PercentDecode {
     PercentDecode {
-        bytes: input.iter()
+        bytes: input.iter(),
     }
 }
 
@@ -387,10 +385,8 @@ impl<'a> PercentDecode<'a> {
                 let unchanged_bytes_len = initial_bytes.len() - bytes_iter.len() - 3;
                 let mut decoded = initial_bytes[..unchanged_bytes_len].to_owned();
                 decoded.push(decoded_byte);
-                decoded.extend(PercentDecode {
-                    bytes: bytes_iter
-                });
-                return Some(decoded)
+                decoded.extend(PercentDecode { bytes: bytes_iter });
+                return Some(decoded);
             }
         }
         // Nothing to decode
@@ -402,18 +398,14 @@ impl<'a> PercentDecode<'a> {
     /// This is return `Err` when the percent-decoded bytes are not well-formed in UTF-8.
     pub fn decode_utf8(self) -> Result<Cow<'a, str>, str::Utf8Error> {
         match self.clone().into() {
-            Cow::Borrowed(bytes) => {
-                match str::from_utf8(bytes) {
-                    Ok(s) => Ok(s.into()),
-                    Err(e) => Err(e),
-                }
-            }
-            Cow::Owned(bytes) => {
-                match String::from_utf8(bytes) {
-                    Ok(s) => Ok(s.into()),
-                    Err(e) => Err(e.utf8_error()),
-                }
-            }
+            Cow::Borrowed(bytes) => match str::from_utf8(bytes) {
+                Ok(s) => Ok(s.into()),
+                Err(e) => Err(e),
+            },
+            Cow::Owned(bytes) => match String::from_utf8(bytes) {
+                Ok(s) => Ok(s.into()),
+                Err(e) => Err(e.utf8_error()),
+            },
         }
     }
 
@@ -442,5 +434,3 @@ fn decode_utf8_lossy(input: Cow<[u8]>) -> Cow<str> {
         }
     }
 }
-
-

+ 27 - 16
src/encoding.rs

@@ -6,24 +6,28 @@
 // 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")]
+extern crate encoding;
 
 use std::borrow::Cow;
-#[cfg(feature = "query_encoding")] use std::fmt::{self, Debug, Formatter};
+#[cfg(feature = "query_encoding")]
+use std::fmt::{self, Debug, Formatter};
 
-#[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")]
+use self::encoding::label::encoding_from_whatwg_label;
+#[cfg(feature = "query_encoding")]
+pub use self::encoding::types::EncodingRef;
+#[cfg(feature = "query_encoding")]
+use self::encoding::types::{DecoderTrap, EncoderTrap};
 
 #[cfg(feature = "query_encoding")]
 #[derive(Copy, Clone)]
 pub struct EncodingOverride {
     /// `None` means UTF-8.
-    encoding: Option<EncodingRef>
+    encoding: Option<EncodingRef>,
 }
 
 #[cfg(feature = "query_encoding")]
@@ -34,7 +38,11 @@ impl EncodingOverride {
 
     pub fn from_encoding(encoding: EncodingRef) -> Self {
         EncodingOverride {
-            encoding: if encoding.name() == "utf-8" { None } else { Some(encoding) }
+            encoding: if encoding.name() == "utf-8" {
+                None
+            } else {
+                Some(encoding)
+            },
         }
     }
 
@@ -47,16 +55,16 @@ impl EncodingOverride {
         // Don't use String::from_utf8_lossy since no encoding label contains U+FFFD
         // https://encoding.spec.whatwg.org/#names-and-labels
         ::std::str::from_utf8(label)
-        .ok()
-        .and_then(encoding_from_whatwg_label)
-        .map(Self::from_encoding)
+            .ok()
+            .and_then(encoding_from_whatwg_label)
+            .map(Self::from_encoding)
     }
 
     /// https://encoding.spec.whatwg.org/#get-an-output-encoding
     pub fn to_output_encoding(self) -> Self {
         if let Some(encoding) = self.encoding {
             if matches!(encoding.name(), "utf-16le" | "utf-16be") {
-                return Self::utf8()
+                return Self::utf8();
             }
         }
         self
@@ -76,7 +84,10 @@ impl EncodingOverride {
     pub fn decode<'a>(&self, input: Cow<'a, [u8]>) -> Cow<'a, str> {
         match self.encoding {
             // `encoding.decode` never returns `Err` when called with `DecoderTrap::Replace`
-            Some(encoding) => encoding.decode(&input, DecoderTrap::Replace).unwrap().into(),
+            Some(encoding) => encoding
+                .decode(&input, DecoderTrap::Replace)
+                .unwrap()
+                .into(),
             None => decode_utf8_lossy(input),
         }
     }
@@ -85,7 +96,7 @@ impl EncodingOverride {
         match self.encoding {
             // `encoding.encode` never returns `Err` when called with `EncoderTrap::NcrEscape`
             Some(encoding) => Cow::Owned(encoding.encode(&input, EncoderTrap::NcrEscape).unwrap()),
-            None => encode_utf8(input)
+            None => encode_utf8(input),
         }
     }
 }
@@ -96,7 +107,7 @@ impl Debug for EncodingOverride {
         write!(f, "EncodingOverride {{ encoding: ")?;
         match self.encoding {
             Some(e) => write!(f, "{} }}", e.name()),
-            None => write!(f, "None }}")
+            None => write!(f, "None }}"),
         }
     }
 }
@@ -141,6 +152,6 @@ pub fn decode_utf8_lossy(input: Cow<[u8]>) -> Cow<str> {
 pub fn encode_utf8(input: Cow<str>) -> Cow<[u8]> {
     match input {
         Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
-        Cow::Owned(s) => Cow::Owned(s.into_bytes())
+        Cow::Owned(s) => Cow::Owned(s.into_bytes()),
     }
 }

+ 84 - 42
src/form_urlencoded.rs

@@ -14,12 +14,11 @@
 //! and a sequence of (name, value) pairs.
 
 use encoding::EncodingOverride;
-use percent_encoding::{percent_encode_byte, percent_decode};
+use percent_encoding::{percent_decode, percent_encode_byte};
 use std::borrow::{Borrow, Cow};
 use std::fmt;
 use std::str;
 
-
 /// Convert a byte string in the `application/x-www-form-urlencoded` syntax
 /// into a iterator of (name, value) pairs.
 ///
@@ -35,7 +34,6 @@ pub fn parse(input: &[u8]) -> Parse {
     }
 }
 
-
 /// Convert a byte string in the `application/x-www-form-urlencoded` syntax
 /// into a iterator of (name, value) pairs.
 ///
@@ -51,14 +49,14 @@ pub fn parse(input: &[u8]) -> Parse {
 ///    `EncodingRef` is defined in [rust-encoding](https://github.com/lifthrasiir/rust-encoding).
 /// * `use_charset`: The *use _charset_ flag*. If in doubt, set to `false`.
 #[cfg(feature = "query_encoding")]
-pub fn parse_with_encoding<'a>(input: &'a [u8],
-                               encoding_override: Option<::encoding::EncodingRef>,
-                               use_charset: bool)
-                               -> Result<Parse<'a>, ()> {
-
+pub fn parse_with_encoding<'a>(
+    input: &'a [u8],
+    encoding_override: Option<::encoding::EncodingRef>,
+    use_charset: bool,
+) -> Result<Parse<'a>, ()> {
     let mut encoding = EncodingOverride::from_opt_encoding(encoding_override);
     if !(encoding.is_utf8() || input.is_ascii()) {
-        return Err(())
+        return Err(());
     }
     if use_charset {
         for sequence in input.split(|&b| b == b'&') {
@@ -69,7 +67,7 @@ pub fn parse_with_encoding<'a>(input: &'a [u8],
                 // https://encoding.spec.whatwg.org/#names-and-labels
                 if let Some(e) = EncodingOverride::lookup(value) {
                     encoding = e;
-                    break
+                    break;
                 }
             }
         }
@@ -93,21 +91,18 @@ impl<'a> Iterator for Parse<'a> {
     fn next(&mut self) -> Option<Self::Item> {
         loop {
             if self.input.is_empty() {
-                return None
+                return None;
             }
             let mut split2 = self.input.splitn(2, |&b| b == b'&');
             let sequence = split2.next().unwrap();
             self.input = split2.next().unwrap_or(&[][..]);
             if sequence.is_empty() {
-                continue
+                continue;
             }
             let mut split2 = sequence.splitn(2, |&b| b == b'=');
             let name = split2.next().unwrap();
             let value = split2.next().unwrap_or(&[][..]);
-            return Some((
-                decode(name, self.encoding),
-                decode(value, self.encoding),
-            ))
+            return Some((decode(name, self.encoding), decode(value, self.encoding)));
         }
     }
 }
@@ -147,14 +142,16 @@ impl<'a> Parse<'a> {
 /// Like `Parse`, but yields pairs of `String` instead of pairs of `Cow<str>`.
 #[derive(Debug)]
 pub struct ParseIntoOwned<'a> {
-    inner: Parse<'a>
+    inner: Parse<'a>,
 }
 
 impl<'a> Iterator for ParseIntoOwned<'a> {
     type Item = (String, String);
 
     fn next(&mut self) -> Option<Self::Item> {
-        self.inner.next().map(|(k, v)| (k.into_owned(), v.into_owned()))
+        self.inner
+            .next()
+            .map(|(k, v)| (k.into_owned(), v.into_owned()))
     }
 }
 
@@ -163,9 +160,7 @@ impl<'a> Iterator for ParseIntoOwned<'a> {
 ///
 /// Return an iterator of `&str` slices.
 pub fn byte_serialize(input: &[u8]) -> ByteSerialize {
-    ByteSerialize {
-        bytes: input,
-    }
+    ByteSerialize { bytes: input }
 }
 
 /// Return value of `byte_serialize()`.
@@ -185,7 +180,11 @@ impl<'a> Iterator for ByteSerialize<'a> {
         if let Some((&first, tail)) = self.bytes.split_first() {
             if !byte_serialized_unchanged(first) {
                 self.bytes = tail;
-                return Some(if first == b' ' { "+" } else { percent_encode_byte(first) })
+                return Some(if first == b' ' {
+                    "+"
+                } else {
+                    percent_encode_byte(first)
+                });
             }
             let position = tail.iter().position(|&b| !byte_serialized_unchanged(b));
             let (unchanged_slice, remaining) = match position {
@@ -234,14 +233,22 @@ pub trait Target {
 }
 
 impl Target for String {
-    fn as_mut_string(&mut self) -> &mut String { self }
-    fn finish(self) -> Self { self }
+    fn as_mut_string(&mut self) -> &mut String {
+        self
+    }
+    fn finish(self) -> Self {
+        self
+    }
     type Finished = Self;
 }
 
 impl<'a> Target for &'a mut String {
-    fn as_mut_string(&mut self) -> &mut String { &mut **self }
-    fn finish(self) -> Self { self }
+    fn as_mut_string(&mut self) -> &mut String {
+        &mut **self
+    }
+    fn finish(self) -> Self {
+        self
+    }
     type Finished = Self;
 }
 
@@ -284,7 +291,7 @@ impl<T: Target> Serializer<T> {
     /// If that suffix is non-empty,
     /// its content is assumed to already be in `application/x-www-form-urlencoded` syntax.
     pub fn for_suffix(mut target: T, start_position: usize) -> Self {
-        &target.as_mut_string()[start_position..];  // Panic if out of bounds
+        &target.as_mut_string()[start_position..]; // Panic if out of bounds
         Serializer {
             target: Some(target),
             start_position: start_position,
@@ -310,7 +317,8 @@ impl<T: Target> Serializer<T> {
 
     /// Set the character encoding to be used for names and values before percent-encoding.
     pub fn custom_encoding_override<F>(&mut self, encode: F) -> &mut Self
-        where F: FnMut(&str) -> Cow<[u8]> + 'static
+    where
+        F: FnMut(&str) -> Cow<[u8]> + 'static,
     {
         self.custom_encoding = Some(SilentDebug(Box::new(encode)));
         self
@@ -320,8 +328,14 @@ impl<T: Target> Serializer<T> {
     ///
     /// Panics if called after `.finish()`.
     pub fn append_pair(&mut self, name: &str, value: &str) -> &mut Self {
-        append_pair(string(&mut self.target), self.start_position, self.encoding,
-                    &mut self.custom_encoding, name, value);
+        append_pair(
+            string(&mut self.target),
+            self.start_position,
+            self.encoding,
+            &mut self.custom_encoding,
+            name,
+            value,
+        );
         self
     }
 
@@ -333,13 +347,24 @@ impl<T: Target> Serializer<T> {
     ///
     /// Panics if called after `.finish()`.
     pub fn extend_pairs<I, K, V>(&mut self, iter: I) -> &mut Self
-    where I: IntoIterator, I::Item: Borrow<(K, V)>, K: AsRef<str>, V: AsRef<str> {
+    where
+        I: IntoIterator,
+        I::Item: Borrow<(K, V)>,
+        K: AsRef<str>,
+        V: AsRef<str>,
+    {
         {
             let string = string(&mut self.target);
             for pair in iter {
                 let &(ref k, ref v) = pair.borrow();
-                append_pair(string, self.start_position, self.encoding,
-                            &mut self.custom_encoding, k.as_ref(), v.as_ref());
+                append_pair(
+                    string,
+                    self.start_position,
+                    self.encoding,
+                    &mut self.custom_encoding,
+                    k.as_ref(),
+                    v.as_ref(),
+                );
             }
         }
         self
@@ -352,8 +377,10 @@ impl<T: Target> Serializer<T> {
     /// Panics if called after `.finish()`.
     #[cfg(feature = "query_encoding")]
     pub fn append_charset(&mut self) -> &mut Self {
-        assert!(self.custom_encoding.is_none(),
-                "Cannot use both custom_encoding_override() and append_charset()");
+        assert!(
+            self.custom_encoding.is_none(),
+            "Cannot use both custom_encoding_override() and append_charset()"
+        );
         {
             let string = string(&mut self.target);
             append_separator_if_needed(string, self.start_position);
@@ -376,7 +403,10 @@ impl<T: Target> Serializer<T> {
     ///
     /// Panics if called more than once.
     pub fn finish(&mut self) -> T::Finished {
-        self.target.take().expect("url::form_urlencoded::Serializer double finish").finish()
+        self.target
+            .take()
+            .expect("url::form_urlencoded::Serializer double finish")
+            .finish()
     }
 }
 
@@ -387,20 +417,32 @@ fn append_separator_if_needed(string: &mut String, start_position: usize) {
 }
 
 fn string<T: Target>(target: &mut Option<T>) -> &mut String {
-    target.as_mut().expect("url::form_urlencoded::Serializer finished").as_mut_string()
+    target
+        .as_mut()
+        .expect("url::form_urlencoded::Serializer finished")
+        .as_mut_string()
 }
 
-fn append_pair(string: &mut String, start_position: usize, encoding: EncodingOverride,
-               custom_encoding: &mut Option<SilentDebug<Box<dyn FnMut(&str) -> Cow<[u8]>>>>,
-               name: &str, value: &str) {
+fn append_pair(
+    string: &mut String,
+    start_position: usize,
+    encoding: EncodingOverride,
+    custom_encoding: &mut Option<SilentDebug<Box<dyn FnMut(&str) -> Cow<[u8]>>>>,
+    name: &str,
+    value: &str,
+) {
     append_separator_if_needed(string, start_position);
     append_encoded(name, string, encoding, custom_encoding);
     string.push('=');
     append_encoded(value, string, encoding, custom_encoding);
 }
 
-fn append_encoded(s: &str, string: &mut String, encoding: EncodingOverride,
-               custom_encoding: &mut Option<SilentDebug<Box<dyn FnMut(&str) -> Cow<[u8]>>>>) {
+fn append_encoded(
+    s: &str,
+    string: &mut String,
+    encoding: EncodingOverride,
+    custom_encoding: &mut Option<SilentDebug<Box<dyn FnMut(&str) -> Cow<[u8]>>>>,
+) {
     let bytes = if let Some(SilentDebug(ref mut custom)) = *custom_encoding {
         custom(s)
     } else {

+ 125 - 69
src/host.rs

@@ -6,15 +6,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[cfg(feature = "heapsize")] use heapsize::HeapSizeOf;
+#[cfg(feature = "heapsize")]
+use heapsize::HeapSizeOf;
+use idna;
+use parser::{ParseError, ParseResult};
+use percent_encoding::{percent_decode, utf8_percent_encode, SIMPLE_ENCODE_SET};
 use std::cmp;
 use std::fmt::{self, Formatter};
 use std::io;
 use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
 use std::vec;
-use parser::{ParseResult, ParseError};
-use percent_encoding::{percent_decode, utf8_percent_encode, SIMPLE_ENCODE_SET};
-use idna;
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub enum HostInternal {
@@ -27,9 +28,12 @@ pub enum HostInternal {
 #[cfg(feature = "heapsize")]
 known_heap_size!(0, HostInternal);
 
-#[cfg(feature="serde")]
+#[cfg(feature = "serde")]
 impl ::serde::Serialize for HostInternal {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: ::serde::Serializer,
+    {
         // This doesn’t use `derive` because that involves
         // large dependencies (that take a long time to build), and
         // either Macros 1.1 which are not stable yet or a cumbersome build script.
@@ -42,13 +46,17 @@ impl ::serde::Serialize for HostInternal {
             HostInternal::Domain => Some(None),
             HostInternal::Ipv4(addr) => Some(Some(IpAddr::V4(addr))),
             HostInternal::Ipv6(addr) => Some(Some(IpAddr::V6(addr))),
-        }.serialize(serializer)
+        }
+        .serialize(serializer)
     }
 }
 
-#[cfg(feature="serde")]
+#[cfg(feature = "serde")]
 impl<'de> ::serde::Deserialize<'de> for HostInternal {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: ::serde::Deserializer<'de> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: ::serde::Deserializer<'de>,
+    {
         use std::net::IpAddr;
         Ok(match ::serde::Deserialize::deserialize(deserializer)? {
             None => HostInternal::None,
@@ -71,7 +79,7 @@ impl<S> From<Host<S>> for HostInternal {
 
 /// The host name of an URL.
 #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub enum Host<S=String> {
+pub enum Host<S = String> {
     /// A DNS domain name, as '.' dot-separated labels.
     /// Non-ASCII labels are encoded in punycode per IDNA if this is the host of
     /// a special URL, or percent encoded for non-special URLs. Hosts for
@@ -91,21 +99,28 @@ pub enum Host<S=String> {
     Ipv6(Ipv6Addr),
 }
 
-#[cfg(feature="serde")]
+#[cfg(feature = "serde")]
 impl<S: ::serde::Serialize> ::serde::Serialize for Host<S> {
-    fn serialize<R>(&self, serializer: R) -> Result<R::Ok, R::Error> where R: ::serde::Serializer {
+    fn serialize<R>(&self, serializer: R) -> Result<R::Ok, R::Error>
+    where
+        R: ::serde::Serializer,
+    {
         use std::net::IpAddr;
         match *self {
             Host::Domain(ref s) => Ok(s),
             Host::Ipv4(addr) => Err(IpAddr::V4(addr)),
             Host::Ipv6(addr) => Err(IpAddr::V6(addr)),
-        }.serialize(serializer)
+        }
+        .serialize(serializer)
     }
 }
 
-#[cfg(feature="serde")]
+#[cfg(feature = "serde")]
 impl<'de, S: ::serde::Deserialize<'de>> ::serde::Deserialize<'de> for Host<S> {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: ::serde::Deserializer<'de> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: ::serde::Deserializer<'de>,
+    {
         use std::net::IpAddr;
         Ok(match ::serde::Deserialize::deserialize(deserializer)? {
             Ok(s) => Host::Domain(s),
@@ -143,16 +158,34 @@ impl Host<String> {
     pub fn parse(input: &str) -> Result<Self, ParseError> {
         if input.starts_with('[') {
             if !input.ends_with(']') {
-                return Err(ParseError::InvalidIpv6Address)
+                return Err(ParseError::InvalidIpv6Address);
             }
-            return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6)
+            return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
         }
         let domain = percent_decode(input.as_bytes()).decode_utf8_lossy();
         let domain = idna::domain_to_ascii(&domain)?;
-        if domain.find(|c| matches!(c,
-            '\0' | '\t' | '\n' | '\r' | ' ' | '#' | '%' | '/' | ':' | '?' | '@' | '[' | '\\' | ']'
-        )).is_some() {
-            return Err(ParseError::InvalidDomainCharacter)
+        if domain
+            .find(|c| {
+                matches!(
+                    c,
+                    '\0' | '\t'
+                        | '\n'
+                        | '\r'
+                        | ' '
+                        | '#'
+                        | '%'
+                        | '/'
+                        | ':'
+                        | '?'
+                        | '@'
+                        | '['
+                        | '\\'
+                        | ']'
+                )
+            })
+            .is_some()
+        {
+            return Err(ParseError::InvalidDomainCharacter);
         }
         if let Some(address) = parse_ipv4addr(&domain)? {
             Ok(Host::Ipv4(address))
@@ -165,14 +198,31 @@ impl Host<String> {
     pub fn parse_opaque(input: &str) -> Result<Self, ParseError> {
         if input.starts_with('[') {
             if !input.ends_with(']') {
-                return Err(ParseError::InvalidIpv6Address)
+                return Err(ParseError::InvalidIpv6Address);
             }
-            return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6)
+            return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
         }
-        if input.find(|c| matches!(c,
-            '\0' | '\t' | '\n' | '\r' | ' ' | '#' | '/' | ':' | '?' | '@' | '[' | '\\' | ']'
-        )).is_some() {
-            return Err(ParseError::InvalidDomainCharacter)
+        if input
+            .find(|c| {
+                matches!(
+                    c,
+                    '\0' | '\t'
+                        | '\n'
+                        | '\r'
+                        | ' '
+                        | '#'
+                        | '/'
+                        | ':'
+                        | '?'
+                        | '@'
+                        | '['
+                        | '\\'
+                        | ']'
+                )
+            })
+            .is_some()
+        {
+            return Err(ParseError::InvalidDomainCharacter);
         }
         let s = utf8_percent_encode(input, SIMPLE_ENCODE_SET).to_string();
         Ok(Host::Domain(s))
@@ -196,7 +246,7 @@ impl<S: AsRef<str>> fmt::Display for Host<S> {
 /// This mostly exists because coherence rules don’t allow us to implement
 /// `ToSocketAddrs for (Host<S>, u16)`.
 #[derive(Clone, Debug)]
-pub struct HostAndPort<S=String> {
+pub struct HostAndPort<S = String> {
     pub host: Host<S>,
     pub port: u16,
 }
@@ -206,7 +256,7 @@ impl<'a> HostAndPort<&'a str> {
     pub fn to_owned(&self) -> HostAndPort<String> {
         HostAndPort {
             host: self.host.to_owned(),
-            port: self.port
+            port: self.port,
         }
     }
 }
@@ -219,7 +269,6 @@ impl<S: AsRef<str>> fmt::Display for HostAndPort<S> {
     }
 }
 
-
 impl<S: AsRef<str>> ToSocketAddrs for HostAndPort<S> {
     type Iter = SocketAddrs;
 
@@ -228,13 +277,15 @@ impl<S: AsRef<str>> ToSocketAddrs for HostAndPort<S> {
         match self.host {
             Host::Domain(ref domain) => Ok(SocketAddrs {
                 // FIXME: use std::net::lookup_host when it’s stable.
-                state: SocketAddrsState::Domain((domain.as_ref(), port).to_socket_addrs()?)
+                state: SocketAddrsState::Domain((domain.as_ref(), port).to_socket_addrs()?),
             }),
             Host::Ipv4(address) => Ok(SocketAddrs {
-                state: SocketAddrsState::One(SocketAddr::V4(SocketAddrV4::new(address, port)))
+                state: SocketAddrsState::One(SocketAddr::V4(SocketAddrV4::new(address, port))),
             }),
             Host::Ipv6(address) => Ok(SocketAddrs {
-                state: SocketAddrsState::One(SocketAddr::V6(SocketAddrV6::new(address, port, 0, 0)))
+                state: SocketAddrsState::One(SocketAddr::V6(SocketAddrV6::new(
+                    address, port, 0, 0,
+                ))),
             }),
         }
     }
@@ -243,7 +294,7 @@ impl<S: AsRef<str>> ToSocketAddrs for HostAndPort<S> {
 /// Socket addresses for an URL.
 #[derive(Debug)]
 pub struct SocketAddrs {
-    state: SocketAddrsState
+    state: SocketAddrsState,
 }
 
 #[derive(Debug)]
@@ -262,7 +313,7 @@ impl Iterator for SocketAddrs {
                 self.state = SocketAddrsState::Done;
                 Some(s)
             }
-            SocketAddrsState::Done => None
+            SocketAddrsState::Done => None,
         }
     }
 }
@@ -344,10 +395,12 @@ fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> {
     // So instead we check if the input looks like a real number and only return
     // an error when it's an overflow.
     let valid_number = match r {
-        8 => input.chars().all(|c| c >= '0' && c <='7'),
-        10 => input.chars().all(|c| c >= '0' && c <='9'),
-        16 => input.chars().all(|c| (c >= '0' && c <='9') || (c >='a' && c <= 'f') || (c >= 'A' && c <= 'F')),
-        _ => false
+        8 => input.chars().all(|c| c >= '0' && c <= '7'),
+        10 => input.chars().all(|c| c >= '0' && c <= '9'),
+        16 => input
+            .chars()
+            .all(|c| (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')),
+        _ => false,
     };
 
     if !valid_number {
@@ -369,7 +422,7 @@ fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> {
 /// <https://url.spec.whatwg.org/#concept-ipv4-parser>
 fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> {
     if input.is_empty() {
-        return Ok(None)
+        return Ok(None);
     }
     let mut parts: Vec<&str> = input.split('.').collect();
     if parts.last() == Some(&"") {
@@ -387,7 +440,7 @@ fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> {
         match parse_ipv4number(part) {
             Ok(Some(n)) => numbers.push(n),
             Ok(None) => return Ok(None),
-            Err(()) => overflow = true
+            Err(()) => overflow = true,
         };
     }
     if overflow {
@@ -395,7 +448,7 @@ fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> {
     }
     let mut ipv4 = numbers.pop().expect("a non-empty list of numbers");
     // Equivalent to: ipv4 >= 256 ** (4 − numbers.len())
-    if ipv4 > u32::max_value() >> (8 * numbers.len() as u32)  {
+    if ipv4 > u32::max_value() >> (8 * numbers.len() as u32) {
         return Err(ParseError::InvalidIpv4Address);
     }
     if numbers.iter().any(|x| *x > 255) {
@@ -418,12 +471,12 @@ fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
     let mut i = 0;
 
     if len < 2 {
-        return Err(ParseError::InvalidIpv6Address)
+        return Err(ParseError::InvalidIpv6Address);
     }
 
     if input[0] == b':' {
         if input[1] != b':' {
-            return Err(ParseError::InvalidIpv6Address)
+            return Err(ParseError::InvalidIpv6Address);
         }
         i = 2;
         piece_pointer = 1;
@@ -432,16 +485,16 @@ fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
 
     while i < len {
         if piece_pointer == 8 {
-            return Err(ParseError::InvalidIpv6Address)
+            return Err(ParseError::InvalidIpv6Address);
         }
         if input[i] == b':' {
             if compress_pointer.is_some() {
-                return Err(ParseError::InvalidIpv6Address)
+                return Err(ParseError::InvalidIpv6Address);
             }
             i += 1;
             piece_pointer += 1;
             compress_pointer = Some(piece_pointer);
-            continue
+            continue;
         }
         let start = i;
         let end = cmp::min(len, start + 4);
@@ -451,33 +504,33 @@ fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
                 Some(digit) => {
                     value = value * 0x10 + digit as u16;
                     i += 1;
-                },
-                None => break
+                }
+                None => break,
             }
         }
         if i < len {
             match input[i] {
                 b'.' => {
                     if i == start {
-                        return Err(ParseError::InvalidIpv6Address)
+                        return Err(ParseError::InvalidIpv6Address);
                     }
                     i = start;
                     if piece_pointer > 6 {
-                        return Err(ParseError::InvalidIpv6Address)
+                        return Err(ParseError::InvalidIpv6Address);
                     }
                     is_ip_v4 = true;
-                },
+                }
                 b':' => {
                     i += 1;
                     if i == len {
-                        return Err(ParseError::InvalidIpv6Address)
+                        return Err(ParseError::InvalidIpv6Address);
                     }
-                },
-                _ => return Err(ParseError::InvalidIpv6Address)
+                }
+                _ => return Err(ParseError::InvalidIpv6Address),
             }
         }
         if is_ip_v4 {
-            break
+            break;
         }
         pieces[piece_pointer] = value;
         piece_pointer += 1;
@@ -485,7 +538,7 @@ fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
 
     if is_ip_v4 {
         if piece_pointer > 6 {
-            return Err(ParseError::InvalidIpv6Address)
+            return Err(ParseError::InvalidIpv6Address);
         }
         let mut numbers_seen = 0;
         while i < len {
@@ -493,23 +546,23 @@ fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
                 if numbers_seen < 4 && (i < len && input[i] == b'.') {
                     i += 1
                 } else {
-                    return Err(ParseError::InvalidIpv6Address)
+                    return Err(ParseError::InvalidIpv6Address);
                 }
             }
 
             let mut ipv4_piece = None;
             while i < len {
                 let digit = match input[i] {
-                    c @ b'0' ..= b'9' => c - b'0',
-                    _ => break
+                    c @ b'0'..=b'9' => c - b'0',
+                    _ => break,
                 };
                 match ipv4_piece {
                     None => ipv4_piece = Some(digit as u16),
-                    Some(0) => return Err(ParseError::InvalidIpv6Address),  // No leading zero
+                    Some(0) => return Err(ParseError::InvalidIpv6Address), // No leading zero
                     Some(ref mut v) => {
                         *v = *v * 10 + digit as u16;
                         if *v > 255 {
-                            return Err(ParseError::InvalidIpv6Address)
+                            return Err(ParseError::InvalidIpv6Address);
                         }
                     }
                 }
@@ -519,7 +572,7 @@ fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
             pieces[piece_pointer] = if let Some(v) = ipv4_piece {
                 pieces[piece_pointer] * 0x100 + v
             } else {
-                return Err(ParseError::InvalidIpv6Address)
+                return Err(ParseError::InvalidIpv6Address);
             };
             numbers_seen += 1;
 
@@ -529,12 +582,12 @@ fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
         }
 
         if numbers_seen != 4 {
-            return Err(ParseError::InvalidIpv6Address)
+            return Err(ParseError::InvalidIpv6Address);
         }
     }
 
     if i < len {
-        return Err(ParseError::InvalidIpv6Address)
+        return Err(ParseError::InvalidIpv6Address);
     }
 
     match compress_pointer {
@@ -547,10 +600,13 @@ fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
                 piece_pointer -= 1;
             }
         }
-        _ => if piece_pointer != 8 {
-            return Err(ParseError::InvalidIpv6Address)
+        _ => {
+            if piece_pointer != 8 {
+                return Err(ParseError::InvalidIpv6Address);
+            }
         }
     }
-    Ok(Ipv6Addr::new(pieces[0], pieces[1], pieces[2], pieces[3],
-                     pieces[4], pieces[5], pieces[6], pieces[7]))
+    Ok(Ipv6Addr::new(
+        pieces[0], pieces[1], pieces[2], pieces[3], pieces[4], pieces[5], pieces[6], pieces[7],
+    ))
 }

+ 292 - 151
src/lib.rs

@@ -107,47 +107,56 @@ assert_eq!(css_url.as_str(), "http://servo.github.io/rust-url/main.css");
 
 #![doc(html_root_url = "https://docs.rs/url/1.7.0")]
 
-#[macro_use] extern crate matches;
-#[cfg(feature="serde")] extern crate serde;
-#[cfg(feature="heapsize")] #[macro_use] extern crate heapsize;
+#[macro_use]
+extern crate matches;
+#[cfg(feature = "serde")]
+extern crate serde;
+#[cfg(feature = "heapsize")]
+#[macro_use]
+extern crate heapsize;
 
 pub extern crate idna;
 #[macro_use]
 pub extern crate percent_encoding;
 
 use encoding::EncodingOverride;
-#[cfg(feature = "heapsize")] use heapsize::HeapSizeOf;
+#[cfg(feature = "heapsize")]
+use heapsize::HeapSizeOf;
 use host::HostInternal;
-use parser::{Parser, Context, SchemeType, to_u32};
-use percent_encoding::{PATH_SEGMENT_ENCODE_SET, USERINFO_ENCODE_SET,
-                       percent_encode, percent_decode, utf8_percent_encode};
+use parser::{to_u32, Context, Parser, SchemeType};
+use percent_encoding::{
+    percent_decode, percent_encode, utf8_percent_encode, PATH_SEGMENT_ENCODE_SET,
+    USERINFO_ENCODE_SET,
+};
 use std::borrow::Borrow;
 use std::cmp;
-#[cfg(feature = "serde")] use std::error::Error;
-use std::fmt::{self, Write, Debug, Formatter};
+#[cfg(feature = "serde")]
+use std::error::Error;
+use std::fmt::{self, Debug, Formatter, Write};
 use std::hash;
 use std::io;
 use std::mem;
-use std::net::{ToSocketAddrs, IpAddr};
+use std::net::{IpAddr, ToSocketAddrs};
 use std::ops::{Range, RangeFrom, RangeTo};
 use std::path::{Path, PathBuf};
 use std::str;
 
-pub use origin::{Origin, OpaqueOrigin};
 pub use host::{Host, HostAndPort, SocketAddrs};
-pub use path_segments::PathSegmentsMut;
+pub use origin::{OpaqueOrigin, Origin};
 pub use parser::{ParseError, SyntaxViolation};
+pub use path_segments::PathSegmentsMut;
 pub use slicing::Position;
 
 mod encoding;
 mod host;
 mod origin;
-mod path_segments;
 mod parser;
+mod path_segments;
 mod slicing;
 
 pub mod form_urlencoded;
-#[doc(hidden)] pub mod quirks;
+#[doc(hidden)]
+pub mod quirks;
 
 /// A parsed URL record.
 #[derive(Clone)]
@@ -164,15 +173,15 @@ pub struct Url {
     serialization: String,
 
     // Components
-    scheme_end: u32,  // Before ':'
-    username_end: u32,  // Before ':' (if a password is given) or '@' (if not)
+    scheme_end: u32,   // Before ':'
+    username_end: u32, // Before ':' (if a password is given) or '@' (if not)
     host_start: u32,
     host_end: u32,
     host: HostInternal,
     port: Option<u16>,
-    path_start: u32,  // Before initial '/', if any
-    query_start: Option<u32>,  // Before '?', unlike Position::QueryStart
-    fragment_start: Option<u32>,  // Before '#', unlike Position::FragmentStart
+    path_start: u32,             // Before initial '/', if any
+    query_start: Option<u32>,    // Before '?', unlike Position::QueryStart
+    fragment_start: Option<u32>, // Before '#', unlike Position::FragmentStart
 }
 
 #[cfg(feature = "heapsize")]
@@ -245,18 +254,21 @@ impl<'a> ParseOptions<'a> {
             query_encoding_override: self.encoding_override,
             violation_fn: self.violation_fn,
             context: Context::UrlParser,
-        }.parse_url(input)
+        }
+        .parse_url(input)
     }
 }
 
 impl<'a> Debug for ParseOptions<'a> {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
-        write!(f,
-               "ParseOptions {{ base_url: {:?}, encoding_override: {:?}, \
-                violation_fn: {:?} }}",
-               self.base_url,
-               self.encoding_override,
-               self.violation_fn.map(|_| "…"))
+        write!(
+            f,
+            "ParseOptions {{ base_url: {:?}, encoding_override: {:?}, \
+             violation_fn: {:?} }}",
+            self.base_url,
+            self.encoding_override,
+            self.violation_fn.map(|_| "…")
+        )
     }
 }
 
@@ -313,10 +325,11 @@ impl Url {
     /// [`ParseError`]: enum.ParseError.html
     #[inline]
     pub fn parse_with_params<I, K, V>(input: &str, iter: I) -> Result<Url, ::ParseError>
-        where I: IntoIterator,
-              I::Item: Borrow<(K, V)>,
-              K: AsRef<str>,
-              V: AsRef<str>
+    where
+        I: IntoIterator,
+        I::Item: Borrow<(K, V)>,
+        K: AsRef<str>,
+        V: AsRef<str>,
     {
         let mut url = Url::options().parse(input);
 
@@ -446,10 +459,13 @@ impl Url {
         macro_rules! assert {
             ($x: expr) => {
                 if !$x {
-                    return Err(format!("!( {} ) for URL {:?}",
-                                       stringify!($x), self.serialization))
+                    return Err(format!(
+                        "!( {} ) for URL {:?}",
+                        stringify!($x),
+                        self.serialization
+                    ));
                 }
-            }
+            };
         }
 
         macro_rules! assert_eq {
@@ -468,11 +484,13 @@ impl Url {
 
         assert!(self.scheme_end >= 1);
         assert!(matches!(self.byte_at(0), b'a'..=b'z' | b'A'..=b'Z'));
-        assert!(self.slice(1..self.scheme_end).chars()
-                .all(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '+' | '-' | '.')));
+        assert!(self
+            .slice(1..self.scheme_end)
+            .chars()
+            .all(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '+' | '-' | '.')));
         assert_eq!(self.byte_at(self.scheme_end), b':');
 
-        if self.slice(self.scheme_end + 1 ..).starts_with("//") {
+        if self.slice(self.scheme_end + 1..).starts_with("//") {
             // URL with authority
             match self.byte_at(self.username_end) {
                 b':' => {
@@ -503,7 +521,10 @@ impl Url {
             } else {
                 assert_eq!(self.byte_at(self.host_end), b':');
                 let port_str = self.slice(self.host_end + 1..self.path_start);
-                assert_eq!(self.port, Some(port_str.parse::<u16>().expect("Couldn't parse port?")));
+                assert_eq!(
+                    self.port,
+                    Some(port_str.parse::<u16>().expect("Couldn't parse port?"))
+                );
             }
             assert_eq!(self.byte_at(self.path_start), b'/');
         } else {
@@ -533,10 +554,12 @@ impl Url {
         assert_eq!(self.username_end, other.username_end);
         assert_eq!(self.host_start, other.host_start);
         assert_eq!(self.host_end, other.host_end);
-        assert!(self.host == other.host ||
+        assert!(
+            self.host == other.host ||
                 // XXX No host round-trips to empty host.
                 // See https://github.com/whatwg/url/issues/79
-                (self.host_str(), other.host_str()) == (None, Some("")));
+                (self.host_str(), other.host_str()) == (None, Some(""))
+        );
         assert_eq!(self.port, other.port);
         assert_eq!(self.path_start, other.path_start);
         assert_eq!(self.query_start, other.query_start);
@@ -982,15 +1005,19 @@ impl Url {
     /// }
     /// ```
     pub fn with_default_port<F>(&self, f: F) -> io::Result<HostAndPort<&str>>
-    where F: FnOnce(&Url) -> Result<u16, ()> {
+    where
+        F: FnOnce(&Url) -> Result<u16, ()>,
+    {
         Ok(HostAndPort {
-            host: self.host()
-                      .ok_or(())
-                      .or_else(|()| io_error("URL has no host"))?,
-            port: self.port_or_known_default()
-                      .ok_or(())
-                      .or_else(|()| f(self))
-                      .or_else(|()| io_error("URL has no port number"))?
+            host: self
+                .host()
+                .ok_or(())
+                .or_else(|()| io_error("URL has no host"))?,
+            port: self
+                .port_or_known_default()
+                .ok_or(())
+                .or_else(|()| f(self))
+                .or_else(|()| io_error("URL has no port number"))?,
         })
     }
 
@@ -1020,8 +1047,7 @@ impl Url {
     pub fn path(&self) -> &str {
         match (self.query_start, self.fragment_start) {
             (None, None) => self.slice(self.path_start..),
-            (Some(next_component_start), _) |
-            (None, Some(next_component_start)) => {
+            (Some(next_component_start), _) | (None, Some(next_component_start)) => {
                 self.slice(self.path_start..next_component_start)
             }
         }
@@ -1327,7 +1353,10 @@ impl Url {
             self.serialization.push('?');
         }
 
-        let query = UrlQuery { url: Some(self), fragment: fragment };
+        let query = UrlQuery {
+            url: Some(self),
+            fragment: fragment,
+        };
         form_urlencoded::Serializer::for_suffix(query, query_start + "?".len())
     }
 
@@ -1337,7 +1366,7 @@ impl Url {
                 let after_path = self.slice(i..).to_owned();
                 self.serialization.truncate(i as usize);
                 after_path
-            },
+            }
             (None, None) => String::new(),
         }
     }
@@ -1378,7 +1407,7 @@ impl Url {
                 }
                 parser.parse_cannot_be_a_base_path(parser::Input::new(path));
             } else {
-                let mut has_host = true;  // FIXME
+                let mut has_host = true; // FIXME
                 parser.parse_path_start(scheme_type, &mut has_host, parser::Input::new(path));
             }
         });
@@ -1402,8 +1431,12 @@ impl Url {
             *index -= old_after_path_position;
             *index += new_after_path_position;
         };
-        if let Some(ref mut index) = self.query_start { adjust(index) }
-        if let Some(ref mut index) = self.fragment_start { adjust(index) }
+        if let Some(ref mut index) = self.query_start {
+            adjust(index)
+        }
+        if let Some(ref mut index) = self.fragment_start {
+            adjust(index)
+        }
         self.serialization.push_str(after_path)
     }
 
@@ -1452,7 +1485,7 @@ impl Url {
     pub fn set_port(&mut self, mut port: Option<u16>) -> Result<(), ()> {
         // has_host implies !cannot_be_a_base
         if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
-            return Err(())
+            return Err(());
         }
         if port.is_some() && port == parser::default_port(self.scheme()) {
             port = None
@@ -1465,11 +1498,16 @@ impl Url {
         match (self.port, port) {
             (None, None) => {}
             (Some(_), None) => {
-                self.serialization.drain(self.host_end as usize .. self.path_start as usize);
+                self.serialization
+                    .drain(self.host_end as usize..self.path_start as usize);
                 let offset = self.path_start - self.host_end;
                 self.path_start = self.host_end;
-                if let Some(ref mut index) = self.query_start { *index -= offset }
-                if let Some(ref mut index) = self.fragment_start { *index -= offset }
+                if let Some(ref mut index) = self.query_start {
+                    *index -= offset
+                }
+                if let Some(ref mut index) = self.fragment_start {
+                    *index -= offset
+                }
             }
             (Some(old), Some(new)) if old == new => {}
             (_, Some(new)) => {
@@ -1483,8 +1521,12 @@ impl Url {
                     *index -= old_path_start;
                     *index += new_path_start;
                 };
-                if let Some(ref mut index) = self.query_start { adjust(index) }
-                if let Some(ref mut index) = self.fragment_start { adjust(index) }
+                if let Some(ref mut index) = self.query_start {
+                    adjust(index)
+                }
+                if let Some(ref mut index) = self.fragment_start {
+                    adjust(index)
+                }
                 self.serialization.push_str(&path_and_after);
             }
         }
@@ -1575,7 +1617,7 @@ impl Url {
     /// [`ParseError`]: enum.ParseError.html
     pub fn set_host(&mut self, host: Option<&str>) -> Result<(), ParseError> {
         if self.cannot_be_a_base() {
-            return Err(ParseError::SetHostOnCannotBeABaseUrl)
+            return Err(ParseError::SetHostOnCannotBeABaseUrl);
         }
 
         if let Some(host) = host {
@@ -1589,27 +1631,36 @@ impl Url {
             }
         } else if self.has_host() {
             if SchemeType::from(self.scheme()).is_special() {
-                return Err(ParseError::EmptyHost)
+                return Err(ParseError::EmptyHost);
             }
             debug_assert!(self.byte_at(self.scheme_end) == b':');
             debug_assert!(self.byte_at(self.path_start) == b'/');
             let new_path_start = self.scheme_end + 1;
-            self.serialization.drain(new_path_start as usize..self.path_start as usize);
+            self.serialization
+                .drain(new_path_start as usize..self.path_start as usize);
             let offset = self.path_start - new_path_start;
             self.path_start = new_path_start;
             self.username_end = new_path_start;
             self.host_start = new_path_start;
             self.host_end = new_path_start;
             self.port = None;
-            if let Some(ref mut index) = self.query_start { *index -= offset }
-            if let Some(ref mut index) = self.fragment_start { *index -= offset }
+            if let Some(ref mut index) = self.query_start {
+                *index -= offset
+            }
+            if let Some(ref mut index) = self.fragment_start {
+                *index -= offset
+            }
         }
         Ok(())
     }
 
     /// opt_new_port: None means leave unchanged, Some(None) means remove any port number.
     fn set_host_internal(&mut self, host: Host<String>, opt_new_port: Option<Option<u16>>) {
-        let old_suffix_pos = if opt_new_port.is_some() { self.path_start } else { self.host_end };
+        let old_suffix_pos = if opt_new_port.is_some() {
+            self.path_start
+        } else {
+            self.host_end
+        };
         let suffix = self.slice(old_suffix_pos..).to_owned();
         self.serialization.truncate(self.host_start as usize);
         if !self.has_authority() {
@@ -1638,8 +1689,12 @@ impl Url {
             *index += new_suffix_pos;
         };
         adjust(&mut self.path_start);
-        if let Some(ref mut index) = self.query_start { adjust(index) }
-        if let Some(ref mut index) = self.fragment_start { adjust(index) }
+        if let Some(ref mut index) = self.query_start {
+            adjust(index)
+        }
+        if let Some(ref mut index) = self.fragment_start {
+            adjust(index)
+        }
     }
 
     /// Change this URL’s host to the given IP address.
@@ -1681,7 +1736,7 @@ impl Url {
     ///
     pub fn set_ip_host(&mut self, address: IpAddr) -> Result<(), ()> {
         if self.cannot_be_a_base() {
-            return Err(())
+            return Err(());
         }
 
         let address = match address {
@@ -1721,13 +1776,14 @@ impl Url {
     pub fn set_password(&mut self, password: Option<&str>) -> Result<(), ()> {
         // has_host implies !cannot_be_a_base
         if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
-            return Err(())
+            return Err(());
         }
         if let Some(password) = password {
             let host_and_after = self.slice(self.host_start..).to_owned();
             self.serialization.truncate(self.username_end as usize);
             self.serialization.push(':');
-            self.serialization.extend(utf8_percent_encode(password, USERINFO_ENCODE_SET));
+            self.serialization
+                .extend(utf8_percent_encode(password, USERINFO_ENCODE_SET));
             self.serialization.push('@');
 
             let old_host_start = self.host_start;
@@ -1739,28 +1795,37 @@ impl Url {
             self.host_start = new_host_start;
             adjust(&mut self.host_end);
             adjust(&mut self.path_start);
-            if let Some(ref mut index) = self.query_start { adjust(index) }
-            if let Some(ref mut index) = self.fragment_start { adjust(index) }
+            if let Some(ref mut index) = self.query_start {
+                adjust(index)
+            }
+            if let Some(ref mut index) = self.fragment_start {
+                adjust(index)
+            }
 
             self.serialization.push_str(&host_and_after);
-        } else if self.byte_at(self.username_end) == b':' {  // If there is a password to remove
+        } else if self.byte_at(self.username_end) == b':' {
+            // If there is a password to remove
             let has_username_or_password = self.byte_at(self.host_start - 1) == b'@';
             debug_assert!(has_username_or_password);
             let username_start = self.scheme_end + 3;
             let empty_username = username_start == self.username_end;
-            let start = self.username_end;  // Remove the ':'
+            let start = self.username_end; // Remove the ':'
             let end = if empty_username {
                 self.host_start // Remove the '@' as well
             } else {
-                self.host_start - 1  // Keep the '@' to separate the username from the host
+                self.host_start - 1 // Keep the '@' to separate the username from the host
             };
-            self.serialization.drain(start as usize .. end as usize);
+            self.serialization.drain(start as usize..end as usize);
             let offset = end - start;
             self.host_start -= offset;
             self.host_end -= offset;
             self.path_start -= offset;
-            if let Some(ref mut index) = self.query_start { *index -= offset }
-            if let Some(ref mut index) = self.fragment_start { *index -= offset }
+            if let Some(ref mut index) = self.query_start {
+                *index -= offset
+            }
+            if let Some(ref mut index) = self.fragment_start {
+                *index -= offset
+            }
         }
         Ok(())
     }
@@ -1803,16 +1868,17 @@ impl Url {
     pub fn set_username(&mut self, username: &str) -> Result<(), ()> {
         // has_host implies !cannot_be_a_base
         if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
-            return Err(())
+            return Err(());
         }
         let username_start = self.scheme_end + 3;
         debug_assert!(self.slice(self.scheme_end..username_start) == "://");
         if self.slice(username_start..self.username_end) == username {
-            return Ok(())
+            return Ok(());
         }
         let after_username = self.slice(self.username_end..).to_owned();
         self.serialization.truncate(username_start as usize);
-        self.serialization.extend(utf8_percent_encode(username, USERINFO_ENCODE_SET));
+        self.serialization
+            .extend(utf8_percent_encode(username, USERINFO_ENCODE_SET));
 
         let mut removed_bytes = self.username_end;
         self.username_end = to_u32(self.serialization.len()).unwrap();
@@ -1841,8 +1907,12 @@ impl Url {
         adjust(&mut self.host_start);
         adjust(&mut self.host_end);
         adjust(&mut self.path_start);
-        if let Some(ref mut index) = self.query_start { adjust(index) }
-        if let Some(ref mut index) = self.fragment_start { adjust(index) }
+        if let Some(ref mut index) = self.query_start {
+            adjust(index)
+        }
+        if let Some(ref mut index) = self.fragment_start {
+            adjust(index)
+        }
         Ok(())
     }
 
@@ -1907,9 +1977,10 @@ impl Url {
     pub fn set_scheme(&mut self, scheme: &str) -> Result<(), ()> {
         let mut parser = Parser::for_setter(String::new());
         let remaining = parser.parse_scheme(parser::Input::new(scheme))?;
-        if !remaining.is_empty() ||
-                (!self.has_host() && SchemeType::from(&parser.serialization).is_special()) {
-            return Err(())
+        if !remaining.is_empty()
+            || (!self.has_host() && SchemeType::from(&parser.serialization).is_special())
+        {
+            return Err(());
         }
         let old_scheme_end = self.scheme_end;
         let new_scheme_end = to_u32(parser.serialization.len()).unwrap();
@@ -1923,8 +1994,12 @@ impl Url {
         adjust(&mut self.host_start);
         adjust(&mut self.host_end);
         adjust(&mut self.path_start);
-        if let Some(ref mut index) = self.query_start { adjust(index) }
-        if let Some(ref mut index) = self.fragment_start { adjust(index) }
+        if let Some(ref mut index) = self.query_start {
+            adjust(index)
+        }
+        if let Some(ref mut index) = self.fragment_start {
+            adjust(index)
+        }
 
         parser.serialization.push_str(self.slice(old_scheme_end..));
         self.serialization = parser.serialization;
@@ -1958,7 +2033,7 @@ impl Url {
     /// # run().unwrap();
     /// # }
     /// ```
-    #[cfg(any(unix, windows, target_os="redox"))]
+    #[cfg(any(unix, windows, target_os = "redox"))]
     pub fn from_file_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> {
         let mut serialization = "file://".to_owned();
         let host_start = serialization.len() as u32;
@@ -1994,7 +2069,7 @@ impl Url {
     ///
     /// Note that `std::path` does not consider trailing slashes significant
     /// and usually does not include them (e.g. in `Path::parent()`).
-    #[cfg(any(unix, windows, target_os="redox"))]
+    #[cfg(any(unix, windows, target_os = "redox"))]
     pub fn from_directory_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> {
         let mut url = Url::from_file_path(path)?;
         if !url.serialization.ends_with('/') {
@@ -2011,18 +2086,38 @@ impl Url {
     /// This method is only available if the `serde` Cargo feature is enabled.
     #[cfg(feature = "serde")]
     #[deny(unused)]
-    pub fn serialize_internal<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
+    pub fn serialize_internal<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
         use serde::Serialize;
         // Destructuring first lets us ensure that adding or removing fields forces this method
         // to be updated
-        let Url { ref serialization, ref scheme_end,
-                  ref username_end, ref host_start,
-                  ref host_end, ref host, ref port,
-                  ref path_start, ref query_start,
-                  ref fragment_start} = *self;
-        (serialization, scheme_end, username_end,
-         host_start, host_end, host, port, path_start,
-         query_start, fragment_start).serialize(serializer)
+        let Url {
+            ref serialization,
+            ref scheme_end,
+            ref username_end,
+            ref host_start,
+            ref host_end,
+            ref host,
+            ref port,
+            ref path_start,
+            ref query_start,
+            ref fragment_start,
+        } = *self;
+        (
+            serialization,
+            scheme_end,
+            username_end,
+            host_start,
+            host_end,
+            host,
+            port,
+            path_start,
+            query_start,
+            fragment_start,
+        )
+            .serialize(serializer)
     }
 
     /// Serialize with Serde using the internal representation of the `Url` struct.
@@ -2033,11 +2128,23 @@ impl Url {
     /// This method is only available if the `serde` Cargo feature is enabled.
     #[cfg(feature = "serde")]
     #[deny(unused)]
-    pub fn deserialize_internal<'de, D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
+    pub fn deserialize_internal<'de, D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
         use serde::de::{Deserialize, Error, Unexpected};
-        let (serialization, scheme_end, username_end,
-             host_start, host_end, host, port, path_start,
-             query_start, fragment_start) = Deserialize::deserialize(deserializer)?;
+        let (
+            serialization,
+            scheme_end,
+            username_end,
+            host_start,
+            host_end,
+            host,
+            port,
+            path_start,
+            query_start,
+            fragment_start,
+        ) = Deserialize::deserialize(deserializer)?;
         let url = Url {
             serialization: serialization,
             scheme_end: scheme_end,
@@ -2048,7 +2155,7 @@ impl Url {
             port: port,
             path_start: path_start,
             query_start: query_start,
-            fragment_start: fragment_start
+            fragment_start: fragment_start,
         };
         if cfg!(debug_assertions) {
             url.check_invariants().map_err(|reason| {
@@ -2059,7 +2166,6 @@ impl Url {
         Ok(url)
     }
 
-
     /// Assuming the URL is in the `file` scheme or similar,
     /// convert its path to an absolute `std::path::Path`.
     ///
@@ -2079,15 +2185,15 @@ impl Url {
     /// (That is, if the percent-decoded path contains a NUL byte or,
     /// for a Windows path, is not UTF-8.)
     #[inline]
-    #[cfg(any(unix, windows, target_os="redox"))]
+    #[cfg(any(unix, windows, target_os = "redox"))]
     pub fn to_file_path(&self) -> Result<PathBuf, ()> {
         if let Some(segments) = self.path_segments() {
             let host = match self.host() {
                 None | Some(Host::Domain("localhost")) => None,
                 Some(_) if cfg!(windows) && self.scheme() == "file" => {
-                    Some(&self.serialization[self.host_start as usize .. self.host_end as usize])
-                },
-                _ => return Err(())
+                    Some(&self.serialization[self.host_start as usize..self.host_end as usize])
+                }
+                _ => return Err(()),
             };
 
             return file_url_segments_to_pathbuf(host, segments);
@@ -2098,7 +2204,10 @@ impl Url {
     // Private helper methods:
 
     #[inline]
-    fn slice<R>(&self, range: R) -> &str where R: RangeArg {
+    fn slice<R>(&self, range: R) -> &str
+    where
+        R: RangeArg,
+    {
         range.slice_of(&self.serialization)
     }
 
@@ -2173,7 +2282,10 @@ impl PartialOrd for Url {
 /// URLs hash like their serialization.
 impl hash::Hash for Url {
     #[inline]
-    fn hash<H>(&self, state: &mut H) where H: hash::Hasher {
+    fn hash<H>(&self, state: &mut H)
+    where
+        H: hash::Hasher,
+    {
         hash::Hash::hash(&self.serialization, state)
     }
 }
@@ -2193,30 +2305,33 @@ trait RangeArg {
 impl RangeArg for Range<u32> {
     #[inline]
     fn slice_of<'a>(&self, s: &'a str) -> &'a str {
-        &s[self.start as usize .. self.end as usize]
+        &s[self.start as usize..self.end as usize]
     }
 }
 
 impl RangeArg for RangeFrom<u32> {
     #[inline]
     fn slice_of<'a>(&self, s: &'a str) -> &'a str {
-        &s[self.start as usize ..]
+        &s[self.start as usize..]
     }
 }
 
 impl RangeArg for RangeTo<u32> {
     #[inline]
     fn slice_of<'a>(&self, s: &'a str) -> &'a str {
-        &s[.. self.end as usize]
+        &s[..self.end as usize]
     }
 }
 
 /// Serializes this URL into a `serde` stream.
 ///
 /// This implementation is only available if the `serde` Cargo feature is enabled.
-#[cfg(feature="serde")]
+#[cfg(feature = "serde")]
 impl serde::Serialize for Url {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
         serializer.serialize_str(self.as_str())
     }
 }
@@ -2224,10 +2339,13 @@ impl serde::Serialize for Url {
 /// Deserializes this URL from a `serde` stream.
 ///
 /// This implementation is only available if the `serde` Cargo feature is enabled.
-#[cfg(feature="serde")]
+#[cfg(feature = "serde")]
 impl<'de> serde::Deserialize<'de> for Url {
-    fn deserialize<D>(deserializer: D) -> Result<Url, D::Error> where D: serde::Deserializer<'de> {
-        use serde::de::{Unexpected, Error};
+    fn deserialize<D>(deserializer: D) -> Result<Url, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        use serde::de::{Error, Unexpected};
         let string_representation: String = serde::Deserialize::deserialize(deserializer)?;
         Url::parse(&string_representation).map_err(|err| {
             Error::invalid_value(Unexpected::Str(&string_representation), &err.description())
@@ -2236,11 +2354,13 @@ impl<'de> serde::Deserialize<'de> for Url {
 }
 
 #[cfg(any(unix, target_os = "redox"))]
-fn path_to_file_url_segments(path: &Path, serialization: &mut String)
-                             -> Result<(u32, HostInternal), ()> {
+fn path_to_file_url_segments(
+    path: &Path,
+    serialization: &mut String,
+) -> Result<(u32, HostInternal), ()> {
     use std::os::unix::prelude::OsStrExt;
     if !path.is_absolute() {
-        return Err(())
+        return Err(());
     }
     let host_end = to_u32(serialization.len()).unwrap();
     let mut empty = true;
@@ -2249,7 +2369,9 @@ fn path_to_file_url_segments(path: &Path, serialization: &mut String)
         empty = false;
         serialization.push('/');
         serialization.extend(percent_encode(
-            component.as_os_str().as_bytes(), PATH_SEGMENT_ENCODE_SET));
+            component.as_os_str().as_bytes(),
+            PATH_SEGMENT_ENCODE_SET,
+        ));
     }
     if empty {
         // An URL’s path must not be empty.
@@ -2259,18 +2381,22 @@ fn path_to_file_url_segments(path: &Path, serialization: &mut String)
 }
 
 #[cfg(windows)]
-fn path_to_file_url_segments(path: &Path, serialization: &mut String)
-                             -> Result<(u32, HostInternal), ()> {
+fn path_to_file_url_segments(
+    path: &Path,
+    serialization: &mut String,
+) -> Result<(u32, HostInternal), ()> {
     path_to_file_url_segments_windows(path, serialization)
 }
 
 // Build this unconditionally to alleviate https://github.com/servo/rust-url/issues/102
 #[cfg_attr(not(windows), allow(dead_code))]
-fn path_to_file_url_segments_windows(path: &Path, serialization: &mut String)
-                                     -> Result<(u32, HostInternal), ()> {
-    use std::path::{Prefix, Component};
+fn path_to_file_url_segments_windows(
+    path: &Path,
+    serialization: &mut String,
+) -> Result<(u32, HostInternal), ()> {
+    use std::path::{Component, Prefix};
     if !path.is_absolute() {
-        return Err(())
+        return Err(());
     }
     let mut components = path.components();
 
@@ -2284,7 +2410,7 @@ fn path_to_file_url_segments_windows(path: &Path, serialization: &mut String)
                 serialization.push('/');
                 serialization.push(letter as char);
                 serialization.push(':');
-            },
+            }
             Prefix::UNC(server, share) | Prefix::VerbatimUNC(server, share) => {
                 let host = Host::parse(server.to_str().ok_or(())?).map_err(|_| ())?;
                 write!(serialization, "{}", host).unwrap();
@@ -2293,26 +2419,33 @@ fn path_to_file_url_segments_windows(path: &Path, serialization: &mut String)
                 serialization.push('/');
                 let share = share.to_str().ok_or(())?;
                 serialization.extend(percent_encode(share.as_bytes(), PATH_SEGMENT_ENCODE_SET));
-            },
-            _ => return Err(())
+            }
+            _ => return Err(()),
         },
 
-        _ => return Err(())
+        _ => return Err(()),
     }
 
     for component in components {
-        if component == Component::RootDir { continue }
+        if component == Component::RootDir {
+            continue;
+        }
         // FIXME: somehow work with non-unicode?
         let component = component.as_os_str().to_str().ok_or(())?;
         serialization.push('/');
-        serialization.extend(percent_encode(component.as_bytes(), PATH_SEGMENT_ENCODE_SET));
+        serialization.extend(percent_encode(
+            component.as_bytes(),
+            PATH_SEGMENT_ENCODE_SET,
+        ));
     }
     Ok((host_end, host_internal))
 }
 
-
 #[cfg(any(unix, target_os = "redox"))]
-fn file_url_segments_to_pathbuf(host: Option<&str>, segments: str::Split<char>) -> Result<PathBuf, ()> {
+fn file_url_segments_to_pathbuf(
+    host: Option<&str>,
+    segments: str::Split<char>,
+) -> Result<PathBuf, ()> {
     use std::ffi::OsStr;
     use std::os::unix::prelude::OsStrExt;
 
@@ -2331,20 +2464,27 @@ fn file_url_segments_to_pathbuf(host: Option<&str>, segments: str::Split<char>)
     }
     let os_str = OsStr::from_bytes(&bytes);
     let path = PathBuf::from(os_str);
-    debug_assert!(path.is_absolute(),
-                  "to_file_path() failed to produce an absolute Path");
+    debug_assert!(
+        path.is_absolute(),
+        "to_file_path() failed to produce an absolute Path"
+    );
     Ok(path)
 }
 
 #[cfg(windows)]
-fn file_url_segments_to_pathbuf(host: Option<&str>, segments: str::Split<char>) -> Result<PathBuf, ()> {
+fn file_url_segments_to_pathbuf(
+    host: Option<&str>,
+    segments: str::Split<char>,
+) -> Result<PathBuf, ()> {
     file_url_segments_to_pathbuf_windows(host, segments)
 }
 
 // Build this unconditionally to alleviate https://github.com/servo/rust-url/issues/102
 #[cfg_attr(not(windows), allow(dead_code))]
-fn file_url_segments_to_pathbuf_windows(host: Option<&str>, mut segments: str::Split<char>) -> Result<PathBuf, ()> {
-
+fn file_url_segments_to_pathbuf_windows(
+    host: Option<&str>,
+    mut segments: str::Split<char>,
+) -> Result<PathBuf, ()> {
     let mut string = if let Some(host) = host {
         r"\\".to_owned() + host
     } else {
@@ -2353,23 +2493,23 @@ fn file_url_segments_to_pathbuf_windows(host: Option<&str>, mut segments: str::S
         match first.len() {
             2 => {
                 if !first.starts_with(parser::ascii_alpha) || first.as_bytes()[1] != b':' {
-                    return Err(())
+                    return Err(());
                 }
 
                 first.to_owned()
-            },
+            }
 
             4 => {
                 if !first.starts_with(parser::ascii_alpha) {
-                    return Err(())
+                    return Err(());
                 }
                 let bytes = first.as_bytes();
                 if bytes[1] != b'%' || bytes[2] != b'3' || (bytes[3] != b'a' && bytes[3] != b'A') {
-                    return Err(())
+                    return Err(());
                 }
 
                 first[0..1].to_owned() + ":"
-            },
+            }
 
             _ => return Err(()),
         }
@@ -2385,8 +2525,10 @@ fn file_url_segments_to_pathbuf_windows(host: Option<&str>, mut segments: str::S
         }
     }
     let path = PathBuf::from(string);
-    debug_assert!(path.is_absolute(),
-                  "to_file_path() failed to produce an absolute Path");
+    debug_assert!(
+        path.is_absolute(),
+        "to_file_path() failed to produce an absolute Path"
+    );
     Ok(path)
 }
 
@@ -2409,7 +2551,6 @@ impl<'a> Drop for UrlQuery<'a> {
     }
 }
 
-
 /// Define a new struct
 /// that implements the [`EncodeSet`](percent_encoding/trait.EncodeSet.html) trait,
 /// for use in [`percent_decode()`](percent_encoding/fn.percent_encode.html)

+ 14 - 14
src/origin.rs

@@ -6,7 +6,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[cfg(feature = "heapsize")] use heapsize::HeapSizeOf;
+#[cfg(feature = "heapsize")]
+use heapsize::HeapSizeOf;
 use host::Host;
 use idna::domain_to_unicode;
 use parser::default_port;
@@ -20,16 +21,17 @@ pub fn url_origin(url: &Url) -> Origin {
             let result = Url::parse(url.path());
             match result {
                 Ok(ref url) => url_origin(url),
-                Err(_)  => Origin::new_opaque()
+                Err(_) => Origin::new_opaque(),
             }
-        },
-        "ftp" | "gopher" | "http" | "https" | "ws" | "wss" => {
-            Origin::Tuple(scheme.to_owned(), url.host().unwrap().to_owned(),
-                url.port_or_known_default().unwrap())
-        },
+        }
+        "ftp" | "gopher" | "http" | "https" | "ws" | "wss" => Origin::Tuple(
+            scheme.to_owned(),
+            url.host().unwrap().to_owned(),
+            url.port_or_known_default().unwrap(),
+        ),
         // TODO: Figure out what to do if the scheme is a file
         "file" => Origin::new_opaque(),
-        _ => Origin::new_opaque()
+        _ => Origin::new_opaque(),
     }
 }
 
@@ -56,7 +58,7 @@ pub enum Origin {
     Opaque(OpaqueOrigin),
 
     /// Consists of the URL's scheme, host and port
-    Tuple(String, Host<String>, u16)
+    Tuple(String, Host<String>, u16),
 }
 
 #[cfg(feature = "heapsize")]
@@ -64,15 +66,13 @@ impl HeapSizeOf for Origin {
     fn heap_size_of_children(&self) -> usize {
         match *self {
             Origin::Tuple(ref scheme, ref host, _) => {
-                scheme.heap_size_of_children() +
-                host.heap_size_of_children()
-            },
+                scheme.heap_size_of_children() + host.heap_size_of_children()
+            }
             _ => 0,
         }
     }
 }
 
-
 impl Origin {
     /// Creates a new opaque origin that is only equal to itself.
     pub fn new_opaque() -> Origin {
@@ -110,7 +110,7 @@ impl Origin {
                         let (domain, _errors) = domain_to_unicode(domain);
                         Host::Domain(domain)
                     }
-                    _ => host.clone()
+                    _ => host.clone(),
                 };
                 if default_port(scheme) == Some(port) {
                     format!("{}://{}", scheme, host)

+ 275 - 157
src/parser.rs

@@ -10,14 +10,13 @@ use std::error::Error;
 use std::fmt::{self, Formatter, Write};
 use std::str;
 
-use Url;
 use encoding::EncodingOverride;
 use host::{Host, HostInternal};
 use percent_encoding::{
-    utf8_percent_encode, percent_encode,
-    SIMPLE_ENCODE_SET, DEFAULT_ENCODE_SET, USERINFO_ENCODE_SET, QUERY_ENCODE_SET,
-    PATH_SEGMENT_ENCODE_SET
+    percent_encode, utf8_percent_encode, DEFAULT_ENCODE_SET, PATH_SEGMENT_ENCODE_SET,
+    QUERY_ENCODE_SET, SIMPLE_ENCODE_SET, USERINFO_ENCODE_SET,
 };
+use Url;
 
 define_encode_set! {
     // The backslash (\) character is treated as a path separator in special URLs
@@ -72,7 +71,9 @@ impl fmt::Display for ParseError {
 }
 
 impl From<::idna::uts46::Errors> for ParseError {
-    fn from(_: ::idna::uts46::Errors) -> ParseError { ParseError::IdnaError }
+    fn from(_: ::idna::uts46::Errors) -> ParseError {
+        ParseError::IdnaError
+    }
 }
 
 macro_rules! syntax_violation_enum {
@@ -178,7 +179,9 @@ impl<'i> Input<'i> {
                 vfn(SyntaxViolation::TabOrNewlineIgnored)
             }
         }
-        Input { chars: input.chars() }
+        Input {
+            chars: input.chars(),
+        }
     }
 
     #[inline]
@@ -217,7 +220,7 @@ impl<'i> Input<'i> {
                 remaining = input;
                 count += 1;
             } else {
-                return (count, remaining)
+                return (count, remaining);
             }
         }
     }
@@ -229,10 +232,10 @@ impl<'i> Input<'i> {
             match self.chars.next() {
                 Some(c) => {
                     if !matches!(c, '\t' | '\n' | '\r') {
-                        return Some((c, &utf8[..c.len_utf8()]))
+                        return Some((c, &utf8[..c.len_utf8()]));
                     }
                 }
-                None => return None
+                None => return None,
             }
         }
     }
@@ -243,14 +246,16 @@ pub trait Pattern {
 }
 
 impl Pattern for char {
-    fn split_prefix<'i>(self, input: &mut Input<'i>) -> bool { input.next() == Some(self) }
+    fn split_prefix<'i>(self, input: &mut Input<'i>) -> bool {
+        input.next() == Some(self)
+    }
 }
 
 impl<'a> Pattern for &'a str {
     fn split_prefix<'i>(self, input: &mut Input<'i>) -> bool {
         for c in self.chars() {
             if input.next() != Some(c) {
-                return false
+                return false;
             }
         }
         true
@@ -258,13 +263,17 @@ impl<'a> Pattern for &'a str {
 }
 
 impl<F: FnMut(char) -> bool> Pattern for F {
-    fn split_prefix<'i>(self, input: &mut Input<'i>) -> bool { input.next().map_or(false, self) }
+    fn split_prefix<'i>(self, input: &mut Input<'i>) -> bool {
+        input.next().map_or(false, self)
+    }
 }
 
 impl<'i> Iterator for Input<'i> {
     type Item = char;
     fn next(&mut self) -> Option<char> {
-        self.chars.by_ref().find(|&c| !matches!(c, '\t' | '\n' | '\r'))
+        self.chars
+            .by_ref()
+            .find(|&c| !matches!(c, '\t' | '\n' | '\r'))
     }
 }
 
@@ -312,7 +321,7 @@ impl<'a> Parser<'a> {
     pub fn parse_url(mut self, input: &str) -> ParseResult<Url> {
         let input = Input::with_log(input, self.violation_fn);
         if let Ok(remaining) = self.parse_scheme(input.clone()) {
-            return self.parse_with_scheme(remaining)
+            return self.parse_with_scheme(remaining);
         }
 
         // No-scheme state
@@ -336,7 +345,7 @@ impl<'a> Parser<'a> {
 
     pub fn parse_scheme<'i>(&mut self, mut input: Input<'i>) -> Result<Input<'i>, ()> {
         if input.is_empty() || !input.starts_with(ascii_alpha) {
-            return Err(())
+            return Err(());
         }
         debug_assert!(self.serialization.is_empty());
         while let Some(c) = input.next() {
@@ -347,7 +356,7 @@ impl<'a> Parser<'a> {
                 ':' => return Ok(input),
                 _ => {
                     self.serialization.clear();
-                    return Err(())
+                    return Err(());
                 }
             }
         }
@@ -361,7 +370,7 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_with_scheme(mut self, input: Input) -> ParseResult<Url> {
-        use SyntaxViolation::{ExpectedFileDoubleSlash, ExpectedDoubleSlash};
+        use SyntaxViolation::{ExpectedDoubleSlash, ExpectedFileDoubleSlash};
         let scheme_end = to_u32(self.serialization.len())?;
         let scheme_type = SchemeType::from(&self.serialization);
         self.serialization.push(':');
@@ -369,7 +378,11 @@ impl<'a> Parser<'a> {
             SchemeType::File => {
                 self.log_violation_if(ExpectedFileDoubleSlash, || !input.starts_with("//"));
                 let base_file_url = self.base_url.and_then(|base| {
-                    if base.scheme() == "file" { Some(base) } else { None }
+                    if base.scheme() == "file" {
+                        Some(base)
+                    } else {
+                        None
+                    }
                 });
                 self.serialization.clear();
                 self.parse_file(input, base_file_url)
@@ -378,31 +391,39 @@ impl<'a> Parser<'a> {
                 // special relative or authority state
                 let (slashes_count, remaining) = input.count_matching(|c| matches!(c, '/' | '\\'));
                 if let Some(base_url) = self.base_url {
-                    if slashes_count < 2 &&
-                            base_url.scheme() == &self.serialization[..scheme_end as usize] {
+                    if slashes_count < 2
+                        && base_url.scheme() == &self.serialization[..scheme_end as usize]
+                    {
                         // "Cannot-be-a-base" URLs only happen with "not special" schemes.
                         debug_assert!(!base_url.cannot_be_a_base());
                         self.serialization.clear();
-                        return self.parse_relative(input, scheme_type, base_url)
+                        return self.parse_relative(input, scheme_type, base_url);
                     }
                 }
                 // special authority slashes state
                 self.log_violation_if(ExpectedDoubleSlash, || {
-                    input.clone().take_while(|&c| matches!(c, '/' | '\\'))
-                    .collect::<String>() != "//"
+                    input
+                        .clone()
+                        .take_while(|&c| matches!(c, '/' | '\\'))
+                        .collect::<String>()
+                        != "//"
                 });
                 self.after_double_slash(remaining, scheme_type, scheme_end)
             }
-            SchemeType::NotSpecial => self.parse_non_special(input, scheme_type, scheme_end)
+            SchemeType::NotSpecial => self.parse_non_special(input, scheme_type, scheme_end),
         }
     }
 
     /// Scheme other than file, http, https, ws, ws, ftp, gopher.
-    fn parse_non_special(mut self, input: Input, scheme_type: SchemeType, scheme_end: u32)
-                         -> ParseResult<Url> {
+    fn parse_non_special(
+        mut self,
+        input: Input,
+        scheme_type: SchemeType,
+        scheme_end: u32,
+    ) -> ParseResult<Url> {
         // path or authority state (
         if let Some(input) = input.split_prefix("//") {
-            return self.after_double_slash(input, scheme_type, scheme_end)
+            return self.after_double_slash(input, scheme_type, scheme_end);
         }
         // Anarchist URL (no authority)
         let path_start = to_u32(self.serialization.len())?;
@@ -418,8 +439,16 @@ impl<'a> Parser<'a> {
         } else {
             self.parse_cannot_be_a_base_path(input)
         };
-        self.with_query_and_fragment(scheme_end, username_end, host_start,
-                                     host_end, host, port, path_start, remaining)
+        self.with_query_and_fragment(
+            scheme_end,
+            username_end,
+            host_start,
+            host_end,
+            host,
+            port,
+            path_start,
+            remaining,
+        )
     }
 
     fn parse_file(mut self, input: Input, mut base_file_url: Option<&Url>) -> ParseResult<Url> {
@@ -458,14 +487,13 @@ impl<'a> Parser<'a> {
                         fragment_start: None,
                     })
                 }
-            },
+            }
             Some('?') => {
                 if let Some(base_url) = base_file_url {
                     // Copy everything up to the query string
                     let before_query = match (base_url.query_start, base_url.fragment_start) {
                         (None, None) => &*base_url.serialization,
-                        (Some(i), _) |
-                        (None, Some(i)) => base_url.slice(..i)
+                        (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
                     };
                     self.serialization.push_str(before_query);
                     let (query_start, fragment_start) =
@@ -495,7 +523,7 @@ impl<'a> Parser<'a> {
                         fragment_start: fragment_start,
                     })
                 }
-            },
+            }
             Some('#') => {
                 if let Some(base_url) = base_file_url {
                     self.fragment_only(base_url, input)
@@ -544,7 +572,8 @@ impl<'a> Parser<'a> {
                     // For file URLs that have a host and whose path starts
                     // with the windows drive letter we just remove the host.
                     if !has_host {
-                        self.serialization.drain(host_start as usize..host_end as usize);
+                        self.serialization
+                            .drain(host_start as usize..host_end as usize);
                         host_end = host_start;
                         host = HostInternal::None;
                     }
@@ -575,7 +604,11 @@ impl<'a> Parser<'a> {
                         }
                     }
                     let remaining = self.parse_path(
-                        SchemeType::File, &mut false, path_start, input_after_first_char);
+                        SchemeType::File,
+                        &mut false,
+                        path_start,
+                        input_after_first_char,
+                    );
                     let (query_start, fragment_start) =
                         self.parse_query_and_fragment(scheme_end, remaining)?;
                     let path_start = path_start as u32;
@@ -600,22 +633,32 @@ impl<'a> Parser<'a> {
                 if let Some(base_url) = base_file_url {
                     let before_query = match (base_url.query_start, base_url.fragment_start) {
                         (None, None) => &*base_url.serialization,
-                        (Some(i), _) |
-                        (None, Some(i)) => base_url.slice(..i)
+                        (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
                     };
                     self.serialization.push_str(before_query);
                     self.pop_path(SchemeType::File, base_url.path_start as usize);
                     let remaining = self.parse_path(
-                        SchemeType::File, &mut true, base_url.path_start as usize, input);
+                        SchemeType::File,
+                        &mut true,
+                        base_url.path_start as usize,
+                        input,
+                    );
                     self.with_query_and_fragment(
-                        base_url.scheme_end, base_url.username_end, base_url.host_start,
-                        base_url.host_end, base_url.host, base_url.port, base_url.path_start, remaining)
+                        base_url.scheme_end,
+                        base_url.username_end,
+                        base_url.host_start,
+                        base_url.host_end,
+                        base_url.host,
+                        base_url.port,
+                        base_url.path_start,
+                        remaining,
+                    )
                 } else {
                     self.serialization.push_str("file:///");
                     let scheme_end = "file".len() as u32;
                     let path_start = "file://".len();
-                    let remaining = self.parse_path(
-                        SchemeType::File, &mut false, path_start, input);
+                    let remaining =
+                        self.parse_path(SchemeType::File, &mut false, path_start, input);
                     let (query_start, fragment_start) =
                         self.parse_query_and_fragment(scheme_end, remaining)?;
                     let path_start = path_start as u32;
@@ -636,8 +679,12 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn parse_relative(mut self, input: Input, scheme_type: SchemeType, base_url: &Url)
-                      -> ParseResult<Url> {
+    fn parse_relative(
+        mut self,
+        input: Input,
+        scheme_type: SchemeType,
+        base_url: &Url,
+    ) -> ParseResult<Url> {
         // relative state
         debug_assert!(self.serialization.is_empty());
         let (first_char, input_after_first_char) = input.split_first();
@@ -654,13 +701,12 @@ impl<'a> Parser<'a> {
                     fragment_start: None,
                     ..*base_url
                 })
-            },
+            }
             Some('?') => {
                 // Copy everything up to the query string
                 let before_query = match (base_url.query_start, base_url.fragment_start) {
                     (None, None) => &*base_url.serialization,
-                    (Some(i), _) |
-                    (None, Some(i)) => base_url.slice(..i)
+                    (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
                 };
                 self.serialization.push_str(before_query);
                 let (query_start, fragment_start) =
@@ -671,49 +717,75 @@ impl<'a> Parser<'a> {
                     fragment_start: fragment_start,
                     ..*base_url
                 })
-            },
+            }
             Some('#') => self.fragment_only(base_url, input),
             Some('/') | Some('\\') => {
                 let (slashes_count, remaining) = input.count_matching(|c| matches!(c, '/' | '\\'));
                 if slashes_count >= 2 {
                     self.log_violation_if(SyntaxViolation::ExpectedDoubleSlash, || {
-                        input.clone().take_while(|&c| matches!(c, '/' | '\\'))
-                        .collect::<String>() != "//"
+                        input
+                            .clone()
+                            .take_while(|&c| matches!(c, '/' | '\\'))
+                            .collect::<String>()
+                            != "//"
                     });
                     let scheme_end = base_url.scheme_end;
                     debug_assert!(base_url.byte_at(scheme_end) == b':');
-                    self.serialization.push_str(base_url.slice(..scheme_end + 1));
-                    return self.after_double_slash(remaining, scheme_type, scheme_end)
+                    self.serialization
+                        .push_str(base_url.slice(..scheme_end + 1));
+                    return self.after_double_slash(remaining, scheme_type, scheme_end);
                 }
                 let path_start = base_url.path_start;
                 debug_assert!(base_url.byte_at(path_start) == b'/');
-                self.serialization.push_str(base_url.slice(..path_start + 1));
+                self.serialization
+                    .push_str(base_url.slice(..path_start + 1));
                 let remaining = self.parse_path(
-                    scheme_type, &mut true, path_start as usize, input_after_first_char);
+                    scheme_type,
+                    &mut true,
+                    path_start as usize,
+                    input_after_first_char,
+                );
                 self.with_query_and_fragment(
-                    base_url.scheme_end, base_url.username_end, base_url.host_start,
-                    base_url.host_end, base_url.host, base_url.port, base_url.path_start, remaining)
+                    base_url.scheme_end,
+                    base_url.username_end,
+                    base_url.host_start,
+                    base_url.host_end,
+                    base_url.host,
+                    base_url.port,
+                    base_url.path_start,
+                    remaining,
+                )
             }
             _ => {
                 let before_query = match (base_url.query_start, base_url.fragment_start) {
                     (None, None) => &*base_url.serialization,
-                    (Some(i), _) |
-                    (None, Some(i)) => base_url.slice(..i)
+                    (Some(i), _) | (None, Some(i)) => base_url.slice(..i),
                 };
                 self.serialization.push_str(before_query);
                 // FIXME spec says just "remove last entry", not the "pop" algorithm
                 self.pop_path(scheme_type, base_url.path_start as usize);
-                let remaining = self.parse_path(
-                    scheme_type, &mut true, base_url.path_start as usize, input);
+                let remaining =
+                    self.parse_path(scheme_type, &mut true, base_url.path_start as usize, input);
                 self.with_query_and_fragment(
-                    base_url.scheme_end, base_url.username_end, base_url.host_start,
-                    base_url.host_end, base_url.host, base_url.port, base_url.path_start, remaining)
+                    base_url.scheme_end,
+                    base_url.username_end,
+                    base_url.host_start,
+                    base_url.host_end,
+                    base_url.host,
+                    base_url.port,
+                    base_url.path_start,
+                    remaining,
+                )
             }
         }
     }
 
-    fn after_double_slash(mut self, input: Input, scheme_type: SchemeType, scheme_end: u32)
-                          -> ParseResult<Url> {
+    fn after_double_slash(
+        mut self,
+        input: Input,
+        scheme_type: SchemeType,
+        scheme_end: u32,
+    ) -> ParseResult<Url> {
         self.serialization.push('/');
         self.serialization.push('/');
         // authority state
@@ -724,15 +796,25 @@ impl<'a> Parser<'a> {
             self.parse_host_and_port(remaining, scheme_end, scheme_type)?;
         // path state
         let path_start = to_u32(self.serialization.len())?;
-        let remaining = self.parse_path_start(
-            scheme_type, &mut true, remaining);
-        self.with_query_and_fragment(scheme_end, username_end, host_start,
-                                     host_end, host, port, path_start, remaining)
+        let remaining = self.parse_path_start(scheme_type, &mut true, remaining);
+        self.with_query_and_fragment(
+            scheme_end,
+            username_end,
+            host_start,
+            host_end,
+            host,
+            port,
+            path_start,
+            remaining,
+        )
     }
 
     /// Return (username_end, remaining)
-    fn parse_userinfo<'i>(&mut self, mut input: Input<'i>, scheme_type: SchemeType)
-                          -> ParseResult<(u32, Input<'i>)> {
+    fn parse_userinfo<'i>(
+        &mut self,
+        mut input: Input<'i>,
+        scheme_type: SchemeType,
+    ) -> ParseResult<(u32, Input<'i>)> {
         let mut last_at = None;
         let mut remaining = input.clone();
         let mut char_count = 0;
@@ -745,7 +827,7 @@ impl<'a> Parser<'a> {
                         self.log_violation(SyntaxViolation::EmbeddedCredentials)
                     }
                     last_at = Some((char_count, remaining.clone()))
-                },
+                }
                 '/' | '?' | '#' => break,
                 '\\' if scheme_type.is_special() => break,
                 _ => (),
@@ -755,7 +837,7 @@ impl<'a> Parser<'a> {
         let (mut userinfo_char_count, remaining) = match last_at {
             None => return Ok((to_u32(self.serialization.len())?, input)),
             Some((0, remaining)) => return Ok((to_u32(self.serialization.len())?, remaining)),
-            Some(x) => x
+            Some(x) => x,
         };
 
         let mut username_end = None;
@@ -777,7 +859,8 @@ impl<'a> Parser<'a> {
                     has_username = true;
                 }
                 self.check_url_code_point(c, &input);
-                self.serialization.extend(utf8_percent_encode(utf8_c, USERINFO_ENCODE_SET));
+                self.serialization
+                    .extend(utf8_percent_encode(utf8_c, USERINFO_ENCODE_SET));
             }
         }
         let username_end = match username_end {
@@ -790,9 +873,12 @@ impl<'a> Parser<'a> {
         Ok((username_end, remaining))
     }
 
-    fn parse_host_and_port<'i>(&mut self, input: Input<'i>,
-                                   scheme_end: u32, scheme_type: SchemeType)
-                                   -> ParseResult<(u32, HostInternal, Option<u16>, Input<'i>)> {
+    fn parse_host_and_port<'i>(
+        &mut self,
+        input: Input<'i>,
+        scheme_end: u32,
+        scheme_type: SchemeType,
+    ) -> ParseResult<(u32, HostInternal, Option<u16>, Input<'i>)> {
         let (host, remaining) = Parser::parse_host(input, scheme_type)?;
         write!(&mut self.serialization, "{}", host).unwrap();
         let host_end = to_u32(self.serialization.len())?;
@@ -808,8 +894,10 @@ impl<'a> Parser<'a> {
         Ok((host_end, host.into(), port, remaining))
     }
 
-    pub fn parse_host(mut input: Input, scheme_type: SchemeType)
-                             -> ParseResult<(Host<String>, Input)> {
+    pub fn parse_host(
+        mut input: Input,
+        scheme_type: SchemeType,
+    ) -> ParseResult<(Host<String>, Input)> {
         // Undo the Input abstraction here to avoid allocating in the common case
         // where the host part of the input does not contain any tab or newline
         let input_str = input.chars.as_str();
@@ -833,7 +921,7 @@ impl<'a> Parser<'a> {
                     inside_square_brackets = false;
                     non_ignored_chars += 1
                 }
-                _ => non_ignored_chars += 1
+                _ => non_ignored_chars += 1,
             }
             bytes += c.len_utf8();
         }
@@ -850,7 +938,7 @@ impl<'a> Parser<'a> {
             }
         }
         if scheme_type.is_special() && host_str.is_empty() {
-            return Err(ParseError::EmptyHost)
+            return Err(ParseError::EmptyHost);
         }
         if !scheme_type.is_special() {
             let host = Host::parse_opaque(host_str)?;
@@ -860,8 +948,10 @@ impl<'a> Parser<'a> {
         Ok((host, input))
     }
 
-    pub fn parse_file_host<'i>(&mut self, input: Input<'i>)
-                               -> ParseResult<(bool, HostInternal, Input<'i>)> {
+    pub fn parse_file_host<'i>(
+        &mut self,
+        input: Input<'i>,
+    ) -> ParseResult<(bool, HostInternal, Input<'i>)> {
         // Undo the Input abstraction here to avoid allocating in the common case
         // where the host part of the input does not contain any tab or newline
         let input_str = input.chars.as_str();
@@ -890,7 +980,7 @@ impl<'a> Parser<'a> {
             }
         }
         if is_windows_drive_letter(host_str) {
-            return Ok((false, HostInternal::None, input))
+            return Ok((false, HostInternal::None, input));
         }
         let host = if host_str.is_empty() {
             HostInternal::None
@@ -906,23 +996,27 @@ impl<'a> Parser<'a> {
         Ok((true, host, remaining))
     }
 
-    pub fn parse_port<P>(mut input: Input, default_port: P,
-                                context: Context)
-                                -> ParseResult<(Option<u16>, Input)>
-                                where P: Fn() -> Option<u16> {
+    pub fn parse_port<P>(
+        mut input: Input,
+        default_port: P,
+        context: Context,
+    ) -> ParseResult<(Option<u16>, Input)>
+    where
+        P: Fn() -> Option<u16>,
+    {
         let mut port: u32 = 0;
         let mut has_any_digit = false;
         while let (Some(c), remaining) = input.split_first() {
             if let Some(digit) = c.to_digit(10) {
                 port = port * 10 + digit;
                 if port > ::std::u16::MAX as u32 {
-                    return Err(ParseError::InvalidPort)
+                    return Err(ParseError::InvalidPort);
                 }
                 has_any_digit = true;
             } else if context == Context::UrlParser && !matches!(c, '/' | '\\' | '?' | '#') {
-                return Err(ParseError::InvalidPort)
+                return Err(ParseError::InvalidPort);
             } else {
-                break
+                break;
             }
             input = remaining;
         }
@@ -933,16 +1027,21 @@ impl<'a> Parser<'a> {
         Ok((opt_port, input))
     }
 
-    pub fn parse_path_start<'i>(&mut self, scheme_type: SchemeType, has_host: &mut bool,
-                            mut input: Input<'i>)
-                            -> Input<'i> {
+    pub fn parse_path_start<'i>(
+        &mut self,
+        scheme_type: SchemeType,
+        has_host: &mut bool,
+        mut input: Input<'i>,
+    ) -> Input<'i> {
         // Path start state
         match input.split_first() {
             (Some('/'), remaining) => input = remaining,
-            (Some('\\'), remaining) => if scheme_type.is_special() {
-                self.log_violation(SyntaxViolation::Backslash);
-                input = remaining
-            },
+            (Some('\\'), remaining) => {
+                if scheme_type.is_special() {
+                    self.log_violation(SyntaxViolation::Backslash);
+                    input = remaining
+                }
+            }
             _ => {}
         }
         let path_start = self.serialization.len();
@@ -950,9 +1049,13 @@ impl<'a> Parser<'a> {
         self.parse_path(scheme_type, has_host, path_start, input)
     }
 
-    pub fn parse_path<'i>(&mut self, scheme_type: SchemeType, has_host: &mut bool,
-                          path_start: usize, mut input: Input<'i>)
-                          -> Input<'i> {
+    pub fn parse_path<'i>(
+        &mut self,
+        scheme_type: SchemeType,
+        has_host: &mut bool,
+        path_start: usize,
+        mut input: Input<'i>,
+    ) -> Input<'i> {
         // Relative path state
         debug_assert!(self.serialization.ends_with('/'));
         loop {
@@ -960,62 +1063,70 @@ impl<'a> Parser<'a> {
             let mut ends_with_slash = false;
             loop {
                 let input_before_c = input.clone();
-                let (c, utf8_c) = if let Some(x) = input.next_utf8() { x } else { break };
+                let (c, utf8_c) = if let Some(x) = input.next_utf8() {
+                    x
+                } else {
+                    break;
+                };
                 match c {
                     '/' if self.context != Context::PathSegmentSetter => {
                         ends_with_slash = true;
-                        break
-                    },
-                    '\\' if self.context != Context::PathSegmentSetter &&
-                            scheme_type.is_special() => {
+                        break;
+                    }
+                    '\\' if self.context != Context::PathSegmentSetter
+                        && scheme_type.is_special() =>
+                    {
                         self.log_violation(SyntaxViolation::Backslash);
                         ends_with_slash = true;
-                        break
-                    },
+                        break;
+                    }
                     '?' | '#' if self.context == Context::UrlParser => {
                         input = input_before_c;
-                        break
-                    },
+                        break;
+                    }
                     _ => {
                         self.check_url_code_point(c, &input);
                         if self.context == Context::PathSegmentSetter {
                             if scheme_type.is_special() {
                                 self.serialization.extend(utf8_percent_encode(
-                                    utf8_c, SPECIAL_PATH_SEGMENT_ENCODE_SET));
+                                    utf8_c,
+                                    SPECIAL_PATH_SEGMENT_ENCODE_SET,
+                                ));
                             } else {
-                                self.serialization.extend(utf8_percent_encode(
-                                    utf8_c, PATH_SEGMENT_ENCODE_SET));
+                                self.serialization
+                                    .extend(utf8_percent_encode(utf8_c, PATH_SEGMENT_ENCODE_SET));
                             }
                         } else {
-                            self.serialization.extend(utf8_percent_encode(
-                                utf8_c, DEFAULT_ENCODE_SET));
+                            self.serialization
+                                .extend(utf8_percent_encode(utf8_c, DEFAULT_ENCODE_SET));
                         }
                     }
                 }
             }
             match &self.serialization[segment_start..] {
-                ".." | "%2e%2e" | "%2e%2E" | "%2E%2e" | "%2E%2E" | "%2e." | "%2E." | ".%2e" | ".%2E"  => {
+                ".." | "%2e%2e" | "%2e%2E" | "%2E%2e" | "%2E%2E" | "%2e." | "%2E." | ".%2e"
+                | ".%2E" => {
                     debug_assert!(self.serialization.as_bytes()[segment_start - 1] == b'/');
-                    self.serialization.truncate(segment_start - 1);  // Truncate "/.."
+                    self.serialization.truncate(segment_start - 1); // Truncate "/.."
                     self.pop_path(scheme_type, path_start);
                     if !self.serialization[path_start..].ends_with('/') {
                         self.serialization.push('/')
                     }
-                },
+                }
                 "." | "%2e" | "%2E" => {
                     self.serialization.truncate(segment_start);
-                },
+                }
                 _ => {
-                    if scheme_type.is_file() && is_windows_drive_letter(
-                        &self.serialization[path_start + 1..]
-                    ) {
+                    if scheme_type.is_file()
+                        && is_windows_drive_letter(&self.serialization[path_start + 1..])
+                    {
                         if self.serialization.ends_with('|') {
                             self.serialization.pop();
                             self.serialization.push(':');
                         }
                         if *has_host {
                             self.log_violation(SyntaxViolation::FileWithHostAndWindowsDrive);
-                            *has_host = false;  // FIXME account for this in callers
+                            *has_host = false; // FIXME account for this in callers
                         }
                     }
                     if ends_with_slash {
@@ -1024,7 +1135,7 @@ impl<'a> Parser<'a> {
                 }
             }
             if !ends_with_slash {
-                break
+                break;
             }
         }
         input
@@ -1038,14 +1149,12 @@ impl<'a> Parser<'a> {
             let segment_start = path_start + slash_position + 1;
             // Don’t pop a Windows drive letter
             // FIXME: *normalized* Windows drive letter
-            if !(
-                scheme_type.is_file() &&
-                is_windows_drive_letter(&self.serialization[segment_start..])
-            ) {
+            if !(scheme_type.is_file()
+                && is_windows_drive_letter(&self.serialization[segment_start..]))
+            {
                 self.serialization.truncate(segment_start);
             }
         }
-
     }
 
     pub fn parse_cannot_be_a_base_path<'i>(&mut self, mut input: Input<'i>) -> Input<'i> {
@@ -1057,20 +1166,26 @@ impl<'a> Parser<'a> {
                 }
                 Some((c, utf8_c)) => {
                     self.check_url_code_point(c, &input);
-                    self.serialization.extend(utf8_percent_encode(
-                        utf8_c, SIMPLE_ENCODE_SET));
+                    self.serialization
+                        .extend(utf8_percent_encode(utf8_c, SIMPLE_ENCODE_SET));
                 }
-                None => return input
+                None => return input,
             }
         }
     }
 
-    fn with_query_and_fragment(mut self, scheme_end: u32, username_end: u32,
-                               host_start: u32, host_end: u32, host: HostInternal,
-                               port: Option<u16>, path_start: u32, remaining: Input)
-                               -> ParseResult<Url> {
-        let (query_start, fragment_start) =
-            self.parse_query_and_fragment(scheme_end, remaining)?;
+    fn with_query_and_fragment(
+        mut self,
+        scheme_end: u32,
+        username_end: u32,
+        host_start: u32,
+        host_end: u32,
+        host: HostInternal,
+        port: Option<u16>,
+        path_start: u32,
+        remaining: Input,
+    ) -> ParseResult<Url> {
+        let (query_start, fragment_start) = self.parse_query_and_fragment(scheme_end, remaining)?;
         Ok(Url {
             serialization: self.serialization,
             scheme_end: scheme_end,
@@ -1081,13 +1196,16 @@ impl<'a> Parser<'a> {
             port: port,
             path_start: path_start,
             query_start: query_start,
-            fragment_start: fragment_start
+            fragment_start: fragment_start,
         })
     }
 
     /// Return (query_start, fragment_start)
-    fn parse_query_and_fragment(&mut self, scheme_end: u32, mut input: Input)
-                                -> ParseResult<(Option<u32>, Option<u32>)> {
+    fn parse_query_and_fragment(
+        &mut self,
+        scheme_end: u32,
+        mut input: Input,
+    ) -> ParseResult<(Option<u32>, Option<u32>)> {
         let mut query_start = None;
         match input.next() {
             Some('#') => {}
@@ -1098,11 +1216,11 @@ impl<'a> Parser<'a> {
                 if let Some(remaining) = remaining {
                     input = remaining
                 } else {
-                    return Ok((query_start, None))
+                    return Ok((query_start, None));
                 }
             }
             None => return Ok((None, None)),
-            _ => panic!("Programming error. parse_query_and_fragment() called without ? or #")
+            _ => panic!("Programming error. parse_query_and_fragment() called without ? or #"),
         }
 
         let fragment_start = to_u32(self.serialization.len())?;
@@ -1111,14 +1229,13 @@ impl<'a> Parser<'a> {
         Ok((query_start, Some(fragment_start)))
     }
 
-    pub fn parse_query<'i>(&mut self, scheme_end: u32, mut input: Input<'i>)
-                           -> Option<Input<'i>> {
-        let mut query = String::new();  // FIXME: use a streaming decoder instead
+    pub fn parse_query<'i>(&mut self, scheme_end: u32, mut input: Input<'i>) -> Option<Input<'i>> {
+        let mut query = String::new(); // FIXME: use a streaming decoder instead
         let mut remaining = None;
         while let Some(c) = input.next() {
             if c == '#' && self.context == Context::UrlParser {
                 remaining = Some(input);
-                break
+                break;
             } else {
                 self.check_url_code_point(c, &input);
                 query.push(c);
@@ -1130,7 +1247,8 @@ impl<'a> Parser<'a> {
             _ => EncodingOverride::utf8(),
         };
         let query_bytes = encoding.encode(query.into());
-        self.serialization.extend(percent_encode(&query_bytes, QUERY_ENCODE_SET));
+        self.serialization
+            .extend(percent_encode(&query_bytes, QUERY_ENCODE_SET));
         remaining
     }
 
@@ -1140,7 +1258,8 @@ impl<'a> Parser<'a> {
             None => &*base_url.serialization,
         };
         debug_assert!(self.serialization.is_empty());
-        self.serialization.reserve(before_fragment.len() + input.chars.as_str().len());
+        self.serialization
+            .reserve(before_fragment.len() + input.chars.as_str().len());
         self.serialization.push_str(before_fragment);
         self.serialization.push('#');
         let next = input.next();
@@ -1155,12 +1274,12 @@ impl<'a> Parser<'a> {
 
     pub fn parse_fragment(&mut self, mut input: Input) {
         while let Some((c, utf8_c)) = input.next_utf8() {
-            if c ==  '\0' {
+            if c == '\0' {
                 self.log_violation(SyntaxViolation::NullInFragment)
             } else {
                 self.check_url_code_point(c, &input);
-                self.serialization.extend(utf8_percent_encode(utf8_c,
-                                                              SIMPLE_ENCODE_SET));
+                self.serialization
+                    .extend(utf8_percent_encode(utf8_c, SIMPLE_ENCODE_SET));
             }
         }
     }
@@ -1170,7 +1289,8 @@ impl<'a> Parser<'a> {
             if c == '%' {
                 let mut input = input.clone();
                 if !matches!((input.next(), input.next()), (Some(a), Some(b))
-                             if is_ascii_hex_digit(a) && is_ascii_hex_digit(b)) {
+                             if is_ascii_hex_digit(a) && is_ascii_hex_digit(b))
+                {
                     vfn(SyntaxViolation::PercentDecode)
                 }
             } else if !is_url_code_point(c) {
@@ -1214,7 +1334,7 @@ fn is_url_code_point(c: char) -> bool {
 /// https://url.spec.whatwg.org/#c0-controls-and-space
 #[inline]
 fn c0_control_or_space(ch: char) -> bool {
-    ch <= ' '  // U+0000 to U+0020
+    ch <= ' ' // U+0000 to U+0020
 }
 
 /// https://url.spec.whatwg.org/#ascii-alpha
@@ -1235,13 +1355,11 @@ pub fn to_u32(i: usize) -> ParseResult<u32> {
 /// Wether the scheme is file:, the path has a single segment, and that segment
 /// is a Windows drive letter
 fn is_windows_drive_letter(segment: &str) -> bool {
-    segment.len() == 2
-    && starts_with_windows_drive_letter(segment)
+    segment.len() == 2 && starts_with_windows_drive_letter(segment)
 }
 
 fn starts_with_windows_drive_letter(s: &str) -> bool {
-    ascii_alpha(s.as_bytes()[0] as char)
-    && matches!(s.as_bytes()[1], b':' | b'|')
+    ascii_alpha(s.as_bytes()[0] as char) && matches!(s.as_bytes()[1], b':' | b'|')
 }
 
 fn starts_with_windows_drive_letter_segment(input: &Input) -> bool {

+ 21 - 9
src/path_segments.rs

@@ -6,7 +6,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use parser::{self, SchemeType, to_u32};
+use parser::{self, to_u32, SchemeType};
 use std::str;
 use Url;
 
@@ -56,7 +56,8 @@ pub fn new(url: &mut Url) -> PathSegmentsMut {
 
 impl<'a> Drop for PathSegmentsMut<'a> {
     fn drop(&mut self) {
-        self.url.restore_after_path(self.old_after_path_position, &self.after_path)
+        self.url
+            .restore_after_path(self.old_after_path_position, &self.after_path)
     }
 }
 
@@ -126,8 +127,12 @@ impl<'a> PathSegmentsMut<'a> {
     ///
     /// Returns `&mut Self` so that method calls can be chained.
     pub fn pop(&mut self) -> &mut Self {
-        let last_slash = self.url.serialization[self.after_first_slash..].rfind('/').unwrap_or(0);
-        self.url.serialization.truncate(self.after_first_slash + last_slash);
+        let last_slash = self.url.serialization[self.after_first_slash..]
+            .rfind('/')
+            .unwrap_or(0);
+        self.url
+            .serialization
+            .truncate(self.after_first_slash + last_slash);
         self
     }
 
@@ -194,7 +199,10 @@ impl<'a> PathSegmentsMut<'a> {
     /// # run().unwrap();
     /// ```
     pub fn extend<I>(&mut self, segments: I) -> &mut Self
-    where I: IntoIterator, I::Item: AsRef<str> {
+    where
+        I: IntoIterator,
+        I::Item: AsRef<str>,
+    {
         let scheme_type = SchemeType::from(self.url.scheme());
         let path_start = self.url.path_start as usize;
         self.url.mutate(|parser| {
@@ -202,14 +210,18 @@ impl<'a> PathSegmentsMut<'a> {
             for segment in segments {
                 let segment = segment.as_ref();
                 if matches!(segment, "." | "..") {
-                    continue
+                    continue;
                 }
                 if parser.serialization.len() > path_start + 1 {
                     parser.serialization.push('/');
                 }
-                let mut has_host = true;  // FIXME account for this?
-                parser.parse_path(scheme_type, &mut has_host, path_start,
-                                  parser::Input::new(segment));
+                let mut has_host = true; // FIXME account for this?
+                parser.parse_path(
+                    scheme_type,
+                    &mut has_host,
+                    path_start,
+                    parser::Input::new(segment),
+                );
             }
         });
         self

+ 19 - 10
src/quirks.rs

@@ -11,8 +11,8 @@
 //! Unless you need to be interoperable with web browsers,
 //! you probably want to use `Url` method instead.
 
-use {Url, Position, Host, ParseError, idna};
-use parser::{Parser, SchemeType, default_port, Context, Input};
+use parser::{default_port, Context, Input, Parser, SchemeType};
+use {idna, Host, ParseError, Position, Url};
 
 /// https://url.spec.whatwg.org/#dom-url-domaintoascii
 pub fn domain_to_ascii(domain: &str) -> String {
@@ -84,7 +84,11 @@ pub fn password(url: &Url) -> &str {
 
 /// Setter for https://url.spec.whatwg.org/#dom-url-password
 pub fn set_password(url: &mut Url, new_password: &str) -> Result<(), ()> {
-    url.set_password(if new_password.is_empty() { None } else { Some(new_password) })
+    url.set_password(if new_password.is_empty() {
+        None
+    } else {
+        Some(new_password)
+    })
 }
 
 /// Getter for https://url.spec.whatwg.org/#dom-url-host
@@ -96,7 +100,7 @@ pub fn host(url: &Url) -> &str {
 /// Setter for https://url.spec.whatwg.org/#dom-url-host
 pub fn set_host(url: &mut Url, new_host: &str) -> Result<(), ()> {
     if url.cannot_be_a_base() {
-        return Err(())
+        return Err(());
     }
     let host;
     let opt_port;
@@ -108,12 +112,13 @@ pub fn set_host(url: &mut Url, new_host: &str) -> Result<(), ()> {
                 host = h;
                 opt_port = if let Some(remaining) = remaining.split_prefix(':') {
                     Parser::parse_port(remaining, || default_port(scheme), Context::Setter)
-                    .ok().map(|(port, _remaining)| port)
+                        .ok()
+                        .map(|(port, _remaining)| port)
                 } else {
                     None
                 };
             }
-            Err(_) => return Err(())
+            Err(_) => return Err(()),
         }
     }
     url.set_host_internal(host, opt_port);
@@ -129,7 +134,7 @@ pub fn hostname(url: &Url) -> &str {
 /// Setter for https://url.spec.whatwg.org/#dom-url-hostname
 pub fn set_hostname(url: &mut Url, new_hostname: &str) -> Result<(), ()> {
     if url.cannot_be_a_base() {
-        return Err(())
+        return Err(());
     }
     let result = Parser::parse_host(Input::new(new_hostname), SchemeType::from(url.scheme()));
     if let Ok((host, _remaining)) = result {
@@ -153,9 +158,13 @@ pub fn set_port(url: &mut Url, new_port: &str) -> Result<(), ()> {
         // has_host implies !cannot_be_a_base
         let scheme = url.scheme();
         if !url.has_host() || url.host() == Some(Host::Domain("")) || scheme == "file" {
-            return Err(())
+            return Err(());
         }
-        result = Parser::parse_port(Input::new(new_port), || default_port(scheme), Context::Setter)
+        result = Parser::parse_port(
+            Input::new(new_port),
+            || default_port(scheme),
+            Context::Setter,
+        )
     }
     if let Ok((new_port, _remaining)) = result {
         url.set_port_internal(new_port);
@@ -168,7 +177,7 @@ pub fn set_port(url: &mut Url, new_port: &str) -> Result<(), ()> {
 /// Getter for https://url.spec.whatwg.org/#dom-url-pathname
 #[inline]
 pub fn pathname(url: &Url) -> &str {
-     url.path()
+    url.path()
 }
 
 /// Setter for https://url.spec.whatwg.org/#dom-url-pathname

+ 37 - 32
src/slicing.rs

@@ -6,7 +6,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::ops::{Range, RangeFrom, RangeTo, RangeFull, Index};
+use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
 use Url;
 
 impl Index<RangeFull> for Url {
@@ -94,7 +94,7 @@ pub enum Position {
     BeforeQuery,
     AfterQuery,
     BeforeFragment,
-    AfterFragment
+    AfterFragment,
 }
 
 impl Url {
@@ -105,43 +105,49 @@ impl Url {
 
             Position::AfterScheme => self.scheme_end as usize,
 
-            Position::BeforeUsername => if self.has_authority() {
-                self.scheme_end as usize + "://".len()
-            } else {
-                debug_assert!(self.byte_at(self.scheme_end) == b':');
-                debug_assert!(self.scheme_end + ":".len() as u32 == self.username_end);
-                self.scheme_end as usize + ":".len()
-            },
+            Position::BeforeUsername => {
+                if self.has_authority() {
+                    self.scheme_end as usize + "://".len()
+                } else {
+                    debug_assert!(self.byte_at(self.scheme_end) == b':');
+                    debug_assert!(self.scheme_end + ":".len() as u32 == self.username_end);
+                    self.scheme_end as usize + ":".len()
+                }
+            }
 
             Position::AfterUsername => self.username_end as usize,
 
-            Position::BeforePassword => if self.has_authority() &&
-                                           self.byte_at(self.username_end) == b':' {
-                self.username_end as usize + ":".len()
-            } else {
-                debug_assert!(self.username_end == self.host_start);
-                self.username_end as usize
-            },
-
-            Position::AfterPassword => if self.has_authority() &&
-                                          self.byte_at(self.username_end) == b':' {
-                debug_assert!(self.byte_at(self.host_start - "@".len() as u32) == b'@');
-                self.host_start as usize - "@".len()
-            } else {
-                debug_assert!(self.username_end == self.host_start);
-                self.host_start as usize
-            },
+            Position::BeforePassword => {
+                if self.has_authority() && self.byte_at(self.username_end) == b':' {
+                    self.username_end as usize + ":".len()
+                } else {
+                    debug_assert!(self.username_end == self.host_start);
+                    self.username_end as usize
+                }
+            }
+
+            Position::AfterPassword => {
+                if self.has_authority() && self.byte_at(self.username_end) == b':' {
+                    debug_assert!(self.byte_at(self.host_start - "@".len() as u32) == b'@');
+                    self.host_start as usize - "@".len()
+                } else {
+                    debug_assert!(self.username_end == self.host_start);
+                    self.host_start as usize
+                }
+            }
 
             Position::BeforeHost => self.host_start as usize,
 
             Position::AfterHost => self.host_end as usize,
 
-            Position::BeforePort => if self.port.is_some() {
-                debug_assert!(self.byte_at(self.host_end) == b':');
-                self.host_end as usize + ":".len()
-            } else {
-                self.host_end as usize
-            },
+            Position::BeforePort => {
+                if self.port.is_some() {
+                    debug_assert!(self.byte_at(self.host_end) == b':');
+                    self.host_end as usize + ":".len()
+                } else {
+                    self.host_end as usize
+                }
+            }
 
             Position::AfterPort => self.path_start as usize,
 
@@ -179,4 +185,3 @@ impl Url {
         }
     }
 }
-

+ 32 - 20
tests/data.rs

@@ -8,29 +8,29 @@
 
 //! Data-driven tests
 
-extern crate serde_json;
 extern crate rustc_test as test;
+extern crate serde_json;
 extern crate url;
 
 use serde_json::Value;
-use url::{Url, quirks};
 use std::str::FromStr;
+use url::{quirks, Url};
 
 fn check_invariants(url: &Url) {
     url.check_invariants().unwrap();
-    #[cfg(feature="serde")] {
+    #[cfg(feature = "serde")]
+    {
         let bytes = serde_json::to_vec(url).unwrap();
         let new_url: Url = serde_json::from_slice(&bytes).unwrap();
         assert_eq!(url, &new_url);
     }
 }
 
-
 fn run_parsing(input: &str, base: &str, expected: Result<ExpectedAttributes, ()>) {
     let base = match Url::parse(&base) {
         Ok(base) => base,
         Err(_) if expected.is_err() => return,
-        Err(message) => panic!("Error parsing base {:?}: {}", base, message)
+        Err(message) => panic!("Error parsing base {:?}: {}", base, message),
     };
     let (url, expected) = match (base.join(&input), expected) {
         (Ok(url), Ok(expected)) => (url, expected),
@@ -42,14 +42,18 @@ fn run_parsing(input: &str, base: &str, expected: Result<ExpectedAttributes, ()>
     check_invariants(&url);
 
     macro_rules! assert_eq {
-        ($expected: expr, $got: expr) => {
-            {
-                let expected = $expected;
-                let got = $got;
-                assert!(expected == got, "{:?} != {} {:?} for URL {:?}",
-                        got, stringify!($expected), expected, url);
-            }
-        }
+        ($expected: expr, $got: expr) => {{
+            let expected = $expected;
+            let got = $got;
+            assert!(
+                expected == got,
+                "{:?} != {} {:?} for URL {:?}",
+                got,
+                stringify!($expected),
+                expected,
+                url
+            );
+        }};
     }
 
     macro_rules! assert_attributes {
@@ -95,7 +99,11 @@ impl JsonExt for Value {
     }
 
     fn string(self) -> String {
-        if let Value::String(s) = self { s } else { panic!("Not a Value::String") }
+        if let Value::String(s) = self {
+            s
+        } else {
+            panic!("Not a Value::String")
+        }
     }
 
     fn take_string(&mut self, key: &str) -> String {
@@ -109,7 +117,7 @@ fn collect_parsing<F: FnMut(String, test::TestFn)>(add_test: &mut F) {
         .expect("JSON parse error in urltestdata.json");
     for entry in json.as_array_mut().unwrap() {
         if entry.is_string() {
-            continue  // ignore comments
+            continue; // ignore comments
         }
         let base = entry.take_string("base");
         let input = entry.take_string("input");
@@ -118,8 +126,7 @@ fn collect_parsing<F: FnMut(String, test::TestFn)>(add_test: &mut F) {
         } else {
             Ok(ExpectedAttributes {
                 href: entry.take_string("href"),
-                origin: entry.take_key("origin")
-                    .map(|s| s.string()),
+                origin: entry.take_key("origin").map(|s| s.string()),
                 protocol: entry.take_string("protocol"),
                 username: entry.take_string("username"),
                 password: entry.take_string("password"),
@@ -131,12 +138,17 @@ fn collect_parsing<F: FnMut(String, test::TestFn)>(add_test: &mut F) {
                 hash: entry.take_string("hash"),
             })
         };
-        add_test(format!("{:?} @ base {:?}", input, base),
-                 test::TestFn::dyn_test_fn(move || run_parsing(&input, &base, expected)));
+        add_test(
+            format!("{:?} @ base {:?}", input, base),
+            test::TestFn::dyn_test_fn(move || run_parsing(&input, &base, expected)),
+        );
     }
 }
 
-fn collect_setters<F>(add_test: &mut F) where F: FnMut(String, test::TestFn) {
+fn collect_setters<F>(add_test: &mut F)
+where
+    F: FnMut(String, test::TestFn),
+{
     let mut json = Value::from_str(include_str!("setters_tests.json"))
         .expect("JSON parse error in setters_tests.json");
 

+ 132 - 50
tests/unit.rs

@@ -15,7 +15,7 @@ use std::borrow::Cow;
 use std::cell::{Cell, RefCell};
 use std::net::{Ipv4Addr, Ipv6Addr};
 use std::path::{Path, PathBuf};
-use url::{Host, HostAndPort, Url, form_urlencoded};
+use url::{form_urlencoded, Host, HostAndPort, Url};
 
 #[test]
 fn size() {
@@ -24,7 +24,9 @@ fn size() {
 }
 
 macro_rules! assert_from_file_path {
-    ($path: expr) => { assert_from_file_path!($path, $path) };
+    ($path: expr) => {
+        assert_from_file_path!($path, $path)
+    };
     ($path: expr, $url_path: expr) => {{
         let url = Url::from_file_path(Path::new($path)).unwrap();
         assert_eq!(url.host(), None);
@@ -33,8 +35,6 @@ macro_rules! assert_from_file_path {
     }};
 }
 
-
-
 #[test]
 fn new_file_paths() {
     if cfg!(unix) {
@@ -73,7 +73,10 @@ fn new_path_windows_fun() {
         assert_from_file_path!("C:\\foo\\ba\0r", "/C:/foo/ba%00r");
 
         // Invalid UTF-8
-        assert!(Url::parse("file:///C:/foo/ba%80r").unwrap().to_file_path().is_err());
+        assert!(Url::parse("file:///C:/foo/ba%80r")
+            .unwrap()
+            .to_file_path()
+            .is_err());
 
         // test windows canonicalized path
         let path = PathBuf::from(r"\\?\C:\foo\bar");
@@ -85,7 +88,6 @@ fn new_path_windows_fun() {
     }
 }
 
-
 #[test]
 fn new_directory_paths() {
     if cfg!(unix) {
@@ -99,7 +101,10 @@ fn new_directory_paths() {
     if cfg!(windows) {
         assert_eq!(Url::from_directory_path(Path::new("relative")), Err(()));
         assert_eq!(Url::from_directory_path(Path::new(r"..\relative")), Err(()));
-        assert_eq!(Url::from_directory_path(Path::new(r"\drive-relative")), Err(()));
+        assert_eq!(
+            Url::from_directory_path(Path::new(r"\drive-relative")),
+            Err(())
+        );
         assert_eq!(Url::from_directory_path(Path::new(r"\\ucn\")), Err(()));
 
         let url = Url::from_directory_path(Path::new(r"C:\foo\bar")).unwrap();
@@ -126,10 +131,16 @@ fn from_str() {
 
 #[test]
 fn parse_with_params() {
-    let url = Url::parse_with_params("http://testing.com/this?dont=clobberme",
-                                     &[("lang", "rust")]).unwrap();
+    let url = Url::parse_with_params(
+        "http://testing.com/this?dont=clobberme",
+        &[("lang", "rust")],
+    )
+    .unwrap();
 
-    assert_eq!(url.as_str(), "http://testing.com/this?dont=clobberme&lang=rust");
+    assert_eq!(
+        url.as_str(),
+        "http://testing.com/this?dont=clobberme&lang=rust"
+    );
 }
 
 #[test]
@@ -144,8 +155,8 @@ fn issue_124() {
 
 #[test]
 fn test_equality() {
-    use std::hash::{Hash, Hasher};
     use std::collections::hash_map::DefaultHasher;
+    use std::hash::{Hash, Hasher};
 
     fn check_eq(a: &Url, b: &Url) {
         assert_eq!(a, b);
@@ -195,13 +206,29 @@ fn host() {
         assert_eq!(Url::parse(input).unwrap().host(), Some(host));
     }
     assert_host("http://www.mozilla.org", Host::Domain("www.mozilla.org"));
-    assert_host("http://1.35.33.49", Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)));
-    assert_host("http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]", Host::Ipv6(Ipv6Addr::new(
-        0x2001, 0x0db8, 0x85a3, 0x08d3, 0x1319, 0x8a2e, 0x0370, 0x7344)));
+    assert_host(
+        "http://1.35.33.49",
+        Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)),
+    );
+    assert_host(
+        "http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]",
+        Host::Ipv6(Ipv6Addr::new(
+            0x2001, 0x0db8, 0x85a3, 0x08d3, 0x1319, 0x8a2e, 0x0370, 0x7344,
+        )),
+    );
     assert_host("http://1.35.+33.49", Host::Domain("1.35.+33.49"));
-    assert_host("http://[::]", Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)));
-    assert_host("http://[::1]", Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
-    assert_host("http://0x1.0X23.0x21.061", Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)));
+    assert_host(
+        "http://[::]",
+        Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)),
+    );
+    assert_host(
+        "http://[::1]",
+        Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
+    );
+    assert_host(
+        "http://0x1.0X23.0x21.061",
+        Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)),
+    );
     assert_host("http://0x1232131", Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)));
     assert_host("http://111", Host::Ipv4(Ipv4Addr::new(0, 0, 0, 111)));
     assert_host("http://2..2.3", Host::Domain("2..2.3"));
@@ -216,15 +243,26 @@ fn host_serialization() {
     // but https://url.spec.whatwg.org/#concept-ipv6-serializer specifies not to.
 
     // Not [::0.0.0.2] / [::ffff:0.0.0.2]
-    assert_eq!(Url::parse("http://[0::2]").unwrap().host_str(), Some("[::2]"));
-    assert_eq!(Url::parse("http://[0::ffff:0:2]").unwrap().host_str(), Some("[::ffff:0:2]"));
+    assert_eq!(
+        Url::parse("http://[0::2]").unwrap().host_str(),
+        Some("[::2]")
+    );
+    assert_eq!(
+        Url::parse("http://[0::ffff:0:2]").unwrap().host_str(),
+        Some("[::ffff:0:2]")
+    );
 }
 
 #[test]
 fn test_idna() {
     assert!("http://goșu.ro".parse::<Url>().is_ok());
-    assert_eq!(Url::parse("http://☃.net/").unwrap().host(), Some(Host::Domain("xn--n3h.net")));
-    assert!("https://r2---sn-huoa-cvhl.googlevideo.com/crossdomain.xml".parse::<Url>().is_ok());
+    assert_eq!(
+        Url::parse("http://☃.net/").unwrap().host(),
+        Some(Host::Domain("xn--n3h.net"))
+    );
+    assert!("https://r2---sn-huoa-cvhl.googlevideo.com/crossdomain.xml"
+        .parse::<Url>()
+        .is_ok());
 }
 
 #[test]
@@ -235,9 +273,18 @@ fn test_serialization() {
         ("http://@emptyuser.com/", "http://emptyuser.com/"),
         ("http://:@emptypass.com/", "http://emptypass.com/"),
         ("http://user@user.com/", "http://user@user.com/"),
-        ("http://user:pass@userpass.com/", "http://user:pass@userpass.com/"),
-        ("http://slashquery.com/path/?q=something", "http://slashquery.com/path/?q=something"),
-        ("http://noslashquery.com/path?q=something", "http://noslashquery.com/path?q=something")
+        (
+            "http://user:pass@userpass.com/",
+            "http://user:pass@userpass.com/",
+        ),
+        (
+            "http://slashquery.com/path/?q=something",
+            "http://slashquery.com/path/?q=something",
+        ),
+        (
+            "http://noslashquery.com/path?q=something",
+            "http://noslashquery.com/path?q=something",
+        ),
     ];
     for &(input, result) in &data {
         let url = Url::parse(input).unwrap();
@@ -250,11 +297,16 @@ fn test_form_urlencoded() {
     let pairs: &[(Cow<str>, Cow<str>)] = &[
         ("foo".into(), "é&".into()),
         ("bar".into(), "".into()),
-        ("foo".into(), "#".into())
+        ("foo".into(), "#".into()),
     ];
-    let encoded = form_urlencoded::Serializer::new(String::new()).extend_pairs(pairs).finish();
+    let encoded = form_urlencoded::Serializer::new(String::new())
+        .extend_pairs(pairs)
+        .finish();
     assert_eq!(encoded, "foo=%C3%A9%26&bar=&foo=%23");
-    assert_eq!(form_urlencoded::parse(encoded.as_bytes()).collect::<Vec<_>>(), pairs.to_vec());
+    assert_eq!(
+        form_urlencoded::parse(encoded.as_bytes()).collect::<Vec<_>>(),
+        pairs.to_vec()
+    );
 }
 
 #[test]
@@ -281,27 +333,33 @@ fn host_and_port_display() {
     assert_eq!(
         format!(
             "{}",
-            HostAndPort{ host: Host::Domain("www.mozilla.org"), port: 80}
+            HostAndPort {
+                host: Host::Domain("www.mozilla.org"),
+                port: 80
+            }
         ),
         "www.mozilla.org:80"
     );
     assert_eq!(
         format!(
             "{}",
-            HostAndPort::<String>{ host: Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)), port: 65535 }
+            HostAndPort::<String> {
+                host: Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)),
+                port: 65535
+            }
         ),
         "1.35.33.49:65535"
     );
     assert_eq!(
         format!(
             "{}",
-            HostAndPort::<String>{
+            HostAndPort::<String> {
                 host: Host::Ipv6(Ipv6Addr::new(
                     0x2001, 0x0db8, 0x85a3, 0x08d3, 0x1319, 0x8a2e, 0x0370, 0x7344
                 )),
                 port: 1337
-            })
-        ,
+            }
+        ),
         "[2001:db8:85a3:8d3:1319:8a2e:370:7344]:1337"
     )
 }
@@ -322,8 +380,13 @@ fn issue_61() {
 fn issue_197() {
     let mut url = Url::from_file_path("/").expect("Failed to parse path");
     url.check_invariants().unwrap();
-    assert_eq!(url, Url::parse("file:///").expect("Failed to parse path + protocol"));
-    url.path_segments_mut().expect("path_segments_mut").pop_if_empty();
+    assert_eq!(
+        url,
+        Url::parse("file:///").expect("Failed to parse path + protocol")
+    );
+    url.path_segments_mut()
+        .expect("path_segments_mut")
+        .pop_if_empty();
 }
 
 #[test]
@@ -345,12 +408,19 @@ fn append_trailing_slash() {
 /// https://github.com/servo/rust-url/issues/227
 fn extend_query_pairs_then_mutate() {
     let mut url: Url = "http://localhost:6767/foo/bar".parse().unwrap();
-    url.query_pairs_mut().extend_pairs(vec![ ("auth", "my-token") ].into_iter());
+    url.query_pairs_mut()
+        .extend_pairs(vec![("auth", "my-token")].into_iter());
     url.check_invariants().unwrap();
-    assert_eq!(url.to_string(), "http://localhost:6767/foo/bar?auth=my-token");
+    assert_eq!(
+        url.to_string(),
+        "http://localhost:6767/foo/bar?auth=my-token"
+    );
     url.path_segments_mut().unwrap().push("some_other_path");
     url.check_invariants().unwrap();
-    assert_eq!(url.to_string(), "http://localhost:6767/foo/bar/some_other_path?auth=my-token");
+    assert_eq!(
+        url.to_string(),
+        "http://localhost:6767/foo/bar/some_other_path?auth=my-token"
+    );
 }
 
 #[test]
@@ -387,7 +457,10 @@ fn test_set_host() {
 #[test]
 // https://github.com/servo/rust-url/issues/166
 fn test_leading_dots() {
-    assert_eq!(Host::parse(".org").unwrap(), Host::Domain(".org".to_owned()));
+    assert_eq!(
+        Host::parse(".org").unwrap(),
+        Host::Domain(".org".to_owned())
+    );
     assert_eq!(Url::parse("file://./foo").unwrap().domain(), Some("."));
 }
 
@@ -402,7 +475,10 @@ fn define_encode_set_scopes() {
         pub QUERY_ENCODE_SET = [SIMPLE_ENCODE_SET] | {' ', '"', '#', '<', '>'}
     }
 
-    assert_eq!(utf8_percent_encode("foo bar", QUERY_ENCODE_SET).collect::<String>(), "foo%20bar");
+    assert_eq!(
+        utf8_percent_encode("foo bar", QUERY_ENCODE_SET).collect::<String>(),
+        "foo%20bar"
+    );
 
     mod m {
         use url::percent_encoding::{utf8_percent_encode, SIMPLE_ENCODE_SET};
@@ -413,7 +489,10 @@ fn define_encode_set_scopes() {
         }
 
         pub fn test() {
-            assert_eq!(utf8_percent_encode("foo bar", QUERY_ENCODE_SET).collect::<String>(), "foo%20bar");
+            assert_eq!(
+                utf8_percent_encode("foo bar", QUERY_ENCODE_SET).collect::<String>(),
+                "foo%20bar"
+            );
         }
     }
 
@@ -423,8 +502,8 @@ fn define_encode_set_scopes() {
 #[test]
 /// https://github.com/servo/rust-url/issues/302
 fn test_origin_hash() {
-    use std::hash::{Hash,Hasher};
     use std::collections::hash_map::DefaultHasher;
+    use std::hash::{Hash, Hasher};
 
     fn hash<T: Hash>(value: &T) -> u64 {
         let mut hasher = DefaultHasher::new();
@@ -443,7 +522,9 @@ fn test_origin_hash() {
         Url::parse("ftp://example.net").unwrap().origin(),
         Url::parse("file://example.net").unwrap().origin(),
         Url::parse("http://user@example.net/").unwrap().origin(),
-        Url::parse("http://user:pass@example.net/").unwrap().origin(),
+        Url::parse("http://user:pass@example.net/")
+            .unwrap()
+            .origin(),
     ];
 
     for origin_to_compare in &origins_to_compare {
@@ -465,7 +546,7 @@ fn test_origin_hash() {
 #[test]
 fn test_windows_unc_path() {
     if !cfg!(windows) {
-        return
+        return;
     }
 
     let url = Url::from_file_path(Path::new(r"\\host\share\path\file.txt")).unwrap();
@@ -495,7 +576,8 @@ fn test_syntax_violation_callback() {
     let violation = Cell::new(None);
     let url = Url::options()
         .syntax_violation_callback(Some(&|v| violation.set(Some(v))))
-        .parse("http:////mozilla.org:42").unwrap();
+        .parse("http:////mozilla.org:42")
+        .unwrap();
     assert_eq!(url.port(), Some(42));
 
     let v = violation.take().unwrap();
@@ -511,13 +593,15 @@ fn test_syntax_violation_callback_lifetimes() {
 
     let url = Url::options()
         .syntax_violation_callback(Some(&vfn))
-        .parse("http:////mozilla.org:42").unwrap();
+        .parse("http:////mozilla.org:42")
+        .unwrap();
     assert_eq!(url.port(), Some(42));
     assert_eq!(violation.take(), Some(ExpectedDoubleSlash));
 
     let url = Url::options()
         .syntax_violation_callback(Some(&vfn))
-        .parse("http://mozilla.org\\path").unwrap();
+        .parse("http://mozilla.org\\path")
+        .unwrap();
     assert_eq!(url.path(), "/path");
     assert_eq!(violation.take(), Some(Backslash));
 }
@@ -528,13 +612,11 @@ fn test_options_reuse() {
     let violations = RefCell::new(Vec::new());
     let vfn = |v| violations.borrow_mut().push(v);
 
-    let options = Url::options()
-        .syntax_violation_callback(Some(&vfn));
+    let options = Url::options().syntax_violation_callback(Some(&vfn));
     let url = options.parse("http:////mozilla.org").unwrap();
 
     let options = options.base_url(Some(&url));
     let url = options.parse("/sub\\path").unwrap();
     assert_eq!(url.as_str(), "http://mozilla.org/sub/path");
-    assert_eq!(*violations.borrow(),
-               vec!(ExpectedDoubleSlash, Backslash));
+    assert_eq!(*violations.borrow(), vec!(ExpectedDoubleSlash, Backslash));
 }

+ 101 - 48
url_serde/src/lib.rs

@@ -67,25 +67,30 @@ ipc::channel::<Serde<Url>>()
 #![deny(unsafe_code)]
 
 extern crate serde;
-#[cfg(test)] #[macro_use] extern crate serde_derive;
-#[cfg(test)] extern crate serde_json;
+#[cfg(test)]
+#[macro_use]
+extern crate serde_derive;
+#[cfg(test)]
+extern crate serde_json;
 extern crate url;
 
-use serde::{Deserialize, Serialize, Serializer, Deserializer};
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
 use std::cmp::PartialEq;
 use std::error::Error;
 use std::fmt;
 use std::io::Write;
 use std::ops::{Deref, DerefMut};
 use std::str;
-use url::{Url, Host};
+use url::{Host, Url};
 
 /// Serialises `value` with a given serializer.
 ///
 /// This is useful to serialize `rust-url` types used in structure fields or
 /// tuple members with `#[serde(serialize_with = "url_serde::serialize")]`.
 pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
-    where S: Serializer, for<'a> Ser<'a, T>: Serialize
+where
+    S: Serializer,
+    for<'a> Ser<'a, T>: Serialize,
 {
     Ser::new(value).serialize(serializer)
 }
@@ -98,7 +103,10 @@ pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
 #[derive(Debug)]
 pub struct Ser<'a, T: 'a>(&'a T);
 
-impl<'a, T> Ser<'a, T> where Ser<'a, T>: Serialize {
+impl<'a, T> Ser<'a, T>
+where
+    Ser<'a, T>: Serialize,
+{
     /// Returns a new `Ser` wrapper.
     #[inline(always)]
     pub fn new(value: &'a T) -> Self {
@@ -108,14 +116,20 @@ impl<'a, T> Ser<'a, T> where Ser<'a, T>: Serialize {
 
 /// Serializes this URL into a `serde` stream.
 impl<'a> Serialize for Ser<'a, Url> {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
         serializer.serialize_str(self.0.as_str())
     }
 }
 
 /// Serializes this Option<URL> into a `serde` stream.
 impl<'a> Serialize for Ser<'a, Option<Url>> {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
         if let Some(url) = self.0.as_ref() {
             serializer.serialize_some(url.as_str())
         } else {
@@ -124,8 +138,14 @@ impl<'a> Serialize for Ser<'a, Option<Url>> {
     }
 }
 
-impl<'a, String> Serialize for Ser<'a, Host<String>> where String: AsRef<str> {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
+impl<'a, String> Serialize for Ser<'a, Host<String>>
+where
+    String: AsRef<str>,
+{
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
         match *self.0 {
             Host::Domain(ref s) => serializer.serialize_str(s.as_ref()),
             Host::Ipv4(_) | Host::Ipv6(_) => {
@@ -166,7 +186,9 @@ fn display_into_buffer<'a, T: fmt::Display>(value: &T, buffer: &'a mut [u8]) ->
 /// This is useful to deserialize Url types used in structure fields or
 /// tuple members with `#[serde(deserialize_with = "url_serde::deserialize")]`.
 pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
-    where D: Deserializer<'de>, De<T>: Deserialize<'de>
+where
+    D: Deserializer<'de>,
+    De<T>: Deserialize<'de>,
 {
     De::deserialize(deserializer).map(De::into_inner)
 }
@@ -180,7 +202,10 @@ pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
 #[derive(Debug)]
 pub struct De<T>(T);
 
-impl<'de, T> De<T> where De<T>: serde::Deserialize<'de> {
+impl<'de, T> De<T>
+where
+    De<T>: serde::Deserialize<'de>,
+{
     /// Consumes this wrapper, returning the deserialized value.
     #[inline(always)]
     pub fn into_inner(self) -> T {
@@ -190,35 +215,43 @@ impl<'de, T> De<T> where De<T>: serde::Deserialize<'de> {
 
 /// Deserializes this URL from a `serde` stream.
 impl<'de> Deserialize<'de> for De<Url> {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
         let string_representation: String = Deserialize::deserialize(deserializer)?;
-        Url::parse(&string_representation).map(De).map_err(|err| {
-            serde::de::Error::custom(err.description())
-        })
+        Url::parse(&string_representation)
+            .map(De)
+            .map_err(|err| serde::de::Error::custom(err.description()))
     }
 }
 
 /// Deserializes this Option<URL> from a `serde` stream.
 impl<'de> Deserialize<'de> for De<Option<Url>> {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
         let option_representation: Option<String> = Deserialize::deserialize(deserializer)?;
         if let Some(s) = option_representation {
             return Url::parse(&s)
                 .map(Some)
                 .map(De)
-                .map_err(|err| {serde::de::Error::custom(err.description())});
+                .map_err(|err| serde::de::Error::custom(err.description()));
         }
         Ok(De(None))
-
     }
 }
 
 impl<'de> Deserialize<'de> for De<Host> {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
         let string_representation: String = Deserialize::deserialize(deserializer)?;
-        Host::parse(&string_representation).map(De).map_err(|err| {
-            serde::de::Error::custom(err.description())
-        })
+        Host::parse(&string_representation)
+            .map(De)
+            .map_err(|err| serde::de::Error::custom(err.description()))
     }
 }
 
@@ -231,7 +264,9 @@ pub struct Serde<T>(pub T);
 pub type SerdeUrl = Serde<Url>;
 
 impl<'de, T> Serde<T>
-where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
+where
+    De<T>: Deserialize<'de>,
+    for<'a> Ser<'a, T>: Serialize,
 {
     /// Consumes this wrapper, returning the inner value.
     #[inline(always)]
@@ -241,7 +276,10 @@ where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
 }
 
 impl<'de, T> fmt::Debug for Serde<T>
-where T: fmt::Debug, De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
+where
+    T: fmt::Debug,
+    De<T>: Deserialize<'de>,
+    for<'a> Ser<'a, T>: Serialize,
 {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
         self.0.fmt(formatter)
@@ -249,7 +287,9 @@ where T: fmt::Debug, De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
 }
 
 impl<'de, T> Deref for Serde<T>
-where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
+where
+    De<T>: Deserialize<'de>,
+    for<'a> Ser<'a, T>: Serialize,
 {
     type Target = T;
 
@@ -259,7 +299,9 @@ where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
 }
 
 impl<'de, T> DerefMut for Serde<T>
-where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
+where
+    De<T>: Deserialize<'de>,
+    for<'a> Ser<'a, T>: Serialize,
 {
     fn deref_mut(&mut self) -> &mut T {
         &mut self.0
@@ -267,7 +309,9 @@ where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
 }
 
 impl<'de, T: PartialEq> PartialEq<T> for Serde<T>
-where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
+where
+    De<T>: Deserialize<'de>,
+    for<'a> Ser<'a, T>: Serialize,
 {
     fn eq(&self, other: &T) -> bool {
         self.0 == *other
@@ -275,20 +319,26 @@ where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
 }
 
 impl<'de, T> Deserialize<'de> for Serde<T>
-where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
+where
+    De<T>: Deserialize<'de>,
+    for<'a> Ser<'a, T>: Serialize,
 {
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-        where D: Deserializer<'de>
+    where
+        D: Deserializer<'de>,
     {
         De::deserialize(deserializer).map(De::into_inner).map(Serde)
     }
 }
 
 impl<'de, T> Serialize for Serde<T>
-where De<T>: Deserialize<'de>, for<'a> Ser<'a, T>: Serialize
+where
+    De<T>: Deserialize<'de>,
+    for<'a> Ser<'a, T>: Serialize,
 {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-        where S: Serializer
+    where
+        S: Serializer,
     {
         Ser(&self.0).serialize(serializer)
     }
@@ -307,18 +357,17 @@ fn test_derive_deserialize_with_for_url() {
     #[derive(Deserialize, Debug, Eq, PartialEq)]
     struct Test {
         #[serde(deserialize_with = "deserialize", rename = "_url_")]
-        url: Url
+        url: Url,
     }
 
     let url_str = "http://www.test.com/foo/bar?$param=bazz";
 
     let expected = Test {
-        url: Url::parse(url_str).unwrap()
+        url: Url::parse(url_str).unwrap(),
     };
     let json_string = format!(r#"{{"_url_": "{}"}}"#, url_str);
     let got: Test = serde_json::from_str(&json_string).unwrap();
     assert_eq!(expected, got);
-
 }
 
 #[test]
@@ -326,21 +375,19 @@ fn test_derive_deserialize_with_for_option_url() {
     #[derive(Deserialize, Debug, Eq, PartialEq)]
     struct Test {
         #[serde(deserialize_with = "deserialize", rename = "_url_")]
-        url: Option<Url>
+        url: Option<Url>,
     }
 
     let url_str = "http://www.test.com/foo/bar?$param=bazz";
 
     let expected = Test {
-        url: Some(Url::parse(url_str).unwrap())
+        url: Some(Url::parse(url_str).unwrap()),
     };
     let json_string = format!(r#"{{"_url_": "{}"}}"#, url_str);
     let got: Test = serde_json::from_str(&json_string).unwrap();
     assert_eq!(expected, got);
 
-    let expected = Test {
-        url: None
-    };
+    let expected = Test { url: None };
     let json_string = r#"{"_url_": null}"#;
     let got: Test = serde_json::from_str(&json_string).unwrap();
     assert_eq!(expected, got);
@@ -351,13 +398,15 @@ fn test_derive_serialize_with_for_url() {
     #[derive(Serialize, Debug, Eq, PartialEq)]
     struct Test {
         #[serde(serialize_with = "serialize", rename = "_url_")]
-        url: Url
+        url: Url,
     }
 
     let url_str = "http://www.test.com/foo/bar?$param=bazz";
 
     let expected = format!(r#"{{"_url_":"{}"}}"#, url_str);
-    let input = Test {url: Url::parse(url_str).unwrap()};
+    let input = Test {
+        url: Url::parse(url_str).unwrap(),
+    };
     let got = serde_json::to_string(&input).unwrap();
     assert_eq!(expected, got);
 }
@@ -367,18 +416,20 @@ fn test_derive_serialize_with_for_option_url() {
     #[derive(Serialize, Debug, Eq, PartialEq)]
     struct Test {
         #[serde(serialize_with = "serialize", rename = "_url_")]
-        url: Option<Url>
+        url: Option<Url>,
     }
 
     let url_str = "http://www.test.com/foo/bar?$param=bazz";
 
     let expected = format!(r#"{{"_url_":"{}"}}"#, url_str);
-    let input = Test {url: Some(Url::parse(url_str).unwrap())};
+    let input = Test {
+        url: Some(Url::parse(url_str).unwrap()),
+    };
     let got = serde_json::to_string(&input).unwrap();
     assert_eq!(expected, got);
 
     let expected = format!(r#"{{"_url_":null}}"#);
-    let input = Test {url: None};
+    let input = Test { url: None };
     let got = serde_json::to_string(&input).unwrap();
     assert_eq!(expected, got);
 }
@@ -388,7 +439,7 @@ fn test_derive_with_for_url() {
     #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
     struct Test {
         #[serde(with = "self", rename = "_url_")]
-        url: Url
+        url: Url,
     }
 
     let url_str = "http://www.test.com/foo/bar?$param=bazz";
@@ -396,13 +447,15 @@ fn test_derive_with_for_url() {
 
     // test deserialization
     let expected = Test {
-        url: Url::parse(url_str).unwrap()
+        url: Url::parse(url_str).unwrap(),
     };
     let got: Test = serde_json::from_str(&json_string).unwrap();
     assert_eq!(expected, got);
 
     // test serialization
-    let input = Test {url: Url::parse(url_str).unwrap()};
+    let input = Test {
+        url: Url::parse(url_str).unwrap(),
+    };
     let got = serde_json::to_string(&input).unwrap();
     assert_eq!(json_string, got);
 }