mirror of https://github.com/rwf2/Rocket.git
parent
50a635ed8e
commit
d7933dd6fd
|
@ -32,10 +32,35 @@ crate fn _uri_macro(input: TokenStream) -> Result<TokenStream> {
|
|||
Ok(quote!(#path!(#input2)).into())
|
||||
}
|
||||
|
||||
fn extract_exprs(internal: &InternalUriParams) -> Result<Vec<&Expr>> {
|
||||
fn extract_exprs<'a>(internal: &'a InternalUriParams) -> Result<(
|
||||
impl Iterator<Item = (&'a Ident, &'a Type, &'a Expr)>,
|
||||
impl Iterator<Item = (&'a Ident, &'a Type, &'a ArgExpr)>,
|
||||
)>
|
||||
{
|
||||
let route_name = &internal.uri_params.route_path;
|
||||
match internal.validate() {
|
||||
Validation::Ok(exprs) => Ok(exprs),
|
||||
Validation::Ok(exprs) => {
|
||||
let path_param_count = internal.route_uri.path().matches('<').count();
|
||||
for expr in exprs.iter().take(path_param_count) {
|
||||
if !expr.as_expr().is_some() {
|
||||
return Err(expr.span().unstable()
|
||||
.error("path parameters cannot be ignored"));
|
||||
}
|
||||
}
|
||||
|
||||
// Create an iterator over all `ident`, `ty`, and `expr` triples.
|
||||
let arguments = internal.fn_args.iter()
|
||||
.zip(exprs.into_iter())
|
||||
.map(|(FnArg { ident, ty }, expr)| (ident, ty, expr));
|
||||
|
||||
// Create iterators for just the path and query parts.
|
||||
let path_params = arguments.clone()
|
||||
.take(path_param_count)
|
||||
.map(|(i, t, e)| (i, t, e.unwrap_expr()));
|
||||
|
||||
let query_params = arguments.skip(path_param_count);
|
||||
Ok((path_params, query_params))
|
||||
}
|
||||
Validation::Unnamed(expected, actual) => {
|
||||
let mut diag = internal.uri_params.args_span().error(
|
||||
format!("`{}` route uri expects {} but {} supplied", quote!(#route_name),
|
||||
|
@ -125,7 +150,7 @@ fn explode_path<'a, I: Iterator<Item = (&'a Ident, &'a Type, &'a Expr)>>(
|
|||
quote!(#uri_mod::UriArgumentsKind::Dynamic(&[#(#dyn_exprs),*]))
|
||||
}
|
||||
|
||||
fn explode_query<'a, I: Iterator<Item = (&'a Ident, &'a Type, &'a Expr)>>(
|
||||
fn explode_query<'a, I: Iterator<Item = (&'a Ident, &'a Type, &'a ArgExpr)>>(
|
||||
uri: &Origin,
|
||||
bindings: &mut Vec<TokenStream2>,
|
||||
mut items: I
|
||||
|
@ -137,30 +162,38 @@ fn explode_query<'a, I: Iterator<Item = (&'a Ident, &'a Type, &'a Expr)>>(
|
|||
|
||||
let query_arg = quote!(#uri_mod::UriQueryArgument);
|
||||
let uri_display = quote!(#uri_mod::UriDisplay<#uri_mod::Query>);
|
||||
let dyn_exprs = RouteSegment::parse_query(uri)?.map(|segment| {
|
||||
let dyn_exprs = RouteSegment::parse_query(uri)?.filter_map(|segment| {
|
||||
let segment = segment.expect("segment okay; prechecked on parse");
|
||||
match segment.kind {
|
||||
Kind::Static => {
|
||||
let string = &segment.string;
|
||||
quote!(#query_arg::Raw(#string))
|
||||
}
|
||||
Kind::Single => {
|
||||
let (ident, ty, expr) = items.next().expect("one item for each dyn");
|
||||
add_binding(bindings, &ident, &ty, &expr, Source::Query);
|
||||
let name = &segment.name;
|
||||
|
||||
quote_spanned!(expr.span() =>
|
||||
#query_arg::NameValue(#name, &#ident as &dyn #uri_display)
|
||||
)
|
||||
}
|
||||
Kind::Multi => {
|
||||
let (ident, ty, expr) = items.next().expect("one item for each dyn");
|
||||
add_binding(bindings, &ident, &ty, &expr, Source::Query);
|
||||
quote_spanned!(expr.span() =>
|
||||
#query_arg::Value(&#ident as &dyn #uri_display)
|
||||
)
|
||||
}
|
||||
if segment.kind == Kind::Static {
|
||||
let string = &segment.string;
|
||||
return Some(quote!(#query_arg::Raw(#string)));
|
||||
}
|
||||
|
||||
let (ident, ty, arg_expr) = items.next().expect("one item for each dyn");
|
||||
let expr = match arg_expr.as_expr() {
|
||||
Some(expr) => expr,
|
||||
None => {
|
||||
// Force a typecheck for the `Ignoreable` trait. Note that write
|
||||
// out the path to `is_ignorable` to get the right span.
|
||||
bindings.push(quote_spanned! { arg_expr.span() =>
|
||||
rocket::http::uri::assert_ignorable::<#uri_mod::Query, #ty>();
|
||||
});
|
||||
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let name = &segment.name;
|
||||
add_binding(bindings, &ident, &ty, &expr, Source::Query);
|
||||
Some(match segment.kind {
|
||||
Kind::Single => quote_spanned! { expr.span() =>
|
||||
#query_arg::NameValue(#name, &#ident as &dyn #uri_display)
|
||||
},
|
||||
Kind::Multi => quote_spanned! { expr.span() =>
|
||||
#query_arg::Value(&#ident as &dyn #uri_display)
|
||||
},
|
||||
Kind::Static => unreachable!("Kind::Static returns early")
|
||||
})
|
||||
});
|
||||
|
||||
Some(quote!(#uri_mod::UriArgumentsKind::Dynamic(&[#(#dyn_exprs),*])))
|
||||
|
@ -182,17 +215,7 @@ fn build_origin(internal: &InternalUriParams) -> Origin<'static> {
|
|||
crate fn _uri_internal_macro(input: TokenStream) -> Result<TokenStream> {
|
||||
// Parse the internal invocation and the user's URI param expressions.
|
||||
let internal = syn::parse::<InternalUriParams>(input).map_err(syn_to_diag)?;
|
||||
let exprs = extract_exprs(&internal)?;
|
||||
|
||||
// Create an iterator over all of the `ident`, `ty`, and `expr` triple.
|
||||
let arguments = internal.fn_args.iter()
|
||||
.zip(exprs.iter())
|
||||
.map(|(FnArg { ident, ty }, &expr)| (ident, ty, expr));
|
||||
|
||||
// Create iterators for just the path and query parts.
|
||||
let path_param_count = internal.route_uri.path().matches('<').count();
|
||||
let path_params = arguments.clone().take(path_param_count);
|
||||
let query_params = arguments.skip(path_param_count);
|
||||
let (path_params, query_params) = extract_exprs(&internal)?;
|
||||
|
||||
let mut bindings = vec![];
|
||||
let uri = build_origin(&internal);
|
||||
|
|
|
@ -12,10 +12,16 @@ use self::syn::punctuated::Punctuated;
|
|||
use http::{uri::Origin, ext::IntoOwned};
|
||||
use indexmap::IndexMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ArgExpr {
|
||||
Expr(Expr),
|
||||
Ignored(Token![_]),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Arg {
|
||||
Unnamed(Expr),
|
||||
Named(Ident, Token![=], Expr),
|
||||
Unnamed(ArgExpr),
|
||||
Named(Ident, Token![=], ArgExpr),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -48,7 +54,7 @@ pub enum Validation<'a> {
|
|||
// (Missing, Extra, Duplicate)
|
||||
Named(Vec<&'a Ident>, Vec<&'a Ident>, Vec<&'a Ident>),
|
||||
// Everything is okay; here are the expressions in the route decl order.
|
||||
Ok(Vec<&'a Expr>)
|
||||
Ok(Vec<&'a ArgExpr>)
|
||||
}
|
||||
|
||||
// This is invoked by Rocket itself. The `uri!` macro expands to a call to a
|
||||
|
@ -72,16 +78,26 @@ pub struct InternalUriParams {
|
|||
pub uri_params: UriParams,
|
||||
}
|
||||
|
||||
impl Parse for ArgExpr {
|
||||
fn parse(input: ParseStream) -> parse::Result<Self> {
|
||||
if input.peek(Token![_]) {
|
||||
return Ok(ArgExpr::Ignored(input.parse::<Token![_]>()?));
|
||||
}
|
||||
|
||||
input.parse::<Expr>().map(ArgExpr::Expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Arg {
|
||||
fn parse(input: ParseStream) -> parse::Result<Self> {
|
||||
let has_key = input.peek2(Token![=]);
|
||||
if has_key {
|
||||
let ident = input.parse::<Ident>()?;
|
||||
let eq_token = input.parse::<Token![=]>()?;
|
||||
let expr = input.parse::<Expr>()?;
|
||||
let expr = input.parse::<ArgExpr>()?;
|
||||
Ok(Arg::Named(ident, eq_token, expr))
|
||||
} else {
|
||||
let expr = input.parse::<Expr>()?;
|
||||
let expr = input.parse::<ArgExpr>()?;
|
||||
Ok(Arg::Unnamed(expr))
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +224,7 @@ impl InternalUriParams {
|
|||
else { Validation::Ok(args.unnamed().unwrap().collect()) }
|
||||
},
|
||||
Args::Named(_) => {
|
||||
let mut params: IndexMap<&Ident, Option<&Expr>> = self.fn_args.iter()
|
||||
let mut params: IndexMap<&Ident, Option<&ArgExpr>> = self.fn_args.iter()
|
||||
.map(|FnArg { ident, .. }| (ident, None))
|
||||
.collect();
|
||||
|
||||
|
@ -253,18 +269,18 @@ impl Arg {
|
|||
fn is_named(&self) -> bool {
|
||||
match *self {
|
||||
Arg::Named(..) => true,
|
||||
Arg::Unnamed(_) => false,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
fn unnamed(&self) -> &Expr {
|
||||
fn unnamed(&self) -> &ArgExpr {
|
||||
match self {
|
||||
Arg::Unnamed(expr) => expr,
|
||||
_ => panic!("Called Arg::unnamed() on an Arg::named!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn named(&self) -> (&Ident, &Expr) {
|
||||
fn named(&self) -> (&Ident, &ArgExpr) {
|
||||
match self {
|
||||
Arg::Named(ident, _, expr) => (ident, expr),
|
||||
_ => panic!("Called Arg::named() on an Arg::Unnamed!"),
|
||||
|
@ -279,14 +295,14 @@ impl Args {
|
|||
}
|
||||
}
|
||||
|
||||
fn named(&self) -> Option<impl Iterator<Item = (&Ident, &Expr)>> {
|
||||
fn named(&self) -> Option<impl Iterator<Item = (&Ident, &ArgExpr)>> {
|
||||
match self {
|
||||
Args::Named(args) => Some(args.iter().map(|arg| arg.named())),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn unnamed(&self) -> Option<impl Iterator<Item = &Expr>> {
|
||||
fn unnamed(&self) -> Option<impl Iterator<Item = &ArgExpr>> {
|
||||
match self {
|
||||
Args::Unnamed(args) => Some(args.iter().map(|arg| arg.unnamed())),
|
||||
_ => None
|
||||
|
@ -294,12 +310,36 @@ impl Args {
|
|||
}
|
||||
}
|
||||
|
||||
impl ArgExpr {
|
||||
pub fn as_expr(&self) -> Option<&Expr> {
|
||||
match self {
|
||||
ArgExpr::Expr(expr) => Some(expr),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_expr(&self) -> &Expr {
|
||||
match self {
|
||||
ArgExpr::Expr(expr) => expr,
|
||||
_ => panic!("Called ArgExpr::expr() on ArgExpr::Ignored!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ArgExpr {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
match self {
|
||||
ArgExpr::Expr(e) => e.to_tokens(tokens),
|
||||
ArgExpr::Ignored(e) => e.to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Arg {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
match self {
|
||||
Arg::Unnamed(e) => e.to_tokens(tokens),
|
||||
Arg::Named(ident, eq, expr) => tokens.extend(quote!(#ident #eq #expr))
|
||||
Arg::Named(ident, eq, expr) => tokens.extend(quote!(#ident #eq #expr)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use proc_macro::{Span, TokenStream};
|
|||
use devise::*;
|
||||
|
||||
use derive::from_form::Form;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
|
||||
const NO_EMPTY_FIELDS: &str = "fieldless structs or variants are not supported";
|
||||
const NO_NULLARY: &str = "nullary items are not supported";
|
||||
|
@ -37,17 +38,21 @@ fn validate_enum(gen: &DeriveGenerator, data: Enum) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn derive_uri_display_query(input: TokenStream) -> TokenStream {
|
||||
let display_trait = quote!(::rocket::http::uri::UriDisplay<::rocket::http::uri::Query>);
|
||||
let formatter = quote!(::rocket::http::uri::Formatter<::rocket::http::uri::Query>);
|
||||
DeriveGenerator::build_for(input, quote!(impl #display_trait))
|
||||
let Query = quote!(::rocket::http::uri::Query);
|
||||
let UriDisplay = quote!(::rocket::http::uri::UriDisplay<#Query>);
|
||||
let Formatter = quote!(::rocket::http::uri::Formatter<#Query>);
|
||||
let FromUriParam = quote!(::rocket::http::uri::FromUriParam);
|
||||
|
||||
let uri_display = DeriveGenerator::build_for(input.clone(), quote!(impl #UriDisplay))
|
||||
.generic_support(GenericSupport::Type | GenericSupport::Lifetime)
|
||||
.data_support(DataSupport::Struct | DataSupport::Enum)
|
||||
.validate_enum(validate_enum)
|
||||
.validate_struct(validate_struct)
|
||||
.map_type_generic(move |_, ident, _| quote!(#ident : #display_trait))
|
||||
.map_type_generic(move |_, ident, _| quote!(#ident : #UriDisplay))
|
||||
.function(move |_, inner| quote! {
|
||||
fn fmt(&self, f: &mut #formatter) -> ::std::fmt::Result {
|
||||
fn fmt(&self, f: &mut #Formatter) -> ::std::fmt::Result {
|
||||
#inner
|
||||
Ok(())
|
||||
}
|
||||
|
@ -67,7 +72,49 @@ pub fn derive_uri_display_query(input: TokenStream) -> TokenStream {
|
|||
|
||||
Ok(tokens)
|
||||
})
|
||||
.to_tokens()
|
||||
.to_tokens();
|
||||
|
||||
let i = input.clone();
|
||||
let gen_trait = quote!(impl #FromUriParam<#Query, Self>);
|
||||
let from_self = DeriveGenerator::build_for(i, gen_trait)
|
||||
.generic_support(GenericSupport::Type | GenericSupport::Lifetime)
|
||||
.data_support(DataSupport::Struct | DataSupport::Enum)
|
||||
.function(|_, _| quote! {
|
||||
type Target = Self;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: Self) -> Self { param }
|
||||
})
|
||||
.to_tokens();
|
||||
|
||||
let i = input.clone();
|
||||
let gen_trait = quote!(impl<'__r> #FromUriParam<#Query, &'__r Self>);
|
||||
let from_ref = DeriveGenerator::build_for(i, gen_trait)
|
||||
.generic_support(GenericSupport::Type | GenericSupport::Lifetime)
|
||||
.data_support(DataSupport::Struct | DataSupport::Enum)
|
||||
.function(|_, _| quote! {
|
||||
type Target = &'__r Self;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'__r Self) -> &'__r Self { param }
|
||||
})
|
||||
.to_tokens();
|
||||
|
||||
let i = input.clone();
|
||||
let gen_trait = quote!(impl<'__r> #FromUriParam<#Query, &'__r mut Self>);
|
||||
let from_mut = DeriveGenerator::build_for(i, gen_trait)
|
||||
.generic_support(GenericSupport::Type | GenericSupport::Lifetime)
|
||||
.data_support(DataSupport::Struct | DataSupport::Enum)
|
||||
.function(|_, _| quote! {
|
||||
type Target = &'__r mut Self;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'__r mut Self) -> &'__r mut Self { param }
|
||||
})
|
||||
.to_tokens();
|
||||
|
||||
let mut ts = TokenStream2::from(uri_display);
|
||||
ts.extend(TokenStream2::from(from_self));
|
||||
ts.extend(TokenStream2::from(from_ref));
|
||||
ts.extend(TokenStream2::from(from_mut));
|
||||
ts.into()
|
||||
}
|
||||
|
||||
pub fn derive_uri_display_path(input: TokenStream) -> TokenStream {
|
||||
|
|
|
@ -858,9 +858,11 @@ pub fn catchers(input: TokenStream) -> TokenStream {
|
|||
/// # #![feature(proc_macro_hygiene, decl_macro)]
|
||||
/// # #[macro_use] extern crate rocket;
|
||||
/// #
|
||||
/// #[get("/person/<name>/<age>")]
|
||||
/// fn person(name: String, age: u8) -> String {
|
||||
/// format!("Hello {}! You're {} years old.", name, age)
|
||||
/// #[get("/person/<name>?<age>")]
|
||||
/// fn person(name: String, age: Option<u8>) -> String {
|
||||
/// # "".into() /*
|
||||
/// ...
|
||||
/// # */
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
@ -870,21 +872,29 @@ pub fn catchers(input: TokenStream) -> TokenStream {
|
|||
/// # #![feature(proc_macro_hygiene, decl_macro)]
|
||||
/// # #[macro_use] extern crate rocket;
|
||||
/// #
|
||||
/// # #[get("/person/<name>/<age>")]
|
||||
/// # fn person(name: String, age: u8) { }
|
||||
/// # #[get("/person/<name>?<age>")]
|
||||
/// # fn person(name: String, age: Option<u8>) { }
|
||||
/// #
|
||||
/// // with unnamed parameters, in route path declaration order
|
||||
/// let mike = uri!(person: "Mike Smith", 28);
|
||||
/// assert_eq!(mike.path(), "/person/Mike%20Smith/28");
|
||||
/// assert_eq!(mike.to_string(), "/person/Mike%20Smith?age=28");
|
||||
///
|
||||
/// // with named parameters, order irrelevant
|
||||
/// let mike = uri!(person: name = "Mike", age = 28);
|
||||
/// let mike = uri!(person: age = 28, name = "Mike");
|
||||
/// assert_eq!(mike.path(), "/person/Mike/28");
|
||||
/// assert_eq!(mike.to_string(), "/person/Mike?age=28");
|
||||
///
|
||||
/// // with a specific mount-point
|
||||
/// let mike = uri!("/api", person: name = "Mike", age = 28);
|
||||
/// assert_eq!(mike.path(), "/api/person/Mike/28");
|
||||
/// assert_eq!(mike.to_string(), "/api/person/Mike?age=28");
|
||||
///
|
||||
/// // with unnamed values ignored
|
||||
/// let mike = uri!(person: "Mike", _);
|
||||
/// assert_eq!(mike.to_string(), "/person/Mike");
|
||||
///
|
||||
/// // with named values ignored
|
||||
/// let mike = uri!(person: name = "Mike", age = _);
|
||||
/// assert_eq!(mike.to_string(), "/person/Mike");
|
||||
/// ```
|
||||
///
|
||||
/// ## Grammar
|
||||
|
@ -896,8 +906,9 @@ pub fn catchers(input: TokenStream) -> TokenStream {
|
|||
///
|
||||
/// mount = STRING
|
||||
/// params := unnamed | named
|
||||
/// unnamed := EXPR (',' EXPR)*
|
||||
/// named := IDENT = EXPR (',' named)?
|
||||
/// unnamed := expr (',' expr)*
|
||||
/// named := IDENT = expr (',' named)?
|
||||
/// expr := EXPR | '_'
|
||||
///
|
||||
/// EXPR := a valid Rust expression (examples: `foo()`, `12`, `"hey"`)
|
||||
/// IDENT := a valid Rust identifier (examples: `name`, `age`)
|
||||
|
@ -913,7 +924,19 @@ pub fn catchers(input: TokenStream) -> TokenStream {
|
|||
/// converted into a [`Uri`] using `.into()` as needed.
|
||||
///
|
||||
/// A `uri!` invocation only typechecks if the type of every value in the
|
||||
/// invocation matches the type declared for the parameter in the given route.
|
||||
/// invocation matches the type declared for the parameter in the given route,
|
||||
/// after conversion with [`FromUriParam`], or if a value is ignored using `_`
|
||||
/// and the corresponding route type implements [`Ignorable`].
|
||||
///
|
||||
/// Each value passed into `uri!` is rendered in its appropriate place in the
|
||||
/// URI using the [`UriDisplay`] implementation for the value's type. The
|
||||
/// `UriDisplay` implementation ensures that the rendered value is URI-safe.
|
||||
///
|
||||
/// If a mount-point is provided, the mount-point is prepended to the route's
|
||||
/// URI.
|
||||
///
|
||||
/// ### Conversion
|
||||
///
|
||||
/// The [`FromUriParam`] trait is used to typecheck and perform a conversion for
|
||||
/// each value. If a `FromUriParam<S>` implementation exists for a type `T`,
|
||||
/// then a value of type `S` can be used in `uri!` macro for a route URI
|
||||
|
@ -925,17 +948,18 @@ pub fn catchers(input: TokenStream) -> TokenStream {
|
|||
/// impl<'a> FromUriParam<&'a str> for String { .. }
|
||||
/// ```
|
||||
///
|
||||
/// Each value passed into `uri!` is rendered in its appropriate place in the
|
||||
/// URI using the [`UriDisplay`] implementation for the value's type. The
|
||||
/// `UriDisplay` implementation ensures that the rendered value is URI-safe.
|
||||
/// ### Ignorables
|
||||
///
|
||||
/// If a mount-point is provided, the mount-point is prepended to the route's
|
||||
/// URI.
|
||||
/// Query parameters can be ignored using `_` in place of an expression. The
|
||||
/// corresponding type in the route URI must implement [`Ignorable`]. Ignored
|
||||
/// parameters are not interpolated into the resulting `Origin`. Path parameters
|
||||
/// are not ignorable.
|
||||
///
|
||||
/// [`Uri`]: ../rocket/http/uri/enum.Uri.html
|
||||
/// [`Origin`]: ../rocket/http/uri/struct.Origin.html
|
||||
/// [`FromUriParam`]: ../rocket/http/uri/trait.FromUriParam.html
|
||||
/// [`UriDisplay`]: ../rocket/http/uri/trait.UriDisplay.html
|
||||
/// [`Ignorable`]: ../rocket/http/uri/trait.Ignorable.html
|
||||
#[proc_macro]
|
||||
pub fn uri(input: TokenStream) -> TokenStream {
|
||||
emit!(bang::uri_macro(input))
|
||||
|
|
|
@ -208,9 +208,9 @@ 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!("/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: 10, PathBuf::from("a/b")) => "/a/10/then/a/b",
|
||||
uri!(param_and_segments: id = 10, path = PathBuf::from("a/b"))
|
||||
=> "/a/10/then/a/b",
|
||||
|
@ -223,7 +223,7 @@ fn check_with_segments() {
|
|||
assert_uri_eq! {
|
||||
uri!(segments: "one/two/three") => "/a/one/two/three",
|
||||
uri!("/oh", segments: path = "one/two/three") => "/oh/a/one/two/three",
|
||||
uri!(segments: "one/ tw?o/") => "/a/one/%20tw%3Fo/",
|
||||
uri!(segments: "one/ tw?o/") => "/a/one/%20tw%3Fo",
|
||||
uri!(param_and_segments: id = 10, path = "a/b") => "/a/10/then/a/b",
|
||||
uri!(guarded_segments: 10, "a/b") => "/a/10/then/a/b",
|
||||
uri!(guarded_segments: id = 10, path = "a/b") => "/a/10/then/a/b",
|
||||
|
@ -283,8 +283,10 @@ fn check_location_promotion() {
|
|||
|
||||
assert_uri_eq! {
|
||||
uri!(simple2: 1, &S1("A".into()).0) => "/1/A",
|
||||
uri!(simple2: 1, &mut S1("A".into()).0) => "/1/A",
|
||||
uri!(simple2: 1, S1("A".into()).0) => "/1/A",
|
||||
uri!(simple2: 1, &S2 { name: "A".into() }.name) => "/1/A",
|
||||
uri!(simple2: 1, &mut S2 { name: "A".into() }.name) => "/1/A",
|
||||
uri!(simple2: 1, S2 { name: "A".into() }.name) => "/1/A",
|
||||
uri!(simple2: 1, &s1.0) => "/1/Bob",
|
||||
uri!(simple2: 1, &s2.name) => "/1/Bob",
|
||||
|
@ -350,3 +352,64 @@ mod typed_uris {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromForm, UriDisplayQuery)]
|
||||
struct Third<'r> {
|
||||
one: String,
|
||||
two: &'r RawStr,
|
||||
}
|
||||
|
||||
#[post("/<foo>/<bar>?<q1>&<rest..>")]
|
||||
fn optionals(
|
||||
foo: Option<usize>,
|
||||
bar: Result<String, &RawStr>,
|
||||
q1: Result<usize, &RawStr>,
|
||||
rest: Option<Form<Third>>
|
||||
) { }
|
||||
|
||||
#[test]
|
||||
fn test_optional_uri_parameters() {
|
||||
assert_uri_eq! {
|
||||
uri!(optionals:
|
||||
foo = 10,
|
||||
bar = &"hi there",
|
||||
q1 = 10,
|
||||
rest = Third { one: "hi there".into(), two: "a b".into() }
|
||||
) => "/10/hi%20there?q1=10&one=hi%20there&two=a%20b",
|
||||
|
||||
uri!(optionals:
|
||||
foo = &10,
|
||||
bar = &"hi there",
|
||||
q1 = &10,
|
||||
rest = &Third { one: "hi there".into(), two: "a b".into() }
|
||||
) => "/10/hi%20there?q1=10&one=hi%20there&two=a%20b",
|
||||
|
||||
uri!(optionals:
|
||||
foo = &mut 10,
|
||||
bar = &mut "hi there",
|
||||
q1 = &mut 10,
|
||||
rest = &mut Third { one: "hi there".into(), two: "a b".into() }
|
||||
) => "/10/hi%20there?q1=10&one=hi%20there&two=a%20b",
|
||||
|
||||
uri!(optionals:
|
||||
foo = 10,
|
||||
bar = &"hi there",
|
||||
q1 = _,
|
||||
rest = Third { one: "hi there".into(), two: "a b".into() }
|
||||
) => "/10/hi%20there?one=hi%20there&two=a%20b",
|
||||
|
||||
uri!(optionals:
|
||||
foo = 10,
|
||||
bar = &"hi there",
|
||||
q1 = 10,
|
||||
rest = _
|
||||
) => "/10/hi%20there?q1=10",
|
||||
|
||||
uri!(optionals:
|
||||
foo = 10,
|
||||
bar = &"hi there",
|
||||
q1 = _,
|
||||
rest = _,
|
||||
) => "/10/hi%20there",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// normalize-stderr-test: "<(.*) as (.*)>" -> "$1 as $$TRAIT"
|
||||
// normalize-stderr-test: "and \d+ others" -> "and $$N others"
|
||||
|
||||
#![feature(proc_macro_hygiene, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
@ -13,7 +16,7 @@ impl<'a> FromParam<'a> for S {
|
|||
}
|
||||
|
||||
#[post("/<id>")]
|
||||
fn simple(id: i32) { }
|
||||
fn simple(id: usize) { }
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn not_uri_display(id: i32, name: S) { }
|
||||
|
@ -32,7 +35,7 @@ impl<'q> FromQuery<'q> for S {
|
|||
}
|
||||
|
||||
#[post("/?<id>")]
|
||||
fn simple_q(id: i32) { }
|
||||
fn simple_q(id: isize) { }
|
||||
|
||||
#[post("/?<id>&<rest..>")]
|
||||
fn other_q(id: usize, rest: S) { }
|
||||
|
@ -42,13 +45,13 @@ fn optionals_q(id: Option<i32>, name: Result<String, &RawStr>) { }
|
|||
|
||||
fn main() {
|
||||
uri!(simple: id = "hi");
|
||||
//~^ ERROR i32: rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>
|
||||
//~^ ERROR usize: rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>
|
||||
|
||||
uri!(simple: "hello");
|
||||
//~^ ERROR i32: rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>
|
||||
//~^ ERROR usize: rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>
|
||||
|
||||
uri!(simple: id = 239239i64);
|
||||
//~^ ERROR i32: rocket::http::uri::FromUriParam<rocket::http::uri::Path, i64>
|
||||
//~^ ERROR usize: rocket::http::uri::FromUriParam<rocket::http::uri::Path, i64>
|
||||
|
||||
uri!(not_uri_display: 10, S);
|
||||
//~^ ERROR S: rocket::http::uri::FromUriParam<rocket::http::uri::Path, _>
|
||||
|
@ -61,10 +64,10 @@ fn main() {
|
|||
//~^^ ERROR String: rocket::http::uri::FromUriParam<rocket::http::uri::Path, std::result::Result<_, _>>
|
||||
|
||||
uri!(simple_q: "hi");
|
||||
//~^ ERROR i32: rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>
|
||||
//~^ ERROR isize: rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>
|
||||
|
||||
uri!(simple_q: id = "hi");
|
||||
//~^ ERROR i32: rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>
|
||||
//~^ ERROR isize: rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>
|
||||
|
||||
uri!(other_q: 100, S);
|
||||
//~^ ERROR S: rocket::http::uri::FromUriParam<rocket::http::uri::Query, _>
|
||||
|
@ -72,11 +75,16 @@ fn main() {
|
|||
uri!(other_q: rest = S, id = 100);
|
||||
//~^ ERROR S: rocket::http::uri::FromUriParam<rocket::http::uri::Query, _>
|
||||
|
||||
// This one is okay.
|
||||
uri!(optionals_q: None, Err("foo".into()));
|
||||
uri!(other_q: rest = _, id = 100);
|
||||
//~^ ERROR S: rocket::http::uri::Ignorable<rocket::http::uri::Query>
|
||||
|
||||
// For queries, we need to know the exact variant.
|
||||
uri!(other_q: rest = S, id = _);
|
||||
//~^ ERROR S: rocket::http::uri::FromUriParam<rocket::http::uri::Query, _>
|
||||
//~^^ ERROR usize: rocket::http::uri::Ignorable<rocket::http::uri::Query>
|
||||
|
||||
// These are all okay.
|
||||
uri!(optionals_q: _, _);
|
||||
uri!(optionals_q: id = 10, name = "Bob".to_string());
|
||||
//~^ ERROR Option<i32>: rocket::http::uri::FromUriParam<rocket::http::uri::Query, {integer}>
|
||||
//~^^ ERROR: Result<std::string::String, &rocket::http::RawStr>: rocket::http::uri::FromUriParam<rocket::http::uri::Query, std::string::String>
|
||||
uri!(optionals_q: _, "Bob".into());
|
||||
uri!(optionals_q: id = _, name = _);
|
||||
}
|
||||
|
|
|
@ -1,99 +1,129 @@
|
|||
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:44:23
|
||||
error[E0277]: the trait bound `usize: rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:47:23
|
||||
|
|
||||
44 | uri!(simple: id = "hi");
|
||||
| ^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>` is not implemented for `i32`
|
||||
47 | uri!(simple: id = "hi");
|
||||
| ^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>` is not implemented for `usize`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
usize as $TRAIT
|
||||
usize as $TRAIT
|
||||
usize as $TRAIT
|
||||
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
|
||||
|
||||
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:47:18
|
||||
error[E0277]: the trait bound `usize: rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:50:18
|
||||
|
|
||||
47 | uri!(simple: "hello");
|
||||
| ^^^^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>` is not implemented for `i32`
|
||||
50 | uri!(simple: "hello");
|
||||
| ^^^^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>` is not implemented for `usize`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
usize as $TRAIT
|
||||
usize as $TRAIT
|
||||
usize as $TRAIT
|
||||
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
|
||||
|
||||
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<rocket::http::uri::Path, i64>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:50:23
|
||||
error[E0277]: the trait bound `usize: rocket::http::uri::FromUriParam<rocket::http::uri::Path, i64>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:53:23
|
||||
|
|
||||
50 | uri!(simple: id = 239239i64);
|
||||
| ^^^^^^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, i64>` is not implemented for `i32`
|
||||
53 | uri!(simple: id = 239239i64);
|
||||
| ^^^^^^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, i64>` is not implemented for `usize`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
usize as $TRAIT
|
||||
usize as $TRAIT
|
||||
usize as $TRAIT
|
||||
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
|
||||
|
||||
error[E0277]: the trait bound `S: rocket::http::uri::FromUriParam<rocket::http::uri::Path, _>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:53:31
|
||||
--> $DIR/typed-uri-bad-type.rs:56:31
|
||||
|
|
||||
53 | uri!(not_uri_display: 10, S);
|
||||
56 | uri!(not_uri_display: 10, S);
|
||||
| ^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, _>` is not implemented for `S`
|
||||
|
||||
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<rocket::http::uri::Path, std::option::Option<{integer}>>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:59:26
|
||||
--> $DIR/typed-uri-bad-type.rs:62:26
|
||||
|
|
||||
59 | uri!(optionals: id = Some(10), name = Ok("bob".into()));
|
||||
62 | uri!(optionals: id = Some(10), name = Ok("bob".into()));
|
||||
| ^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, std::option::Option<{integer}>>` is not implemented for `i32`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
i32 as $TRAIT
|
||||
i32 as $TRAIT
|
||||
i32 as $TRAIT
|
||||
= note: required because of the requirements on the impl of `rocket::http::uri::FromUriParam<rocket::http::uri::Path, std::option::Option<{integer}>>` for `std::option::Option<i32>`
|
||||
|
||||
error[E0277]: the trait bound `std::string::String: rocket::http::uri::FromUriParam<rocket::http::uri::Path, std::result::Result<_, _>>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:59:43
|
||||
--> $DIR/typed-uri-bad-type.rs:62:43
|
||||
|
|
||||
59 | uri!(optionals: id = Some(10), name = Ok("bob".into()));
|
||||
62 | uri!(optionals: id = Some(10), name = Ok("bob".into()));
|
||||
| ^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, std::result::Result<_, _>>` is not implemented for `std::string::String`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<std::string::String as rocket::http::uri::FromUriParam<P, &'a str>>
|
||||
std::string::String as $TRAIT
|
||||
std::string::String as $TRAIT
|
||||
std::string::String as $TRAIT
|
||||
std::string::String as $TRAIT
|
||||
and $N others
|
||||
= note: required because of the requirements on the impl of `rocket::http::uri::FromUriParam<rocket::http::uri::Path, std::result::Result<_, _>>` for `std::result::Result<std::string::String, &rocket::http::RawStr>`
|
||||
|
||||
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:63:20
|
||||
error[E0277]: the trait bound `isize: rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:66:20
|
||||
|
|
||||
63 | uri!(simple_q: "hi");
|
||||
| ^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>` is not implemented for `i32`
|
||||
66 | uri!(simple_q: "hi");
|
||||
| ^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>` is not implemented for `isize`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
isize as $TRAIT
|
||||
isize as $TRAIT
|
||||
isize as $TRAIT
|
||||
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
|
||||
|
||||
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:66:25
|
||||
error[E0277]: the trait bound `isize: rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:69:25
|
||||
|
|
||||
66 | uri!(simple_q: id = "hi");
|
||||
| ^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>` is not implemented for `i32`
|
||||
69 | uri!(simple_q: id = "hi");
|
||||
| ^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>` is not implemented for `isize`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
isize as $TRAIT
|
||||
isize as $TRAIT
|
||||
isize as $TRAIT
|
||||
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
|
||||
|
||||
error[E0277]: the trait bound `S: rocket::http::uri::FromUriParam<rocket::http::uri::Query, _>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:69:24
|
||||
--> $DIR/typed-uri-bad-type.rs:72:24
|
||||
|
|
||||
69 | uri!(other_q: 100, S);
|
||||
72 | uri!(other_q: 100, S);
|
||||
| ^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Query, _>` is not implemented for `S`
|
||||
|
||||
error[E0277]: the trait bound `S: rocket::http::uri::FromUriParam<rocket::http::uri::Query, _>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:72:26
|
||||
--> $DIR/typed-uri-bad-type.rs:75:26
|
||||
|
|
||||
72 | uri!(other_q: rest = S, id = 100);
|
||||
75 | uri!(other_q: rest = S, id = 100);
|
||||
| ^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Query, _>` is not implemented for `S`
|
||||
|
||||
error[E0277]: the trait bound `std::option::Option<i32>: rocket::http::uri::FromUriParam<rocket::http::uri::Query, {integer}>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:79:28
|
||||
error[E0277]: the trait bound `S: rocket::http::uri::Ignorable<rocket::http::uri::Query>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:78:26
|
||||
|
|
||||
79 | uri!(optionals_q: id = 10, name = "Bob".to_string());
|
||||
| ^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Query, {integer}>` is not implemented for `std::option::Option<i32>`
|
||||
78 | uri!(other_q: rest = _, id = 100);
|
||||
| ^ the trait `rocket::http::uri::Ignorable<rocket::http::uri::Query>` is not implemented for `S`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<std::option::Option<T> as rocket::http::uri::FromUriParam<rocket::http::uri::Path, A>>
|
||||
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
|
||||
= note: required by `rocket::http::uri::assert_ignorable`
|
||||
|
||||
error[E0277]: the trait bound `std::result::Result<std::string::String, &rocket::http::RawStr>: rocket::http::uri::FromUriParam<rocket::http::uri::Query, std::string::String>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:79:39
|
||||
error[E0277]: the trait bound `usize: rocket::http::uri::Ignorable<rocket::http::uri::Query>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:81:34
|
||||
|
|
||||
79 | uri!(optionals_q: id = 10, name = "Bob".to_string());
|
||||
| ^^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Query, std::string::String>` is not implemented for `std::result::Result<std::string::String, &rocket::http::RawStr>`
|
||||
81 | uri!(other_q: rest = S, id = _);
|
||||
| ^ the trait `rocket::http::uri::Ignorable<rocket::http::uri::Query>` is not implemented for `usize`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<std::result::Result<T, E> as rocket::http::uri::FromUriParam<rocket::http::uri::Path, A>>
|
||||
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
|
||||
= note: required by `rocket::http::uri::assert_ignorable`
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
error[E0277]: the trait bound `S: rocket::http::uri::FromUriParam<rocket::http::uri::Query, _>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:81:26
|
||||
|
|
||||
81 | uri!(other_q: rest = S, id = _);
|
||||
| ^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Query, _>` is not implemented for `S`
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use rocket::http::Cookies;
|
||||
use rocket::http::{Cookies, RawStr};
|
||||
|
||||
#[post("/<id>")]
|
||||
fn has_one(id: i32) { }
|
||||
|
@ -15,6 +15,9 @@ fn has_one_guarded(cookies: Cookies, id: i32) { }
|
|||
#[post("/<id>?<name>")]
|
||||
fn has_two(cookies: Cookies, id: i32, name: String) { }
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn optionals(id: Option<i32>, name: Result<String, &RawStr>) { }
|
||||
|
||||
fn main() {
|
||||
uri!(has_one); //~ ERROR expects 1 parameter but 0
|
||||
|
||||
|
@ -69,4 +72,10 @@ fn main() {
|
|||
uri!(has_two: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
//~^ HELP missing parameter: `name`
|
||||
//~^^ HELP unknown parameter: `cookies`
|
||||
|
||||
uri!(optionals: id = _, name = "bob".into());
|
||||
//~^ ERROR cannot be ignored
|
||||
|
||||
uri!(optionals: id = 10, name = _);
|
||||
//~^ ERROR cannot be ignored
|
||||
}
|
||||
|
|
|
@ -1,229 +1,241 @@
|
|||
error: `has_one` route uri expects 1 parameter but 0 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:19:10
|
||||
--> $DIR/typed-uris-bad-params.rs:22:10
|
||||
|
|
||||
19 | uri!(has_one); //~ ERROR expects 1 parameter but 0
|
||||
22 | uri!(has_one); //~ ERROR expects 1 parameter but 0
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: `has_one` route uri expects 1 parameter but 2 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:21:19
|
||||
--> $DIR/typed-uris-bad-params.rs:24:19
|
||||
|
|
||||
21 | uri!(has_one: 1, 23); //~ ERROR expects 1 parameter but 2
|
||||
24 | uri!(has_one: 1, 23); //~ ERROR expects 1 parameter but 2
|
||||
| ^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: `has_one` route uri expects 1 parameter but 2 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:22:19
|
||||
--> $DIR/typed-uris-bad-params.rs:25:19
|
||||
|
|
||||
22 | uri!(has_one: "Hello", 23, ); //~ ERROR expects 1 parameter but 2
|
||||
25 | uri!(has_one: "Hello", 23, ); //~ ERROR expects 1 parameter but 2
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: `has_one_guarded` route uri expects 1 parameter but 2 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:23:27
|
||||
--> $DIR/typed-uris-bad-params.rs:26:27
|
||||
|
|
||||
23 | uri!(has_one_guarded: "hi", 100); //~ ERROR expects 1 parameter but 2
|
||||
26 | uri!(has_one_guarded: "hi", 100); //~ ERROR expects 1 parameter but 2
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: `has_two` route uri expects 2 parameters but 3 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:25:19
|
||||
--> $DIR/typed-uris-bad-params.rs:28:19
|
||||
|
|
||||
25 | uri!(has_two: 10, "hi", "there"); //~ ERROR expects 2 parameters but 3
|
||||
28 | uri!(has_two: 10, "hi", "there"); //~ ERROR expects 2 parameters but 3
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: expected parameters: id: i32, name: String
|
||||
|
||||
error: `has_two` route uri expects 2 parameters but 1 was supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:26:19
|
||||
--> $DIR/typed-uris-bad-params.rs:29:19
|
||||
|
|
||||
26 | uri!(has_two: 10); //~ ERROR expects 2 parameters but 1
|
||||
29 | uri!(has_two: 10); //~ ERROR expects 2 parameters but 1
|
||||
| ^^
|
||||
|
|
||||
= note: expected parameters: id: i32, name: String
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:28:19
|
||||
--> $DIR/typed-uris-bad-params.rs:31:19
|
||||
|
|
||||
28 | uri!(has_one: id = 100, name = "hi"); //~ ERROR invalid parameters
|
||||
31 | uri!(has_one: id = 100, name = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `name`
|
||||
--> $DIR/typed-uris-bad-params.rs:28:29
|
||||
--> $DIR/typed-uris-bad-params.rs:31:29
|
||||
|
|
||||
28 | uri!(has_one: id = 100, name = "hi"); //~ ERROR invalid parameters
|
||||
31 | uri!(has_one: id = 100, name = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:31:19
|
||||
--> $DIR/typed-uris-bad-params.rs:34:19
|
||||
|
|
||||
31 | uri!(has_one: name = 100, id = 100); //~ ERROR invalid parameters
|
||||
34 | uri!(has_one: name = 100, id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `name`
|
||||
--> $DIR/typed-uris-bad-params.rs:31:19
|
||||
--> $DIR/typed-uris-bad-params.rs:34:19
|
||||
|
|
||||
31 | uri!(has_one: name = 100, id = 100); //~ ERROR invalid parameters
|
||||
34 | uri!(has_one: name = 100, id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:34:19
|
||||
--> $DIR/typed-uris-bad-params.rs:37:19
|
||||
|
|
||||
34 | uri!(has_one: name = 100, age = 50, id = 100); //~ ERROR invalid parameters
|
||||
37 | uri!(has_one: name = 100, age = 50, id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameters: `name`, `age`
|
||||
--> $DIR/typed-uris-bad-params.rs:34:19
|
||||
--> $DIR/typed-uris-bad-params.rs:37:19
|
||||
|
|
||||
34 | uri!(has_one: name = 100, age = 50, id = 100); //~ ERROR invalid parameters
|
||||
37 | uri!(has_one: name = 100, age = 50, id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^ ^^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:37:19
|
||||
--> $DIR/typed-uris-bad-params.rs:40:19
|
||||
|
|
||||
37 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters
|
||||
40 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameters: `name`, `age`
|
||||
--> $DIR/typed-uris-bad-params.rs:37:19
|
||||
--> $DIR/typed-uris-bad-params.rs:40:19
|
||||
|
|
||||
37 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters
|
||||
40 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters
|
||||
| ^^^^ ^^^
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:37:51
|
||||
--> $DIR/typed-uris-bad-params.rs:40:51
|
||||
|
|
||||
37 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters
|
||||
40 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters
|
||||
| ^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:41:19
|
||||
|
|
||||
41 | uri!(has_one: id = 100, id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:41:29
|
||||
|
|
||||
41 | uri!(has_one: id = 100, id = 100); //~ ERROR invalid parameters
|
||||
| ^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:44:19
|
||||
|
|
||||
44 | uri!(has_one: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
44 | uri!(has_one: id = 100, id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:44:29
|
||||
|
|
||||
44 | uri!(has_one: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
44 | uri!(has_one: id = 100, id = 100); //~ ERROR invalid parameters
|
||||
| ^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:47:19
|
||||
|
|
||||
47 | uri!(has_one: name = "hi"); //~ ERROR invalid parameters
|
||||
47 | uri!(has_one: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:47:29
|
||||
|
|
||||
47 | uri!(has_one: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
| ^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:50:19
|
||||
|
|
||||
50 | uri!(has_one: name = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
= help: missing parameter: `id`
|
||||
help: unknown parameter: `name`
|
||||
--> $DIR/typed-uris-bad-params.rs:47:19
|
||||
--> $DIR/typed-uris-bad-params.rs:50:19
|
||||
|
|
||||
47 | uri!(has_one: name = "hi"); //~ ERROR invalid parameters
|
||||
50 | uri!(has_one: name = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^
|
||||
|
||||
error: invalid parameters for `has_one_guarded` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:51:27
|
||||
|
|
||||
51 | uri!(has_one_guarded: cookies = "hi", id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `cookies`
|
||||
--> $DIR/typed-uris-bad-params.rs:51:27
|
||||
|
|
||||
51 | uri!(has_one_guarded: cookies = "hi", id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^
|
||||
|
||||
error: invalid parameters for `has_one_guarded` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:54:27
|
||||
|
|
||||
54 | uri!(has_one_guarded: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
54 | uri!(has_one_guarded: cookies = "hi", id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `cookies`
|
||||
--> $DIR/typed-uris-bad-params.rs:54:37
|
||||
--> $DIR/typed-uris-bad-params.rs:54:27
|
||||
|
|
||||
54 | uri!(has_one_guarded: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
54 | uri!(has_one_guarded: cookies = "hi", id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^
|
||||
|
||||
error: invalid parameters for `has_one_guarded` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:57:27
|
||||
|
|
||||
57 | uri!(has_one_guarded: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `cookies`
|
||||
--> $DIR/typed-uris-bad-params.rs:57:37
|
||||
|
|
||||
57 | uri!(has_one_guarded: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^
|
||||
|
||||
error: invalid parameters for `has_two` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:57:19
|
||||
--> $DIR/typed-uris-bad-params.rs:60:19
|
||||
|
|
||||
57 | uri!(has_two: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
60 | uri!(has_two: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32, name: String
|
||||
= help: missing parameter: `name`
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:57:29
|
||||
--> $DIR/typed-uris-bad-params.rs:60:29
|
||||
|
|
||||
57 | uri!(has_two: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
60 | uri!(has_two: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
| ^^
|
||||
|
||||
error: invalid parameters for `has_two` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:61:19
|
||||
--> $DIR/typed-uris-bad-params.rs:64:19
|
||||
|
|
||||
61 | uri!(has_two: name = "hi"); //~ ERROR invalid parameters
|
||||
64 | uri!(has_two: name = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32, name: String
|
||||
= help: missing parameter: `id`
|
||||
|
||||
error: invalid parameters for `has_two` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:64:19
|
||||
--> $DIR/typed-uris-bad-params.rs:67:19
|
||||
|
|
||||
64 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters
|
||||
67 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32, name: String
|
||||
= help: missing parameter: `name`
|
||||
help: unknown parameter: `cookies`
|
||||
--> $DIR/typed-uris-bad-params.rs:64:19
|
||||
--> $DIR/typed-uris-bad-params.rs:67:19
|
||||
|
|
||||
64 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters
|
||||
67 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters
|
||||
| ^^^^^^^
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:64:45
|
||||
--> $DIR/typed-uris-bad-params.rs:67:45
|
||||
|
|
||||
64 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters
|
||||
67 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters
|
||||
| ^^ ^^
|
||||
|
||||
error: invalid parameters for `has_two` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:69:19
|
||||
--> $DIR/typed-uris-bad-params.rs:72:19
|
||||
|
|
||||
69 | uri!(has_two: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
72 | uri!(has_two: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32, name: String
|
||||
= help: missing parameter: `name`
|
||||
help: unknown parameter: `cookies`
|
||||
--> $DIR/typed-uris-bad-params.rs:69:29
|
||||
--> $DIR/typed-uris-bad-params.rs:72:29
|
||||
|
|
||||
69 | uri!(has_two: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
72 | uri!(has_two: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^
|
||||
|
||||
error: aborting due to 19 previous errors
|
||||
error: path parameters cannot be ignored
|
||||
--> $DIR/typed-uris-bad-params.rs:76:26
|
||||
|
|
||||
76 | uri!(optionals: id = _, name = "bob".into());
|
||||
| ^
|
||||
|
||||
error: path parameters cannot be ignored
|
||||
--> $DIR/typed-uris-bad-params.rs:79:37
|
||||
|
|
||||
79 | uri!(optionals: id = 10, name = _);
|
||||
| ^
|
||||
|
||||
error: aborting due to 21 previous errors
|
||||
|
||||
|
|
|
@ -153,29 +153,14 @@ pub struct Formatter<'i, P: UriPart> {
|
|||
inner: &'i mut (dyn Write + 'i),
|
||||
previous: bool,
|
||||
fresh: bool,
|
||||
delimiter: char,
|
||||
_marker: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<'i> Formatter<'i, Path> {
|
||||
#[inline(always)]
|
||||
crate fn new(inner: &'i mut (dyn Write + 'i)) -> Self {
|
||||
Formatter::make(inner, '/')
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> Formatter<'i, Query> {
|
||||
#[inline(always)]
|
||||
crate fn new(inner: &'i mut (dyn Write + 'i)) -> Self {
|
||||
Formatter::make(inner, '&')
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i, P: UriPart> Formatter<'i, P> {
|
||||
#[inline(always)]
|
||||
fn make(inner: &'i mut (dyn Write + 'i), delimiter: char) -> Self {
|
||||
crate fn new(inner: &'i mut (dyn Write + 'i)) -> Self {
|
||||
Formatter {
|
||||
inner, delimiter,
|
||||
inner,
|
||||
prefixes: SmallVec::new(),
|
||||
previous: false,
|
||||
fresh: true,
|
||||
|
@ -229,13 +214,13 @@ impl<'i, P: UriPart> Formatter<'i, P> {
|
|||
// cases for both Path and Query, and doing it this way allows us to
|
||||
// keep the uri part generic _generic_ in other implementations that use
|
||||
// `write_raw`.
|
||||
if self.fresh && self.delimiter == '/' {
|
||||
if self.fresh && P::DELIMITER == '/' {
|
||||
if self.previous {
|
||||
self.inner.write_char(self.delimiter)?;
|
||||
self.inner.write_char(P::DELIMITER)?;
|
||||
}
|
||||
} else if self.fresh && self.delimiter == '&' {
|
||||
} else if self.fresh && P::DELIMITER == '&' {
|
||||
if self.previous {
|
||||
self.inner.write_char(self.delimiter)?;
|
||||
self.inner.write_char(P::DELIMITER)?;
|
||||
}
|
||||
|
||||
if !self.prefixes.is_empty() {
|
||||
|
@ -457,8 +442,9 @@ impl<'a> UriArguments<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
let query: Option<Cow<'static, str>> = self.query.map(|query| match query {
|
||||
Static(query) => query.into(),
|
||||
let query: Option<Cow<'_, str>> = self.query.and_then(|q| match q {
|
||||
Static(query) => Some(query.into()),
|
||||
Dynamic(args) if args.is_empty() => None,
|
||||
Dynamic(args) => {
|
||||
let mut string = String::new();
|
||||
{
|
||||
|
@ -472,7 +458,7 @@ impl<'a> UriArguments<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
string.into()
|
||||
Some(string.into())
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -5,12 +5,30 @@ use uri::{self, UriPart, UriDisplay};
|
|||
|
||||
/// Conversion trait for parameters used in [`uri!`] invocations.
|
||||
///
|
||||
/// Rocket provides a blanket implementation for all types that implement
|
||||
/// [`UriDisplay`]. As such, this trait typically does not need to be implemented.
|
||||
/// Instead, implement [`UriDisplay`].
|
||||
///
|
||||
/// # Overview
|
||||
///
|
||||
/// In addition to implementing [`UriDisplay`], to use a custom type in a `uri!`
|
||||
/// expression, the `FromUriParam` trait must be implemented. The `UriDisplay`
|
||||
/// derive automatically generates _identity_ implementations of `FromUriParam`,
|
||||
/// so in the majority of cases, as with `UriDisplay`, this trait is never
|
||||
/// implemented manually.
|
||||
///
|
||||
/// In the rare case that `UriDisplay` is implemented manually, this trait, too,
|
||||
/// must be implemented explicitly. In the majority of cases, implementation can
|
||||
/// be automated. Rocket provides the [`impl_from_uri_param_identity`] macro to
|
||||
/// generate the _identity_ implementations automatically. For a type `T`, these
|
||||
/// are:
|
||||
///
|
||||
/// * `impl<P: UriPart> FromUriParam<P, T> for T`
|
||||
/// * `impl<'x, P: UriPart> FromUriParam<P, &'x T> for T`
|
||||
/// * `impl<'x, P: UriPart> FromUriParam<P, &'x mut T> for T`
|
||||
///
|
||||
/// See [`impl_from_uri_param_identity`] for usage details.
|
||||
///
|
||||
/// [`impl_from_uri_param_identity`]: ../macro.impl_from_uri_param_identity.html
|
||||
///
|
||||
/// # Code Generation
|
||||
///
|
||||
/// This trait is invoked once per expression passed into a [`uri!`] invocation.
|
||||
/// In particular, for a route URI parameter of type `T` and a user-supplied
|
||||
/// expression of type `S`, `<T as FromUriParam<S>>::from_uri_param` is
|
||||
|
@ -51,7 +69,30 @@ use uri::{self, UriPart, UriDisplay};
|
|||
///
|
||||
/// # Provided Implementations
|
||||
///
|
||||
/// See [Foreign Impls](#foreign-impls) for implementations provided by Rocket.
|
||||
/// The following types have _identity_ implementations:
|
||||
///
|
||||
/// * `String`, `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `u8`, `u16`,
|
||||
/// `u32`, `u64`, `u128`, `usize`, `f32`, `f64`, `bool`, `IpAddr`,
|
||||
/// `Ipv4Addr`, `Ipv6Addr`, `&str`, `&RawStr`, `Cow<str>`
|
||||
///
|
||||
/// The following conversions are implemented:
|
||||
///
|
||||
/// * `&str` to `String`
|
||||
/// * `&str` to `RawStr`
|
||||
/// * `String` to `&str`
|
||||
/// * `String` to `RawStr`
|
||||
///
|
||||
/// The following types have _identity_ implementations _only in [`Path`]_:
|
||||
///
|
||||
/// * `&Path`, `PathBuf`
|
||||
///
|
||||
/// The following conversions are implemented _only in [`Path`]_:
|
||||
///
|
||||
/// * `&str` to `&Path`
|
||||
/// * `&str` to `PathBuf`
|
||||
/// * `PathBuf` to `&Path`
|
||||
///
|
||||
/// See [Foreign Impls](#foreign-impls) for all provided implementations.
|
||||
///
|
||||
/// # Implementing
|
||||
///
|
||||
|
@ -145,6 +186,7 @@ use uri::{self, UriPart, UriDisplay};
|
|||
/// [`uri!`]: ::rocket_codegen::uri
|
||||
/// [`UriDisplay`]: uri::UriDisplay
|
||||
/// [`FromUriParam::Target`]: uri::FromUriParam::Target
|
||||
/// [`Path`]: uri::Path
|
||||
pub trait FromUriParam<P: UriPart, T> {
|
||||
/// The resulting type of this conversion.
|
||||
type Target: UriDisplay<P>;
|
||||
|
@ -155,57 +197,121 @@ pub trait FromUriParam<P: UriPart, T> {
|
|||
fn from_uri_param(param: T) -> Self::Target;
|
||||
}
|
||||
|
||||
impl<P: UriPart, T: UriDisplay<P>> FromUriParam<P, T> for T {
|
||||
type Target = T;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: T) -> T { param }
|
||||
use std::{borrow::Cow, net::{IpAddr, Ipv4Addr, Ipv6Addr}};
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! impl_conversion_ref {
|
||||
($(($($l:tt)+) $A:ty => $B:ty),*) => ( impl_conversion_ref!(@_ $(($($l)+,) $A => $B),*); );
|
||||
($($A:ty => $B:ty),*) => ( impl_conversion_ref!(@_ $(() $A => $B),*); );
|
||||
|
||||
(@_ $(($($l:tt)*) $A:ty => $B:ty),*) => ($(
|
||||
impl_conversion_ref!([P] ($($l)* P: $crate::uri::UriPart) $A => $B);
|
||||
)*);
|
||||
|
||||
($([$P:ty] ($($l:tt)*) $A:ty => $B:ty),*) => ($(
|
||||
impl_conversion_ref!(@_ [$P] ($($l)*) $A => $B);
|
||||
impl_conversion_ref!(@_ [$P] ('x, $($l)*) &'x $A => $B);
|
||||
impl_conversion_ref!(@_ [$P] ('x, $($l)*) &'x mut $A => $B);
|
||||
)*);
|
||||
|
||||
($([$P:ty] $A:ty => $B:ty),*) => ( impl_conversion_ref!($([$P] () $A => $B),*););
|
||||
|
||||
(@_ [$P:ty] ($($l:tt)*) $A:ty => $B:ty) => (
|
||||
impl<$($l)*> $crate::uri::FromUriParam<$P, $A> for $B {
|
||||
type Target = $A;
|
||||
#[inline(always)] fn from_uri_param(param: $A) -> $A { param }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
impl<'a, P: UriPart, T: UriDisplay<P>> FromUriParam<P, &'a T> for T {
|
||||
type Target = &'a T;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'a T) -> &'a T { param }
|
||||
/// Macro to automatically generated _identity_ [`FromUriParam`] trait
|
||||
/// implementations.
|
||||
///
|
||||
/// For a type `T`, the _identity_ implementations of `FromUriParam` are:
|
||||
///
|
||||
/// * `impl UriPart> FromUriParam<P, T> for T`
|
||||
/// * `impl<'x> FromUriParam<P, &'x T> for T`
|
||||
/// * `impl<'x> FromUriParam<P, &'x mut T> for T`
|
||||
///
|
||||
/// where `P` is one of:
|
||||
///
|
||||
/// * `P: UriPart` (the generic `P`)
|
||||
/// * [`Path`]
|
||||
/// * [`Query`]
|
||||
///
|
||||
/// This macro can be invoked in four ways:
|
||||
///
|
||||
/// 1. `impl_from_uri_param_identity!(Type);`
|
||||
///
|
||||
/// Generates the three _identity_ implementations for the generic `P`.
|
||||
///
|
||||
/// * Example: `impl_from_uri_param_identity!(MyType);`
|
||||
/// * Generates: `impl<P: UriPart> FromUriParam<P, _> for MyType { ... }`
|
||||
///
|
||||
/// 2. `impl_from_uri_param_identity!((generics*) Type);`
|
||||
///
|
||||
/// Generates the three _identity_ implementations for the generic `P`,
|
||||
/// adding the tokens `generics` to the `impl` generics of the generated
|
||||
/// implementation.
|
||||
///
|
||||
/// * Example: `impl_from_uri_param_identity!(('a) MyType<'a>);`
|
||||
/// * Generates: `impl<'a, P: UriPart> FromUriParam<P, _> for MyType<'a> { ... }`
|
||||
///
|
||||
/// 3. `impl_from_uri_param_identity!([Part] Type);`
|
||||
///
|
||||
/// Generates the three _identity_ implementations for the `UriPart`
|
||||
/// `Part`, where `Part` is a path to [`Path`] or [`Query`].
|
||||
///
|
||||
/// * Example: `impl_from_uri_param_identity!([Path] MyType);`
|
||||
/// * Generates: `impl FromUriParam<Path, _> for MyType { ... }`
|
||||
///
|
||||
/// 4. `impl_from_uri_param_identity!([Part] (generics*) Type);`
|
||||
///
|
||||
/// See 2 and 3.
|
||||
///
|
||||
/// * Example: `impl_from_uri_param_identity!([Path] ('a) MyType<'a>);`
|
||||
/// * Generates: `impl<'a> FromUriParam<Path, _> for MyType<'a> { ... }`
|
||||
///
|
||||
/// [`FromUriParam`]: uri::FromUriParam
|
||||
/// [`Path`]: uri::Path
|
||||
/// [`Query`]: uri::Query
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! impl_from_uri_param_identity {
|
||||
($(($($l:tt)*) $T:ty),*) => ($( impl_conversion_ref!(($($l)*) $T => $T); )*);
|
||||
($([$P:ty] ($($l:tt)*) $T:ty),*) => ($( impl_conversion_ref!([$P] ($($l)*) $T => $T); )*);
|
||||
($([$P:ty] $T:ty),*) => ($( impl_conversion_ref!([$P] $T => $T); )*);
|
||||
($($T:ty),*) => ($( impl_conversion_ref!($T => $T); )*);
|
||||
}
|
||||
|
||||
impl<'a, P: UriPart, T: UriDisplay<P>> FromUriParam<P, &'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_from_uri_param_identity! {
|
||||
String,
|
||||
i8, i16, i32, i64, i128, isize,
|
||||
u8, u16, u32, u64, u128, usize,
|
||||
f32, f64, bool,
|
||||
IpAddr, Ipv4Addr, Ipv6Addr
|
||||
}
|
||||
|
||||
/// A no cost conversion allowing an `&str` to be used in place of a `String`.
|
||||
impl<'a, P: UriPart> FromUriParam<P, &'a str> for String {
|
||||
type Target = &'a str;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'a str) -> &'a str { param }
|
||||
impl_from_uri_param_identity! {
|
||||
('a) &'a str,
|
||||
('a) &'a RawStr,
|
||||
('a) Cow<'a, str>
|
||||
}
|
||||
|
||||
/// A no cost conversion allowing an `&str` to be used in place of an `&RawStr`.
|
||||
impl<'a, 'b, P: UriPart> FromUriParam<P, &'a str> for &'b RawStr {
|
||||
type Target = &'a str;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'a str) -> &'a str { param }
|
||||
impl_conversion_ref! {
|
||||
('a) &'a str => String,
|
||||
('a, 'b) &'a str => &'b RawStr,
|
||||
|
||||
('a) String => &'a str,
|
||||
('a) String => &'a RawStr
|
||||
}
|
||||
|
||||
/// A no cost conversion allowing a `String` to be used in place of an `&RawStr`.
|
||||
impl<'a, P: UriPart> FromUriParam<P, String> for &'a RawStr {
|
||||
type Target = String;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: String) -> String { param }
|
||||
}
|
||||
impl_from_uri_param_identity!([uri::Path] ('a) &'a Path);
|
||||
impl_from_uri_param_identity!([uri::Path] PathBuf);
|
||||
|
||||
/// A no cost conversion allowing a `String` to be used in place of an `&str`.
|
||||
impl<'a, P: UriPart> FromUriParam<P, String> for &'a str {
|
||||
type Target = String;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: String) -> String { param }
|
||||
}
|
||||
|
||||
/// A no cost conversion allowing an `&Path` to be used in place of a `PathBuf`.
|
||||
impl<'a> FromUriParam<uri::Path, &'a Path> for PathBuf {
|
||||
type Target = &'a Path;
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'a Path) -> &'a Path { param }
|
||||
impl_conversion_ref! {
|
||||
[uri::Path] ('a) &'a Path => PathBuf,
|
||||
[uri::Path] ('a) PathBuf => &'a Path
|
||||
}
|
||||
|
||||
/// A no cost conversion allowing an `&str` to be used in place of a `PathBuf`.
|
||||
|
@ -218,9 +324,18 @@ impl<'a> FromUriParam<uri::Path, &'a str> for PathBuf {
|
|||
}
|
||||
}
|
||||
|
||||
/// A no cost conversion allowing any `T` to be used in place of an `Option<T>`
|
||||
/// in path parts.
|
||||
impl<A, T: FromUriParam<uri::Path, A>> FromUriParam<uri::Path, A> for Option<T> {
|
||||
/// A no cost conversion allowing an `&&str` to be used in place of a `PathBuf`.
|
||||
impl<'a, 'b> FromUriParam<uri::Path, &'a &'b str> for PathBuf {
|
||||
type Target = &'b Path;
|
||||
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: &'a &'b str) -> &'b Path {
|
||||
Path::new(*param)
|
||||
}
|
||||
}
|
||||
|
||||
/// A no cost conversion allowing any `T` to be used in place of an `Option<T>`.
|
||||
impl<P: UriPart, A, T: FromUriParam<P, A>> FromUriParam<P, A> for Option<T> {
|
||||
type Target = T::Target;
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -229,9 +344,8 @@ impl<A, T: FromUriParam<uri::Path, A>> FromUriParam<uri::Path, A> for Option<T>
|
|||
}
|
||||
}
|
||||
|
||||
/// A no cost conversion allowing any `T` to be used in place of an `Result<T,
|
||||
/// E>` in path parts.
|
||||
impl<A, E, T: FromUriParam<uri::Path, A>> FromUriParam<uri::Path, A> for Result<T, E> {
|
||||
/// A no cost conversion allowing `T` to be used in place of an `Result<T, E>`.
|
||||
impl<P: UriPart, A, E, T: FromUriParam<P, A>> FromUriParam<P, A> for Result<T, E> {
|
||||
type Target = T::Target;
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -50,7 +50,9 @@ mod private {
|
|||
/// [`Path`]: uri::Path
|
||||
/// [`UriDisplay`]: uri::UriDisplay
|
||||
/// [`Formatter`]: uri::Formatter
|
||||
pub trait UriPart: private::Sealed { }
|
||||
pub trait UriPart: private::Sealed {
|
||||
const DELIMITER: char;
|
||||
}
|
||||
|
||||
/// Marker type indicating use of a type for the path [`UriPart`] of a URI.
|
||||
///
|
||||
|
@ -77,5 +79,10 @@ pub enum Path { }
|
|||
/// [`UriPart`]: uri::UriPart
|
||||
pub enum Query { }
|
||||
|
||||
impl UriPart for Path { }
|
||||
impl UriPart for Query { }
|
||||
impl UriPart for Path {
|
||||
const DELIMITER: char = '/';
|
||||
}
|
||||
|
||||
impl UriPart for Query {
|
||||
const DELIMITER: char = '&';
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use std::{fmt, path};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use percent_encoding::utf8_percent_encode;
|
||||
use RawStr;
|
||||
use uri::{Uri, UriPart, Path, Query, Formatter};
|
||||
|
||||
use uri::{Uri, UriPart, Path, Query, Formatter, UNSAFE_PATH_ENCODE_SET};
|
||||
use {RawStr, ext::Normalize};
|
||||
|
||||
/// Trait implemented by types that can be displayed as part of a URI in `uri!`.
|
||||
/// Trait implemented by types that can be displayed as part of a URI in
|
||||
/// [`uri!`].
|
||||
///
|
||||
/// Types implementing this trait can be displayed in a URI-safe manner. Unlike
|
||||
/// `Display`, the string written by a `UriDisplay` implementation must be
|
||||
|
@ -14,7 +13,7 @@ use {RawStr, ext::Normalize};
|
|||
/// percent-encoded or consist only of characters that are alphanumeric, "-",
|
||||
/// ".", "_", or "~" - the "unreserved" characters.
|
||||
///
|
||||
/// # Marker Generic: `UriDisplay<Path>` vs. `UriDisplay<Query>`
|
||||
/// # Marker Generic: `Path`, `Query`
|
||||
///
|
||||
/// The [`UriPart`] parameter `P` in `UriDisplay<P>` must be either [`Path`] or
|
||||
/// [`Query`] (see the [`UriPart`] documentation for how this is enforced),
|
||||
|
@ -51,11 +50,12 @@ use {RawStr, ext::Normalize};
|
|||
///
|
||||
/// # Code Generation
|
||||
///
|
||||
/// When the `uri!` macro is used to generate a URI for a route, the types for
|
||||
/// When the [`uri!`] macro is used to generate a URI for a route, the types for
|
||||
/// the route's _path_ URI parameters must implement `UriDisplay<Path>`, while
|
||||
/// types in the route's query parameters must implement `UriDisplay<Query>`.
|
||||
/// The `UriDisplay` implementation for these types is used when generating the
|
||||
/// URI.
|
||||
/// Any parameters ignored with `_` must be of a type that implements
|
||||
/// [`Ignorable`]. 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 route:
|
||||
|
@ -64,7 +64,7 @@ use {RawStr, ext::Normalize};
|
|||
/// # #![feature(proc_macro_hygiene, decl_macro)]
|
||||
/// # #[macro_use] extern crate rocket;
|
||||
/// #[get("/item/<id>?<track>")]
|
||||
/// fn get_item(id: i32, track: String) { /* .. */ }
|
||||
/// fn get_item(id: i32, track: Option<String>) { /* .. */ }
|
||||
/// ```
|
||||
///
|
||||
/// A URI for this route can be generated as follows:
|
||||
|
@ -74,7 +74,7 @@ use {RawStr, ext::Normalize};
|
|||
/// # #[macro_use] extern crate rocket;
|
||||
/// # type T = ();
|
||||
/// # #[get("/item/<id>?<track>")]
|
||||
/// # fn get_item(id: i32, track: String) { /* .. */ }
|
||||
/// # fn get_item(id: i32, track: Option<String>) { /* .. */ }
|
||||
/// #
|
||||
/// // With unnamed parameters.
|
||||
/// uri!(get_item: 100, "inbound");
|
||||
|
@ -82,6 +82,11 @@ use {RawStr, ext::Normalize};
|
|||
/// // With named parameters.
|
||||
/// uri!(get_item: id = 100, track = "inbound");
|
||||
/// uri!(get_item: track = "inbound", id = 100);
|
||||
///
|
||||
/// // Ignoring `track`.
|
||||
/// uri!(get_item: 100, _);
|
||||
/// uri!(get_item: id = 100, track = _);
|
||||
/// uri!(get_item: track = _, id = 100);
|
||||
/// ```
|
||||
///
|
||||
/// After verifying parameters and their types, Rocket will generate code
|
||||
|
@ -96,10 +101,12 @@ use {RawStr, ext::Normalize};
|
|||
/// ```
|
||||
///
|
||||
/// For this expression to typecheck, `i32` must implement `UriDisplay<Path>`
|
||||
/// and `Value` must implement `UriDisplay<Query>`. As can be seen, the
|
||||
/// implementations will be used to display the value in a URI-safe manner.
|
||||
/// and `&str` must implement `UriDisplay<Query>`. What's more, when `track` is
|
||||
/// ignored, `Option<String>` is required to implement [`Ignorable`]. As can be
|
||||
/// seen, the implementations will be used to display the value in a URI-safe
|
||||
/// manner.
|
||||
///
|
||||
/// [`uri!`]: /rocket_codegen/#typed-uris-uri
|
||||
/// [`uri!`]: ../../../rocket_codegen/macro.uri.html
|
||||
///
|
||||
/// # Provided Implementations
|
||||
///
|
||||
|
@ -161,6 +168,8 @@ use {RawStr, ext::Normalize};
|
|||
/// If the `Result` is `Ok`, uses the implementation of `UriDisplay` for
|
||||
/// `T`. Otherwise, nothing is rendered.
|
||||
///
|
||||
/// [`FromUriParam`]: uri::FromUriParam
|
||||
///
|
||||
/// # Deriving
|
||||
///
|
||||
/// Manually implementing `UriDisplay` should be done with care. For most use
|
||||
|
@ -195,6 +204,7 @@ use {RawStr, ext::Normalize};
|
|||
/// [`Formatter::write_value()`] for every unnamed field. See the [`UriDisplay`
|
||||
/// derive] documentation for full details.
|
||||
///
|
||||
/// [`Ignorable`]: uri::Ignorable
|
||||
/// [`UriDisplay` derive]: ../../../rocket_codegen/derive.UriDisplay.html
|
||||
/// [`Formatter::write_named_value()`]: uri::Formatter::write_named_value()
|
||||
/// [`Formatter::write_value()`]: uri::Formatter::write_value()
|
||||
|
@ -250,19 +260,22 @@ use {RawStr, ext::Normalize};
|
|||
/// }
|
||||
///
|
||||
/// use std::fmt;
|
||||
/// use rocket::http::uri::{Formatter, UriDisplay, Path};
|
||||
/// use rocket::http::impl_from_uri_param_identity;
|
||||
/// use rocket::http::uri::{Formatter, FromUriParam, UriDisplay, Path};
|
||||
/// use rocket::response::Redirect;
|
||||
///
|
||||
/// impl UriDisplay<Path> for Name {
|
||||
/// // Delegates to the `UriDisplay` implementation for `String` via the
|
||||
/// // call to `write_value` to ensure /// that the written string is
|
||||
/// // URI-safe. In this case, the string will /// be percent encoded.
|
||||
/// // call to `write_value` to ensure that the written string is
|
||||
/// // URI-safe. In this case, the string will be percent encoded.
|
||||
/// // Prefixes the inner name with `name:`.
|
||||
/// fn fmt(&self, f: &mut Formatter<Path>) -> fmt::Result {
|
||||
/// f.write_value(&format!("name:{}", self.0))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl_from_uri_param_identity!([Path] Name);
|
||||
///
|
||||
/// #[get("/name/<name>")]
|
||||
/// fn redirector(name: Name) -> Redirect {
|
||||
/// Redirect::to(uri!(real: name))
|
||||
|
@ -281,26 +294,15 @@ pub trait UriDisplay<P: UriPart> {
|
|||
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result;
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for &'a UriDisplay<Path> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
UriDisplay::fmt(*self, &mut <Formatter<Path>>::new(f))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for &'a UriDisplay<Query> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
UriDisplay::fmt(*self, &mut <Formatter<Query>>::new(f))
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl<P: UriPart> UriDisplay<P> for RawStr {
|
||||
impl<'a, P: UriPart> fmt::Display for &'a UriDisplay<P> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
|
||||
f.write_raw(&Uri::percent_encode(self.as_str()))
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
UriDisplay::fmt(*self, &mut <Formatter<P>>::new(f))
|
||||
}
|
||||
}
|
||||
|
||||
// Direct implementations: these are the leaves of a call to `UriDisplay::fmt`.
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl<P: UriPart> UriDisplay<P> for str {
|
||||
#[inline(always)]
|
||||
|
@ -309,37 +311,19 @@ impl<P: UriPart> UriDisplay<P> for str {
|
|||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl<'a, P: UriPart> UriDisplay<P> for Cow<'a, str> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
|
||||
f.write_raw(&Uri::percent_encode(self))
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string.
|
||||
impl<P: UriPart> UriDisplay<P> for String {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
|
||||
f.write_raw(&Uri::percent_encode(self.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes each segment in the path and normalizes separators.
|
||||
impl UriDisplay<Path> for path::PathBuf {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<Path>) -> fmt::Result {
|
||||
self.as_path().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes each segment in the path and normalizes separators.
|
||||
impl UriDisplay<Path> for path::Path {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter<Path>) -> fmt::Result {
|
||||
let string = self.normalized_str();
|
||||
let enc: Cow<str> = utf8_percent_encode(&string, UNSAFE_PATH_ENCODE_SET).into();
|
||||
f.write_raw(&enc)
|
||||
use std::path::Component;
|
||||
|
||||
for component in self.components() {
|
||||
match component {
|
||||
Component::Prefix(_) | Component::RootDir => continue,
|
||||
_ => f.write_value(&component.as_os_str().to_string_lossy())?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,38 +349,240 @@ impl_with_display! {
|
|||
IpAddr, Ipv4Addr, Ipv6Addr
|
||||
}
|
||||
|
||||
macro_rules! impl_for_ref {
|
||||
($($T:ty),+) => {$(
|
||||
/// Uses the implementation of `UriDisplay` for `T`.
|
||||
impl<'a, P: UriPart, T: UriDisplay<P> + ?Sized> UriDisplay<P> for $T {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
|
||||
UriDisplay::fmt(*self, f)
|
||||
}
|
||||
}
|
||||
)+}
|
||||
// These are second level implementations: they all defer to an existing
|
||||
// implementation.
|
||||
|
||||
/// Percent-encodes the raw string. Defers to `str`.
|
||||
impl<P: UriPart> UriDisplay<P> for RawStr {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
|
||||
self.as_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl_for_ref!(&'a mut T, &'a T);
|
||||
/// Percent-encodes the raw string. Defers to `str`.
|
||||
impl<P: UriPart> UriDisplay<P> for String {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
|
||||
self.as_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes the raw string. Defers to `str`.
|
||||
impl<'a, P: UriPart> UriDisplay<P> for Cow<'a, str> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
|
||||
self.as_ref().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encodes each segment in the path and normalizes separators.
|
||||
impl UriDisplay<Path> for path::PathBuf {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<Path>) -> fmt::Result {
|
||||
self.as_path().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Defers to the `UriDisplay<P>` implementation for `T`.
|
||||
impl<'a, P: UriPart, T: UriDisplay<P> + ?Sized> UriDisplay<P> for &'a T {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
|
||||
UriDisplay::fmt(*self, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Defers to the `UriDisplay<P>` implementation for `T`.
|
||||
impl<'a, P: UriPart, T: UriDisplay<P> + ?Sized> UriDisplay<P> for &'a mut T {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
|
||||
UriDisplay::fmt(*self, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Defers to the `UriDisplay<Query>` implementation for `T`.
|
||||
impl<T: UriDisplay<Query>> UriDisplay<Query> for Option<T> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
|
||||
if let Some(v) = self {
|
||||
f.write_value(&v)?;
|
||||
match self {
|
||||
Some(v) => v.fmt(f),
|
||||
None => Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, T: UriDisplay<Query>> UriDisplay<Query> for Result<T, E> {
|
||||
/// Defers to the `UriDisplay<Query>` implementation for `T`.
|
||||
impl<T: UriDisplay<Query>, E> UriDisplay<Query> for Result<T, E> {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
|
||||
if let Ok(v) = self {
|
||||
f.write_value(&v)?;
|
||||
match self {
|
||||
Ok(v) => v.fmt(f),
|
||||
Err(_) => Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// And finally, the `Ignorable` trait, which has sugar of `_` in the `uri!`
|
||||
// macro, which expands to a typecheck.
|
||||
|
||||
/// Trait implemented by types that can be ignored in `uri!`.
|
||||
///
|
||||
/// When a parameter is explicitly ignored in `uri!` by supplying `_` as the
|
||||
/// parameter's value, that parameter's type is required to implement this
|
||||
/// trait for the corresponding `UriPart`.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![feature(proc_macro_hygiene, decl_macro)]
|
||||
/// # #[macro_use] extern crate rocket;
|
||||
/// #[get("/item/<id>?<track>")]
|
||||
/// fn get_item(id: i32, track: Option<u8>) { /* .. */ }
|
||||
///
|
||||
/// // Ignore the `track` parameter: `Option<u8>` must be `Ignorable`.
|
||||
/// uri!(get_item: 100, _);
|
||||
/// uri!(get_item: id = 100, track = _);
|
||||
///
|
||||
/// // Provide a value for `track`.
|
||||
/// uri!(get_item: 100, 4);
|
||||
/// uri!(get_item: id = 100, track = 4);
|
||||
/// ```
|
||||
///
|
||||
/// # Implementations
|
||||
///
|
||||
/// Only `Option<T>` and `Result<T, E>` implement this trait. You may implement
|
||||
/// this trait for your own ignorable types as well:
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate rocket;
|
||||
/// use rocket::http::uri::{Ignorable, Query};
|
||||
///
|
||||
/// # struct MyType;
|
||||
/// impl Ignorable<Query> for MyType { }
|
||||
/// ```
|
||||
pub trait Ignorable<P: UriPart> { }
|
||||
|
||||
impl<T> Ignorable<Query> for Option<T> { }
|
||||
impl<T, E> Ignorable<Query> for Result<T, E> { }
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn assert_ignorable<P: UriPart, T: Ignorable<P>>() { }
|
||||
|
||||
#[cfg(test)]
|
||||
mod uri_display_tests {
|
||||
use std::path;
|
||||
use uri::{FromUriParam, UriDisplay, Query, Path};
|
||||
|
||||
macro_rules! uri_display {
|
||||
(<$P:ident, $Target:ty> $source:expr) => ({
|
||||
let tmp = $source;
|
||||
let target = <$Target as FromUriParam<$P, _>>::from_uri_param(tmp);
|
||||
format!("{}", &target as &dyn UriDisplay<$P>)
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! assert_display {
|
||||
(<$P:ident, $Target:ty> $source:expr, $expected:expr) => ({
|
||||
assert_eq!(uri_display!(<$P, $Target> $source), $expected);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uri_display_encoding() {
|
||||
assert_display!(<Query, String> "hello", "hello");
|
||||
assert_display!(<Query, String> "hi hi", "hi%20hi");
|
||||
assert_display!(<Query, &str> "hi hi", "hi%20hi");
|
||||
assert_display!(<Query, &str> &"hi hi", "hi%20hi");
|
||||
assert_display!(<Query, usize> 10, "10");
|
||||
assert_display!(<Query, u8> 10, "10");
|
||||
assert_display!(<Query, i32> 10, "10");
|
||||
assert_display!(<Query, isize> 10, "10");
|
||||
|
||||
assert_display!(<Path, String> "hello", "hello");
|
||||
assert_display!(<Path, String> "hi hi", "hi%20hi");
|
||||
assert_display!(<Path, &str> "hi hi", "hi%20hi");
|
||||
assert_display!(<Path, &str> &"hi hi", "hi%20hi");
|
||||
assert_display!(<Path, usize> 10, "10");
|
||||
assert_display!(<Path, u8> 10, "10");
|
||||
assert_display!(<Path, i32> 10, "10");
|
||||
assert_display!(<Path, isize> 10, "10");
|
||||
|
||||
assert_display!(<Query, &str> &"hi there", "hi%20there");
|
||||
assert_display!(<Query, isize> &10, "10");
|
||||
assert_display!(<Query, u8> &10, "10");
|
||||
|
||||
assert_display!(<Path, &str> &"hi there", "hi%20there");
|
||||
assert_display!(<Path, isize> &10, "10");
|
||||
assert_display!(<Path, u8> &10, "10");
|
||||
|
||||
assert_display!(<Path, Option<&str>> &"hi there", "hi%20there");
|
||||
assert_display!(<Path, Option<isize>> &10, "10");
|
||||
assert_display!(<Path, Option<u8>> &10, "10");
|
||||
assert_display!(<Query, Option<&str>> &"hi there", "hi%20there");
|
||||
assert_display!(<Query, Option<isize>> &10, "10");
|
||||
assert_display!(<Query, Option<u8>> &10, "10");
|
||||
|
||||
assert_display!(<Path, Result<&str, usize>> &"hi there", "hi%20there");
|
||||
assert_display!(<Path, Result<isize, &str>> &10, "10");
|
||||
assert_display!(<Path, Result<u8, String>> &10, "10");
|
||||
assert_display!(<Query, Result<&str, usize>> &"hi there", "hi%20there");
|
||||
assert_display!(<Query, Result<isize, &str>> &10, "10");
|
||||
assert_display!(<Query, Result<u8, String>> &10, "10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paths() {
|
||||
assert_display!(<Path, path::PathBuf> "hello", "hello");
|
||||
assert_display!(<Path, path::PathBuf> "hi there", "hi%20there");
|
||||
assert_display!(<Path, path::PathBuf> "hello/world", "hello/world");
|
||||
assert_display!(<Path, path::PathBuf> "hello//world", "hello/world");
|
||||
assert_display!(<Path, path::PathBuf> "hello/ world", "hello/%20world");
|
||||
|
||||
assert_display!(<Path, path::PathBuf> "hi/wo rld", "hi/wo%20rld");
|
||||
|
||||
assert_display!(<Path, path::PathBuf> &"hi/wo rld", "hi/wo%20rld");
|
||||
assert_display!(<Path, path::PathBuf> &"hi there", "hi%20there");
|
||||
}
|
||||
|
||||
struct Wrapper<T>(T);
|
||||
|
||||
impl<A, T: FromUriParam<Query, A>> FromUriParam<Query, A> for Wrapper<T> {
|
||||
type Target = T::Target;
|
||||
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: A) -> Self::Target {
|
||||
T::from_uri_param(param)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromUriParam<Path, usize> for Wrapper<usize> {
|
||||
type Target = usize;
|
||||
|
||||
#[inline(always)]
|
||||
fn from_uri_param(param: usize) -> Self::Target {
|
||||
param
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uri_display_encoding_wrapped() {
|
||||
assert_display!(<Query, Option<Wrapper<&str>>> &"hi there", "hi%20there");
|
||||
assert_display!(<Query, Option<Wrapper<&str>>> "hi there", "hi%20there");
|
||||
|
||||
assert_display!(<Query, Option<Wrapper<isize>>> 10, "10");
|
||||
assert_display!(<Query, Option<Wrapper<usize>>> 18, "18");
|
||||
assert_display!(<Path, Option<Wrapper<usize>>> 238, "238");
|
||||
|
||||
assert_display!(<Path, Result<Option<Wrapper<usize>>, usize>> 238, "238");
|
||||
assert_display!(<Path, Option<Result<Wrapper<usize>, usize>>> 123, "123");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_ignorables() {
|
||||
use uri::assert_ignorable;
|
||||
|
||||
assert_ignorable::<Query, Option<usize>>();
|
||||
assert_ignorable::<Query, Option<Wrapper<usize>>>();
|
||||
assert_ignorable::<Query, Result<Wrapper<usize>, usize>>();
|
||||
assert_ignorable::<Query, Option<Result<Wrapper<usize>, usize>>>();
|
||||
assert_ignorable::<Query, Result<Option<Wrapper<usize>>, usize>>();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue