mirror of https://github.com/rwf2/Rocket.git
Fixup URI (de)serialization.
This commit is contained in:
parent
1233518733
commit
faaa9c9065
|
@ -4,7 +4,7 @@ pub(crate) mod tables;
|
||||||
|
|
||||||
#[cfg(test)] mod tests;
|
#[cfg(test)] mod tests;
|
||||||
|
|
||||||
use crate::uri::{Uri, Origin, Absolute, Authority, Reference};
|
use crate::uri::{Uri, Origin, Absolute, Authority, Reference, Asterisk};
|
||||||
|
|
||||||
use self::parser::*;
|
use self::parser::*;
|
||||||
|
|
||||||
|
@ -32,6 +32,11 @@ pub fn absolute_from_str(s: &str) -> Result<Absolute<'_>, Error<'_>> {
|
||||||
Ok(parse!(absolute: RawInput::new(s.as_bytes()))?)
|
Ok(parse!(absolute: RawInput::new(s.as_bytes()))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn asterisk_from_str(s: &str) -> Result<Asterisk, Error<'_>> {
|
||||||
|
Ok(parse!(asterisk: RawInput::new(s.as_bytes()))?)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn reference_from_str(s: &str) -> Result<Reference<'_>, Error<'_>> {
|
pub fn reference_from_str(s: &str) -> Result<Reference<'_>, Error<'_>> {
|
||||||
Ok(parse!(reference: RawInput::new(s.as_bytes()))?)
|
Ok(parse!(reference: RawInput::new(s.as_bytes()))?)
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub fn uri<'a>(input: &mut RawInput<'a>) -> Result<'a, Uri<'a>> {
|
||||||
// To resolve all ambiguities with preference, we might need to look at the
|
// To resolve all ambiguities with preference, we might need to look at the
|
||||||
// complete string twice: origin/ref, asterisk/ref, authority/absolute.
|
// complete string twice: origin/ref, asterisk/ref, authority/absolute.
|
||||||
switch! {
|
switch! {
|
||||||
complete(|i| eat(i, b'*')) => Uri::Asterisk(Asterisk),
|
asterisk@complete(asterisk) => Uri::Asterisk(asterisk),
|
||||||
origin@complete(origin) => Uri::Origin(origin),
|
origin@complete(origin) => Uri::Origin(origin),
|
||||||
authority@complete(authority) => Uri::Authority(authority),
|
authority@complete(authority) => Uri::Authority(authority),
|
||||||
absolute@complete(absolute) => Uri::Absolute(absolute),
|
absolute@complete(absolute) => Uri::Absolute(absolute),
|
||||||
|
@ -43,6 +43,12 @@ pub fn uri<'a>(input: &mut RawInput<'a>) -> Result<'a, Uri<'a>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[parser]
|
||||||
|
pub fn asterisk<'a>(input: &mut RawInput<'a>) -> Result<'a, Asterisk> {
|
||||||
|
eat(b'*')?;
|
||||||
|
Asterisk
|
||||||
|
}
|
||||||
|
|
||||||
#[parser]
|
#[parser]
|
||||||
pub fn origin<'a>(input: &mut RawInput<'a>) -> Result<'a, Origin<'a>> {
|
pub fn origin<'a>(input: &mut RawInput<'a>) -> Result<'a, Origin<'a>> {
|
||||||
let (_, path, query) = (peek(b'/')?, path()?, query()?);
|
let (_, path, query) = (peek(b'/')?, path()?, query()?);
|
||||||
|
|
|
@ -65,19 +65,28 @@ use crate::uri::{Authority, Path, Query, Data, Error, as_utf8_unchecked, fmt};
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ## Serde
|
/// # (De)serialization
|
||||||
///
|
///
|
||||||
/// For convience, `Absolute` implements `Serialize` and `Deserialize`.
|
/// `Absolute` is both `Serialize` and `Deserialize`:
|
||||||
/// Because `Absolute` has a lifetime parameter, serde requires a borrow
|
|
||||||
/// attribute for the derive macro to work. If you want to own the Uri,
|
|
||||||
/// rather than borrow from the deserializer, use `'static`.
|
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```rust
|
||||||
/// #[derive(Deserialize)]
|
/// # #[cfg(feature = "serde")] mod serde {
|
||||||
/// struct Uris<'a> {
|
/// # use _serde as serde;
|
||||||
/// #[serde(borrow)]
|
/// use serde::{Serialize, Deserialize};
|
||||||
/// absolute: Absolute<'a>,
|
/// use rocket::http::uri::Absolute;
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize, Serialize)]
|
||||||
|
/// # #[serde(crate = "_serde")]
|
||||||
|
/// struct UriOwned {
|
||||||
|
/// uri: Absolute<'static>,
|
||||||
/// }
|
/// }
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize, Serialize)]
|
||||||
|
/// # #[serde(crate = "_serde")]
|
||||||
|
/// struct UriBorrowed<'a> {
|
||||||
|
/// uri: Absolute<'a>,
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Absolute<'a> {
|
pub struct Absolute<'a> {
|
||||||
|
@ -130,14 +139,12 @@ impl<'a> Absolute<'a> {
|
||||||
crate::parse::uri::absolute_from_str(string)
|
crate::parse::uri::absolute_from_str(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the string `string` into an `Absolute`. Parsing will never
|
/// Parses the string `string` into an `Absolute`. Allocates minimally on
|
||||||
/// May allocate on error.
|
/// success and error.
|
||||||
///
|
///
|
||||||
/// TODO: avoid allocation
|
/// This method should be used instead of [`Absolute::parse()`] when the
|
||||||
///
|
/// source URI is already a `String`. Returns an `Error` if `string` is not
|
||||||
/// This method should be used instead of [`Absolute::parse()`] when
|
/// a valid absolute URI.
|
||||||
/// the source URI is already a `String`. Returns an `Error` if `string` is
|
|
||||||
/// not a valid absolute URI.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -151,9 +158,10 @@ impl<'a> Absolute<'a> {
|
||||||
/// assert_eq!(uri.path(), "/foo/2/three");
|
/// assert_eq!(uri.path(), "/foo/2/three");
|
||||||
/// assert!(uri.query().is_none());
|
/// assert!(uri.query().is_none());
|
||||||
/// ```
|
/// ```
|
||||||
|
// TODO: Avoid all allocations.
|
||||||
pub fn parse_owned(string: String) -> Result<Absolute<'static>, Error<'static>> {
|
pub fn parse_owned(string: String) -> Result<Absolute<'static>, Error<'static>> {
|
||||||
let absolute = Absolute::parse(&string).map_err(|e| e.into_owned())?;
|
let absolute = Absolute::parse(&string).map_err(|e| e.into_owned())?;
|
||||||
debug_assert!(absolute.source.is_some(), "Origin source parsed w/o source");
|
debug_assert!(absolute.source.is_some(), "Absolute parsed w/o source");
|
||||||
|
|
||||||
let absolute = Absolute {
|
let absolute = Absolute {
|
||||||
scheme: absolute.scheme.into_owned(),
|
scheme: absolute.scheme.into_owned(),
|
||||||
|
@ -513,43 +521,4 @@ impl std::fmt::Display for Absolute<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
impl_serde!(Absolute<'a>, "an absolute-form URI");
|
||||||
mod serde {
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use super::Absolute;
|
|
||||||
use _serde::{ser::{Serialize, Serializer}, de::{Deserialize, Deserializer, Error, Visitor}};
|
|
||||||
|
|
||||||
impl<'a> Serialize for Absolute<'a> {
|
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
serializer.serialize_str(&self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AbsoluteVistor;
|
|
||||||
|
|
||||||
impl<'a> Visitor<'a> for AbsoluteVistor {
|
|
||||||
type Value = Absolute<'a>;
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(formatter, "absolute Uri")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
|
||||||
Absolute::parse_owned(v.to_string()).map_err(Error::custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
|
|
||||||
Absolute::parse_owned(v).map_err(Error::custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_borrowed_str<E: Error>(self, v: &'a str) -> Result<Self::Value, E> {
|
|
||||||
Absolute::parse(v).map_err(Error::custom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'de: 'a> Deserialize<'de> for Absolute<'a> {
|
|
||||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
deserializer.deserialize_str(AbsoluteVistor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,54 +1,70 @@
|
||||||
|
use crate::ext::IntoOwned;
|
||||||
|
use crate::uri::Error;
|
||||||
|
|
||||||
/// The literal `*` URI.
|
/// The literal `*` URI.
|
||||||
///
|
///
|
||||||
/// ## Serde
|
/// # (De)serialization
|
||||||
///
|
///
|
||||||
/// For convience, `Asterisk` implements `Serialize` and `Deserialize`.
|
/// `Asterisk` is both `Serialize` and `Deserialize`:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #[cfg(feature = "serde")] mod serde {
|
||||||
|
/// # use _serde as serde;
|
||||||
|
/// use serde::{Serialize, Deserialize};
|
||||||
|
/// use rocket::http::uri::Asterisk;
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize, Serialize)]
|
||||||
|
/// # #[serde(crate = "_serde")]
|
||||||
|
/// struct UriOwned {
|
||||||
|
/// uri: Asterisk,
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
|
||||||
pub struct Asterisk;
|
pub struct Asterisk;
|
||||||
|
|
||||||
|
impl Asterisk {
|
||||||
|
/// Parses the string `string` into an `Asterisk`. Parsing will never
|
||||||
|
/// allocate. Returns an `Error` if `string` is not a valid asterisk URI.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #[macro_use] extern crate rocket;
|
||||||
|
/// use rocket::http::uri::Asterisk;
|
||||||
|
///
|
||||||
|
/// assert!(Asterisk::parse("*").is_ok());
|
||||||
|
/// assert!(Asterisk::parse("/foo/bar").is_err());
|
||||||
|
///
|
||||||
|
/// // Prefer to use `uri!()` when the input is statically known:
|
||||||
|
/// let uri = uri!("*");
|
||||||
|
/// assert_eq!(uri, Asterisk);
|
||||||
|
/// ```
|
||||||
|
pub fn parse(string: &str) -> Result<Asterisk, Error<'_>> {
|
||||||
|
crate::parse::uri::asterisk_from_str(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses the string `string` into an `Asterisk`. This is equivalent to
|
||||||
|
/// [`Asterisk::parse()`].
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #[macro_use] extern crate rocket;
|
||||||
|
/// use rocket::http::uri::Asterisk;
|
||||||
|
///
|
||||||
|
/// assert!(Asterisk::parse_owned("*".to_string()).is_ok());
|
||||||
|
/// assert!(Asterisk::parse_owned("/foo/bar".to_string()).is_err());
|
||||||
|
/// ```
|
||||||
|
pub fn parse_owned(string: String) -> Result<Asterisk, Error<'static>> {
|
||||||
|
Asterisk::parse(&string).map_err(|e| e.into_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Asterisk {
|
impl std::fmt::Display for Asterisk {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
"*".fmt(f)
|
"*".fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
impl_serde!(Asterisk, "an asterisk-form URI, '*'");
|
||||||
mod serde {
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use super::Asterisk;
|
|
||||||
use _serde::{ser::{Serialize, Serializer}, de::{Deserialize, Deserializer, Error, Visitor}};
|
|
||||||
|
|
||||||
impl Serialize for Asterisk {
|
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
serializer.serialize_str("*")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AsteriskVistor;
|
|
||||||
|
|
||||||
impl<'a> Visitor<'a> for AsteriskVistor {
|
|
||||||
type Value = Asterisk;
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(formatter, "asterisk Uri")
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method should be the only one that needs to be implemented, since the
|
|
||||||
// other two methods (`visit_string` & `visit_borrowed_str`) have default implementations
|
|
||||||
// that just call this one. We don't benefit from taking ownership or borrowing from the
|
|
||||||
// deserializer, so this should be perfect.
|
|
||||||
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
|
||||||
if v == "*" {
|
|
||||||
Ok(Asterisk)
|
|
||||||
}else {
|
|
||||||
Err(E::custom(format!("`{}` is not a valid asterisk uri", v)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Asterisk {
|
|
||||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
deserializer.deserialize_str(AsteriskVistor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -21,19 +21,28 @@ use crate::uri::{as_utf8_unchecked, error::Error};
|
||||||
///
|
///
|
||||||
/// Only the host part of the URI is required.
|
/// Only the host part of the URI is required.
|
||||||
///
|
///
|
||||||
/// ## Serde
|
/// # (De)serialization
|
||||||
///
|
///
|
||||||
/// For convience, `Authority` implements `Serialize` and `Deserialize`.
|
/// `Authority` is both `Serialize` and `Deserialize`:
|
||||||
/// Because `Authority` has a lifetime parameter, serde requires a borrow
|
|
||||||
/// attribute for the derive macro to work. If you want to own the Uri,
|
|
||||||
/// rather than borrow from the deserializer, use `'static`.
|
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```rust
|
||||||
/// #[derive(Deserialize)]
|
/// # #[cfg(feature = "serde")] mod serde {
|
||||||
/// struct Uris<'a> {
|
/// # use _serde as serde;
|
||||||
/// #[serde(borrow)]
|
/// use serde::{Serialize, Deserialize};
|
||||||
/// authority: Authority<'a>,
|
/// use rocket::http::uri::Authority;
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize, Serialize)]
|
||||||
|
/// # #[serde(crate = "_serde")]
|
||||||
|
/// struct UriOwned {
|
||||||
|
/// uri: Authority<'static>,
|
||||||
/// }
|
/// }
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize, Serialize)]
|
||||||
|
/// # #[serde(crate = "_serde")]
|
||||||
|
/// struct UriBorrowed<'a> {
|
||||||
|
/// uri: Authority<'a>,
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Authority<'a> {
|
pub struct Authority<'a> {
|
||||||
|
@ -124,8 +133,8 @@ impl<'a> Authority<'a> {
|
||||||
crate::parse::uri::authority_from_str(string)
|
crate::parse::uri::authority_from_str(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the string `string` into an `Authority`. Parsing will never allocate.
|
/// Parses the string `string` into an `Authority`. Parsing never allocates
|
||||||
/// May allocate on error.
|
/// on success. May allocate on error.
|
||||||
///
|
///
|
||||||
/// This method should be used instead of [`Authority::parse()`] when
|
/// This method should be used instead of [`Authority::parse()`] when
|
||||||
/// the source URI is already a `String`. Returns an `Error` if `string` is
|
/// the source URI is already a `String`. Returns an `Error` if `string` is
|
||||||
|
@ -145,7 +154,7 @@ impl<'a> Authority<'a> {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn parse_owned(string: String) -> Result<Authority<'static>, Error<'static>> {
|
pub fn parse_owned(string: String) -> Result<Authority<'static>, Error<'static>> {
|
||||||
let authority = Authority::parse(&string).map_err(|e| e.into_owned())?;
|
let authority = Authority::parse(&string).map_err(|e| e.into_owned())?;
|
||||||
debug_assert!(authority.source.is_some(), "Origin source parsed w/o source");
|
debug_assert!(authority.source.is_some(), "Authority parsed w/o source");
|
||||||
|
|
||||||
let authority = Authority {
|
let authority = Authority {
|
||||||
host: authority.host.into_owned(),
|
host: authority.host.into_owned(),
|
||||||
|
@ -256,43 +265,4 @@ impl<'a> TryFrom<&'a str> for Authority<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
impl_serde!(Authority<'a>, "an authority-form URI");
|
||||||
mod serde {
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use super::Authority;
|
|
||||||
use _serde::{ser::{Serialize, Serializer}, de::{Deserialize, Deserializer, Error, Visitor}};
|
|
||||||
|
|
||||||
impl<'a> Serialize for Authority<'a> {
|
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
serializer.serialize_str(&self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AuthorityVistor;
|
|
||||||
|
|
||||||
impl<'a> Visitor<'a> for AuthorityVistor {
|
|
||||||
type Value = Authority<'a>;
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(formatter, "authority Uri")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
|
||||||
Authority::parse_owned(v.to_string()).map_err(Error::custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
|
|
||||||
Authority::parse_owned(v).map_err(Error::custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_borrowed_str<E: Error>(self, v: &'a str) -> Result<Self::Value, E> {
|
|
||||||
Authority::parse(v).map_err(Error::custom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'de: 'a> Deserialize<'de> for Authority<'a> {
|
|
||||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
deserializer.deserialize_str(AuthorityVistor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,49 @@
|
||||||
//! Types for URIs and traits for rendering URI components.
|
//! 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
mod uri;
|
mod uri;
|
||||||
mod origin;
|
mod origin;
|
||||||
mod reference;
|
mod reference;
|
||||||
|
|
|
@ -86,19 +86,28 @@ use crate::{RawStr, RawStrBuf};
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ## Serde
|
/// # (De)serialization
|
||||||
///
|
///
|
||||||
/// For convience, `Origin` implements `Serialize` and `Deserialize`.
|
/// `Origin` is both `Serialize` and `Deserialize`:
|
||||||
/// Because `Origin` has a lifetime parameter, serde requires a borrow
|
|
||||||
/// attribute for the derive macro to work. If you want to own the Uri,
|
|
||||||
/// rather than borrow from the deserializer, use `'static`.
|
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```rust
|
||||||
/// #[derive(Deserialize)]
|
/// # #[cfg(feature = "serde")] mod serde {
|
||||||
/// struct Uris<'a> {
|
/// # use _serde as serde;
|
||||||
/// #[serde(borrow)]
|
/// use serde::{Serialize, Deserialize};
|
||||||
/// origin: Origin<'a>,
|
/// use rocket::http::uri::Origin;
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize, Serialize)]
|
||||||
|
/// # #[serde(crate = "_serde")]
|
||||||
|
/// struct UriOwned {
|
||||||
|
/// uri: Origin<'static>,
|
||||||
/// }
|
/// }
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize, Serialize)]
|
||||||
|
/// # #[serde(crate = "_serde")]
|
||||||
|
/// struct UriBorrowed<'a> {
|
||||||
|
/// uri: Origin<'a>,
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Origin<'a> {
|
pub struct Origin<'a> {
|
||||||
|
@ -283,7 +292,7 @@ impl<'a> Origin<'a> {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn parse_owned(string: String) -> Result<Origin<'static>, Error<'static>> {
|
pub fn parse_owned(string: String) -> Result<Origin<'static>, Error<'static>> {
|
||||||
let origin = Origin::parse(&string).map_err(|e| e.into_owned())?;
|
let origin = Origin::parse(&string).map_err(|e| e.into_owned())?;
|
||||||
debug_assert!(origin.source.is_some(), "Origin source parsed w/o source");
|
debug_assert!(origin.source.is_some(), "Origin parsed w/o source");
|
||||||
|
|
||||||
Ok(Origin {
|
Ok(Origin {
|
||||||
path: origin.path.into_owned(),
|
path: origin.path.into_owned(),
|
||||||
|
@ -501,46 +510,7 @@ impl std::fmt::Display for Origin<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
impl_serde!(Origin<'a>, "an origin-form URI");
|
||||||
mod serde {
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use super::Origin;
|
|
||||||
use _serde::{ser::{Serialize, Serializer}, de::{Deserialize, Deserializer, Error, Visitor}};
|
|
||||||
|
|
||||||
impl<'a> Serialize for Origin<'a> {
|
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
serializer.serialize_str(&self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OriginVistor;
|
|
||||||
|
|
||||||
impl<'a> Visitor<'a> for OriginVistor {
|
|
||||||
type Value = Origin<'a>;
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(formatter, "origin Uri")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
|
||||||
Origin::parse_owned(v.to_string()).map_err(E::custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
|
|
||||||
Origin::parse_owned(v).map_err(E::custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_borrowed_str<E: Error>(self, v: &'a str) -> Result<Self::Value, E> {
|
|
||||||
Origin::parse(v).map_err(E::custom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'de: 'a> Deserialize<'de> for Origin<'a> {
|
|
||||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
deserializer.deserialize_str(OriginVistor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -46,19 +46,28 @@ use crate::parse::{Extent, IndexedStr};
|
||||||
/// `Reference` when possible, as is demonstrated above for `absolute` and
|
/// `Reference` when possible, as is demonstrated above for `absolute` and
|
||||||
/// `origin`.
|
/// `origin`.
|
||||||
///
|
///
|
||||||
/// ## Serde
|
/// # (De)serialization
|
||||||
///
|
///
|
||||||
/// For convience, `Reference` implements `Serialize` and `Deserialize`.
|
/// `Reference` is both `Serialize` and `Deserialize`:
|
||||||
/// Because `Reference` has a lifetime parameter, serde requires a borrow
|
|
||||||
/// attribute for the derive macro to work. If you want to own the Uri,
|
|
||||||
/// rather than borrow from the deserializer, use `'static`.
|
|
||||||
///
|
///
|
||||||
/// ```ignore
|
/// ```rust
|
||||||
/// #[derive(Deserialize)]
|
/// # #[cfg(feature = "serde")] mod serde {
|
||||||
/// struct Uris<'a> {
|
/// # use _serde as serde;
|
||||||
/// #[serde(borrow)]
|
/// use serde::{Serialize, Deserialize};
|
||||||
/// reference: Reference<'a>,
|
/// use rocket::http::uri::Reference;
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize, Serialize)]
|
||||||
|
/// # #[serde(crate = "_serde")]
|
||||||
|
/// struct UriOwned {
|
||||||
|
/// uri: Reference<'static>,
|
||||||
/// }
|
/// }
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize, Serialize)]
|
||||||
|
/// # #[serde(crate = "_serde")]
|
||||||
|
/// struct UriBorrowed<'a> {
|
||||||
|
/// uri: Reference<'a>,
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Reference<'a> {
|
pub struct Reference<'a> {
|
||||||
|
@ -159,14 +168,12 @@ impl<'a> Reference<'a> {
|
||||||
/// assert_eq!(uri.query().unwrap(), "query");
|
/// assert_eq!(uri.query().unwrap(), "query");
|
||||||
/// assert_eq!(uri.fragment().unwrap(), "fragment");
|
/// assert_eq!(uri.fragment().unwrap(), "fragment");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn parse(string: &'a str) -> Result<Self, Error<'a>> {
|
pub fn parse(string: &'a str) -> Result<Reference<'a>, Error<'a>> {
|
||||||
crate::parse::uri::reference_from_str(string)
|
crate::parse::uri::reference_from_str(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the string `string` into a `Reference`. Never allocates on
|
/// Parses the string `string` into a `Reference`. Allocates minimally on
|
||||||
/// success. May allocate on error.
|
/// success and error.
|
||||||
///
|
|
||||||
/// TODO: Avoid allocation
|
|
||||||
///
|
///
|
||||||
/// This method should be used instead of [`Reference::parse()`] when the
|
/// This method should be used instead of [`Reference::parse()`] when the
|
||||||
/// source URI is already a `String`. Returns an `Error` if `string` is not
|
/// source URI is already a `String`. Returns an `Error` if `string` is not
|
||||||
|
@ -184,9 +191,10 @@ impl<'a> Reference<'a> {
|
||||||
/// assert_eq!(uri.query().unwrap(), "2");
|
/// assert_eq!(uri.query().unwrap(), "2");
|
||||||
/// assert_eq!(uri.fragment().unwrap(), "3");
|
/// assert_eq!(uri.fragment().unwrap(), "3");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn parse_owned(string: String) -> Result<Self, Error<'a>> {
|
// TODO: Avoid all allocations.
|
||||||
|
pub fn parse_owned(string: String) -> Result<Reference<'static>, Error<'static>> {
|
||||||
let uri_ref = Reference::parse(&string).map_err(|e| e.into_owned())?;
|
let uri_ref = Reference::parse(&string).map_err(|e| e.into_owned())?;
|
||||||
debug_assert!(uri_ref.source.is_some(), "UriRef parsed w/o source");
|
debug_assert!(uri_ref.source.is_some(), "Reference parsed w/o source");
|
||||||
|
|
||||||
Ok(Reference {
|
Ok(Reference {
|
||||||
scheme: uri_ref.scheme.into_owned(),
|
scheme: uri_ref.scheme.into_owned(),
|
||||||
|
@ -548,43 +556,4 @@ impl std::fmt::Display for Reference<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
impl_serde!(Reference<'a>, "a URI-reference");
|
||||||
mod serde {
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use super::Reference;
|
|
||||||
use _serde::{ser::{Serialize, Serializer}, de::{Deserialize, Deserializer, Error, Visitor}};
|
|
||||||
|
|
||||||
impl<'a> Serialize for Reference<'a> {
|
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
serializer.serialize_str(&self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ReferenceVistor;
|
|
||||||
|
|
||||||
impl<'a> Visitor<'a> for ReferenceVistor {
|
|
||||||
type Value = Reference<'a>;
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(formatter, "reference Uri")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
|
||||||
Reference::parse_owned(v.to_string()).map_err(Error::custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
|
|
||||||
Reference::parse_owned(v).map_err(Error::custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_borrowed_str<E: Error>(self, v: &'a str) -> Result<Self::Value, E> {
|
|
||||||
Reference::parse(v).map_err(Error::custom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'de: 'a> Deserialize<'de> for Reference<'a> {
|
|
||||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
deserializer.deserialize_str(ReferenceVistor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -33,11 +33,6 @@ use crate::uri::error::{Error, TryFromUriError};
|
||||||
/// methods of the internal structure.
|
/// methods of the internal structure.
|
||||||
///
|
///
|
||||||
/// [RFC 7230]: https://tools.ietf.org/html/rfc7230
|
/// [RFC 7230]: https://tools.ietf.org/html/rfc7230
|
||||||
///
|
|
||||||
/// ## Serde
|
|
||||||
/// Parsing a string into a `Uri` is ambgious, so `Uri` does not implement `Serialize`
|
|
||||||
/// or `Deserialize`, although all of the variants do. See [`Uri::parse_any`] for more
|
|
||||||
/// information
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Uri<'a> {
|
pub enum Uri<'a> {
|
||||||
/// An asterisk: exactly `*`.
|
/// An asterisk: exactly `*`.
|
||||||
|
|
|
@ -1,32 +1,25 @@
|
||||||
use figment::{Figment, providers::Serialized};
|
|
||||||
use rocket::{Config, uri};
|
|
||||||
use rocket_http::uri::{Absolute, Asterisk, Authority, Origin, Reference};
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
use figment::{Figment, providers::Serialized};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
use rocket::{Config, uri};
|
||||||
|
use rocket::http::uri::{Absolute, Asterisk, Authority, Origin, Reference};
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||||
struct UriContainer<'a> {
|
struct UriContainer<'a> {
|
||||||
asterisk: Asterisk,
|
asterisk: Asterisk,
|
||||||
#[serde(borrow)]
|
|
||||||
origin: Origin<'a>,
|
origin: Origin<'a>,
|
||||||
#[serde(borrow)]
|
|
||||||
authority: Authority<'a>,
|
authority: Authority<'a>,
|
||||||
#[serde(borrow)]
|
|
||||||
absolute: Absolute<'a>,
|
absolute: Absolute<'a>,
|
||||||
#[serde(borrow)]
|
|
||||||
reference: Reference<'a>,
|
reference: Reference<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||||
struct UriContainerOwned {
|
struct UriContainerOwned {
|
||||||
asterisk: Asterisk,
|
asterisk: Asterisk,
|
||||||
#[serde(borrow)]
|
|
||||||
origin: Origin<'static>,
|
origin: Origin<'static>,
|
||||||
#[serde(borrow)]
|
|
||||||
authority: Authority<'static>,
|
authority: Authority<'static>,
|
||||||
#[serde(borrow)]
|
|
||||||
absolute: Absolute<'static>,
|
absolute: Absolute<'static>,
|
||||||
#[serde(borrow)]
|
|
||||||
reference: Reference<'static>,
|
reference: Reference<'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,22 +44,6 @@ fn uri_serde() {
|
||||||
reference: uri!("https://rocket.rs:8000/index.html").into(),
|
reference: uri!("https://rocket.rs:8000/index.html").into(),
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn uri_serde_owned() {
|
|
||||||
figment::Jail::expect_with(|jail| {
|
|
||||||
jail.create_file("Rocket.toml", r#"
|
|
||||||
[default]
|
|
||||||
asterisk = "*"
|
|
||||||
origin = "/foo/bar?baz"
|
|
||||||
authority = "user:pass@rocket.rs:80"
|
|
||||||
absolute = "https://rocket.rs/foo/bar"
|
|
||||||
reference = "https://rocket.rs:8000/index.html"
|
|
||||||
"#)?;
|
|
||||||
|
|
||||||
let uris: UriContainerOwned = Config::figment().extract()?;
|
let uris: UriContainerOwned = Config::figment().extract()?;
|
||||||
assert_eq!(uris, UriContainerOwned {
|
assert_eq!(uris, UriContainerOwned {
|
||||||
asterisk: Asterisk,
|
asterisk: Asterisk,
|
||||||
|
@ -90,7 +67,7 @@ fn uri_serde_round_trip() {
|
||||||
reference: uri!("https://rocket.rs:8000/index.html").into(),
|
reference: uri!("https://rocket.rs:8000/index.html").into(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let uris: UriContainer<'_> = tmp.extract().expect("Parsing failed");
|
let uris: UriContainer<'_> = tmp.extract().unwrap();
|
||||||
assert_eq!(uris, UriContainer {
|
assert_eq!(uris, UriContainer {
|
||||||
asterisk: Asterisk,
|
asterisk: Asterisk,
|
||||||
origin: uri!("/foo/bar?baz"),
|
origin: uri!("/foo/bar?baz"),
|
||||||
|
@ -98,4 +75,39 @@ fn uri_serde_round_trip() {
|
||||||
absolute: uri!("https://rocket.rs/foo/bar"),
|
absolute: uri!("https://rocket.rs/foo/bar"),
|
||||||
reference: uri!("https://rocket.rs:8000/index.html").into(),
|
reference: uri!("https://rocket.rs:8000/index.html").into(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let uris: UriContainerOwned = tmp.extract().unwrap();
|
||||||
|
assert_eq!(uris, UriContainerOwned {
|
||||||
|
asterisk: Asterisk,
|
||||||
|
origin: uri!("/foo/bar?baz"),
|
||||||
|
authority: uri!("user:pass@rocket.rs:80"),
|
||||||
|
absolute: uri!("https://rocket.rs/foo/bar"),
|
||||||
|
reference: uri!("https://rocket.rs:8000/index.html").into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let tmp = Figment::from(Serialized::defaults(UriContainerOwned {
|
||||||
|
asterisk: Asterisk,
|
||||||
|
origin: uri!("/foo/bar?baz"),
|
||||||
|
authority: uri!("user:pass@rocket.rs:80"),
|
||||||
|
absolute: uri!("https://rocket.rs/foo/bar"),
|
||||||
|
reference: uri!("https://rocket.rs:8000/index.html").into(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let uris: UriContainer<'_> = tmp.extract().unwrap();
|
||||||
|
assert_eq!(uris, UriContainer {
|
||||||
|
asterisk: Asterisk,
|
||||||
|
origin: uri!("/foo/bar?baz"),
|
||||||
|
authority: uri!("user:pass@rocket.rs:80"),
|
||||||
|
absolute: uri!("https://rocket.rs/foo/bar"),
|
||||||
|
reference: uri!("https://rocket.rs:8000/index.html").into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let uris: UriContainerOwned = tmp.extract().unwrap();
|
||||||
|
assert_eq!(uris, UriContainerOwned {
|
||||||
|
asterisk: Asterisk,
|
||||||
|
origin: uri!("/foo/bar?baz"),
|
||||||
|
authority: uri!("user:pass@rocket.rs:80"),
|
||||||
|
absolute: uri!("https://rocket.rs/foo/bar"),
|
||||||
|
reference: uri!("https://rocket.rs:8000/index.html").into(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue