From 8bbc39d43de13f4efbe66d70eaf517ca305898e6 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 19 Jul 2019 10:11:11 +0200 Subject: [PATCH 1/3] Make HostInternal private --- src/host.rs | 2 +- src/parser.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/host.rs b/src/host.rs index ea66139c3..1310af2cf 100644 --- a/src/host.rs +++ b/src/host.rs @@ -14,7 +14,7 @@ use std::fmt::{self, Formatter}; use std::net::{Ipv4Addr, Ipv6Addr}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum HostInternal { +pub(crate) enum HostInternal { None, Domain, Ipv4(Ipv4Addr), diff --git a/src/parser.rs b/src/parser.rs index 96906f94a..b65857f70 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -972,7 +972,7 @@ impl<'a> Parser<'a> { Ok((host, input)) } - pub fn parse_file_host<'i>( + pub(crate) fn parse_file_host<'i>( &mut self, input: Input<'i>, ) -> ParseResult<(bool, HostInternal, Input<'i>)> { From 171e65e5c982480fc81b428d44228f153435d0a8 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 19 Jul 2019 10:11:36 +0200 Subject: [PATCH 2/3] Derive serde traits for Host and HostInternal --- Cargo.toml | 2 +- src/host.rs | 74 +++-------------------------------------------------- 2 files changed, 5 insertions(+), 71 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2c755ccc6..98b7e25ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ bencher = "0.1" idna = { version = "0.2.0", path = "./idna" } matches = "0.1" percent-encoding = { version = "2.0.0", path = "./percent_encoding" } -serde = {version = "1.0", optional = true} +serde = {version = "1.0", optional = true, features = ["derive"]} [[bench]] name = "parse_url" diff --git a/src/host.rs b/src/host.rs index 1310af2cf..9afc6d8e7 100644 --- a/src/host.rs +++ b/src/host.rs @@ -9,10 +9,13 @@ use idna; use parser::{ParseError, ParseResult}; use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; use std::cmp; use std::fmt::{self, Formatter}; use std::net::{Ipv4Addr, Ipv6Addr}; +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(crate) enum HostInternal { None, @@ -21,45 +24,6 @@ pub(crate) enum HostInternal { Ipv6(Ipv6Addr), } -#[cfg(feature = "serde")] -impl ::serde::Serialize for HostInternal { - fn serialize(&self, serializer: S) -> Result - 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. - // - // Implementing `Serializer` correctly for an enum is tricky, - // so let’s use existing enums that already do. - use std::net::IpAddr; - match *self { - HostInternal::None => None, - HostInternal::Domain => Some(None), - HostInternal::Ipv4(addr) => Some(Some(IpAddr::V4(addr))), - HostInternal::Ipv6(addr) => Some(Some(IpAddr::V6(addr))), - } - .serialize(serializer) - } -} - -#[cfg(feature = "serde")] -impl<'de> ::serde::Deserialize<'de> for HostInternal { - fn deserialize(deserializer: D) -> Result - where - D: ::serde::Deserializer<'de>, - { - use std::net::IpAddr; - Ok(match ::serde::Deserialize::deserialize(deserializer)? { - None => HostInternal::None, - Some(None) => HostInternal::Domain, - Some(Some(IpAddr::V4(addr))) => HostInternal::Ipv4(addr), - Some(Some(IpAddr::V6(addr))) => HostInternal::Ipv6(addr), - }) - } -} - impl From> for HostInternal { fn from(host: Host) -> HostInternal { match host { @@ -71,6 +35,7 @@ impl From> for HostInternal { } /// The host name of an URL. +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum Host { /// A DNS domain name, as '.' dot-separated labels. @@ -92,37 +57,6 @@ pub enum Host { Ipv6(Ipv6Addr), } -#[cfg(feature = "serde")] -impl ::serde::Serialize for Host { - fn serialize(&self, serializer: R) -> Result - 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) - } -} - -#[cfg(feature = "serde")] -impl<'de, S: ::serde::Deserialize<'de>> ::serde::Deserialize<'de> for Host { - fn deserialize(deserializer: D) -> Result - where - D: ::serde::Deserializer<'de>, - { - use std::net::IpAddr; - Ok(match ::serde::Deserialize::deserialize(deserializer)? { - Ok(s) => Host::Domain(s), - Err(IpAddr::V4(addr)) => Host::Ipv4(addr), - Err(IpAddr::V6(addr)) => Host::Ipv6(addr), - }) - } -} - impl<'a> Host<&'a str> { /// Return a copy of `self` that owns an allocated `String` but does not borrow an `&Url`. pub fn to_owned(&self) -> Host { From c2ff51b1b17b55d2b7db4ee32537151e01577721 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 19 Jul 2019 10:23:43 +0200 Subject: [PATCH 3/3] Optimise default string deserialization for Url --- src/lib.rs | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 92777e592..4f73ba590 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2281,11 +2281,28 @@ impl<'de> serde::Deserialize<'de> for Url { 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()) - }) + use serde::de::{Error, Unexpected, Visitor}; + + struct UrlVisitor; + + impl<'de> Visitor<'de> for UrlVisitor { + type Value = Url; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string representing an URL") + } + + fn visit_str(self, s: &str) -> Result + where + E: Error, + { + Url::parse(s).map_err(|err| { + Error::invalid_value(Unexpected::Str(s), &err.description()) + }) + } + } + + deserializer.deserialize_str(UrlVisitor) } }