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

Make the port numer Option<u16> instead of String.

See https://www.w3.org/Bugs/Public/show_bug.cgi?id=26446

[breaking-change]
Simon Sapin пре 12 година
родитељ
комит
852d8a90ce
4 измењених фајлова са 56 додато и 55 уклоњено
  1. 27 25
      src/lib.rs
  2. 24 25
      src/parser.rs
  3. 4 4
      src/tests.rs
  4. 1 1
      src/urltestdata.txt

+ 27 - 25
src/lib.rs

@@ -59,7 +59,7 @@ let issue_list_url = Url::parse(
 
 assert!(issue_list_url.scheme == "https".to_string());
 assert!(issue_list_url.domain() == Some("github.com"));
-assert!(issue_list_url.port() == Some(""));
+assert!(issue_list_url.port() == None);
 assert!(issue_list_url.path() == Some(&["rust-lang".to_string(),
                                         "rust".to_string(),
                                         "issues".to_string()]));
@@ -230,9 +230,9 @@ pub struct RelativeSchemeData {
     /// The host of the URL, either a domain name or an IPv4 address
     pub host: Host,
 
-    /// The port number of the URL, in ASCII decimal,
-    /// or the empty string for no port number (in the file scheme) or the default port number.
-    pub port: String,
+    /// The port number of the URL.
+    /// `None` for file-like schemes, or to indicate the default port number.
+    pub port: Option<u16>,
 
     /// The path of the URL, as vector of pecent-encoded strings.
     ///
@@ -319,12 +319,12 @@ impl<'a> UrlParser<'a> {
     /// fn whatwg_scheme_type_mapper(scheme: &str) -> SchemeType {
     ///     match scheme {
     ///         "file" => FileLikeRelativeScheme,
-    ///         "ftp" => RelativeScheme("21"),
-    ///         "gopher" => RelativeScheme("70"),
-    ///         "http" => RelativeScheme("80"),
-    ///         "https" => RelativeScheme("443"),
-    ///         "ws" => RelativeScheme("80"),
-    ///         "wss" => RelativeScheme("443"),
+    ///         "ftp" => RelativeScheme(21),
+    ///         "gopher" => RelativeScheme(70),
+    ///         "http" => RelativeScheme(80),
+    ///         "https" => RelativeScheme(443),
+    ///         "ws" => RelativeScheme(80),
+    ///         "wss" => RelativeScheme(443),
     ///         _ => NonRelativeScheme,
     ///     }
     /// }
@@ -380,7 +380,7 @@ pub enum SchemeType {
     /// Relative URL references are supported, if a base URL was given.
     /// The string value indicates the default port number as a string of ASCII digits,
     /// or the empty string to indicate no default port number.
-    RelativeScheme(&'static str),
+    RelativeScheme(u16),
 
     /// Indicate a *relative* scheme similar to the *file* scheme.
     ///
@@ -395,12 +395,12 @@ pub enum SchemeType {
 pub fn whatwg_scheme_type_mapper(scheme: &str) -> SchemeType {
     match scheme {
         "file" => FileLikeRelativeScheme,
-        "ftp" => RelativeScheme("21"),
-        "gopher" => RelativeScheme("70"),
-        "http" => RelativeScheme("80"),
-        "https" => RelativeScheme("443"),
-        "ws" => RelativeScheme("80"),
-        "wss" => RelativeScheme("443"),
+        "ftp" => RelativeScheme(21),
+        "gopher" => RelativeScheme(70),
+        "http" => RelativeScheme(80),
+        "https" => RelativeScheme(443),
+        "ws" => RelativeScheme(80),
+        "wss" => RelativeScheme(443),
         _ => NonRelativeScheme,
     }
 }
@@ -455,7 +455,7 @@ impl Url {
             scheme_data: RelativeSchemeData(RelativeSchemeData {
                 username: "".to_string(),
                 password: None,
-                port: "".to_string(),
+                port: None,
                 host: Domain("".to_string()),
                 path: path,
             }),
@@ -617,15 +617,15 @@ impl Url {
         self.relative_scheme_data().map(|scheme_data| scheme_data.host.serialize())
     }
 
-    /// If the URL is in a *relative scheme*, return its port.
+    /// If the URL is in a *relative scheme* and has a port number, return it.
     #[inline]
-    pub fn port<'a>(&'a self) -> Option<&'a str> {
-        self.relative_scheme_data().map(|scheme_data| scheme_data.port.as_slice())
+    pub fn port<'a>(&'a self) -> Option<u16> {
+        self.relative_scheme_data().and_then(|scheme_data| scheme_data.port)
     }
 
     /// If the URL is in a *relative scheme*, return a mutable reference to its port.
     #[inline]
-    pub fn port_mut<'a>(&'a mut self) -> Option<&'a mut String> {
+    pub fn port_mut<'a>(&'a mut self) -> Option<&'a mut Option<u16>> {
         self.relative_scheme_data_mut().map(|scheme_data| &mut scheme_data.port)
     }
 
@@ -837,9 +837,11 @@ impl Show for RelativeSchemeData {
             try!(formatter.write(b"@"));
         }
         try!(self.host.fmt(formatter));
-        if !self.port.is_empty() {
-            try!(formatter.write(b":"));
-            try!(formatter.write(self.port.as_bytes()));
+        match self.port {
+            Some(port) => {
+                try!(write!(formatter, ":{}", port));
+            },
+            None => {}
         }
         PathFormatter { path: &self.path }.fmt(formatter)
     }

+ 24 - 25
src/parser.rs

@@ -127,7 +127,7 @@ pub fn parse_url(input: &str, parser: &UrlParser) -> ParseResult<Url> {
                 // FIXME: Should not have to use a made-up base URL.
                 _ => parse_relative_url(remaining, scheme, scheme_type, &RelativeSchemeData {
                     username: String::new(), password: None, host: Domain(String::new()),
-                    port: String::new(), path: Vec::new()
+                    port: None, path: Vec::new()
                 }, &None, parser)
             }
         },
@@ -224,7 +224,7 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
                         remaining, UrlParserContext, scheme_type, parser));
                     let scheme_data = RelativeSchemeData(RelativeSchemeData {
                         username: String::new(), password: None,
-                        host: host, port: String::new(), path: path
+                        host: host, port: None, path: path
                     });
                     let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
                     Ok(Url { scheme: scheme, scheme_data: scheme_data,
@@ -239,7 +239,7 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
                 let scheme_data = RelativeSchemeData(if scheme_type == FileLikeRelativeScheme {
                     RelativeSchemeData {
                         username: String::new(), password: None, host:
-                        Domain(String::new()), port: String::new(), path: path
+                        Domain(String::new()), port: None, path: path
                     }
                 } else {
                     RelativeSchemeData {
@@ -280,7 +280,7 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
                  (RelativeSchemeData(RelativeSchemeData {
                     username: String::new(), password: None,
                     host: Domain(String::new()),
-                    port: String::new(),
+                    port: None,
                     path: path
                 }), remaining)
             } else {
@@ -372,12 +372,12 @@ fn parse_password(input: &str, parser: &UrlParser) -> ParseResult<String> {
 
 
 pub fn parse_host<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParser)
-                          -> ParseResult<(Host, String, &'a str)> {
+                          -> ParseResult<(Host, Option<u16>, &'a str)> {
     let (host, remaining) = try!(parse_hostname(input, parser));
     let (port, remaining) = if remaining.starts_with(":") {
         try!(parse_port(remaining.slice_from(1), scheme_type, parser))
     } else {
-        (String::new(), remaining)
+        (None, remaining)
     };
     Ok((host, port, remaining))
 }
@@ -415,19 +415,18 @@ pub fn parse_hostname<'a>(input: &'a str, parser: &UrlParser)
 
 
 pub fn parse_port<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParser)
-                  -> ParseResult<(String, &'a str)> {
-    let mut port = String::new();
-    let mut has_initial_zero = false;
+                      -> ParseResult<(Option<u16>, &'a str)> {
+    let mut port = 0;
+    let mut has_any_digit = false;
     let mut end = input.len();
     for (i, c) in input.char_indices() {
         match c {
-            '1'..'9' => port.push_char(c),
-            '0' => {
-                if port.is_empty() {
-                    has_initial_zero = true
-                } else {
-                    port.push_char(c)
+            '0'..'9' => {
+                port = port * 10 + (c as u32 - '0' as u32);
+                if port > ::std::u16::MAX as u32 {
+                    return Err(InvalidPort)
                 }
+                has_any_digit = true;
             },
             '/' | '\\' | '?' | '#' => {
                 end = i;
@@ -437,17 +436,17 @@ pub fn parse_port<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParse
             _ => return Err(InvalidPort)
         }
     }
-    if port.is_empty() && has_initial_zero {
-        port.push_str("0")
-    }
-    match scheme_type {
-        RelativeScheme(default_port) => {
-            if port.as_slice() == default_port {
-                port.truncate(0)
+    let port = port as u16;
+    let port = if has_any_digit {
+        match scheme_type {
+            RelativeScheme(default_port) => {
+                if port == default_port { None } else { Some(port) }
             }
-        },
-        _ => {},  // Can only happen when UrlUtils is misused
-    }
+            _ => Some(port as u16),  // Can only happen when UrlUtils is misused
+        }
+    } else {
+        None
+    };
     return Ok((port, input.slice_from(end)))
 }
 

+ 4 - 4
src/tests.rs

@@ -83,7 +83,7 @@ fn url_parsing() {
                 assert_eq!(String::new(), expected_username);
                 assert_eq!(None, expected_password);
                 assert_eq!(String::new(), expected_host);
-                assert_eq!(String::new(), expected_port);
+                assert_eq!(None, expected_port);
             },
         }
         fn opt_prepend(prefix: &str, opt_s: Option<String>) -> Option<String> {
@@ -103,7 +103,7 @@ struct Test {
     username: String,
     password: Option<String>,
     host: String,
-    port: String,
+    port: Option<u16>,
     path: Option<String>,
     query: Option<String>,
     fragment: Option<String>,
@@ -133,7 +133,7 @@ fn parse_test_data(input: &str) -> Vec<Test> {
             username: String::new(),
             password: None,
             host: String::new(),
-            port: String::new(),
+            port: None,
             path: None,
             query: None,
             fragment: None,
@@ -150,7 +150,7 @@ fn parse_test_data(input: &str) -> Vec<Test> {
                 "u" => test.username = value,
                 "pass" => test.password = Some(value),
                 "h" => test.host = value,
-                "port" => test.port = value,
+                "port" => test.port = Some(from_str(value.as_slice()).unwrap()),
                 "p" => test.path = Some(value),
                 "q" => test.query = Some(value),
                 "f" => test.fragment = Some(value),

+ 1 - 1
src/urltestdata.txt

@@ -20,7 +20,7 @@ http://f:b/c
 http://f:\s/c
 http://f:\n/c  s:http h:f p:/c
 http://f:fifty-two/c
-http://f:999999/c  s:http h:f port:999999 p:/c
+http://f:9999/c  s:http h:f port:9999 p:/c
 http://f:\s21\s/\sb\s?\sd\s#\se\s
   s:http h:example.org p:/foo/bar
 \s\s\t  s:http h:example.org p:/foo/bar