Kaynağa Gözat

All setters.

Simon Sapin 10 yıl önce
ebeveyn
işleme
a1f389fc83
4 değiştirilmiş dosya ile 88 ekleme ve 56 silme
  1. 10 13
      src/host.rs
  2. 24 19
      src/lib.rs
  3. 19 16
      src/parser.rs
  4. 35 8
      src/webidl.rs

+ 10 - 13
src/host.rs

@@ -24,6 +24,16 @@ pub enum HostInternal {
     Ipv6(Ipv6Addr),
 }
 
+impl<S> From<Host<S>> for HostInternal {
+    fn from(host: Host<S>) -> HostInternal {
+        match host {
+            Host::Domain(_) => HostInternal::Domain,
+            Host::Ipv4(address) => HostInternal::Ipv4(address),
+            Host::Ipv6(address) => HostInternal::Ipv6(address),
+        }
+    }
+}
+
 /// The host name of an URL.
 #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
 #[cfg_attr(feature="heap_size", derive(HeapSizeOf))]
@@ -157,19 +167,6 @@ impl Iterator for SocketAddrs {
     }
 }
 
-/// Parse `input` as a host.
-/// If successful, write its serialization to `serialization`
-/// and return the internal representation for `Url`.
-pub fn parse(input: &str, serialization: &mut String) -> ParseResult<HostInternal> {
-    let host = try!(Host::parse(input));
-    write!(serialization, "{}", host).unwrap();
-    match host {
-        Host::Domain(_) => Ok(HostInternal::Domain),
-        Host::Ipv4(address) => Ok(HostInternal::Ipv4(address)),
-        Host::Ipv6(address) => Ok(HostInternal::Ipv6(address)),
-    }
-}
-
 fn write_ipv6(addr: &Ipv6Addr, f: &mut Formatter) -> fmt::Result {
     let segments = addr.segments();
     let (compress_start, compress_end) = longest_zero_sequence(&segments);

+ 24 - 19
src/lib.rs

@@ -573,11 +573,11 @@ impl Url {
         if port.is_some() && port == parser::default_port(self.scheme()) {
             port = None
         }
-        self.set_port_inner(port);
+        self.set_port_internal(port);
         Ok(())
     }
 
-    fn set_port_inner(&mut self, port: Option<u16>) {
+    fn set_port_internal(&mut self, port: Option<u16>) {
         match (self.port, port) {
             (None, None) => {}
             (Some(_), None) => {
@@ -611,7 +611,8 @@ impl Url {
 
     /// Change this URL’s host.
     ///
-    /// If this URL is non-relative, do nothing and return `Err`.
+    /// If this URL is non-relative or there is an error parsing the given `host`,
+    /// do nothing and return `Err`.
     ///
     /// Removing the host (calling this with `None`)
     /// will also remove any username, password, and port number.
@@ -621,7 +622,7 @@ impl Url {
         }
 
         if let Some(host) = host {
-            self.set_host_internal(try!(Host::parse(host).map_err(|_| ())))
+            self.set_host_internal(try!(Host::parse(host).map_err(|_| ())), None)
         } else if self.has_host() {
             // Not debug_assert! since this proves that `unsafe` below is OK:
             assert!(self.byte_at(self.scheme_end) == b':');
@@ -643,8 +644,10 @@ impl Url {
         Ok(())
     }
 
-    fn set_host_internal(&mut self, host: Host<String>) {
-        let after_host = self.slice(self.host_end..).to_owned();
+    /// 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 suffix = self.slice(old_suffix_pos..).to_owned();
         self.serialization.truncate(self.host_start as usize);
         if !self.has_host() {
             debug_assert!(self.slice(self.scheme_end..self.host_start) == ":");
@@ -654,20 +657,22 @@ impl Url {
             self.username_end += 2;
             self.host_start += 2;
         }
-        let old_host_end = self.host_end;
         write!(&mut self.serialization, "{}", host).unwrap();
-        let new_host_end = to_u32(self.serialization.len()).unwrap();
-        self.serialization.push_str(&after_host);
+        self.host_end = to_u32(self.serialization.len()).unwrap();
+        self.host = host.into();
+
+        if let Some(new_port) = opt_new_port {
+            self.port = new_port;
+            if let Some(port) = new_port {
+                write!(&mut self.serialization, ":{}", port).unwrap();
+            }
+        }
+        let new_suffix_pos = to_u32(self.serialization.len()).unwrap();
+        self.serialization.push_str(&suffix);
 
-        self.host = match host {
-            Host::Domain(_) => HostInternal::Domain,
-            Host::Ipv4(address) => HostInternal::Ipv4(address),
-            Host::Ipv6(address) => HostInternal::Ipv6(address),
-        };
-        self.host_end = new_host_end;
         let adjust = |index: &mut u32| {
-            *index -= old_host_end;
-            *index += new_host_end;
+            *index -= old_suffix_pos;
+            *index += new_suffix_pos;
         };
         adjust(&mut self.path_start);
         if let Some(ref mut index) = self.query_start { adjust(index) }
@@ -684,7 +689,7 @@ impl Url {
             return Err(())
         }
 
-        self.set_host_internal(Host::Ipv4(address));
+        self.set_host_internal(Host::Ipv4(address), None);
         Ok(())
     }
 
@@ -698,7 +703,7 @@ impl Url {
             return Err(())
         }
 
-        self.set_host_internal(Host::Ipv6(address));
+        self.set_host_internal(Host::Ipv6(address), None);
         Ok(())
     }
 

+ 19 - 16
src/parser.rs

@@ -11,7 +11,7 @@ use std::error::Error;
 use std::fmt::{self, Formatter, Write};
 
 use super::{Url, EncodingOverride};
-use host::{self, HostInternal};
+use host::{Host, HostInternal};
 use percent_encoding::{
     utf8_percent_encode, percent_encode,
     SIMPLE_ENCODE_SET, DEFAULT_ENCODE_SET, USERINFO_ENCODE_SET, QUERY_ENCODE_SET,
@@ -603,7 +603,9 @@ impl<'a> Parser<'a> {
     pub fn parse_host_and_port<'i>(&mut self, input: &'i str,
                                    scheme_end: u32, scheme_type: SchemeType)
                                    -> ParseResult<(u32, HostInternal, Option<u16>, &'i str)> {
-        let (host, remaining) = try!(self.parse_host(input, scheme_type));
+        let (host, remaining) = try!(
+            Parser::parse_host(input, scheme_type, |m| self.syntax_violation(m)));
+        write!(&mut self.serialization, "{}", host).unwrap();
         let host_end = try!(to_u32(self.serialization.len()));
         let (port, remaining) = if remaining.starts_with(":") {
             let syntax_violation = |message| self.syntax_violation(message);
@@ -615,11 +617,12 @@ impl<'a> Parser<'a> {
         if let Some(port) = port {
             write!(&mut self.serialization, ":{}", port).unwrap()
         }
-        Ok((host_end, host, port, remaining))
+        Ok((host_end, host.into(), port, remaining))
     }
 
-    pub fn parse_host<'i>(&mut self, input: &'i str, scheme_type: SchemeType)
-                          -> ParseResult<(HostInternal, &'i str)> {
+    pub fn parse_host<'i, S>(input: &'i str, scheme_type: SchemeType, syntax_violation: S)
+                             -> ParseResult<(Host<String>, &'i str)>
+                             where S: Fn(&'static str) {
         let mut inside_square_brackets = false;
         let mut has_ignored_chars = false;
         let mut end = input.len();
@@ -638,7 +641,7 @@ impl<'a> Parser<'a> {
                     break
                 }
                 b'\t' | b'\n' | b'\r' => {
-                    self.syntax_violation("invalid character");
+                    syntax_violation("invalid character");
                     has_ignored_chars = true;
                 }
                 b'[' => inside_square_brackets = true,
@@ -656,7 +659,7 @@ impl<'a> Parser<'a> {
         if scheme_type.is_special() && host_input.is_empty() {
             return Err(ParseError::EmptyHost)
         }
-        let host = try!(host::parse(&host_input, &mut self.serialization));
+        let host = try!(Host::parse(&host_input));
         Ok((host, &input[end..]))
     }
 
@@ -687,17 +690,17 @@ impl<'a> Parser<'a> {
         if is_windows_drive_letter(host_input) {
             return Ok((false, HostInternal::None, input))
         }
-        let mut host;
-        if host_input.is_empty() {
-            host = HostInternal::None;
+        let host = if host_input.is_empty() {
+            HostInternal::None
         } else {
-            let host_start = self.serialization.len();
-            host = try!(host::parse(&host_input, &mut self.serialization));
-            if &self.serialization[host_start..] == "localhost" {
-                host = HostInternal::None;
-                self.serialization.truncate(host_start);
+            match try!(Host::parse(&host_input)) {
+                Host::Domain(ref d) if d == "localhost" => HostInternal::None,
+                host => {
+                    write!(&mut self.serialization, "{}", host).unwrap();
+                    host.into()
+                }
             }
-        }
+        };
         Ok((true, host, &input[end..]))
     }
 

+ 35 - 8
src/webidl.rs

@@ -9,7 +9,7 @@
 use {Url, ParseError};
 use host::Host;
 use idna::domain_to_unicode;
-use parser::{Parser, default_port};
+use parser::{Parser, SchemeType, default_port};
 
 /// https://url.spec.whatwg.org/#api
 pub struct WebIdl;
@@ -89,9 +89,30 @@ impl WebIdl {
         host
     }
 
-    /// **Not implemented yet** Setter for https://url.spec.whatwg.org/#dom-url-host
-    pub fn set_host(_url: &mut Url, _new_host: &str) {
-        unimplemented!()  // FIXME
+    /// Setter for https://url.spec.whatwg.org/#dom-url-host
+    pub fn set_host(url: &mut Url, new_host: &str) {
+        if url.non_relative() {
+            return
+        }
+        let host;
+        let opt_port;
+        {
+            let scheme = url.scheme();
+            let result = Parser::parse_host(new_host, SchemeType::from(scheme), |_| ());
+            match result {
+                Ok((h, remaining)) => {
+                    host = h;
+                    opt_port = if remaining.starts_with(':') {
+                        Parser::parse_port(remaining, |_| (), || default_port(scheme))
+                        .ok().map(|(port, _remaining)| port)
+                    } else {
+                        None
+                    };
+                }
+                Err(_) => return
+            }
+        }
+        url.set_host_internal(host, opt_port)
     }
 
     /// Getter for https://url.spec.whatwg.org/#dom-url-hostname
@@ -100,9 +121,15 @@ impl WebIdl {
         url.host_str().unwrap_or("")
     }
 
-    /// **Not implemented yet** Setter for https://url.spec.whatwg.org/#dom-url-hostname
-    pub fn set_hostname(_url: &mut Url, _new_hostname: &str) {
-        unimplemented!()  // FIXME
+    /// Setter for https://url.spec.whatwg.org/#dom-url-hostname
+    pub fn set_hostname(url: &mut Url, new_hostname: &str) {
+        if url.non_relative() {
+            return
+        }
+        let result = Parser::parse_host(new_hostname, SchemeType::from(url.scheme()), |_| ());
+        if let Ok((host, _remaining)) = result {
+            url.set_host_internal(host, None)
+        }
     }
 
     /// Getter for https://url.spec.whatwg.org/#dom-url-port
@@ -128,7 +155,7 @@ impl WebIdl {
             result = Parser::parse_port(new_port, |_| (), || default_port(scheme))
         }
         if let Ok((new_port, _remaining)) = result {
-            url.set_port_inner(new_port)
+            url.set_port_internal(new_port)
         }
     }