diff --git a/src/lib.rs b/src/lib.rs index ba6491143..9b788db5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1659,8 +1659,13 @@ impl Url { self.set_host_internal(Host::parse_opaque(host_substr)?, None); } } else if self.has_host() { - if SchemeType::from(self.scheme()).is_special() { + let scheme_type = SchemeType::from(self.scheme()); + if scheme_type.is_special() { return Err(ParseError::EmptyHost); + } else { + if self.serialization.len() == self.path_start as usize { + self.serialization.push('/'); + } } debug_assert!(self.byte_at(self.scheme_end) == b':'); debug_assert!(self.byte_at(self.path_start) == b'/'); @@ -1963,14 +1968,28 @@ impl Url { /// /// # fn run() -> Result<(), ParseError> { /// let mut url = Url::parse("https://example.net")?; - /// let result = url.set_scheme("foo"); - /// assert_eq!(url.as_str(), "foo://example.net/"); + /// let result = url.set_scheme("http"); + /// assert_eq!(url.as_str(), "http://example.net/"); /// assert!(result.is_ok()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` + /// Change the URL’s scheme from `foo` to `bar`: /// + /// ``` + /// use url::Url; + /// # use url::ParseError; + /// + /// # fn run() -> Result<(), ParseError> { + /// let mut url = Url::parse("foo://example.net")?; + /// let result = url.set_scheme("bar"); + /// assert_eq!(url.as_str(), "bar://example.net"); + /// assert!(result.is_ok()); + /// # Ok(()) + /// # } + /// # run().unwrap(); + /// ``` /// /// Cannot change URL’s scheme from `https` to `foõ`: /// @@ -2003,14 +2022,49 @@ impl Url { /// # } /// # run().unwrap(); /// ``` + /// Cannot change the URL’s scheme from `foo` to `https`: + /// + /// ``` + /// use url::Url; + /// # use url::ParseError; + /// + /// # fn run() -> Result<(), ParseError> { + /// let mut url = Url::parse("foo://example.net")?; + /// let result = url.set_scheme("https"); + /// assert_eq!(url.as_str(), "foo://example.net"); + /// assert!(result.is_err()); + /// # Ok(()) + /// # } + /// # run().unwrap(); + /// ``` + /// Cannot change the URL’s scheme from `http` to `foo`: + /// + /// ``` + /// use url::Url; + /// # use url::ParseError; + /// + /// # fn run() -> Result<(), ParseError> { + /// let mut url = Url::parse("http://example.net")?; + /// let result = url.set_scheme("foo"); + /// assert_eq!(url.as_str(), "http://example.net/"); + /// assert!(result.is_err()); + /// # Ok(()) + /// # } + /// # run().unwrap(); + /// ``` 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))?; let new_scheme_type = SchemeType::from(&parser.serialization); let old_scheme_type = SchemeType::from(self.scheme()); - // Switching from special scheme to non special scheme - // and switching from file to non file is not allowed - if old_scheme_type != new_scheme_type { + // If url’s scheme is a special scheme and buffer is not a special scheme, then return. + if new_scheme_type.is_special() && !old_scheme_type.is_special() || + // If url’s scheme is not a special scheme and buffer is a special scheme, then return. + !new_scheme_type.is_special() && old_scheme_type.is_special() || + // If url includes credentials or has a non-null port, and buffer is "file", then return. + // If url’s scheme is "file" and its host is an empty host or null, then return. + new_scheme_type.is_file() && self.has_authority() + { return Err(()); }