From c2960e7e6f00441ec725c749aef4b22f32aa24b2 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 9 Jun 2021 17:44:12 -0700 Subject: [PATCH] Uniformly implement utility traits on URI types. --- core/http/src/ext.rs | 16 +++++ core/http/src/uri/absolute.rs | 42 +----------- core/http/src/uri/authority.rs | 43 +----------- core/http/src/uri/mod.rs | 45 +------------ core/http/src/uri/origin.rs | 75 +-------------------- core/http/src/uri/reference.rs | 54 +-------------- core/http/src/uri/uri.rs | 118 +++++++++++++++++++++++++++++++++ 7 files changed, 145 insertions(+), 248 deletions(-) diff --git a/core/http/src/ext.rs b/core/http/src/ext.rs index 71758980..1a0ab4e9 100644 --- a/core/http/src/ext.rs +++ b/core/http/src/ext.rs @@ -136,6 +136,22 @@ impl IntoOwned for Cow<'_, B> { } } +macro_rules! impl_into_owned_self { + ($($T:ty),*) => ($( + impl IntoOwned for $T { + type Owned = Self; + + #[inline(always)] + fn into_owned(self) -> ::Owned { + self + } + } + )*) +} + +impl_into_owned_self!(u8, u16, u32, u64, usize); +impl_into_owned_self!(i8, i16, i32, i64, isize); + use std::path::Path; // Outside of http, this is used by a test. diff --git a/core/http/src/uri/absolute.rs b/core/http/src/uri/absolute.rs index 3e442df0..1e5e2cee 100644 --- a/core/http/src/uri/absolute.rs +++ b/core/http/src/uri/absolute.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; -use std::convert::TryFrom; use crate::ext::IntoOwned; use crate::parse::{Extent, IndexedStr}; @@ -97,20 +96,6 @@ pub struct Absolute<'a> { pub(crate) query: Option>, } -impl IntoOwned for Absolute<'_> { - type Owned = Absolute<'static>; - - fn into_owned(self) -> Self::Owned { - Absolute { - source: self.source.into_owned(), - scheme: self.scheme.into_owned(), - authority: self.authority.into_owned(), - path: self.path.into_owned(), - query: self.query.into_owned(), - } - } -} - impl<'a> Absolute<'a> { /// Parses the string `string` into an `Absolute`. Parsing will never /// allocate. Returns an `Error` if `string` is not a valid absolute URI. @@ -480,30 +465,9 @@ impl<'a> Absolute<'a> { } } -impl<'a> TryFrom<&'a String> for Absolute<'a> { - type Error = Error<'a>; +impl_serde!(Absolute<'a>, "an absolute-form URI"); - fn try_from(value: &'a String) -> Result { - Absolute::parse(value.as_str()) - } -} - -impl<'a> TryFrom<&'a str> for Absolute<'a> { - type Error = Error<'a>; - - fn try_from(value: &'a str) -> Result { - Absolute::parse(value) - } -} - -impl<'a, 'b> PartialEq> for Absolute<'a> { - fn eq(&self, other: &Absolute<'b>) -> bool { - self.scheme() == other.scheme() - && self.authority() == other.authority() - && self.path() == other.path() - && self.query() == other.query() - } -} +impl_traits!(Absolute, scheme, authority, path, query); impl std::fmt::Display for Absolute<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -520,5 +484,3 @@ impl std::fmt::Display for Absolute<'_> { Ok(()) } } - -impl_serde!(Absolute<'a>, "an absolute-form URI"); diff --git a/core/http/src/uri/authority.rs b/core/http/src/uri/authority.rs index e7ee07a8..fadf3787 100644 --- a/core/http/src/uri/authority.rs +++ b/core/http/src/uri/authority.rs @@ -1,5 +1,4 @@ use std::fmt::{self, Display}; -use std::convert::TryFrom; use std::borrow::Cow; use crate::ext::IntoOwned; @@ -52,19 +51,6 @@ pub struct Authority<'a> { port: Option, } -impl IntoOwned for Authority<'_> { - type Owned = Authority<'static>; - - fn into_owned(self) -> Authority<'static> { - Authority { - source: self.source.into_owned(), - user_info: self.user_info.into_owned(), - host: self.host.into_owned(), - port: self.port - } - } -} - impl<'a> Authority<'a> { // SAFETY: `source` must be valid UTF-8. // CORRECTNESS: `host` must be non-empty. @@ -225,13 +211,9 @@ impl<'a> Authority<'a> { } } -impl<'b> PartialEq> for Authority<'_> { - fn eq(&self, other: &Authority<'b>) -> bool { - self.user_info() == other.user_info() - && self.host() == other.host() - && self.port() == other.port() - } -} +impl_serde!(Authority<'a>, "an authority-form URI"); + +impl_traits!(Authority, user_info, host, port); impl Display for Authority<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -247,22 +229,3 @@ impl Display for Authority<'_> { Ok(()) } } - -// Because inference doesn't take `&String` to `&str`. -impl<'a> TryFrom<&'a String> for Authority<'a> { - type Error = Error<'a>; - - fn try_from(value: &'a String) -> Result { - Authority::parse(value.as_str()) - } -} - -impl<'a> TryFrom<&'a str> for Authority<'a> { - type Error = Error<'a>; - - fn try_from(value: &'a str) -> Result { - Authority::parse(value) - } -} - -impl_serde!(Authority<'a>, "an authority-form URI"); diff --git a/core/http/src/uri/mod.rs b/core/http/src/uri/mod.rs index 1c7920a6..50946a6a 100644 --- a/core/http/src/uri/mod.rs +++ b/core/http/src/uri/mod.rs @@ -1,49 +1,6 @@ //! Types for URIs and traits for rendering URI components. -macro_rules! impl_serde { - ($T:ty, $expected:literal) => { - #[cfg(feature = "serde")] - mod serde { - use std::fmt; - use std::marker::PhantomData; - use super::*; - - use serde_::ser::{Serialize, Serializer}; - use serde_::de::{Deserialize, Deserializer, Error, Visitor}; - - impl<'a> Serialize for $T { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_str(&self.to_string()) - } - } - - struct DeVisitor<'a>(PhantomData<&'a $T>); - - impl<'de, 'a> Visitor<'de> for DeVisitor<'a> { - type Value = $T; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, $expected) - } - - fn visit_str(self, v: &str) -> Result { - <$T>::parse_owned(v.to_string()).map_err(Error::custom) - } - - fn visit_string(self, v: String) -> Result { - <$T>::parse_owned(v).map_err(Error::custom) - } - } - - impl<'a, 'de> Deserialize<'de> for $T { - fn deserialize>(deserializer: D) -> Result { - deserializer.deserialize_str(DeVisitor(PhantomData)) - } - } - } - }; -} - +#[macro_use] mod uri; mod origin; mod reference; diff --git a/core/http/src/uri/origin.rs b/core/http/src/uri/origin.rs index a5f511b3..6667567b 100644 --- a/core/http/src/uri/origin.rs +++ b/core/http/src/uri/origin.rs @@ -1,6 +1,4 @@ use std::borrow::Cow; -use std::convert::TryFrom; -use std::hash::Hash; use crate::ext::IntoOwned; use crate::parse::{Extent, IndexedStr, uri::tables::is_pchar}; @@ -116,52 +114,6 @@ pub struct Origin<'a> { pub(crate) query: Option>, } -impl Hash for Origin<'_> { - fn hash(&self, state: &mut H) { - self.path().hash(state); - self.query().hash(state); - } -} - -impl<'a, 'b> PartialEq> for Origin<'a> { - fn eq(&self, other: &Origin<'b>) -> bool { - self.path() == other.path() && self.query() == other.query() - } -} - -impl Eq for Origin<'_> { } - -impl PartialEq for Origin<'_> { - fn eq(&self, other: &str) -> bool { - let (path, query) = RawStr::new(other).split_at_byte(b'?'); - self.path() == path && self.query().map_or("", |q| q.as_str()) == query - } -} - -impl PartialEq<&str> for Origin<'_> { - fn eq(&self, other: &&str) -> bool { - self.eq(*other) - } -} - -impl PartialEq> for str { - fn eq(&self, other: &Origin<'_>) -> bool { - other.eq(self) - } -} - -impl IntoOwned for Origin<'_> { - type Owned = Origin<'static>; - - fn into_owned(self) -> Origin<'static> { - Origin { - source: self.source.into_owned(), - path: self.path.into_owned(), - query: self.query.into_owned(), - } - } -} - impl<'a> Origin<'a> { /// The root: `'/'`. #[doc(hidden)] @@ -474,30 +426,9 @@ impl<'a> Origin<'a> { } } -impl TryFrom for Origin<'static> { - type Error = Error<'static>; +impl_serde!(Origin<'a>, "an origin-form URI"); - fn try_from(value: String) -> Result { - Origin::parse_owned(value) - } -} - -// Because inference doesn't take `&String` to `&str`. -impl<'a> TryFrom<&'a String> for Origin<'a> { - type Error = Error<'a>; - - fn try_from(value: &'a String) -> Result { - Origin::parse(value.as_str()) - } -} - -impl<'a> TryFrom<&'a str> for Origin<'a> { - type Error = Error<'a>; - - fn try_from(value: &'a str) -> Result { - Origin::parse(value) - } -} +impl_traits!(Origin, path, query); impl std::fmt::Display for Origin<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -510,8 +441,6 @@ impl std::fmt::Display for Origin<'_> { } } -impl_serde!(Origin<'a>, "an origin-form URI"); - #[cfg(test)] mod tests { use super::Origin; diff --git a/core/http/src/uri/reference.rs b/core/http/src/uri/reference.rs index eb5cc5e4..6ef6149c 100644 --- a/core/http/src/uri/reference.rs +++ b/core/http/src/uri/reference.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, convert::TryFrom}; +use std::borrow::Cow; use crate::RawStr; use crate::ext::IntoOwned; @@ -427,30 +427,9 @@ impl<'a> Reference<'a> { } } -impl PartialEq> for Reference<'_> { - fn eq(&self, other: &Reference<'_>) -> bool { - self.scheme() == other.scheme() - && self.authority() == other.authority() - && self.path() == other.path() - && self.query() == other.query() - && self.fragment() == other.fragment() - } -} +impl_traits!(Reference, authority, scheme, path, query, fragment); -impl IntoOwned for Reference<'_> { - type Owned = Reference<'static>; - - fn into_owned(self) -> Self::Owned { - Reference { - source: self.source.into_owned(), - scheme: self.scheme.into_owned(), - authority: self.authority.into_owned(), - path: self.path.into_owned(), - query: self.query.into_owned(), - fragment: self.fragment.into_owned(), - } - } -} +impl_serde!(Reference<'a>, "a URI-reference"); impl<'a> From> for Reference<'a> { fn from(absolute: Absolute<'a>) -> Self { @@ -507,31 +486,6 @@ impl From for Reference<'_> { } } -impl<'a> TryFrom<&'a str> for Reference<'a> { - type Error = Error<'a>; - - fn try_from(value: &'a str) -> Result { - Reference::parse(value) - } -} - -impl TryFrom for Reference<'static> { - type Error = Error<'static>; - - fn try_from(value: String) -> Result { - Reference::parse_owned(value) - } -} - -// Because inference doesn't take `&String` to `&str`. -impl<'a> TryFrom<&'a String> for Reference<'a> { - type Error = Error<'a>; - - fn try_from(value: &'a String) -> Result { - Reference::parse(value.as_str()) - } -} - impl std::fmt::Display for Reference<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(scheme) = self.scheme() { @@ -555,5 +509,3 @@ impl std::fmt::Display for Reference<'_> { Ok(()) } } - -impl_serde!(Reference<'a>, "a URI-reference"); diff --git a/core/http/src/uri/uri.rs b/core/http/src/uri/uri.rs index aeccf37f..c7644727 100644 --- a/core/http/src/uri/uri.rs +++ b/core/http/src/uri/uri.rs @@ -343,3 +343,121 @@ impl_uri_from!(Authority<'a>); impl_uri_from!(Absolute<'a>); impl_uri_from!(Reference<'a>); impl_uri_from!(Asterisk); + +/// Implements Serialize and Deserialize for any 'URI' looking type. +macro_rules! impl_serde { + ($T:ty, $expected:literal) => { + #[cfg(feature = "serde")] + mod serde { + use std::fmt; + use std::marker::PhantomData; + use super::*; + + use serde_::ser::{Serialize, Serializer}; + use serde_::de::{Deserialize, Deserializer, Error, Visitor}; + + impl<'a> Serialize for $T { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&self.to_string()) + } + } + + struct DeVisitor<'a>(PhantomData<&'a $T>); + + impl<'de, 'a> Visitor<'de> for DeVisitor<'a> { + type Value = $T; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, $expected) + } + + fn visit_str(self, v: &str) -> Result { + <$T>::parse_owned(v.to_string()).map_err(Error::custom) + } + + fn visit_string(self, v: String) -> Result { + <$T>::parse_owned(v).map_err(Error::custom) + } + } + + impl<'a, 'de> Deserialize<'de> for $T { + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_str(DeVisitor(PhantomData)) + } + } + } + }; +} + +/// Implements PartialEq, Eq, Hash, TryFrom, and IntoOwned for a URI. +macro_rules! impl_traits { + ($T:ident, $($field:ident),* $(,)?) => { + impl std::convert::TryFrom for $T<'static> { + type Error = Error<'static>; + + fn try_from(value: String) -> Result { + $T::parse_owned(value) + } + } + + // Because inference doesn't take `&String` to `&str`. + impl<'a> std::convert::TryFrom<&'a String> for $T<'a> { + type Error = Error<'a>; + + fn try_from(value: &'a String) -> Result { + $T::parse(value.as_str()) + } + } + + impl<'a> std::convert::TryFrom<&'a str> for $T<'a> { + type Error = Error<'a>; + + fn try_from(value: &'a str) -> Result { + $T::parse(value) + } + } + + impl<'a, 'b> PartialEq<$T<'b>> for $T<'a> { + fn eq(&self, other: &$T<'b>) -> bool { + true $(&& self.$field() == other.$field())* + } + } + + impl PartialEq for $T<'_> { + fn eq(&self, string: &str) -> bool { + $T::parse(string).map_or(false, |v| &v == self) + } + } + + impl PartialEq<&str> for $T<'_> { + fn eq(&self, other: &&str) -> bool { + self.eq(*other) + } + } + + impl PartialEq<$T<'_>> for str { + fn eq(&self, other: &$T<'_>) -> bool { + other.eq(self) + } + } + + impl Eq for $T<'_> { } + + impl std::hash::Hash for $T<'_> { + fn hash(&self, state: &mut H) { + $(self.$field().hash(state);)* + } + } + + impl crate::ext::IntoOwned for $T<'_> { + type Owned = $T<'static>; + + fn into_owned(self) -> $T<'static> { + $T { + source: self.source.into_owned(), + $($field: self.$field.into_owned()),* + } + } + } + } +}