mirror of https://github.com/rwf2/Rocket.git
Handle segments parameters in 'uri!'.
This commit also moves the 'uri' module into its own directory, includes the beginning of the 'FromUriParam' trait, and abandons the 'url' crate in favor of 'percent_encoding' for percent encoding.
This commit is contained in:
parent
5efc4b1096
commit
3bf577db6e
|
@ -106,7 +106,7 @@ pub fn uri_internal(
|
|||
// Generate the statements to typecheck each parameter. First, the mount.
|
||||
let mut argument_stmts = vec![];
|
||||
let mut format_assign_tokens = vec![];
|
||||
let mut fmt_string = internal.uri.node.replace('<', "{").replace('>', "}");
|
||||
let mut fmt_string = internal.uri_fmt_string();
|
||||
if let Some(mount_point) = internal.uri_params.mount_point {
|
||||
// TODO: Should all expressions, not just string literals, be allowed?
|
||||
// let as_ref = ecx.expr_method_call(span, expr, Ident::from_str("as_ref"), v![]);
|
||||
|
|
|
@ -253,4 +253,11 @@ impl InternalUriParams {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uri_fmt_string(&self) -> String {
|
||||
self.uri.node
|
||||
.replace('<', "{")
|
||||
.replace("..>", "}")
|
||||
.replace('>', "}")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
extern crate rocket;
|
||||
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rocket::http::{RawStr, Cookies};
|
||||
use rocket::http::uri::{Uri, UriDisplay};
|
||||
|
@ -70,6 +71,15 @@ fn complex<'r>(
|
|||
cookies: Cookies
|
||||
) -> &'static str { "" }
|
||||
|
||||
#[post("/a/<path..>")]
|
||||
fn segments(path: PathBuf) -> &'static str { "" }
|
||||
|
||||
#[post("/a/<id>/then/<path..>")]
|
||||
fn param_and_segments(path: PathBuf, id: usize) -> &'static str { "" }
|
||||
|
||||
#[post("/a/<id>/then/<path..>")]
|
||||
fn guarded_segments(cookies: Cookies, path: PathBuf, id: usize) -> &'static str { "" }
|
||||
|
||||
macro assert_uri_eq($($uri:expr => $expected:expr,)+) {
|
||||
$(assert_eq!($uri, Uri::from($expected));)+
|
||||
}
|
||||
|
@ -172,6 +182,23 @@ fn check_guards_ignored() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_with_segments() {
|
||||
assert_uri_eq! {
|
||||
uri!(segments: PathBuf::from("one/two/three")) => "/a/one/two/three",
|
||||
uri!(segments: path = PathBuf::from("one/two/three")) => "/a/one/two/three",
|
||||
uri!("/c", segments: PathBuf::from("one/tw o/")) => "/c/a/one/tw%20o/",
|
||||
uri!("/c", segments: path = PathBuf::from("one/tw o/")) => "/c/a/one/tw%20o/",
|
||||
uri!(segments: PathBuf::from("one/ tw?o/")) => "/a/one/%20tw%3Fo/",
|
||||
uri!(param_and_segments: 10usize, PathBuf::from("a/b")) => "/a/10/then/a/b",
|
||||
uri!(param_and_segments: id = 10usize, path = PathBuf::from("a/b"))
|
||||
=> "/a/10/then/a/b",
|
||||
uri!(guarded_segments: 10usize, PathBuf::from("a/b")) => "/a/10/then/a/b",
|
||||
uri!(guarded_segments: id = 10usize, path = PathBuf::from("a/b"))
|
||||
=> "/a/10/then/a/b",
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_complex() {
|
||||
assert_uri_eq! {
|
||||
|
|
|
@ -20,7 +20,7 @@ tls = ["rustls", "hyper-sync-rustls"]
|
|||
[dependencies]
|
||||
yansi = { version = "0.3.3", features = ["nightly"] }
|
||||
log = "0.3"
|
||||
url = "1"
|
||||
percent-encoding = "1"
|
||||
toml = "0.4.2"
|
||||
num_cpus = "1"
|
||||
state = "0.3.1"
|
||||
|
|
|
@ -6,8 +6,6 @@ use std::ascii::AsciiExt;
|
|||
use std::str::Utf8Error;
|
||||
use std::fmt;
|
||||
|
||||
use url;
|
||||
|
||||
use http::uncased::UncasedStr;
|
||||
|
||||
/// A reference to a string inside of a raw HTTP message.
|
||||
|
@ -98,7 +96,7 @@ impl RawStr {
|
|||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn percent_decode(&self) -> Result<Cow<str>, Utf8Error> {
|
||||
url::percent_encoding::percent_decode(self.as_bytes()).decode_utf8()
|
||||
::percent_encoding::percent_decode(self.as_bytes()).decode_utf8()
|
||||
}
|
||||
|
||||
/// Returns a percent-decoded version of the string. Any invalid UTF-8
|
||||
|
@ -133,7 +131,7 @@ impl RawStr {
|
|||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn percent_decode_lossy(&self) -> Cow<str> {
|
||||
url::percent_encoding::percent_decode(self.as_bytes()).decode_utf8_lossy()
|
||||
::percent_encoding::percent_decode(self.as_bytes()).decode_utf8_lossy()
|
||||
}
|
||||
|
||||
/// Returns a URL-decoded version of the string. This is identical to
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use http::RawStr;
|
||||
use http::uri::UriDisplay;
|
||||
|
||||
trait FromUriParam<T>: UriDisplay {
|
||||
type Target: UriDisplay;
|
||||
fn from_uri_param(param: T) -> Self::Target;
|
||||
}
|
||||
|
||||
impl<T: UriDisplay> FromUriParam<T> for T {
|
||||
type Target = T;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: T) -> T { param }
|
||||
}
|
||||
|
||||
impl<'a, T: UriDisplay> FromUriParam<&'a T> for T {
|
||||
type Target = &'a T;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'a T) -> &'a T { param }
|
||||
}
|
||||
|
||||
impl<'a, T: UriDisplay> FromUriParam<&'a mut T> for T {
|
||||
type Target = &'a mut T;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'a mut T) -> &'a mut T { param }
|
||||
}
|
||||
|
||||
impl<'a> FromUriParam<&'a str> for String {
|
||||
type Target = &'a str;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'a str) -> &'a str { param }
|
||||
}
|
||||
|
||||
impl<'a, 'b> FromUriParam<&'a str> for &'b RawStr {
|
||||
type Target = &'a str;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'a str) -> &'a str { param }
|
||||
}
|
||||
|
||||
impl<'a> FromUriParam<&'a Path> for PathBuf {
|
||||
type Target = &'a Path;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'a Path) -> &'a Path { param }
|
||||
}
|
||||
|
||||
impl<'a> FromUriParam<&'a str> for PathBuf {
|
||||
type Target = &'a Path;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'a str) -> &'a Path {
|
||||
Path::new(param)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
//! Types for absolute URIs and traits for URI display.
|
||||
|
||||
mod uri;
|
||||
mod uri_display;
|
||||
mod from_uri_param;
|
||||
|
||||
pub use self::uri::*;
|
||||
pub use self::uri_display::*;
|
||||
pub use self::from_uri_param::*;
|
|
@ -1,15 +1,9 @@
|
|||
//! Borrowed and owned string types for absolute URIs.
|
||||
|
||||
use std::fmt;
|
||||
use std::convert::From;
|
||||
use std::borrow::Cow;
|
||||
use std::str::Utf8Error;
|
||||
use std::sync::atomic::{AtomicIsize, Ordering};
|
||||
|
||||
use http::RawStr;
|
||||
|
||||
use url;
|
||||
|
||||
/// Index (start, end) into a string, to prevent borrowing.
|
||||
type Index = (usize, usize);
|
||||
|
||||
|
@ -229,7 +223,7 @@ impl<'a> Uri<'a> {
|
|||
/// assert_eq!(decoded_path, "/Hello, world!");
|
||||
/// ```
|
||||
pub fn percent_decode(string: &[u8]) -> Result<Cow<str>, Utf8Error> {
|
||||
let decoder = url::percent_encoding::percent_decode(string);
|
||||
let decoder = ::percent_encoding::percent_decode(string);
|
||||
decoder.decode_utf8()
|
||||
}
|
||||
|
||||
|
@ -247,7 +241,7 @@ impl<'a> Uri<'a> {
|
|||
/// assert_eq!(decoded_path, "/Hello, world!");
|
||||
/// ```
|
||||
pub fn percent_decode_lossy(string: &[u8]) -> Cow<str> {
|
||||
let decoder = url::percent_encoding::percent_decode(string);
|
||||
let decoder = ::percent_encoding::percent_decode(string);
|
||||
decoder.decode_utf8_lossy()
|
||||
}
|
||||
|
||||
|
@ -264,8 +258,8 @@ impl<'a> Uri<'a> {
|
|||
/// assert_eq!(encoded, "hello%3Fa=%3Cb%3Ehi%3C%2Fb%3E");
|
||||
/// ```
|
||||
pub fn percent_encode(string: &str) -> Cow<str> {
|
||||
let set = url::percent_encoding::PATH_SEGMENT_ENCODE_SET;
|
||||
url::percent_encoding::utf8_percent_encode(string, set).into()
|
||||
let set = ::percent_encoding::PATH_SEGMENT_ENCODE_SET;
|
||||
::percent_encoding::utf8_percent_encode(string, set).into()
|
||||
}
|
||||
|
||||
/// Returns the inner string of this URI.
|
||||
|
@ -348,305 +342,6 @@ impl<'a> fmt::Display for Uri<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Trait implemented by types that can be displayed as part of a URI.
|
||||
///
|
||||
/// Types implementing this trait can be displayed in a URI-safe manner. Unlike
|
||||
/// `Display`, the string written by a `UriDisplay` implementation must be
|
||||
/// URI-safe. In practice, this means that the string must either be
|
||||
/// percent-encoded or consist only of characters that are alphanumeric, "-",
|
||||
/// ".", "_", or "~" - the "unreserved" characters.
|
||||
///
|
||||
/// # Code Generation
|
||||
///
|
||||
/// When the `uri!` macro is used to generate a URI for a route, the types for
|
||||
/// the route's URI parameters must implement `UriDisplay`. The `UriDisplay`
|
||||
/// implementation for these types is used when generating the URI.
|
||||
///
|
||||
/// To illustrate `UriDisplay`'s role in code generation for `uri!`, consider
|
||||
/// the following fictional route and struct definition:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// struct Value { .. };
|
||||
///
|
||||
/// #[get("/item/<id>/<value>")]
|
||||
/// fn get_item(id: i32, value: Value) -> T { .. }
|
||||
/// ```
|
||||
///
|
||||
/// A URI for this route can be generated as follows:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // With unnamed parameters.
|
||||
/// uri!(get_item: 100, Value { .. });
|
||||
///
|
||||
/// // With named parameters.
|
||||
/// uri!(get_item: id = 100, value = Value { .. });
|
||||
/// ```
|
||||
///
|
||||
/// After verifying parameters and their types, Rocket will generate code
|
||||
/// similar to the following:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// format!("/item/{id}/{value}",
|
||||
/// id = &100 as &UriDisplay,
|
||||
/// value = &Value { .. } as &UriDisplay);
|
||||
/// ```
|
||||
///
|
||||
/// For this expression to typecheck, both `i32` and `Value` must implement
|
||||
/// `UriDisplay`. As can be seen, the implementation will be used to display the
|
||||
/// value in a URI-safe manner.
|
||||
///
|
||||
/// [`uri!`]: /rocket_codegen/#procedural-macros
|
||||
///
|
||||
/// # Provided Implementations
|
||||
///
|
||||
/// Rocket implements `UriDisplay` for several built-in types. Their behavior is
|
||||
/// documented here.
|
||||
///
|
||||
/// * **i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64, bool,
|
||||
/// IpAddr, Ipv4Addr, Ipv6Addr**
|
||||
///
|
||||
/// The implementation of `UriDisplay` for these types is identical to the
|
||||
/// `Display` implementation.
|
||||
///
|
||||
/// * **[`&RawStr`](/rocket/http/struct.RawStr.html), String, &str, Cow<str>**
|
||||
///
|
||||
/// The string is percent encoded.
|
||||
///
|
||||
/// * **&T, &mut T** _where_ **T: UriDisplay**
|
||||
///
|
||||
/// Uses the implementation of `UriDisplay` for `T`.
|
||||
///
|
||||
/// # Implementing
|
||||
///
|
||||
/// Implementing `UriDisplay` is similar to implementing `Display` with the
|
||||
/// caveat that extra care must be taken to ensure that the written string is
|
||||
/// URI-safe. As mentioned before, in practice, this means that the string must
|
||||
/// either be percent-encoded or consist only of characters that are
|
||||
/// alphanumeric, "-", ".", "_", or "~".
|
||||
///
|
||||
/// When manually implementing `UriDisplay` for your types, you should defer to
|
||||
/// existing implementations of `UriDisplay` as much as possible. In the example
|
||||
/// below, for instance, `Name`'s implementation defers to `String`'s
|
||||
/// implementation. To percent-encode a string, use [`Uri::percent_encode()`].
|
||||
///
|
||||
/// [`Uri::percent_encode()`]: https://api.rocket.rs/rocket/http/uri/struct.Uri.html#method.percent_encode
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// The following snippet consists of a `Name` type that implements both
|
||||
/// `FromParam` and `UriDisplay`. The `FromParam` implementation allows `Name`
|
||||
/// to be used as the target type of a dynamic parameter, while the `UriDisplay`
|
||||
/// implementation allows URIs to be generated for routes with `Name` as a
|
||||
/// dynamic parameter type.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![feature(plugin, decl_macro)]
|
||||
/// # #![plugin(rocket_codegen)]
|
||||
/// # extern crate rocket;
|
||||
/// # fn main() { }
|
||||
/// use rocket::http::RawStr;
|
||||
/// use rocket::request::FromParam;
|
||||
///
|
||||
/// struct Name(String);
|
||||
///
|
||||
/// impl<'r> FromParam<'r> for Name {
|
||||
/// type Error = &'r RawStr;
|
||||
///
|
||||
/// /// Validates parameters that contain no spaces.
|
||||
/// fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
|
||||
/// let decoded = param.percent_decode().map_err(|_| param)?;
|
||||
/// match decoded.contains(' ') {
|
||||
/// false => Ok(Name(decoded.into_owned())),
|
||||
/// true => Err(param),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// use std::fmt;
|
||||
/// use rocket::http::uri::UriDisplay;
|
||||
/// use rocket::response::Redirect;
|
||||
///
|
||||
/// impl UriDisplay for Name {
|
||||
/// /// Delegates to the `UriDisplay` implementation for `String` to ensure
|
||||
/// /// that the written string is URI-safe. In this case, the string will
|
||||
/// /// be percent encoded.
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// UriDisplay::fmt(&self.0, f)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[get("/name/<name>")]
|
||||
/// fn redirector(name: Name) -> Redirect {
|
||||
/// Redirect::to(uri!(real: name).as_str())
|
||||
/// }
|
||||
///
|
||||
/// #[get("/<name>")]
|
||||
/// fn real(name: Name) -> String {
|
||||
/// format!("Hello, {}!", name.0)
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
// FIXME: Put this more narrative-like text in the guide. Fix it up beforehand.
|
||||
//
|
||||
// Now we have the following routes. The first route accepts a URI parameter of
|
||||
// type `Name` and redirects to the second route:
|
||||
//
|
||||
// ```rust
|
||||
// # #![feature(plugin, decl_macro)]
|
||||
// # #![plugin(rocket_codegen)]
|
||||
// # extern crate rocket;
|
||||
// # use rocket::request::FromParam;
|
||||
// # use rocket::http::RawStr;
|
||||
// # struct Name(String);
|
||||
// # impl Name {
|
||||
// # fn new(name: String) -> Option<Name> {
|
||||
// # if !name.contains(' ') { Some(name) } else { None }
|
||||
// # }
|
||||
// # }
|
||||
// # impl<'r> FromParam<'r> for Name {
|
||||
// # type Error = &'r RawStr;
|
||||
// # fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
|
||||
// # Name::new(param.percent_decode().into_owned()).ok_or(param)
|
||||
// # }
|
||||
// # }
|
||||
// use rocket::response::Redirect;
|
||||
//
|
||||
// #[get("/name/<name>")]
|
||||
// fn redirector(name: Name) -> Redirect {
|
||||
// Redirect::to(&format!("/{}", name.0))
|
||||
// }
|
||||
//
|
||||
// #[get("/<name>")]
|
||||
// fn real(name: Name) -> String {
|
||||
// format!("Hello, {}!", name.0)
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// The redirection in the `redirector` route creates a URI that should lead to
|
||||
// the `real` route. But it does this in an ad-hoc manner. What happens if the
|
||||
// `real` route changes? At best, the redirection will fail and the user will
|
||||
// receive a 404.
|
||||
//
|
||||
// To prevent this kind of issue the `uri!` macro can be used, passing in the
|
||||
// `name` received from the route. When the `Name` type is used along with the
|
||||
// `uri!` macro, the `UriDisplay` trait must be implemented. Both of these
|
||||
// steps are done in the example below:
|
||||
//
|
||||
// ```rust
|
||||
// # #![feature(plugin, decl_macro)]
|
||||
// # #![plugin(rocket_codegen)]
|
||||
// # extern crate rocket;
|
||||
// # use rocket::request::FromParam;
|
||||
// # use rocket::http::RawStr;
|
||||
// # struct Name(String);
|
||||
// # impl Name {
|
||||
// # fn new(name: String) -> Option<Name> {
|
||||
// # if !name.contains(' ') { Some(name) } else { None }
|
||||
// # }
|
||||
// # }
|
||||
// # impl<'r> FromParam<'r> for Name {
|
||||
// # type Error = &'r RawStr;
|
||||
// # fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
|
||||
// # Name::new(param.percent_decode().into_owned()).ok_or(param)
|
||||
// # }
|
||||
// # }
|
||||
// use std::fmt;
|
||||
// use rocket::http::uri::UriDisplay;
|
||||
// use rocket::response::Redirect;
|
||||
//
|
||||
// impl UriDisplay for Name {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// UriDisplay::fmt(&self.0, f)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[get("/name/<name>")]
|
||||
// fn redirector(name: Name) -> Redirect {
|
||||
// Redirect::to(uri!(real: name).as_str())
|
||||
// }
|
||||
//
|
||||
// #[get("/<name>")]
|
||||
// fn real(name: Name) -> String {
|
||||
// format!("Hello, {}!", name.0)
|
||||
// }
|
||||
// ```
|
||||
pub trait UriDisplay {
|
||||
/// Formats `self` in a URI-safe manner using the given formatter.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for &'a UriDisplay {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
UriDisplay::fmt(*self, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl<'a> UriDisplay for &'a RawStr {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Uri::percent_encode((*self).as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl<'a> UriDisplay for &'a str {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Uri::percent_encode(self))
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl<'a> UriDisplay for Cow<'a, str> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Uri::percent_encode(self))
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl UriDisplay for String {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Uri::percent_encode(self.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_with_display {
|
||||
($($T:ty),+) => {$(
|
||||
/// This implementation is identical to the `Display` implementation.
|
||||
impl UriDisplay for $T {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
)+}
|
||||
}
|
||||
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
impl_with_display! {
|
||||
i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64, bool,
|
||||
IpAddr, Ipv4Addr, Ipv6Addr
|
||||
}
|
||||
|
||||
macro_rules! impl_for_ref {
|
||||
($($T:ty),+) => {$(
|
||||
/// Uses the implementation of `UriDisplay` for `T`.
|
||||
impl<'a, T: UriDisplay + ?Sized> UriDisplay for $T {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
UriDisplay::fmt(*self, f)
|
||||
}
|
||||
}
|
||||
)+}
|
||||
}
|
||||
|
||||
impl_for_ref!(&'a mut T, &'a T);
|
||||
|
||||
/// Iterator over the segments of an absolute URI path. Skips empty segments.
|
||||
///
|
||||
/// ### Examples
|
|
@ -0,0 +1,248 @@
|
|||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use http::RawStr;
|
||||
use http::uri::Uri;
|
||||
|
||||
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
|
||||
|
||||
define_encode_set! {
|
||||
/// This encode set is used for strings where '/' characters are known to be
|
||||
/// safe; all other special path segment characters are encoded.
|
||||
pub PATH_ENCODE_SET = [DEFAULT_ENCODE_SET] | {'%'}
|
||||
}
|
||||
|
||||
/// Trait implemented by types that can be displayed as part of a URI.
|
||||
///
|
||||
/// Types implementing this trait can be displayed in a URI-safe manner. Unlike
|
||||
/// `Display`, the string written by a `UriDisplay` implementation must be
|
||||
/// URI-safe. In practice, this means that the string must either be
|
||||
/// percent-encoded or consist only of characters that are alphanumeric, "-",
|
||||
/// ".", "_", or "~" - the "unreserved" characters.
|
||||
///
|
||||
/// # Code Generation
|
||||
///
|
||||
/// When the `uri!` macro is used to generate a URI for a route, the types for
|
||||
/// the route's URI parameters must implement `UriDisplay`. The `UriDisplay`
|
||||
/// implementation for these types is used when generating the URI.
|
||||
///
|
||||
/// To illustrate `UriDisplay`'s role in code generation for `uri!`, consider
|
||||
/// the following fictional route and struct definition:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// struct Value { .. };
|
||||
///
|
||||
/// #[get("/item/<id>/<value>")]
|
||||
/// fn get_item(id: i32, value: Value) -> T { .. }
|
||||
/// ```
|
||||
///
|
||||
/// A URI for this route can be generated as follows:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // With unnamed parameters.
|
||||
/// uri!(get_item: 100, Value { .. });
|
||||
///
|
||||
/// // With named parameters.
|
||||
/// uri!(get_item: id = 100, value = Value { .. });
|
||||
/// ```
|
||||
///
|
||||
/// After verifying parameters and their types, Rocket will generate code
|
||||
/// similar to the following:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// format!("/item/{id}/{value}",
|
||||
/// id = &100 as &UriDisplay,
|
||||
/// value = &Value { .. } as &UriDisplay);
|
||||
/// ```
|
||||
///
|
||||
/// For this expression to typecheck, both `i32` and `Value` must implement
|
||||
/// `UriDisplay`. As can be seen, the implementation will be used to display the
|
||||
/// value in a URI-safe manner.
|
||||
///
|
||||
/// [`uri!`]: /rocket_codegen/#procedural-macros
|
||||
///
|
||||
/// # Provided Implementations
|
||||
///
|
||||
/// Rocket implements `UriDisplay` for several built-in types. Their behavior is
|
||||
/// documented here.
|
||||
///
|
||||
/// * **i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64, bool,
|
||||
/// IpAddr, Ipv4Addr, Ipv6Addr**
|
||||
///
|
||||
/// The implementation of `UriDisplay` for these types is identical to the
|
||||
/// `Display` implementation.
|
||||
///
|
||||
/// * **[`&RawStr`](/rocket/http/struct.RawStr.html), String, &str, Cow<str>**
|
||||
///
|
||||
/// The string is percent encoded.
|
||||
///
|
||||
/// * **&T, &mut T** _where_ **T: UriDisplay**
|
||||
///
|
||||
/// Uses the implementation of `UriDisplay` for `T`.
|
||||
///
|
||||
/// # Implementing
|
||||
///
|
||||
/// Implementing `UriDisplay` is similar to implementing `Display` with the
|
||||
/// caveat that extra care must be taken to ensure that the written string is
|
||||
/// URI-safe. As mentioned before, in practice, this means that the string must
|
||||
/// either be percent-encoded or consist only of characters that are
|
||||
/// alphanumeric, "-", ".", "_", or "~".
|
||||
///
|
||||
/// When manually implementing `UriDisplay` for your types, you should defer to
|
||||
/// existing implementations of `UriDisplay` as much as possible. In the example
|
||||
/// below, for instance, `Name`'s implementation defers to `String`'s
|
||||
/// implementation. To percent-encode a string, use [`Uri::percent_encode()`].
|
||||
///
|
||||
/// [`Uri::percent_encode()`]: https://api.rocket.rs/rocket/http/uri/struct.Uri.html#method.percent_encode
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// The following snippet consists of a `Name` type that implements both
|
||||
/// `FromParam` and `UriDisplay`. The `FromParam` implementation allows `Name`
|
||||
/// to be used as the target type of a dynamic parameter, while the `UriDisplay`
|
||||
/// implementation allows URIs to be generated for routes with `Name` as a
|
||||
/// dynamic parameter type.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![feature(plugin, decl_macro)]
|
||||
/// # #![plugin(rocket_codegen)]
|
||||
/// # extern crate rocket;
|
||||
/// # fn main() { }
|
||||
/// use rocket::http::RawStr;
|
||||
/// use rocket::request::FromParam;
|
||||
///
|
||||
/// struct Name(String);
|
||||
///
|
||||
/// impl<'r> FromParam<'r> for Name {
|
||||
/// type Error = &'r RawStr;
|
||||
///
|
||||
/// /// Validates parameters that contain no spaces.
|
||||
/// fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
|
||||
/// let decoded = param.percent_decode().map_err(|_| param)?;
|
||||
/// match decoded.contains(' ') {
|
||||
/// false => Ok(Name(decoded.into_owned())),
|
||||
/// true => Err(param),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// use std::fmt;
|
||||
/// use rocket::http::uri::UriDisplay;
|
||||
/// use rocket::response::Redirect;
|
||||
///
|
||||
/// impl UriDisplay for Name {
|
||||
/// /// Delegates to the `UriDisplay` implementation for `String` to ensure
|
||||
/// /// that the written string is URI-safe. In this case, the string will
|
||||
/// /// be percent encoded.
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// UriDisplay::fmt(&self.0, f)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[get("/name/<name>")]
|
||||
/// fn redirector(name: Name) -> Redirect {
|
||||
/// Redirect::to(uri!(real: name).as_str())
|
||||
/// }
|
||||
///
|
||||
/// #[get("/<name>")]
|
||||
/// fn real(name: Name) -> String {
|
||||
/// format!("Hello, {}!", name.0)
|
||||
/// }
|
||||
/// ```
|
||||
pub trait UriDisplay {
|
||||
/// Formats `self` in a URI-safe manner using the given formatter.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for &'a UriDisplay {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
UriDisplay::fmt(*self, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl<'a> UriDisplay for &'a RawStr {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Uri::percent_encode((*self).as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl<'a> UriDisplay for &'a str {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Uri::percent_encode(self))
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl<'a> UriDisplay for Cow<'a, str> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Uri::percent_encode(self))
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl UriDisplay for String {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Uri::percent_encode(self.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes each segment in the path.
|
||||
impl UriDisplay for PathBuf {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let string = self.to_string_lossy();
|
||||
let enc: Cow<str> = utf8_percent_encode(&string, PATH_ENCODE_SET).into();
|
||||
write!(f, "{}", enc)
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes each segment in the path.
|
||||
impl<'a> UriDisplay for &'a Path {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let string = self.to_string_lossy();
|
||||
let enc: Cow<str> = utf8_percent_encode(&string, PATH_ENCODE_SET).into();
|
||||
write!(f, "{}", enc)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_with_display {
|
||||
($($T:ty),+) => {$(
|
||||
/// This implementation is identical to the `Display` implementation.
|
||||
impl UriDisplay for $T {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
)+}
|
||||
}
|
||||
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
impl_with_display! {
|
||||
i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64, bool,
|
||||
IpAddr, Ipv4Addr, Ipv6Addr
|
||||
}
|
||||
|
||||
macro_rules! impl_for_ref {
|
||||
($($T:ty),+) => {$(
|
||||
/// Uses the implementation of `UriDisplay` for `T`.
|
||||
impl<'a, T: UriDisplay + ?Sized> UriDisplay for $T {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
UriDisplay::fmt(*self, f)
|
||||
}
|
||||
}
|
||||
)+}
|
||||
}
|
||||
|
||||
impl_for_ref!(&'a mut T, &'a T);
|
|
@ -102,9 +102,9 @@
|
|||
#[macro_use] extern crate pear;
|
||||
#[cfg(feature = "tls")] extern crate rustls;
|
||||
#[cfg(feature = "tls")] extern crate hyper_sync_rustls;
|
||||
#[macro_use] extern crate percent_encoding;
|
||||
extern crate yansi;
|
||||
extern crate hyper;
|
||||
extern crate url;
|
||||
extern crate toml;
|
||||
extern crate num_cpus;
|
||||
extern crate state;
|
||||
|
|
Loading…
Reference in New Issue