mirror of https://github.com/rwf2/Rocket.git
parent
bf9de1d39e
commit
1233518733
|
@ -64,6 +64,21 @@ use crate::uri::{Authority, Path, Query, Data, Error, as_utf8_unchecked, fmt};
|
|||
/// # assert!(!Absolute::parse(uri).unwrap().is_normalized());
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ## Serde
|
||||
///
|
||||
/// For convience, `Absolute` implements `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
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Uris<'a> {
|
||||
/// #[serde(borrow)]
|
||||
/// absolute: Absolute<'a>,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Absolute<'a> {
|
||||
pub(crate) source: Option<Cow<'a, str>>,
|
||||
|
@ -115,6 +130,42 @@ impl<'a> Absolute<'a> {
|
|||
crate::parse::uri::absolute_from_str(string)
|
||||
}
|
||||
|
||||
/// Parses the string `string` into an `Absolute`. Parsing will never
|
||||
/// May allocate on 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 a valid absolute URI.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::http::uri::Absolute;
|
||||
///
|
||||
/// let source = format!("https://rocket.rs/foo/{}/three", 2);
|
||||
/// let uri = Absolute::parse_owned(source).expect("valid URI");
|
||||
/// assert_eq!(uri.authority().unwrap().host(), "rocket.rs");
|
||||
/// assert_eq!(uri.path(), "/foo/2/three");
|
||||
/// assert!(uri.query().is_none());
|
||||
/// ```
|
||||
pub fn parse_owned(string: String) -> Result<Absolute<'static>, Error<'static>> {
|
||||
let absolute = Absolute::parse(&string).map_err(|e| e.into_owned())?;
|
||||
debug_assert!(absolute.source.is_some(), "Origin source parsed w/o source");
|
||||
|
||||
let absolute = Absolute {
|
||||
scheme: absolute.scheme.into_owned(),
|
||||
authority: absolute.authority.into_owned(),
|
||||
query: absolute.query.into_owned(),
|
||||
path: absolute.path.into_owned(),
|
||||
source: Some(Cow::Owned(string)),
|
||||
};
|
||||
|
||||
Ok(absolute)
|
||||
}
|
||||
|
||||
/// Returns the scheme part of the absolute URI.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -383,6 +434,7 @@ impl<'a> Absolute<'a> {
|
|||
}
|
||||
|
||||
/// PRIVATE. Used by codegen.
|
||||
#[doc(hidden)]
|
||||
pub const fn const_new(
|
||||
scheme: &'a str,
|
||||
authority: Option<Authority<'a>>,
|
||||
|
@ -460,3 +512,44 @@ impl std::fmt::Display for Absolute<'_> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
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,4 +1,8 @@
|
|||
/// The literal `*` URI.
|
||||
///
|
||||
/// ## Serde
|
||||
///
|
||||
/// For convience, `Asterisk` implements `Serialize` and `Deserialize`.
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
|
||||
pub struct Asterisk;
|
||||
|
||||
|
@ -7,3 +11,44 @@ impl std::fmt::Display for Asterisk {
|
|||
"*".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,21 @@ use crate::uri::{as_utf8_unchecked, error::Error};
|
|||
/// ```
|
||||
///
|
||||
/// Only the host part of the URI is required.
|
||||
///
|
||||
/// ## Serde
|
||||
///
|
||||
/// For convience, `Authority` implements `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
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Uris<'a> {
|
||||
/// #[serde(borrow)]
|
||||
/// authority: Authority<'a>,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Authority<'a> {
|
||||
pub(crate) source: Option<Cow<'a, str>>,
|
||||
|
@ -109,6 +124,39 @@ impl<'a> Authority<'a> {
|
|||
crate::parse::uri::authority_from_str(string)
|
||||
}
|
||||
|
||||
/// Parses the string `string` into an `Authority`. Parsing will never allocate.
|
||||
/// May allocate on error.
|
||||
///
|
||||
/// This method should be used instead of [`Authority::parse()`] when
|
||||
/// the source URI is already a `String`. Returns an `Error` if `string` is
|
||||
/// not a valid authority URI.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate rocket;
|
||||
/// use rocket::http::uri::Authority;
|
||||
///
|
||||
/// let source = format!("rocket.rs:8000");
|
||||
/// let uri = Authority::parse_owned(source).expect("valid URI");
|
||||
/// assert!(uri.user_info().is_none());
|
||||
/// assert_eq!(uri.host(), "rocket.rs");
|
||||
/// assert_eq!(uri.port(), Some(8000));
|
||||
/// ```
|
||||
pub fn parse_owned(string: String) -> Result<Authority<'static>, Error<'static>> {
|
||||
let authority = Authority::parse(&string).map_err(|e| e.into_owned())?;
|
||||
debug_assert!(authority.source.is_some(), "Origin source parsed w/o source");
|
||||
|
||||
let authority = Authority {
|
||||
host: authority.host.into_owned(),
|
||||
user_info: authority.user_info.into_owned(),
|
||||
port: authority.port,
|
||||
source: Some(Cow::Owned(string)),
|
||||
};
|
||||
|
||||
Ok(authority)
|
||||
}
|
||||
|
||||
/// Returns the user info part of the authority URI, if there is one.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -207,3 +255,44 @@ impl<'a> TryFrom<&'a str> for Authority<'a> {
|
|||
Authority::parse(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,6 +85,21 @@ use crate::{RawStr, RawStrBuf};
|
|||
/// # assert_eq!(abnormal.into_normalized(), expected);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ## Serde
|
||||
///
|
||||
/// For convience, `Origin` implements `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
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Uris<'a> {
|
||||
/// #[serde(borrow)]
|
||||
/// origin: Origin<'a>,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Origin<'a> {
|
||||
pub(crate) source: Option<Cow<'a, str>>,
|
||||
|
@ -486,6 +501,47 @@ impl std::fmt::Display for Origin<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
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)]
|
||||
mod tests {
|
||||
use super::Origin;
|
||||
|
|
|
@ -45,6 +45,21 @@ use crate::parse::{Extent, IndexedStr};
|
|||
/// Note that `uri!()` macro _always_ prefers the more specific URI variant to
|
||||
/// `Reference` when possible, as is demonstrated above for `absolute` and
|
||||
/// `origin`.
|
||||
///
|
||||
/// ## Serde
|
||||
///
|
||||
/// For convience, `Reference` implements `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
|
||||
/// #[derive(Deserialize)]
|
||||
/// struct Uris<'a> {
|
||||
/// #[serde(borrow)]
|
||||
/// reference: Reference<'a>,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Reference<'a> {
|
||||
source: Option<Cow<'a, str>>,
|
||||
|
@ -151,6 +166,8 @@ impl<'a> Reference<'a> {
|
|||
/// Parses the string `string` into a `Reference`. Never allocates on
|
||||
/// success. May allocate on error.
|
||||
///
|
||||
/// TODO: Avoid allocation
|
||||
///
|
||||
/// This method should be used instead of [`Reference::parse()`] when the
|
||||
/// source URI is already a `String`. Returns an `Error` if `string` is not
|
||||
/// a valid URI reference.
|
||||
|
@ -530,3 +547,44 @@ impl std::fmt::Display for Reference<'_> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
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,6 +33,11 @@ use crate::uri::error::{Error, TryFromUriError};
|
|||
/// methods of the internal structure.
|
||||
///
|
||||
/// [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)]
|
||||
pub enum Uri<'a> {
|
||||
/// An asterisk: exactly `*`.
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
use figment::{Figment, providers::Serialized};
|
||||
use rocket::{Config, uri};
|
||||
use rocket_http::uri::{Absolute, Asterisk, Authority, Origin, Reference};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||
struct UriContainer<'a> {
|
||||
asterisk: Asterisk,
|
||||
#[serde(borrow)]
|
||||
origin: Origin<'a>,
|
||||
#[serde(borrow)]
|
||||
authority: Authority<'a>,
|
||||
#[serde(borrow)]
|
||||
absolute: Absolute<'a>,
|
||||
#[serde(borrow)]
|
||||
reference: Reference<'a>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||
struct UriContainerOwned {
|
||||
asterisk: Asterisk,
|
||||
#[serde(borrow)]
|
||||
origin: Origin<'static>,
|
||||
#[serde(borrow)]
|
||||
authority: Authority<'static>,
|
||||
#[serde(borrow)]
|
||||
absolute: Absolute<'static>,
|
||||
#[serde(borrow)]
|
||||
reference: Reference<'static>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uri_serde() {
|
||||
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: UriContainer<'_> = Config::figment().extract()?;
|
||||
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(),
|
||||
});
|
||||
|
||||
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()?;
|
||||
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(),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uri_serde_round_trip() {
|
||||
let tmp = Figment::from(Serialized::defaults(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: UriContainer<'_> = tmp.extract().expect("Parsing failed");
|
||||
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(),
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue