Uniformly implement utility traits on URI types.

This commit is contained in:
Sergio Benitez 2021-06-09 17:44:12 -07:00
parent d2c2725689
commit c2960e7e6f
7 changed files with 145 additions and 248 deletions

View File

@ -136,6 +136,22 @@ impl<B: 'static + ToOwned + ?Sized> 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) -> <Self as IntoOwned>::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.

View File

@ -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<Data<'a, fmt::Query>>,
}
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<Self, Self::Error> {
Absolute::parse(value.as_str())
}
}
impl<'a> TryFrom<&'a str> for Absolute<'a> {
type Error = Error<'a>;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
Absolute::parse(value)
}
}
impl<'a, 'b> PartialEq<Absolute<'b>> 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");

View File

@ -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<u16>,
}
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<Authority<'b>> 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<Self, Self::Error> {
Authority::parse(value.as_str())
}
}
impl<'a> TryFrom<&'a str> for Authority<'a> {
type Error = Error<'a>;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
Authority::parse(value)
}
}
impl_serde!(Authority<'a>, "an authority-form URI");

View File

@ -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<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
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<E: Error>(self, v: &str) -> Result<Self::Value, E> {
<$T>::parse_owned(v.to_string()).map_err(Error::custom)
}
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
<$T>::parse_owned(v).map_err(Error::custom)
}
}
impl<'a, 'de> Deserialize<'de> for $T {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_str(DeVisitor(PhantomData))
}
}
}
};
}
#[macro_use]
mod uri;
mod origin;
mod reference;

View File

@ -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<Data<'a, fmt::Query>>,
}
impl Hash for Origin<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.path().hash(state);
self.query().hash(state);
}
}
impl<'a, 'b> PartialEq<Origin<'b>> for Origin<'a> {
fn eq(&self, other: &Origin<'b>) -> bool {
self.path() == other.path() && self.query() == other.query()
}
}
impl Eq for Origin<'_> { }
impl PartialEq<str> 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<Origin<'_>> 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<String> for Origin<'static> {
type Error = Error<'static>;
impl_serde!(Origin<'a>, "an origin-form URI");
fn try_from(value: String) -> Result<Self, Self::Error> {
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<Self, Self::Error> {
Origin::parse(value.as_str())
}
}
impl<'a> TryFrom<&'a str> for Origin<'a> {
type Error = Error<'a>;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
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;

View File

@ -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<Reference<'_>> 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<Absolute<'a>> for Reference<'a> {
fn from(absolute: Absolute<'a>) -> Self {
@ -507,31 +486,6 @@ impl From<Asterisk> for Reference<'_> {
}
}
impl<'a> TryFrom<&'a str> for Reference<'a> {
type Error = Error<'a>;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
Reference::parse(value)
}
}
impl TryFrom<String> for Reference<'static> {
type Error = Error<'static>;
fn try_from(value: String) -> Result<Self, Self::Error> {
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<Self, Self::Error> {
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");

View File

@ -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<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
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<E: Error>(self, v: &str) -> Result<Self::Value, E> {
<$T>::parse_owned(v.to_string()).map_err(Error::custom)
}
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
<$T>::parse_owned(v).map_err(Error::custom)
}
}
impl<'a, 'de> Deserialize<'de> for $T {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
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<String> for $T<'static> {
type Error = Error<'static>;
fn try_from(value: String) -> Result<Self, Self::Error> {
$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<Self, Self::Error> {
$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<Self, Self::Error> {
$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<str> 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<H: std::hash::Hasher>(&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()),*
}
}
}
}
}