Преглед изворни кода

Upgrade to Rust aa67254 2014-05-08

Simon Sapin пре 12 година
родитељ
комит
c6eed978a3
5 измењених фајлова са 164 додато и 156 уклоњено
  1. 15 15
      form_urlencoded.rs
  2. 58 56
      parser.rs
  3. 14 16
      punycode.rs
  4. 33 30
      tests.rs
  5. 44 39
      url.rs

+ 15 - 15
form_urlencoded.rs

@@ -21,19 +21,19 @@ use encoding::label::encoding_from_whatwg_label;
 use super::{percent_encode_byte, percent_decode};
 
 
-pub fn parse_str(input: &str) -> ~[(~str, ~str)] {
+pub fn parse_str(input: &str) -> Vec<(StrBuf, StrBuf)> {
     parse_bytes(input.as_bytes(), None, false, false).unwrap()
 }
 
 
 pub fn parse_bytes(input: &[u8], encoding_override: Option<EncodingRef>,
-                   mut use_charset: bool, mut isindex: bool) -> Option<~[(~str, ~str)]> {
+                   mut use_charset: bool, mut isindex: bool) -> Option<Vec<(StrBuf, StrBuf)>> {
     let mut encoding_override = encoding_override.unwrap_or(UTF_8 as EncodingRef);
-    let mut pairs = ~[];
+    let mut pairs = Vec::new();
     for piece in input.split(|&b| b == '&' as u8) {
         if piece.is_empty() {
             if isindex {
-                pairs.push((~[], ~[]))
+                pairs.push((Vec::new(), Vec::new()))
             }
         } else {
             let (name, value) = match piece.position_elem(&('=' as u8)) {
@@ -44,7 +44,7 @@ pub fn parse_bytes(input: &[u8], encoding_override: Option<EncodingRef>,
             let value = replace_plus(value);
             if use_charset && name.as_slice() == "_charset_".as_bytes() {
                 // Non-UTF8 here is ok, encoding_from_whatwg_label only matches in the ASCII range.
-                match encoding_from_whatwg_label(unsafe { str::raw::from_utf8(value) }) {
+                match encoding_from_whatwg_label(unsafe { str::raw::from_utf8(value.as_slice()) }) {
                     Some(encoding) => encoding_override = encoding,
                     None => (),
                 }
@@ -59,14 +59,14 @@ pub fn parse_bytes(input: &[u8], encoding_override: Option<EncodingRef>,
     }
 
     #[inline]
-    fn replace_plus(input: &[u8]) -> ~[u8] {
+    fn replace_plus(input: &[u8]) -> Vec<u8> {
         input.iter().map(|&b| if b == '+' as u8 { ' ' as u8 } else { b }).collect()
     }
 
     #[inline]
-    fn decode(input: ~[u8], encoding_override: EncodingRef) -> ~str {
+    fn decode(input: Vec<u8>, encoding_override: EncodingRef) -> StrBuf {
         let bytes = percent_decode(input.as_slice());
-        encoding_override.decode(bytes, encoding::DecodeReplace).unwrap()
+        encoding_override.decode(bytes.as_slice(), encoding::DecodeReplace).unwrap()
     }
 
     Some(pairs.move_iter().map(
@@ -75,9 +75,9 @@ pub fn parse_bytes(input: &[u8], encoding_override: Option<EncodingRef>,
 }
 
 
-pub fn serialize(pairs: ~[(~str, ~str)], encoding_override: Option<EncodingRef>) -> ~str {
+pub fn serialize(pairs: Vec<(StrBuf, StrBuf)>, encoding_override: Option<EncodingRef>) -> StrBuf {
     #[inline]
-    fn byte_serialize(input: &str, output: &mut ~str,
+    fn byte_serialize(input: &str, output: &mut StrBuf,
                      encoding_override: Option<EncodingRef>) {
         let keep_alive;
         let input = match encoding_override {
@@ -88,17 +88,17 @@ pub fn serialize(pairs: ~[(~str, ~str)], encoding_override: Option<EncodingRef>)
             }
         };
 
-        for byte in input.iter() {
-            match *byte {
+        for &byte in input.iter() {
+            match byte {
                 0x20 => output.push_str("+"),
                 0x2A | 0x2D | 0x2E | 0x30 .. 0x39 | 0x41 .. 0x5A | 0x5F | 0x61 .. 0x7A
-                => unsafe { str::raw::push_byte(output, *byte) },
-                _ => percent_encode_byte(*byte, output),
+                => unsafe { output.push_byte(byte) },
+                _ => percent_encode_byte(byte, output),
             }
         }
     }
 
-    let mut output = ~"";
+    let mut output = StrBuf::new();
     for &(ref name, ref value) in pairs.iter() {
         if output.len() > 0 {
             output.push_str("&");

+ 58 - 56
parser.rs

@@ -7,7 +7,6 @@
 // except according to those terms.
 
 
-use std::str;
 use std::ascii::StrAsciiExt;
 
 use encoding;
@@ -41,7 +40,7 @@ fn parse_error(_message: &str) {
 
 
 pub fn parse_url(input: &str, base_url: Option<&Url>) -> ParseResult<Url> {
-    let input = input.trim_chars(& &[' ', '\t', '\n', '\r', '\x0C']);
+    let input = input.trim_chars(&[' ', '\t', '\n', '\r', '\x0C']);
     let (scheme_result, remaining) = parse_scheme(input);
     match scheme_result {
         Some(scheme) => {
@@ -53,13 +52,14 @@ pub fn parse_url(input: &str, base_url: Option<&Url>) -> ParseResult<Url> {
                         parse_relative_url(scheme, remaining, base)
                     },
                     _ => parse_relative_url(scheme, remaining, &Url {
-                        scheme: ~"", query: None, fragment: None,
+                        scheme: StrBuf::new(), query: None, fragment: None,
                         scheme_data: RelativeSchemeData(SchemeRelativeUrl {
-                            userinfo: None, host: Domain(~[]), port: ~"", path: ~[]
+                            userinfo: None, host: Domain(Vec::new()),
+                            port: StrBuf::new(), path: Vec::new()
                         })
                     }),
                 }
-            } else if is_relative_scheme(scheme) {
+            } else if is_relative_scheme(scheme.as_slice()) {
                 match base_url {
                     Some(base) if scheme == base.scheme => {
                         // Relative or authority state
@@ -89,7 +89,7 @@ pub fn parse_url(input: &str, base_url: Option<&Url>) -> ParseResult<Url> {
 }
 
 
-fn parse_scheme<'a>(input: &'a str) -> (Option<~str>, &'a str) {
+fn parse_scheme<'a>(input: &'a str) -> (Option<StrBuf>, &'a str) {
     if input.is_empty() || !is_ascii_alpha(input[0]) {
         return (None, input)
     }
@@ -98,7 +98,7 @@ fn parse_scheme<'a>(input: &'a str) -> (Option<~str>, &'a str) {
         match input[i] as char {
             'a'..'z' | 'A'..'Z' | '0'..'9' | '+' | '-' | '.' => (),
             ':' => return (
-                Some(input.slice_to(i).to_ascii_lower()),
+                Some(input.slice_to(i).to_ascii_lower().into_strbuf()),
                 input.slice_from(i + 1),
             ),
             _ => return (None, input),
@@ -109,13 +109,13 @@ fn parse_scheme<'a>(input: &'a str) -> (Option<~str>, &'a str) {
 }
 
 
-fn parse_absolute_url<'a>(scheme: ~str, input: &'a str) -> ParseResult<Url> {
+fn parse_absolute_url<'a>(scheme: StrBuf, input: &'a str) -> ParseResult<Url> {
     // Authority first slash state
     let remaining = skip_slashes(input);
     // Authority state
     let (userinfo, remaining) = parse_userinfo(remaining);
     // Host state
-    let (host, port, remaining) = match parse_hostname(remaining, scheme) {
+    let (host, port, remaining) = match parse_hostname(remaining, scheme.as_slice()) {
         Err(message) => return Err(message),
         Ok(result) => result,
     };
@@ -129,7 +129,7 @@ fn parse_absolute_url<'a>(scheme: ~str, input: &'a str) -> ParseResult<Url> {
 }
 
 
-fn parse_relative_url<'a>(scheme: ~str, input: &'a str, base: &Url) -> ParseResult<Url> {
+fn parse_relative_url<'a>(scheme: StrBuf, input: &'a str, base: &Url) -> ParseResult<Url> {
     match base.scheme_data {
         OtherSchemeData(_) => Err("Relative URL with a non-relative-scheme base"),
         RelativeSchemeData(ref base_scheme_data) => if input.is_empty() {
@@ -150,7 +150,7 @@ fn parse_relative_url<'a>(scheme: ~str, input: &'a str, base: &Url) -> ParseResu
                                    || is_match!(remaining[2] as char, '/' | '\\' | '?' | '#'))
                             {
                                 // Windows drive letter quirk
-                                (Domain(~[]), remaining)
+                                (Domain(Vec::new()), remaining)
                             } else {
                                 // File host state
                                 match parse_file_host(remaining) {
@@ -161,7 +161,7 @@ fn parse_relative_url<'a>(scheme: ~str, input: &'a str, base: &Url) -> ParseResu
                             let (path, remaining) = parse_path_start(
                                 remaining, /* full_url= */ true, in_file_scheme);
                             let scheme_data = RelativeSchemeData(SchemeRelativeUrl {
-                                userinfo: None, host: host, port: ~"", path: path });
+                                userinfo: None, host: host, port: StrBuf::new(), path: path });
                             let (query, fragment) = parse_query_and_fragment(remaining);
                             Ok(Url { scheme: scheme, scheme_data: scheme_data,
                                      query: query, fragment: fragment })
@@ -171,10 +171,11 @@ fn parse_relative_url<'a>(scheme: ~str, input: &'a str, base: &Url) -> ParseResu
                     } else {
                         // Relative path state
                         let (path, remaining) = parse_path(
-                            ~[], input.slice_from(1), /* full_url= */ true, in_file_scheme);
+                            Vec::new(), input.slice_from(1), /* full_url= */ true, in_file_scheme);
                         let scheme_data = RelativeSchemeData(if in_file_scheme {
                             SchemeRelativeUrl {
-                                userinfo: None, host: Domain(~[]), port: ~"", path: path
+                                userinfo: None, host: Domain(Vec::new()),
+                                port: StrBuf::new(), path: path
                             }
                         } else {
                             SchemeRelativeUrl {
@@ -209,16 +210,17 @@ fn parse_relative_url<'a>(scheme: ~str, input: &'a str, base: &Url) -> ParseResu
                     {
                         // Windows drive letter quirk
                         let (path, remaining) = parse_path(
-                            ~[], input, /* full_url= */ true, in_file_scheme);
+                            Vec::new(), input, /* full_url= */ true, in_file_scheme);
                          (RelativeSchemeData(SchemeRelativeUrl {
                             userinfo: None,
-                            host: Domain(~[]),
-                            port: ~"",
+                            host: Domain(Vec::new()),
+                            port: StrBuf::new(),
                             path: path
                         }), remaining)
                     } else {
                         let base_path = base_scheme_data.path.as_slice();
-                        let initial_path = base_path.slice_to(base_path.len() - 1).to_owned();
+                        let initial_path = Vec::from_slice(
+                            base_path.slice_to(base_path.len() - 1));
                         // Relative path state
                         let (path, remaining) = parse_path(
                             initial_path, input, /* full_url= */ true, in_file_scheme);
@@ -278,7 +280,7 @@ fn parse_userinfo<'a>(input: &'a str) -> (Option<UserInfo>, &'a str) {
 
 
 fn parse_userinfo_inner<'a>(input: &'a str) -> UserInfo {
-    let mut username = ~"";
+    let mut username = StrBuf::new();
     let mut i = 0;
     loop {
         if i >= input.len() {
@@ -308,7 +310,7 @@ fn parse_userinfo_inner<'a>(input: &'a str) -> UserInfo {
             }
         }
     }
-    let mut password = ~"";
+    let mut password = StrBuf::new();
     while i < input.len() {
         match input[i] as char {
             '\t' | '\n' | '\r' => {
@@ -334,13 +336,13 @@ fn parse_userinfo_inner<'a>(input: &'a str) -> UserInfo {
 }
 
 
-fn parse_hostname<'a>(input: &'a str, scheme: &str) -> ParseResult<(Host, ~str, &'a str)> {
+fn parse_hostname<'a>(input: &'a str, scheme: &str) -> ParseResult<(Host, StrBuf, &'a str)> {
     let mut i = 0;
     let mut inside_square_brackets = false;
-    let mut host_input = ~"";
+    let mut host_input = StrBuf::new();
     while i < input.len() {
         match input[i] as char {
-            ':' if !inside_square_brackets => return match Host::parse(host_input) {
+            ':' if !inside_square_brackets => return match Host::parse(host_input.as_slice()) {
                 Err(message) => Err(message),
                 Ok(host) => {
                     match parse_port(input.slice_from(i + 1), scheme) {
@@ -357,30 +359,30 @@ fn parse_hostname<'a>(input: &'a str, scheme: &str) -> ParseResult<(Host, ~str,
                     ']' => inside_square_brackets = false,
                     _ => (),
                 }
-                unsafe { str::raw::push_byte(&mut host_input, input[i]) }
+                unsafe { host_input.push_byte(input[i]) }
             }
         }
         i += 1;
     }
-    match Host::parse(host_input) {
+    match Host::parse(host_input.as_slice()) {
         Err(message) => Err(message),
-        Ok(host) => Ok((host, ~"", input.slice_from(i))),
+        Ok(host) => Ok((host, StrBuf::new(), input.slice_from(i))),
     }
 }
 
 
-fn parse_port<'a>(input: &'a str, scheme: &str) -> ParseResult<(~str, &'a str)> {
-    let mut port = ~"";
+fn parse_port<'a>(input: &'a str, scheme: &str) -> ParseResult<(StrBuf, &'a str)> {
+    let mut port = StrBuf::new();
     let mut has_initial_zero = false;
     let mut i = 0;
     while i < input.len() {
         match input[i] as char {
-            '1' .. '9' => unsafe { str::raw::push_byte(&mut port, input[i]) },
+            '1' .. '9' => unsafe { port.push_byte(input[i]) },
             '0' => {
                 if port.is_empty() {
                     has_initial_zero = true
                 } else {
-                    unsafe { str::raw::push_byte(&mut port, input[i]) }
+                    unsafe { port.push_byte(input[i]) }
                 }
             },
             '/' | '\\' | '?' | '#' => break,
@@ -395,7 +397,7 @@ fn parse_port<'a>(input: &'a str, scheme: &str) -> ParseResult<(~str, &'a str)>
     match (scheme, port.as_slice()) {
         ("ftp", "21") | ("gopher", "70") | ("http", "80") |
         ("https", "443") | ("ws", "80") | ("wss", "443")
-        => port.clear(),
+        => port.truncate(0),
         _ => (),
     }
     return Ok((port, input.slice_from(i)))
@@ -404,19 +406,19 @@ fn parse_port<'a>(input: &'a str, scheme: &str) -> ParseResult<(~str, &'a str)>
 
 fn parse_file_host<'a>(input: &'a str) -> ParseResult<(Host, &'a str)> {
     let mut i = 0;
-    let mut host_input = ~"";
+    let mut host_input = StrBuf::new();
     while i < input.len() {
         match input[i] as char {
             '/' | '\\' | '?' | '#' => break,
             '\t' | '\n' | '\r' => parse_error("Invalid character"),
-            _ => unsafe { str::raw::push_byte(&mut host_input, input[i]) }
+            _ => unsafe { host_input.push_byte(input[i]) }
         }
         i += 1;
     }
     let host = if host_input.is_empty() {
-        Domain(~[])
+        Domain(Vec::new())
     } else {
-        match Host::parse(host_input) {
+        match Host::parse(host_input.as_slice()) {
             Err(message) => return Err(message),
             Ok(host) => host,
         }
@@ -426,7 +428,7 @@ fn parse_file_host<'a>(input: &'a str) -> ParseResult<(Host, &'a str)> {
 
 
 fn parse_path_start<'a>(input: &'a str, full_url: bool, in_file_scheme: bool)
-           -> (~[~str], &'a str) {
+           -> (Vec<StrBuf>, &'a str) {
     let mut i = 0;
     // Relative path start state
     if !input.is_empty() {
@@ -439,17 +441,17 @@ fn parse_path_start<'a>(input: &'a str, full_url: bool, in_file_scheme: bool)
             _ => ()
         }
     }
-    parse_path(~[], input.slice_from(i), full_url, in_file_scheme)
+    parse_path(Vec::new(), input.slice_from(i), full_url, in_file_scheme)
 }
 
 
-fn parse_path<'a>(base_path: ~[~str], input: &'a str, full_url: bool, in_file_scheme: bool)
-           -> (~[~str], &'a str) {
+fn parse_path<'a>(base_path: Vec<StrBuf>, input: &'a str, full_url: bool, in_file_scheme: bool)
+           -> (Vec<StrBuf>, &'a str) {
     // Relative path state
     let mut path = base_path;
     let mut i = 0;
     loop {
-        let mut path_part = ~"";
+        let mut path_part = StrBuf::new();
         let mut ends_with_slash = false;
         while i < input.len() {
             match input[i] as char {
@@ -484,28 +486,28 @@ fn parse_path<'a>(base_path: ~[~str], input: &'a str, full_url: bool, in_file_sc
                 }
             }
         }
-        let lower = path_part.to_ascii_lower();
+        let lower = path_part.as_slice().to_ascii_lower();
         match lower.as_slice() {
             ".." | ".%2e" | "%2e." | "%2e%2e" => {
                 path.pop();
                 if !ends_with_slash {
-                    path.push(~"");
+                    path.push(StrBuf::new());
                 }
             },
             "." | "%2e" => {
                 if !ends_with_slash {
-                    path.push(~"");
+                    path.push(StrBuf::new());
                 }
             },
             _ => {
                 if in_file_scheme
                    && path.is_empty()
                    && path_part.len() == 2
-                   && is_ascii_alpha(path_part[0])
-                   && path_part[1] == ('|' as u8) {
+                   && is_ascii_alpha(path_part.as_bytes()[0])
+                   && path_part.as_bytes()[1] == ('|' as u8) {
                     // Windows drive letter quirk
                     unsafe {
-                        str::raw::as_owned_vec(&mut path_part)[1] = ':' as u8
+                        *path_part.as_mut_vec().get_mut(1) = ':' as u8
                     }
                 }
                 path.push(path_part)
@@ -519,8 +521,8 @@ fn parse_path<'a>(base_path: ~[~str], input: &'a str, full_url: bool, in_file_sc
 }
 
 
-fn parse_scheme_data<'a>(input: &'a str) -> (~str, &'a str) {
-    let mut scheme_data = ~"";
+fn parse_scheme_data<'a>(input: &'a str) -> (StrBuf, &'a str) {
+    let mut scheme_data = StrBuf::new();
     let mut i = 0;
     while i < input.len() {
         match input[i] as char {
@@ -548,7 +550,7 @@ fn parse_scheme_data<'a>(input: &'a str) -> (~str, &'a str) {
 }
 
 
-fn parse_query_and_fragment(input: &str) -> (Option<~str>, Option<~str>) {
+fn parse_query_and_fragment(input: &str) -> (Option<StrBuf>, Option<StrBuf>) {
     if input.is_empty() {
         (None, None)
     } else {
@@ -568,8 +570,8 @@ fn parse_query_and_fragment(input: &str) -> (Option<~str>, Option<~str>) {
 
 
 fn parse_query<'a>(input: &'a str, encoding_override: EncodingRef, full_url: bool)
-               -> (~str, Option<&'a str>) {
-    let mut query = ~"";
+               -> (StrBuf, Option<&'a str>) {
+    let mut query = StrBuf::new();
     let mut i = 0;
     let mut remaining = None;
     while i < input.len() {
@@ -597,22 +599,22 @@ fn parse_query<'a>(input: &'a str, encoding_override: EncodingRef, full_url: boo
             }
         }
     }
-    let query_bytes = encoding_override.encode(query, encoding::EncodeReplace).unwrap();
-    let mut query_encoded = ~"";
+    let query_bytes = encoding_override.encode(query.as_slice(), encoding::EncodeReplace).unwrap();
+    let mut query_encoded = StrBuf::new();
     for &byte in query_bytes.iter() {
         match byte {
             0x00 .. 0x20 | 0x22 | 0x23 | 0x3C | 0x3E | 0x60 | 0x7E .. 0xFF
             => percent_encode_byte(byte, &mut query_encoded),
             _
-            => unsafe { str::raw::push_byte(&mut query_encoded, byte) }
+            => unsafe { query_encoded.push_byte(byte) }
         }
     }
     (query_encoded, remaining)
 }
 
 
-fn parse_fragment<'a>(input: &'a str) -> ~str {
-    let mut fragment = ~"";
+fn parse_fragment<'a>(input: &'a str) -> StrBuf {
+    let mut fragment = StrBuf::new();
     let mut i = 0;
     while i < input.len() {
         match input[i] as char {

+ 14 - 16
punycode.rs

@@ -38,11 +38,11 @@ fn adapt(mut delta: u32, num_points: u32, first_time: bool) -> u32 {
 /// Return None on malformed input or overflow.
 /// Overflow can only happen on inputs that take more than
 /// 63 encoded bytes, the DNS limit on domain name labels.
-pub fn decode(input: &str) -> Option<~[char]> {
+pub fn decode(input: &str) -> Option<Vec<char>> {
     // Handle "basic" (ASCII) code points.
     // They are encoded as-is befor the last delimiter, if any.
     let (mut output, input) = match input.rfind(DELIMITER) {
-        None => (~[], input),
+        None => (Vec::new(), input),
         Some(position) => (
             input.slice_to(position).chars().collect(),
             if position > 0 { input.slice_from(position + 1) } else { input }
@@ -112,12 +112,12 @@ pub fn decode(input: &str) -> Option<~[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<~str> {
+pub fn encode(input: &[char]) -> Option<StrBuf> {
     // 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 mut output = unsafe { str::raw::from_utf8_owned(output_bytes) };
+    let mut output = unsafe { str::raw::from_utf8_owned(output_bytes) }.into_strbuf();
     let basic_length = output.len() as u32;
     if basic_length > 0 {
         output.push_str("-")
@@ -176,13 +176,13 @@ pub fn encode(input: &[char]) -> Option<~str> {
 
 
 #[inline]
-fn value_to_digit(value: u32, output: &mut ~str) {
+fn value_to_digit(value: u32, output: &mut StrBuf) {
     let code_point = match value {
         0 .. 25 => value + 0x61,  // a..z
         26 .. 35 => value - 26 + 0x30,  // 0..9
         _ => fail!()
     };
-    unsafe { str::raw::push_byte(output, code_point as u8) }
+    unsafe { output.push_byte(code_point as u8) }
 }
 
 
@@ -196,16 +196,14 @@ mod tests {
         match decode(encoded) {
             None => fail!("Decoding {:?} failed.", encoded),
             Some(result) => {
-                let result = from_chars(result);
+                let result = from_chars(result.as_slice());
                 assert!(result.as_slice() == decoded,
                         format!("Incorrect decoding of {:?}:\n   {:?}\n!= {:?}\n{}",
                                 encoded, result.as_slice(), decoded, description))
             }
-        }        
-
-        let dec_chars: ~[char] = decoded.chars().collect();
+        }
 
-        match encode(dec_chars) {
+        match encode(decoded.chars().collect::<~[char]>()) {
             None => fail!("Encoding {:?} failed.", decoded),
             Some(result) => {
                 assert!(result.as_slice() == encoded,
@@ -215,8 +213,8 @@ mod tests {
         }
     }
 
-    fn get_string<'a>(map: &'a ~Object, key: &~str) -> &'a str {
-        match map.find(key) {
+    fn get_string<'a>(map: &'a Box<Object>, key: &str) -> &'a str {
+        match map.find(&key.to_owned()) {
             Some(&String(ref s)) => s.as_slice(),
             None => "",
             _ => fail!(),
@@ -230,9 +228,9 @@ mod tests {
             Ok(List(tests)) => for test in tests.iter() {
                 match test {
                     &Object(ref o) => one_test(
-                        get_string(o, &~"description"),
-                        get_string(o, &~"decoded"),
-                        get_string(o, &~"encoded")
+                        get_string(o, "description"),
+                        get_string(o, "decoded"),
+                        get_string(o, "encoded")
                     ),
                     _ => fail!(),
                 }

+ 33 - 30
tests.rs

@@ -27,11 +27,11 @@ fn test_url_parsing() {
             query: expected_query,
             fragment: expected_fragment
         } = test;
-        let base = match Url::parse(base, None) {
+        let base = match Url::parse(base.as_slice(), None) {
             Ok(base) => base,
             Err(message) => fail!("Error parsing base {:?}: {}", base, message)
         };
-        let url = Url::parse(input, Some(&base));
+        let url = Url::parse(input.as_slice(), Some(&base));
         if expected_scheme.is_none() {
             assert!(url.is_err(), "Expected a parse error for URL {:?}", input);
             continue
@@ -45,7 +45,7 @@ fn test_url_parsing() {
         match scheme_data {
             RelativeSchemeData(SchemeRelativeUrl { userinfo, host, port, path }) => {
                 let (username, password) = match userinfo {
-                    None => (~"", None),
+                    None => (StrBuf::new(), None),
                     Some(UserInfo { username, password }) => (username, password),
                 };
                 assert_eq!(username, expected_username);
@@ -53,55 +53,58 @@ fn test_url_parsing() {
                 let host = host.serialize();
                 assert_eq!(host, expected_host)
                 assert_eq!(port, expected_port);
-                assert_eq!(Some("/" + path.connect("/")),
+                assert_eq!(Some("/".to_strbuf().append(path.connect("/"))),
                            expected_path);
             },
             OtherSchemeData(scheme_data) => {
                 assert_eq!(Some(scheme_data), expected_path);
-                assert_eq!(~"", expected_username);
+                assert_eq!(StrBuf::new(), expected_username);
                 assert_eq!(None, expected_password);
-                assert_eq!(~"", expected_host);
-                assert_eq!(~"", expected_port);
+                assert_eq!(StrBuf::new(), expected_host);
+                assert_eq!(StrBuf::new(), expected_port);
             },
         }
-        assert_eq!(query.map(|p| "?" + p), expected_query);
-        assert_eq!(fragment.map(|p| "#" + p), expected_fragment);
+        fn opt_prepend(prefix: &str, opt_s: Option<StrBuf>) -> Option<StrBuf> {
+            opt_s.map(|s| prefix.to_strbuf().append(s.as_slice()))
+        }
+        assert_eq!(opt_prepend("?", query), expected_query);
+        assert_eq!(opt_prepend("#", fragment), expected_fragment);
     }
 }
 
 struct Test {
-    input: ~str,
-    base: ~str,
-    scheme: Option<~str>,
-    username: ~str,
-    password: Option<~str>,
-    host: ~str,
-    port: ~str,
-    path: Option<~str>,
-    query: Option<~str>,
-    fragment: Option<~str>,
+    input: StrBuf,
+    base: StrBuf,
+    scheme: Option<StrBuf>,
+    username: StrBuf,
+    password: Option<StrBuf>,
+    host: StrBuf,
+    port: StrBuf,
+    path: Option<StrBuf>,
+    query: Option<StrBuf>,
+    fragment: Option<StrBuf>,
 }
 
-fn parse_test_data(input: &str) -> ~[Test] {
-    let mut tests: ~[Test] = ~[];
+fn parse_test_data(input: &str) -> Vec<Test> {
+    let mut tests: Vec<Test> = Vec::new();
     for line in input.lines() {
         if line == "" || line[0] == ('#' as u8) {
             continue
         }
-        let mut pieces: ~[&str] = line.split(' ').collect();
+        let mut pieces = line.split(' ').collect::<Vec<&str>>();
         let input = unescape(pieces.shift().unwrap());
         let mut test = Test {
             input: input,
-            base: if pieces.is_empty() || pieces[0] == "" {
-                tests[tests.len() - 1].base.to_owned()
+            base: if pieces.is_empty() || *pieces.get(0) == "" {
+                tests.last().unwrap().base.clone()
             } else {
                 unescape(pieces.shift().unwrap())
             },
             scheme: None,
-            username: ~"",
+            username: StrBuf::new(),
             password: None,
-            host: ~"",
-            port: ~"",
+            host: StrBuf::new(),
+            port: StrBuf::new(),
             path: None,
             query: None,
             fragment: None,
@@ -129,8 +132,8 @@ fn parse_test_data(input: &str) -> ~[Test] {
     tests
 }
 
-fn unescape(input: &str) -> ~str {
-    let mut output = ~"";
+fn unescape(input: &str) -> StrBuf {
+    let mut output = StrBuf::new();
     let mut chars = input.chars();
     loop {
         match chars.next() {
@@ -145,7 +148,7 @@ fn unescape(input: &str) -> ~str {
                         't' => '\t',
                         'f' => '\x0C',
                         'u' => {
-                            let mut hex = ~"";
+                            let mut hex = StrBuf::new();
                             hex.push_char(chars.next().unwrap());
                             hex.push_char(chars.next().unwrap());
                             hex.push_char(chars.next().unwrap());

+ 44 - 39
url.rs

@@ -15,9 +15,7 @@ extern crate encoding;
 #[cfg(test)]
 extern crate serialize;
 
-use std::str;
 use std::cmp;
-
 use std::num::ToStrRadix;
 
 use encoding::Encoding;
@@ -32,37 +30,37 @@ pub mod punycode;
 mod tests;
 
 
-#[deriving(Clone)]
+#[deriving(Clone, Show)]
 pub struct Url {
-    scheme: ~str,
+    scheme: StrBuf,
     scheme_data: SchemeData,
-    query: Option<~str>,  // See form_urlencoded::parse_str() to get name/value pairs.
-    fragment: Option<~str>,
+    query: Option<StrBuf>,  // See form_urlencoded::parse_str() to get name/value pairs.
+    fragment: Option<StrBuf>,
 }
 
-#[deriving(Clone)]
+#[deriving(Clone, Show)]
 pub enum SchemeData {
     RelativeSchemeData(SchemeRelativeUrl),
-    OtherSchemeData(~str),  // data: URLs, mailto: URLs, etc.
+    OtherSchemeData(StrBuf),  // data: URLs, mailto: URLs, etc.
 }
 
-#[deriving(Clone)]
+#[deriving(Clone, Show)]
 pub struct SchemeRelativeUrl {
     userinfo: Option<UserInfo>,
     host: Host,
-    port: ~str,
-    path: ~[~str],
+    port: StrBuf,
+    path: Vec<StrBuf>,
 }
 
-#[deriving(Clone)]
+#[deriving(Clone, Show)]
 pub struct UserInfo {
-    username: ~str,
-    password: Option<~str>,
+    username: StrBuf,
+    password: Option<StrBuf>,
 }
 
-#[deriving(Clone)]
+#[deriving(Clone, Show)]
 pub enum Host {
-    Domain(~[~str]),  // Can only be empty in the file scheme
+    Domain(Vec<StrBuf>),  // Can only be empty in the file scheme
     Ipv6(Ipv6Address)
 }
 
@@ -76,6 +74,12 @@ impl Clone for Ipv6Address {
     }
 }
 
+impl ::std::fmt::Show for Ipv6Address {
+    fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+        self.pieces.as_slice().fmt(formatter)
+    }
+}
+
 
 macro_rules! is_match(
     ($value:expr, $($pattern:pat)|+) => (
@@ -92,7 +96,7 @@ impl Url {
         parser::parse_url(input, base_url)
     }
 
-    pub fn serialize(&self) -> ~str {
+    pub fn serialize(&self) -> StrBuf {
         let mut result = self.serialize_no_fragment();
         match self.fragment {
             None => (),
@@ -104,8 +108,8 @@ impl Url {
         result
     }
 
-    pub fn serialize_no_fragment(&self) -> ~str {
-        let mut result = self.scheme.to_owned();
+    pub fn serialize_no_fragment(&self) -> StrBuf {
+        let mut result = self.scheme.clone();
         result.push_str(":");
         match self.scheme_data {
             RelativeSchemeData(SchemeRelativeUrl {
@@ -127,7 +131,7 @@ impl Url {
                         result.push_str("@");
                     }
                 }
-                result.push_str(host.serialize());
+                result.push_str(host.serialize().as_slice());
                 if port.len() > 0 {
                     result.push_str(":");
                     result.push_str(port.as_slice());
@@ -166,16 +170,17 @@ impl Host {
                 Err("Invalid Ipv6 address")
             }
         } else {
-            let mut percent_encoded = ~"";
+            let mut percent_encoded = StrBuf::new();
             utf8_percent_encode(input, SimpleEncodeSet, &mut percent_encoded);
             let bytes = percent_decode(percent_encoded.as_bytes());
-            let decoded = UTF_8.decode(bytes, encoding::DecodeReplace).unwrap();
-            let mut labels = ~[];
-            for label in decoded.split(&['.', '\u3002', '\uFF0E', '\uFF61']) {
+            let decoded = UTF_8.decode(bytes.as_slice(), encoding::DecodeReplace).unwrap();
+            let mut labels = Vec::new();
+            for label in decoded.as_slice().split(
+                    &['.', '\u3002', '\uFF0E', '\uFF61']) {
                 // TODO: Remove this check and use IDNA "domain to ASCII"
                 // TODO: switch to .map(domain_label_to_ascii).collect() then.
                 if label.is_ascii() {
-                    labels.push(label.to_owned())
+                    labels.push(label.to_strbuf())
                 } else {
                     return Err("Non-ASCII domains (IDNA) are not supported yet.")
                 }
@@ -184,12 +189,12 @@ impl Host {
         }
     }
 
-    pub fn serialize(&self) -> ~str {
+    pub fn serialize(&self) -> StrBuf {
         match *self {
-            Domain(ref labels) => labels.connect("."),
+            Domain(ref labels) => labels.connect(".").into_strbuf(),
             Ipv6(ref address) => {
-                let mut result = ~"[";
-                result.push_str(address.serialize());
+                let mut result = StrBuf::from_str("[");
+                result.push_str(address.serialize().as_slice());
                 result.push_str("]");
                 result
             }
@@ -315,8 +320,8 @@ impl Ipv6Address {
         Ok(Ipv6Address { pieces: pieces })
     }
 
-    pub fn serialize(&self) -> ~str {
-        let mut output = ~"";
+    pub fn serialize(&self) -> StrBuf {
+        let mut output = StrBuf::new();
         let (compress_start, compress_end) = longest_zero_sequence(&self.pieces);
         let mut i = 0;
         while i < 8 {
@@ -331,7 +336,7 @@ impl Ipv6Address {
                     break;
                 }
             }
-            output.push_str(self.pieces[i].to_str_radix(16));
+            output.push_str(self.pieces[i as uint].to_str_radix(16));
             if i < 7 {
                 output.push_str(":");
             }
@@ -358,7 +363,7 @@ fn longest_zero_sequence(pieces: &[u16, ..8]) -> (int, int) {
         };
     );
     for i in range(0, 8) {
-        if pieces[i] == 0 {
+        if pieces[i as uint] == 0 {
             if start < 0 {
                 start = i;
             }
@@ -402,7 +407,7 @@ enum EncodeSet {
 
 
 #[inline]
-fn utf8_percent_encode(input: &str, encode_set: EncodeSet, output: &mut ~str) {
+fn utf8_percent_encode(input: &str, encode_set: EncodeSet, output: &mut StrBuf) {
     use Default = self::DefaultEncodeSet;
     use UserInfo = self::UserInfoEncodeSet;
     use Password = self::PasswordEncodeSet;
@@ -421,16 +426,16 @@ fn utf8_percent_encode(input: &str, encode_set: EncodeSet, output: &mut ~str) {
         } {
             percent_encode_byte(byte, output)
         } else {
-            unsafe { str::raw::push_byte(output, byte) }
+            unsafe { output.push_byte(byte) }
         }
     }
 }
 
 
 #[inline]
-fn percent_encode_byte(byte: u8, output: &mut ~str) {
+fn percent_encode_byte(byte: u8, output: &mut StrBuf) {
     unsafe {
-        str::raw::push_bytes(output, [
+        output.push_bytes([
             '%' as u8, to_hex_upper(byte >> 4), to_hex_upper(byte & 0x0F)
         ])
     }
@@ -438,8 +443,8 @@ fn percent_encode_byte(byte: u8, output: &mut ~str) {
 
 
 #[inline]
-fn percent_decode(input: &[u8]) -> ~[u8] {
-    let mut output = ~[];
+fn percent_decode(input: &[u8]) -> Vec<u8> {
+    let mut output = Vec::new();
     let mut i = 0u;
     while i < input.len() {
         let c = input[i];