Parameterize 'UriDisplay' with 'Path' or 'Query'.

This commit introduces the sealed `UriPart` marker trait as well as the
implementing `Path` and `Query` marker types, allowing for parts of a
URI to be distinguished at the type level. Consequently, `UriDisplay`
has been parameterized with `P: UriPart`, creating `UriDisplay<Path>`
and `UriDisplay<Query>`. The effect of this change is improved type
safely for URI rendering as well as the ability to omit rendering values
in query parts via `Option` and `Result`.

The `UriDisplay` derive was replaced by `UriDisplayQuery` and
`UriDisplayPath` which derive implementations for `UriDisplay<Path>`
and `UriDisplay<Query>`, respectively.

This commit also works around a rustdoc visibility issue by creating a
hidden `http::private` module.

Finally, this commit also removes the now vestigial use of the
`rustc_private` feature in codegen.

Fixes #827.
This commit is contained in:
Sergio Benitez 2018-11-27 10:01:47 -06:00
parent 543b07a4ba
commit b7db74144f
38 changed files with 1164 additions and 438 deletions

View File

@ -18,7 +18,7 @@ database_attribute = []
proc-macro = true
[dependencies]
devise = "0.1"
devise = { git = "http://github.com/SergioBenitez/Devise", rev = "b5295e3e" }
quote = "0.6"
[build-dependencies]

View File

@ -18,7 +18,7 @@ proc-macro = true
indexmap = "1.0"
quote = "0.6.1"
rocket_http = { version = "0.4.0-rc.1", path = "../http/" }
devise = "0.1"
devise = { git = "http://github.com/SergioBenitez/Devise", rev = "b5295e3e" }
[build-dependencies]
yansi = { git = "https://github.com/SergioBenitez/yansi", rev = "59c3a91" }

View File

@ -218,7 +218,8 @@ fn query_exprs(route: &Route) -> Option<TokenStream2> {
let mut #ident: Option<#ty> = None;
},
Kind::Multi => quote_spanned! { span =>
let mut __trail = ::rocket::http::SmallVec::<[___r::FormItem; 8]>::new();
let mut __trail =
::rocket::http::private::SmallVec::<[___r::FormItem; 8]>::new();
},
Kind::Static => quote!()
};

View File

@ -115,14 +115,13 @@ crate fn parse_segment(segment: &str, span: Span) -> PResult<Segment> {
crate fn parse_segments(
string: &str,
sep: char,
source: Source,
span: Span
) -> DResult<Vec<Segment>> {
let mut segments = vec![];
let mut diags = Diagnostics::new();
for result in RouteSegment::parse_many(string, sep, source) {
for result in RouteSegment::parse_many(string, source) {
if let Err((segment_string, error)) = result {
diags.push(into_diagnostic(segment_string, string, span, &error));
if let Error::Trailing(..) = error {

View File

@ -81,6 +81,91 @@ fn extract_exprs(internal: &InternalUriParams) -> Result<Vec<&Expr>> {
}
}
fn add_binding(to: &mut Vec<TokenStream2>, ident: &Ident, ty: &Type, expr: &Expr, source: Source) {
let uri_mod = quote!(rocket::http::uri);
let (span, ident_tmp) = (expr.span(), ident.prepend("tmp_"));
let from_uri_param = if source == Source::Query {
quote_spanned!(span => #uri_mod::FromUriParam<#uri_mod::Query, _>)
} else {
quote_spanned!(span => #uri_mod::FromUriParam<#uri_mod::Path, _>)
};
to.push(quote_spanned!(span =>
let #ident_tmp = #expr;
let #ident = <#ty as #from_uri_param>::from_uri_param(#ident_tmp);
));
}
fn explode_path<'a, I: Iterator<Item = (&'a Ident, &'a Type, &'a Expr)>>(
uri: &Origin,
bindings: &mut Vec<TokenStream2>,
mut items: I
) -> TokenStream2 {
let (uri_mod, path) = (quote!(rocket::http::uri), uri.path());
if !path.contains('<') {
return quote!(#uri_mod::UriArgumentsKind::Static(#path));
}
let uri_display = quote!(#uri_mod::UriDisplay<#uri_mod::Path>);
let dyn_exprs = RouteSegment::parse_path(uri).map(|segment| {
let segment = segment.expect("segment okay; prechecked on parse");
match segment.kind {
Kind::Static => {
let string = &segment.string;
quote!(&#string as &dyn #uri_display)
}
Kind::Single | Kind::Multi => {
let (ident, ty, expr) = items.next().expect("one item for each dyn");
add_binding(bindings, &ident, &ty, &expr, Source::Path);
quote_spanned!(expr.span() => &#ident as &dyn #uri_display)
}
}
});
quote!(#uri_mod::UriArgumentsKind::Dynamic(&[#(#dyn_exprs),*]))
}
fn explode_query<'a, I: Iterator<Item = (&'a Ident, &'a Type, &'a Expr)>>(
uri: &Origin,
bindings: &mut Vec<TokenStream2>,
mut items: I
) -> Option<TokenStream2> {
let (uri_mod, query) = (quote!(rocket::http::uri), uri.query()?);
if !query.contains('<') {
return Some(quote!(#uri_mod::UriArgumentsKind::Static(#query)));
}
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 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)
)
}
}
});
Some(quote!(#uri_mod::UriArgumentsKind::Dynamic(&[#(#dyn_exprs),*])))
}
// Returns an Origin URI with the mount point and route path concatinated. The
// query string is mangled by replacing single dynamic parameters in query parts
// (`<param>`) with `param=<param>`.
@ -90,79 +175,33 @@ fn build_origin(internal: &InternalUriParams) -> Origin<'static> {
.unwrap_or("");
let path = format!("{}/{}", mount_point, internal.route_uri.path());
let query = RouteSegment::parse_query(&internal.route_uri).map(|segments| {
segments.map(|r| r.expect("invalid query segment")).map(|seg| {
match (seg.source, seg.kind) {
(Source::Query, Kind::Single) => format!("{k}=<{k}>", k = seg.name),
_ => seg.string.into_owned()
}
}).collect::<Vec<_>>().join("&")
});
let query = internal.route_uri.query();
Origin::new(path, query).to_normalized().into_owned()
}
fn explode<'a, I>(route_str: &str, items: I) -> TokenStream2
where I: Iterator<Item = (&'a Ident, &'a Type, &'a Expr)>
{
// Generate the statements to typecheck each parameter.
// Building <$T as ::rocket::http::uri::FromUriParam<_>>::from_uri_param($e).
let (mut let_bindings, mut fmt_exprs) = (vec![], vec![]);
for (mut ident, ty, expr) in items {
let (span, expr) = (expr.span(), expr);
let ident_tmp = ident.prepend("tmp_");
let_bindings.push(quote_spanned!(span =>
let #ident_tmp = #expr;
let #ident = <#ty as rocket::http::uri::FromUriParam<_>>::from_uri_param(#ident_tmp);
));
// generating: arg tokens for format string
fmt_exprs.push(quote_spanned! { span =>
&#ident as &dyn rocket::http::uri::UriDisplay
});
}
// Convert all of the '<...>' into '{}'.
let mut inside = false;
let fmt_string: String = route_str.chars().filter_map(|c| {
Some(match c {
'<' => { inside = true; '{' }
'>' => { inside = false; '}' }
_ if !inside => c,
_ => return None
})
}).collect();
// Don't allocate if there are no formatting expressions.
if fmt_exprs.is_empty() {
quote!({ #fmt_string.into() })
} else {
quote!({ #(#let_bindings)* format!(#fmt_string, #(#fmt_exprs),*).into() })
}
}
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 the `ident`, `ty`, and `expr` triple.
let mut arguments = internal.fn_args.iter()
// 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));
// Generate an expression for the path and query.
let origin = build_origin(&internal);
let path_param_count = origin.path().matches('<').count();
let path = explode(origin.path(), arguments.by_ref().take(path_param_count));
let query = Optional(origin.query().map(|q| explode(q, arguments)));
// 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 span = internal.uri_params.route_path.span();
Ok(quote_spanned!(span => {
rocket::http::uri::Origin::new::<
std::borrow::Cow<'static, str>,
std::borrow::Cow<'static, str>,
>(#path, #query)
let mut bindings = vec![];
let uri = build_origin(&internal);
let uri_mod = quote!(rocket::http::uri);
let path = explode_path(&uri, &mut bindings, path_params);
let query = Optional(explode_query(&uri, &mut bindings, query_params));
Ok(quote!({
#(#bindings)*
#uri_mod::UriArguments { path: #path, query: #query, }.into_origin()
}).into())
}

View File

@ -59,7 +59,7 @@ fn validate_struct(gen: &DeriveGenerator, data: Struct) -> Result<()> {
pub fn derive_from_form(input: TokenStream) -> TokenStream {
let form_error = quote!(::rocket::request::FormParseError);
DeriveGenerator::build_for(input, "::rocket::request::FromForm<'__f>")
DeriveGenerator::build_for(input, quote!(impl<'__f> ::rocket::request::FromForm<'__f>))
.generic_support(GenericSupport::Lifetime | GenericSupport::Type)
.replace_generic(0, 0)
.data_support(DataSupport::NamedStruct)

View File

@ -7,7 +7,7 @@ struct Form {
}
pub fn derive_from_form_value(input: TokenStream) -> TokenStream {
DeriveGenerator::build_for(input, "::rocket::request::FromFormValue<'__v>")
DeriveGenerator::build_for(input, quote!(impl<'__v> ::rocket::request::FromFormValue<'__v>))
.generic_support(GenericSupport::None)
.data_support(DataSupport::Enum)
.validate_enum(|generator, data| {

View File

@ -17,7 +17,7 @@ struct FieldAttr {
}
pub fn derive_responder(input: TokenStream) -> TokenStream {
DeriveGenerator::build_for(input, "::rocket::response::Responder<'__r>")
DeriveGenerator::build_for(input, quote!(impl<'__r> ::rocket::response::Responder<'__r>))
.generic_support(GenericSupport::Lifetime)
.data_support(DataSupport::Struct | DataSupport::Enum)
.replace_generic(0, 0)

View File

@ -7,6 +7,7 @@ const NO_EMPTY_FIELDS: &str = "fieldless structs or variants are not supported";
const NO_NULLARY: &str = "nullary items are not supported";
const NO_EMPTY_ENUMS: &str = "empty enums are not supported";
const ONLY_ONE_UNNAMED: &str = "tuple structs or variants must have exactly one field";
const EXACTLY_ONE_FIELD: &str = "struct must have exactly one field";
fn validate_fields(fields: Fields, parent_span: Span) -> Result<()> {
if fields.count() == 0 {
@ -36,15 +37,17 @@ fn validate_enum(gen: &DeriveGenerator, data: Enum) -> Result<()> {
Ok(())
}
pub fn derive_uri_display(input: TokenStream) -> TokenStream {
DeriveGenerator::build_for(input, "::rocket::http::uri::UriDisplay")
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))
.generic_support(GenericSupport::Type | GenericSupport::Lifetime)
.data_support(DataSupport::Struct | DataSupport::Enum)
.validate_enum(validate_enum)
.validate_struct(validate_struct)
.map_type_generic(|_, ident, _| quote!(#ident : ::rocket::http::uri::UriDisplay))
.function(|_, inner| quote! {
fn fmt(&self, f: &mut ::rocket::http::uri::Formatter) -> ::std::fmt::Result {
.map_type_generic(move |_, ident, _| quote!(#ident : #display_trait))
.function(move |_, inner| quote! {
fn fmt(&self, f: &mut #formatter) -> ::std::fmt::Result {
#inner
Ok(())
}
@ -66,3 +69,28 @@ pub fn derive_uri_display(input: TokenStream) -> TokenStream {
})
.to_tokens()
}
pub fn derive_uri_display_path(input: TokenStream) -> TokenStream {
let display_trait = quote!(::rocket::http::uri::UriDisplay<::rocket::http::uri::Path>);
let formatter = quote!(::rocket::http::uri::Formatter<::rocket::http::uri::Path>);
DeriveGenerator::build_for(input, quote!(impl #display_trait))
.data_support(DataSupport::TupleStruct)
.generic_support(GenericSupport::Type | GenericSupport::Lifetime)
.map_type_generic(move |_, ident, _| quote!(#ident : #display_trait))
.validate_fields(|_, fields| match fields.count() {
1 => Ok(()),
_ => Err(fields.span().error(EXACTLY_ONE_FIELD))
})
.function(move |_, inner| quote! {
fn fmt(&self, f: &mut #formatter) -> ::std::fmt::Result {
#inner
Ok(())
}
})
.map_field(|_, field| {
let span = field.span().into();
let accessor = field.accessor();
quote_spanned!(span => f.write_value(&#accessor)?;)
})
.to_tokens()
}

View File

@ -95,12 +95,13 @@ impl ToTokens for MediaType {
let (top, sub) = (self.0.top().as_str(), self.0.sub().as_str());
let (keys, values) = self.0.params().split2();
let (http, cow) = (quote!(::rocket::http), quote!(::std::borrow::Cow));
let cow = quote!(::std::borrow::Cow);
let (pub_http, http) = (quote!(::rocket::http), quote!(::rocket::http::private));
let (http_, http__) = (repeat(&http), repeat(&http));
let (cow_, cow__) = (repeat(&cow), repeat(&cow));
// TODO: Produce less code when possible (for known media types).
tokens.extend(quote!(#http::MediaType {
tokens.extend(quote!(#pub_http::MediaType {
source: #http::Source::None,
top: #http::Indexed::Concrete(#cow::Borrowed(#top)),
sub: #http::Indexed::Concrete(#cow::Borrowed(#sub)),
@ -216,7 +217,7 @@ impl FromMeta for RoutePath {
fn from_meta(meta: MetaItem) -> Result<Self> {
let (origin, string) = (Origin::from_meta(meta)?, StringLit::from_meta(meta)?);
let path_span = string.subspan(1..origin.0.path().len() + 1).expect("path");
let path = parse_segments(origin.0.path(), '/', Source::Path, path_span);
let path = parse_segments(origin.0.path(), Source::Path, path_span);
let query = origin.0.query()
.map(|q| {
@ -227,7 +228,7 @@ impl FromMeta for RoutePath {
// TODO: Show a help message with what's expected.
Err(query_span.error("query cannot contain empty segments").into())
} else {
parse_segments(q, '&', Source::Query, query_span)
parse_segments(q, Source::Query, query_span)
}
}).transpose();

View File

@ -1,7 +1,6 @@
#![feature(proc_macro_diagnostic, proc_macro_span)]
#![feature(crate_visibility_modifier)]
#![feature(transpose_result)]
#![feature(rustc_private)]
#![recursion_limit="128"]
#![doc(html_root_url = "https://api.rocket.rs/v0.4")]
@ -65,8 +64,6 @@ extern crate proc_macro;
extern crate rocket_http as http;
extern crate indexmap;
extern crate syntax_pos;
#[macro_use] mod proc_macro_ext;
mod derive;
mod attribute;
@ -616,21 +613,21 @@ pub fn derive_responder(input: TokenStream) -> TokenStream {
emit!(derive::responder::derive_responder(input))
}
/// Derive for the [`UriDisplay`] trait.
/// Derive for the [`UriDisplay<Query>`] trait.
///
/// The [`UriDisplay`] derive can be applied to enums and structs. When applied
/// to enums, variants must have at least one field. When applied to structs,
/// the struct must have at least one field.
/// The [`UriDisplay<Query>`] derive can be applied to enums and structs. When
/// applied to enums, variants must have at least one field. When applied to
/// structs, the struct must have at least one field.
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// #[derive(UriDisplay)]
/// #[derive(UriDisplayQuery)]
/// enum Kind {
/// A(String),
/// B(usize),
/// }
///
/// #[derive(UriDisplay)]
/// #[derive(UriDisplayQuery)]
/// struct MyStruct {
/// name: String,
/// id: usize,
@ -638,10 +635,10 @@ pub fn derive_responder(input: TokenStream) -> TokenStream {
/// }
/// ```
///
/// Each field's type is required to implement [`UriDisplay`].
/// Each field's type is required to implement [`UriDisplay<Query>`].
///
/// The derive generates an implementation of the [`UriDisplay`] trait. The
/// implementation calls [`Formatter::write_named_value()`] for every named
/// The derive generates an implementation of the [`UriDisplay<Query>`] trait.
/// The implementation calls [`Formatter::write_named_value()`] for every named
/// field, using the field's name (unless overriden, explained next) as the
/// `name` parameter, and [`Formatter::write_value()`] for every unnamed field
/// in the order the fields are declared.
@ -658,9 +655,9 @@ pub fn derive_responder(input: TokenStream) -> TokenStream {
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// # #[derive(UriDisplay)]
/// # #[derive(UriDisplayQuery)]
/// # struct Kind(String);
/// #[derive(UriDisplay)]
/// #[derive(UriDisplayQuery)]
/// struct MyStruct {
/// name: String,
/// id: usize,
@ -675,12 +672,38 @@ pub fn derive_responder(input: TokenStream) -> TokenStream {
/// the example above, the field `MyStruct::kind` is rendered with a name of
/// `type`.
///
/// [`UriDisplay`]: ../rocket/http/uri/trait.UriDisplay.html
/// [`UriDisplay<Query>`]: ../rocket/http/uri/trait.UriDisplay.html
/// [`Formatter::write_named_value()`]: ../rocket/http/uri/struct.Formatter.html#method.write_named_value
/// [`Formatter::write_value()`]: ../rocket/http/uri/struct.Formatter.html#method.write_value
#[proc_macro_derive(UriDisplay, attributes(form))]
pub fn derive_uri_display(input: TokenStream) -> TokenStream {
emit!(derive::uri_display::derive_uri_display(input))
#[proc_macro_derive(UriDisplayQuery, attributes(form))]
pub fn derive_uri_display_query(input: TokenStream) -> TokenStream {
emit!(derive::uri_display::derive_uri_display_query(input))
}
/// Derive for the [`UriDisplay<Path>`] trait.
///
/// The [`UriDisplay<Path>`] derive can only be applied to tuple structs with
/// one field.
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// #[derive(UriDisplayPath)]
/// struct Name(String);
///
/// #[derive(UriDisplayPath)]
/// struct Age(usize);
/// ```
///
/// The field's type is required to implement [`UriDisplay<Path>`].
///
/// The derive generates an implementation of the [`UriDisplay<Path>`] trait.
/// The implementation calls [`Formatter::write_value()`] for the field.
///
/// [`UriDisplay<Path>`]: ../rocket/http/uri/trait.UriDisplay.html
/// [`Formatter::write_value()`]: ../rocket/http/uri/struct.Formatter.html#method.write_value
#[proc_macro_derive(UriDisplayPath)]
pub fn derive_uri_display_path(input: TokenStream) -> TokenStream {
emit!(derive::uri_display::derive_uri_display_path(input))
}
/// Generates a [`Vec`] of [`Route`]s from a set of route paths.

View File

@ -1,7 +1,6 @@
use std::ops::{Bound, RangeBounds};
use std::ops::RangeBounds;
use proc_macro::{Span, Diagnostic, Literal};
use syntax_pos::{Span as InnerSpan, Pos, BytePos};
pub type PResult<T> = ::std::result::Result<T, Diagnostic>;

View File

@ -13,7 +13,7 @@ use rocket::http::{Status, RawStr, ContentType};
// Use all of the code generation avaiable at once.
#[derive(FromForm, UriDisplay)]
#[derive(FromForm, UriDisplayQuery)]
struct Inner<'r> {
field: &'r RawStr
}

View File

@ -6,16 +6,16 @@
use std::path::PathBuf;
use rocket::http::{RawStr, Cookies};
use rocket::http::uri::{Origin, FromUriParam};
use rocket::http::uri::{Origin, FromUriParam, Query};
use rocket::request::Form;
#[derive(FromForm, UriDisplay)]
#[derive(FromForm, UriDisplayQuery)]
struct User<'a> {
name: &'a RawStr,
nickname: String,
}
impl<'a, 'b> FromUriParam<(&'a str, &'b str)> for User<'a> {
impl<'a, 'b> FromUriParam<Query, (&'a str, &'b str)> for User<'a> {
type Target = User<'a>;
fn from_uri_param((name, nickname): (&'a str, &'b str)) -> User<'a> {
User { name: name.into(), nickname: nickname.to_string() }

View File

@ -21,9 +21,62 @@ fn not_uri_display(id: i32, name: S) { }
#[post("/<id>/<name>")]
fn not_uri_display_but_unused(id: i32, name: S) { }
fn main() {
uri!(simple: id = "hi"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str>
uri!(simple: "hello"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str>
uri!(simple: id = 239239i64); //~ ERROR i32: rocket::http::uri::FromUriParam<i64>
uri!(not_uri_display: 10, S); //~ ERROR S: rocket::http::uri::FromUriParam<_>
#[post("/<id>/<name>")]
fn optionals(id: Option<i32>, name: Result<String, &RawStr>) { }
use rocket::request::{Query, FromQuery};
impl<'q> FromQuery<'q> for S {
type Error = ();
fn from_query(query: Query<'q>) -> Result<Self, Self::Error> { Ok(S) }
}
#[post("/?<id>")]
fn simple_q(id: i32) { }
#[post("/?<id>&<rest..>")]
fn other_q(id: usize, rest: S) { }
#[post("/?<id>&<name>")]
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>
uri!(simple: "hello");
//~^ ERROR i32: rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>
uri!(simple: id = 239239i64);
//~^ ERROR i32: 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, _>
// This one is okay. In paths, a value _must_ be supplied.
uri!(optionals: id = 10, name = "bob".to_string());
uri!(optionals: id = Some(10), name = Ok("bob".into()));
//~^ ERROR i32: rocket::http::uri::FromUriParam<rocket::http::uri::Path, std::option::Option<{integer}>>
//~^^ 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>
uri!(simple_q: id = "hi");
//~^ ERROR i32: rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>
uri!(other_q: 100, S);
//~^ ERROR S: rocket::http::uri::FromUriParam<rocket::http::uri::Query, _>
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()));
// For queries, we need to know the exact variant.
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>
}

View File

@ -1,33 +1,99 @@
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<&str>` is not satisfied
--> $DIR/typed-uri-bad-type.rs:25:23
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
|
25 | uri!(simple: id = "hi"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str>
| ^^^^ the trait `rocket::http::uri::FromUriParam<&str>` is not implemented for `i32`
44 | uri!(simple: id = "hi");
| ^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>` is not implemented for `i32`
|
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<&str>` is not satisfied
--> $DIR/typed-uri-bad-type.rs:26:18
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
|
26 | uri!(simple: "hello"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str>
| ^^^^^^^ the trait `rocket::http::uri::FromUriParam<&str>` is not implemented for `i32`
47 | uri!(simple: "hello");
| ^^^^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, &str>` is not implemented for `i32`
|
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<i64>` is not satisfied
--> $DIR/typed-uri-bad-type.rs:27:23
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
|
27 | uri!(simple: id = 239239i64); //~ ERROR i32: rocket::http::uri::FromUriParam<i64>
| ^^^^^^^^^ the trait `rocket::http::uri::FromUriParam<i64>` is not implemented for `i32`
50 | uri!(simple: id = 239239i64);
| ^^^^^^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, i64>` is not implemented for `i32`
|
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
error[E0277]: the trait bound `S: rocket::http::uri::FromUriParam<_>` is not satisfied
--> $DIR/typed-uri-bad-type.rs:28:31
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
|
28 | uri!(not_uri_display: 10, S); //~ ERROR S: rocket::http::uri::FromUriParam<_>
| ^ the trait `rocket::http::uri::FromUriParam<_>` is not implemented for `S`
53 | uri!(not_uri_display: 10, S);
| ^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Path, _>` is not implemented for `S`
error: aborting due to 4 previous errors
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
|
59 | 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`
|
= 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
|
59 | 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>>
= 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
|
63 | uri!(simple_q: "hi");
| ^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>` is not implemented for `i32`
|
= 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
|
66 | uri!(simple_q: id = "hi");
| ^^^^ the trait `rocket::http::uri::FromUriParam<rocket::http::uri::Query, &str>` is not implemented for `i32`
|
= 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
|
69 | 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
|
72 | 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
|
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>`
|
= 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`
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
|
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>`
|
= 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`
error: aborting due to 12 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -12,7 +12,7 @@ fn has_one(id: i32) { }
#[post("/<id>")]
fn has_one_guarded(cookies: Cookies, id: i32) { }
#[post("/<id>/<name>")]
#[post("/<id>?<name>")]
fn has_two(cookies: Cookies, id: i32, name: String) { }
fn main() {

View File

@ -1,30 +1,48 @@
#[macro_use] extern crate rocket;
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
struct Foo1;
//~^ ERROR not supported
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
struct Foo2();
//~^ ERROR not supported
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
enum Foo3 { }
//~^ ERROR not supported
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
enum Foo4 {
Variant,
//~^ ERROR not supported
}
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
struct Foo5(String, String);
//~^ ERROR exactly one
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
struct Foo6 {
#[form(field = 123)]
//~^ ERROR invalid value: expected string
field: String,
}
#[derive(UriDisplayPath)]
struct Foo7(String, usize);
//~^ ERROR exactly one
#[derive(UriDisplayPath)]
struct Foo8;
//~^ ERROR exactly one
#[derive(UriDisplayPath)]
enum Foo9 { }
//~^ ERROR not supported
#[derive(UriDisplayPath)]
struct Foo10 {
//~^ ERROR not supported
named: usize
}

View File

@ -7,8 +7,8 @@ error: fieldless structs or variants are not supported
note: error occurred while deriving `UriDisplay`
--> $DIR/uri_display.rs:3:10
|
3 | #[derive(UriDisplay)]
| ^^^^^^^^^^
3 | #[derive(UriDisplayQuery)]
| ^^^^^^^^^^^^^^^
error: fieldless structs or variants are not supported
--> $DIR/uri_display.rs:8:1
@ -19,8 +19,8 @@ error: fieldless structs or variants are not supported
note: error occurred while deriving `UriDisplay`
--> $DIR/uri_display.rs:7:10
|
7 | #[derive(UriDisplay)]
| ^^^^^^^^^^
7 | #[derive(UriDisplayQuery)]
| ^^^^^^^^^^^^^^^
error: empty enums are not supported
--> $DIR/uri_display.rs:12:1
@ -31,8 +31,8 @@ error: empty enums are not supported
note: error occurred while deriving `UriDisplay`
--> $DIR/uri_display.rs:11:10
|
11 | #[derive(UriDisplay)]
| ^^^^^^^^^^
11 | #[derive(UriDisplayQuery)]
| ^^^^^^^^^^^^^^^
error: fieldless structs or variants are not supported
--> $DIR/uri_display.rs:17:5
@ -43,8 +43,8 @@ error: fieldless structs or variants are not supported
note: error occurred while deriving `UriDisplay`
--> $DIR/uri_display.rs:15:10
|
15 | #[derive(UriDisplay)]
| ^^^^^^^^^^
15 | #[derive(UriDisplayQuery)]
| ^^^^^^^^^^^^^^^
error: tuple structs or variants must have exactly one field
--> $DIR/uri_display.rs:22:12
@ -55,8 +55,8 @@ error: tuple structs or variants must have exactly one field
note: error occurred while deriving `UriDisplay`
--> $DIR/uri_display.rs:21:10
|
21 | #[derive(UriDisplay)]
| ^^^^^^^^^^
21 | #[derive(UriDisplayQuery)]
| ^^^^^^^^^^^^^^^
error: invalid value: expected string literal
--> $DIR/uri_display.rs:27:20
@ -67,8 +67,59 @@ error: invalid value: expected string literal
note: error occurred while deriving `UriDisplay`
--> $DIR/uri_display.rs:25:10
|
25 | #[derive(UriDisplay)]
| ^^^^^^^^^^
25 | #[derive(UriDisplayQuery)]
| ^^^^^^^^^^^^^^^
error: aborting due to 6 previous errors
error: struct must have exactly one field
--> $DIR/uri_display.rs:33:12
|
33 | struct Foo7(String, usize);
| ^^^^^^^^^^^^^^^
|
note: error occurred while deriving `UriDisplay`
--> $DIR/uri_display.rs:32:10
|
32 | #[derive(UriDisplayPath)]
| ^^^^^^^^^^^^^^
error: struct must have exactly one field
--> $DIR/uri_display.rs:37:1
|
37 | struct Foo8;
| ^^^^^^^^^^^^
|
note: error occurred while deriving `UriDisplay`
--> $DIR/uri_display.rs:36:10
|
36 | #[derive(UriDisplayPath)]
| ^^^^^^^^^^^^^^
error: enums are not supported
--> $DIR/uri_display.rs:41:1
|
41 | enum Foo9 { }
| ^^^^^^^^^^^^^^
|
note: error occurred while deriving `UriDisplay`
--> $DIR/uri_display.rs:40:10
|
40 | #[derive(UriDisplayPath)]
| ^^^^^^^^^^^^^^
error: named structs are not supported
--> $DIR/uri_display.rs:45:1
|
45 | / struct Foo10 {
46 | | //~^ ERROR not supported
47 | | named: usize
48 | | }
| |_^
|
note: error occurred while deriving `UriDisplay`
--> $DIR/uri_display.rs:44:10
|
44 | #[derive(UriDisplayPath)]
| ^^^^^^^^^^^^^^
error: aborting due to 10 previous errors

View File

@ -2,44 +2,48 @@
struct BadType;
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
struct Bar1(BadType);
//~^ ERROR UriDisplay
//~^ ERROR UriDisplay<rocket::http::uri::Query>
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
struct Bar2 {
field: BadType,
//~^ ERROR UriDisplay
//~^ ERROR UriDisplay<rocket::http::uri::Query>
}
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
struct Bar3 {
field: String,
bad: BadType,
//~^ ERROR UriDisplay
//~^ ERROR UriDisplay<rocket::http::uri::Query>
}
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
enum Bar4 {
Inner(BadType),
//~^ ERROR UriDisplay
//~^ ERROR UriDisplay<rocket::http::uri::Query>
}
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
enum Bar5 {
Inner {
field: BadType,
//~^ ERROR UriDisplay
//~^ ERROR UriDisplay<rocket::http::uri::Query>
},
}
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
enum Bar6 {
Inner {
field: String,
other: BadType,
//~^ ERROR UriDisplay
//~^ ERROR UriDisplay<rocket::http::uri::Query>
},
}
#[derive(UriDisplayPath)]
struct Baz(BadType);
//~^ ERROR UriDisplay<rocket::http::uri::Path>
fn main() { }

View File

@ -1,54 +1,62 @@
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay` is not satisfied
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not satisfied
--> $DIR/uri_display_type_errors.rs:6:13
|
6 | struct Bar1(BadType);
| ^^^^^^^ the trait `rocket::http::uri::UriDisplay` is not implemented for `BadType`
| ^^^^^^^ the trait `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not implemented for `BadType`
|
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay` for `&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` for `&BadType`
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay` is not satisfied
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not satisfied
--> $DIR/uri_display_type_errors.rs:11:5
|
11 | field: BadType,
| ^^^^^^^^^^^^^^ the trait `rocket::http::uri::UriDisplay` is not implemented for `BadType`
| ^^^^^^^^^^^^^^ the trait `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not implemented for `BadType`
|
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay` for `&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` for `&BadType`
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay` is not satisfied
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not satisfied
--> $DIR/uri_display_type_errors.rs:18:5
|
18 | bad: BadType,
| ^^^^^^^^^^^^ the trait `rocket::http::uri::UriDisplay` is not implemented for `BadType`
| ^^^^^^^^^^^^ the trait `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not implemented for `BadType`
|
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay` for `&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` for `&BadType`
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay` is not satisfied
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not satisfied
--> $DIR/uri_display_type_errors.rs:24:11
|
24 | Inner(BadType),
| ^^^^^^^ the trait `rocket::http::uri::UriDisplay` is not implemented for `BadType`
| ^^^^^^^ the trait `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not implemented for `BadType`
|
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay` for `&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay` for `&&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` for `&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` for `&&BadType`
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay` is not satisfied
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not satisfied
--> $DIR/uri_display_type_errors.rs:31:9
|
31 | field: BadType,
| ^^^^^^^^^^^^^^ the trait `rocket::http::uri::UriDisplay` is not implemented for `BadType`
| ^^^^^^^^^^^^^^ the trait `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not implemented for `BadType`
|
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay` for `&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay` for `&&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` for `&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` for `&&BadType`
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay` is not satisfied
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not satisfied
--> $DIR/uri_display_type_errors.rs:40:9
|
40 | other: BadType,
| ^^^^^^^^^^^^^^ the trait `rocket::http::uri::UriDisplay` is not implemented for `BadType`
| ^^^^^^^^^^^^^^ the trait `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` is not implemented for `BadType`
|
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay` for `&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay` for `&&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` for `&BadType`
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay<rocket::http::uri::Query>` for `&&BadType`
error: aborting due to 6 previous errors
error[E0277]: the trait bound `BadType: rocket::http::uri::UriDisplay<rocket::http::uri::Path>` is not satisfied
--> $DIR/uri_display_type_errors.rs:46:12
|
46 | struct Baz(BadType);
| ^^^^^^^ the trait `rocket::http::uri::UriDisplay<rocket::http::uri::Path>` is not implemented for `BadType`
|
= note: required because of the requirements on the impl of `rocket::http::uri::UriDisplay<rocket::http::uri::Path>` for `&BadType`
error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -3,16 +3,16 @@
#[macro_use] extern crate rocket;
use rocket::http::RawStr;
use rocket::http::uri::UriDisplay;
use rocket::http::uri::{UriDisplay, Query, Path};
macro_rules! assert_uri_display {
macro_rules! assert_uri_display_query {
($v:expr, $s:expr) => (
let uri_string = format!("{}", &$v as &UriDisplay);
let uri_string = format!("{}", &$v as &UriDisplay<Query>);
assert_eq!(uri_string, $s);
)
}
#[derive(UriDisplay, Clone)]
#[derive(UriDisplayQuery, Clone)]
enum Foo<'r> {
First(&'r RawStr),
Second {
@ -28,25 +28,25 @@ enum Foo<'r> {
#[test]
fn uri_display_foo() {
let foo = Foo::First("hello".into());
assert_uri_display!(foo, "hello");
assert_uri_display_query!(foo, "hello");
let foo = Foo::First("hello there".into());
assert_uri_display!(foo, "hello%20there");
assert_uri_display_query!(foo, "hello%20there");
let foo = Foo::Second { inner: "hi".into(), other: 123 };
assert_uri_display!(foo, "inner=hi&other=123");
assert_uri_display_query!(foo, "inner=hi&other=123");
let foo = Foo::Second { inner: "hi bo".into(), other: 321 };
assert_uri_display!(foo, "inner=hi%20bo&other=321");
assert_uri_display_query!(foo, "inner=hi%20bo&other=321");
let foo = Foo::Third { kind: "hello".into() };
assert_uri_display!(foo, "type=hello");
assert_uri_display_query!(foo, "type=hello");
let foo = Foo::Third { kind: "hello there".into() };
assert_uri_display!(foo, "type=hello%20there");
assert_uri_display_query!(foo, "type=hello%20there");
}
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
struct Bar<'a> {
foo: Foo<'a>,
baz: String,
@ -56,18 +56,18 @@ struct Bar<'a> {
fn uri_display_bar() {
let foo = Foo::First("hello".into());
let bar = Bar { foo, baz: "well, hi!".into() };
assert_uri_display!(bar, "foo=hello&baz=well,%20hi!");
assert_uri_display_query!(bar, "foo=hello&baz=well,%20hi!");
let foo = Foo::Second { inner: "hi".into(), other: 123 };
let bar = Bar { foo, baz: "done".into() };
assert_uri_display!(bar, "foo.inner=hi&foo.other=123&baz=done");
assert_uri_display_query!(bar, "foo.inner=hi&foo.other=123&baz=done");
let foo = Foo::Third { kind: "hello".into() };
let bar = Bar { foo, baz: "turkey day".into() };
assert_uri_display!(bar, "foo.type=hello&baz=turkey%20day");
assert_uri_display_query!(bar, "foo.type=hello&baz=turkey%20day");
}
#[derive(UriDisplay)]
#[derive(UriDisplayQuery)]
struct Baz<'a> {
foo: Foo<'a>,
bar: Bar<'a>,
@ -80,7 +80,7 @@ fn uri_display_baz() {
let foo2 = Foo::Second { inner: "bye".into(), other: 321 };
let bar = Bar { foo: foo2, baz: "done".into() };
let baz = Baz { foo: foo1, bar, last: "ok".into() };
assert_uri_display!(baz, "foo.inner=hi&foo.other=123&\
assert_uri_display_query!(baz, "foo.inner=hi&foo.other=123&\
bar.foo.inner=bye&bar.foo.other=321&bar.baz=done&\
last=ok");
@ -88,7 +88,63 @@ fn uri_display_baz() {
let foo2 = Foo::First("bye".into());
let bar = Bar { foo: foo1, baz: "end".into() };
let baz = Baz { foo: foo2, bar, last: "done".into() };
assert_uri_display!(baz, "foo=bye&\
assert_uri_display_query!(baz, "foo=bye&\
bar.foo.type=hello&bar.baz=end&\
last=done");
}
#[derive(UriDisplayQuery)]
struct Bam<'a> {
foo: &'a str,
bar: Option<usize>,
baz: Result<&'a RawStr, usize>,
}
#[test]
fn uri_display_bam() {
let bam = Bam { foo: "hi hi", bar: Some(1), baz: Err(2) };
assert_uri_display_query!(bam, "foo=hi%20hi&bar=1");
let bam = Bam { foo: "hi hi", bar: None, baz: Err(2) };
assert_uri_display_query!(bam, "foo=hi%20hi");
let bam = Bam { foo: "hi hi", bar: Some(1), baz: Ok("tony".into()) };
assert_uri_display_query!(bam, "foo=hi%20hi&bar=1&baz=tony");
let bam = Bam { foo: "hi hi", bar: None, baz: Ok("tony".into()) };
assert_uri_display_query!(bam, "foo=hi%20hi&baz=tony");
}
macro_rules! assert_uri_display_path {
($v:expr, $s:expr) => (
let uri_string = format!("{}", &$v as &UriDisplay<Path>);
assert_eq!(uri_string, $s);
)
}
#[derive(UriDisplayPath)]
struct FooP(&'static str);
#[derive(UriDisplayPath)]
struct BarP<'a>(&'a str);
#[derive(UriDisplayPath)]
struct BazP<'a, T>(&'a T);
#[derive(UriDisplayPath)]
struct BamP<T>(T);
#[derive(UriDisplayPath)]
struct BopP(FooP);
#[test]
fn uri_display_path() {
assert_uri_display_path!(FooP("hi"), "hi");
assert_uri_display_path!(FooP("hi there"), "hi%20there");
assert_uri_display_path!(BarP("hi there"), "hi%20there");
assert_uri_display_path!(BazP(&FooP("hi")), "hi");
assert_uri_display_path!(BazP(&BarP("hi there")), "hi%20there");
assert_uri_display_path!(BamP(12), "12");
assert_uri_display_path!(BamP(BazP(&100)), "100");
assert_uri_display_path!(BopP(FooP("bop foo")), "bop%20foo");
}

View File

@ -3,7 +3,8 @@ use std::ops::Deref;
use std::str::FromStr;
use std::fmt;
use {Header, MediaType, Source};
use header::Header;
use media_type::{MediaType, Source};
use ext::IntoCollection;
use hyper::mime::Mime;

View File

@ -101,6 +101,8 @@ impl<'a, B: 'static + ToOwned + ?Sized> IntoOwned for Cow<'a, B> {
use std::path::Path;
// Outside of http, this is used by a test.
#[doc(hidden)]
pub trait Normalize {
fn normalized_str(&self) -> Cow<str>;
}

View File

@ -2,6 +2,7 @@
#![feature(proc_macro_hygiene)]
#![feature(try_from)]
#![feature(crate_visibility_modifier)]
#![feature(doc_cfg)]
#![recursion_limit="512"]
//! Types that map to concepts in HTTP.
@ -12,11 +13,9 @@
//!
//! [#17]: https://github.com/SergioBenitez/Rocket/issues/17
#[macro_use]
extern crate pear;
#[macro_use] extern crate pear;
#[macro_use] extern crate percent_encoding;
extern crate smallvec;
#[doc(hidden)] #[macro_use]
pub extern crate percent_encoding;
extern crate cookie;
extern crate time;
extern crate indexmap;
@ -51,15 +50,18 @@ crate mod parse;
pub mod uncased;
#[doc(hidden)]
pub mod private {
// We need to export these for codegen, but otherwise it's unnecessary.
// TODO: Expose a `const fn` from ContentType when possible. (see RFC#1817)
// FIXME(rustc): These show up in the rexported module.
#[doc(hidden)] pub use parse::Indexed;
#[doc(hidden)] pub use media_type::{MediaParams, Source};
#[doc(hidden)] pub use smallvec::{SmallVec, Array};
pub use parse::Indexed;
pub use media_type::{MediaParams, Source};
pub use smallvec::{SmallVec, Array};
// This one we need to expose for core.
#[doc(hidden)] pub use cookies::{Key, CookieJar};
pub use cookies::{Key, CookieJar};
}
pub use method::Method;
pub use content_type::ContentType;

View File

@ -30,7 +30,6 @@ impl AsPtr for [u8] {
}
}
#[derive(PartialEq)]
pub enum Indexed<'a, T: ?Sized + ToOwned + 'a> {
Indexed(usize, usize),

View File

@ -3,7 +3,7 @@ use std::borrow::Cow;
use pear::{parser, switch};
use pear::parsers::*;
use {MediaType, Source};
use media_type::{MediaType, Source};
use parse::checkers::{is_whitespace, is_valid_token};
use parse::IndexedStr;

View File

@ -128,9 +128,13 @@ impl<'a> RouteSegment<'a> {
pub fn parse_many(
string: &str,
sep: char,
source: Source,
) -> impl Iterator<Item = SResult> {
let sep = match source {
Source::Query => '&',
_ => '/',
};
let mut last_multi_seg: Option<&str> = None;
string.split(sep).filter(|s| !s.is_empty()).enumerate().map(move |(i, seg)| {
if let Some(multi_seg) = last_multi_seg {
@ -149,10 +153,10 @@ impl<'a> RouteSegment<'a> {
}
pub fn parse_path(uri: &'a Origin) -> impl Iterator<Item = SResult<'a>> {
Self::parse_many(uri.path(), '/', Source::Path)
Self::parse_many(uri.path(), Source::Path)
}
pub fn parse_query(uri: &'a Origin) -> Option<impl Iterator<Item = SResult<'a>>> {
uri.query().map(|q| Self::parse_many(q, '&', Source::Query))
uri.query().map(|q| Self::parse_many(q, Source::Query))
}
}

View File

@ -1,16 +1,35 @@
use std::fmt;
use std::fmt::{self, Write};
use std::marker::PhantomData;
use smallvec::SmallVec;
use uri::UriDisplay;
use uri::{UriPart, Path, Query, UriDisplay, Origin};
/// A struct used to format strings for [`UriDisplay`].
///
/// A mutable version of this struct is passed to [`UriDisplay::fmt()`]. This
/// struct properly formats series of named values for use in URIs. In
/// particular, this struct applies the following transformations:
/// # Marker Generic: `Formatter<Path>` vs. `Formatter<Query>`
///
/// * When **mutliple values** are written, they are separated by `&`.
/// Like [`UriDisplay`], the [`UriPart`] parameter `P` in `Formatter<P>` must be
/// either [`Path`] or [`Query`] resulting in either `Formatter<Path>` or
/// `Formatter<Query>`. The `Path` version is used when formatting parameters
/// in the path part of the URI while the `Query` version is used when
/// formatting parameters in the query part of the URI. The
/// [`write_named_value()`] method is only available to `UriDisplay<Query>`.
///
/// [`UriPart`]: uri::UriPart
/// [`Path`]: uri::Path
/// [`Query`]: uri::Query
///
/// # Overview
///
/// A mutable version of this struct is passed to [`UriDisplay::fmt()`]. This
/// struct properly formats series of values for use in URIs. In particular,
/// this struct applies the following transformations:
///
/// * When **mutliple values** are written, they are separated by `/` for
/// `Path` types and `&` for `Query` types.
///
/// Additionally, for `Formatter<Query>`:
///
/// * When a **named value** is written with [`write_named_value()`], the name
/// is written out, followed by a `=`, followed by the value.
@ -36,8 +55,8 @@ use uri::UriDisplay;
/// written value and, along with `write_value` and `write_raw`, handles nested
/// calls to `write_named_value` automatically, prefixing names when necessary.
/// Unlike the other methods, `write_raw` does _not_ prefix any nested names
/// every time it is called. Instead, it only prefixes names the _first_ time it
/// is called, after a call to `write_named_value` or `write_value`, or after a
/// every time it is called. Instead, it only prefixes the _first_ time it is
/// called, after a call to `write_named_value` or `write_value`, or after a
/// call to [`refresh()`].
///
/// [`refresh()`]: uri::Formatter::refresh()
@ -45,15 +64,15 @@ use uri::UriDisplay;
/// # Example
///
/// The following example uses all of the `write` methods in a varied order to
/// display the semantics of `Formatter`. Note that `UriDisplay` should rarely
/// be implemented manually, preferring to use the derive, and that this
/// display the semantics of `Formatter<Query>`. Note that `UriDisplay` should
/// rarely be implemented manually, preferring to use the derive, and that this
/// implementation is purely demonstrative.
///
/// ```rust
/// # extern crate rocket;
/// use std::fmt;
///
/// use rocket::http::uri::{Formatter, UriDisplay};
/// use rocket::http::uri::{Formatter, UriDisplay, Query};
///
/// struct Outer {
/// value: Inner,
@ -66,8 +85,8 @@ use uri::UriDisplay;
/// extra: usize
/// }
///
/// impl UriDisplay for Outer {
/// fn fmt(&self, f: &mut Formatter) -> fmt::Result {
/// impl UriDisplay<Query> for Outer {
/// fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
/// f.write_named_value("outer_field", &self.value)?;
/// f.write_named_value("another", &self.another)?;
/// f.write_raw("out")?;
@ -76,8 +95,8 @@ use uri::UriDisplay;
/// }
/// }
///
/// impl UriDisplay for Inner {
/// fn fmt(&self, f: &mut Formatter) -> fmt::Result {
/// impl UriDisplay<Query> for Inner {
/// fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
/// f.write_named_value("inner_field", &self.value)?;
/// f.write_value(&self.extra)?;
/// f.write_raw("inside")
@ -86,7 +105,7 @@ use uri::UriDisplay;
///
/// let inner = Inner { value: 0, extra: 1 };
/// let outer = Outer { value: inner, another: 2, extra: 3 };
/// let uri_string = format!("{}", &outer as &UriDisplay);
/// let uri_string = format!("{}", &outer as &UriDisplay<Query>);
/// assert_eq!(uri_string, "outer_field.inner_field=0&\
/// outer_field=1&\
/// outer_field=inside&\
@ -104,58 +123,66 @@ use uri::UriDisplay;
/// # #[macro_use] extern crate rocket;
/// use std::fmt::{self, Write};
///
/// use rocket::http::uri::{Formatter, UriDisplay};
/// use rocket::http::uri::{UriDisplay, Formatter, UriPart, Path, Query};
///
/// pub struct Complex(u8, u8);
///
/// impl UriDisplay for Complex {
/// fn fmt(&self, f: &mut Formatter) -> fmt::Result {
/// impl<P: UriPart> UriDisplay<P> for Complex {
/// fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
/// write!(f, "{}+{}", self.0, self.1)
/// }
/// }
///
/// #[derive(UriDisplay)]
/// let uri_string = format!("{}", &Complex(42, 231) as &UriDisplay<Path>);
/// assert_eq!(uri_string, "42+231");
///
/// #[derive(UriDisplayQuery)]
/// struct Message {
/// number: Complex,
/// }
///
/// let message = Message { number: Complex(42, 231) };
/// let uri_string = format!("{}", &message as &UriDisplay);
/// assert_eq!(uri_string, "number=42+231");
/// let message = Message { number: Complex(42, 47) };
/// let uri_string = format!("{}", &message as &UriDisplay<Query>);
/// assert_eq!(uri_string, "number=42+47");
/// ```
///
/// [`write_value()`]: uri::Formatter::write_value()
/// [`write_raw()`]: uri::Formatter::write_raw()
pub struct Formatter<'i, 'f: 'i> {
crate prefixes: SmallVec<[&'static str; 3]>,
crate inner: &'i mut fmt::Formatter<'f>,
crate previous: bool,
crate fresh: bool
pub struct Formatter<'i, P: UriPart> {
prefixes: SmallVec<[&'static str; 3]>,
inner: &'i mut (dyn Write + 'i),
previous: bool,
fresh: bool,
delimiter: char,
_marker: PhantomData<P>,
}
impl<'i, 'f: 'i> Formatter<'i, 'f> {
crate fn new(formatter: &'i mut fmt::Formatter<'f>) -> Self {
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 {
Formatter {
inner, delimiter,
prefixes: SmallVec::new(),
inner: formatter,
previous: false,
fresh: true,
_marker: PhantomData,
}
}
fn with_prefix<F>(&mut self, prefix: &str, f: F) -> fmt::Result
where F: FnOnce(&mut Self) -> fmt::Result
{
// TODO: PROOF OF CORRECTNESS.
let prefix: &'static str = unsafe { ::std::mem::transmute(prefix) };
self.prefixes.push(prefix);
let result = f(self);
self.prefixes.pop();
result
}
#[inline(always)]
fn refreshed<F: FnOnce(&mut Self) -> fmt::Result>(&mut self, f: F) -> fmt::Result {
self.refresh();
@ -167,7 +194,7 @@ impl<'i, 'f: 'i> Formatter<'i, 'f> {
/// Writes `string` to `self`.
///
/// If `self` is _fresh_ (after a call to other `write_` methods or
/// [`refresh()`]), prefixes any names as necessary.
/// [`refresh()`]), prefixes any names and adds separators as necessary.
///
/// This method is called by the `write!` macro.
///
@ -179,12 +206,12 @@ impl<'i, 'f: 'i> Formatter<'i, 'f> {
/// # extern crate rocket;
/// use std::fmt;
///
/// use rocket::http::uri::{Formatter, UriDisplay};
/// use rocket::http::uri::{Formatter, UriDisplay, UriPart, Path};
///
/// struct Foo;
///
/// impl UriDisplay for Foo {
/// fn fmt(&self, f: &mut Formatter) -> fmt::Result {
/// impl<P: UriPart> UriDisplay<P> for Foo {
/// fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
/// f.write_raw("f")?;
/// f.write_raw("o")?;
/// f.write_raw("o")
@ -192,14 +219,23 @@ impl<'i, 'f: 'i> Formatter<'i, 'f> {
/// }
///
/// let foo = Foo;
/// let uri_string = format!("{}", &foo as &UriDisplay);
/// let uri_string = format!("{}", &foo as &UriDisplay<Path>);
/// assert_eq!(uri_string, "foo");
/// ```
pub fn write_raw<S: AsRef<str>>(&mut self, string: S) -> fmt::Result {
let s = string.as_ref();
if self.fresh {
// This implementation is a bit of a lie to the type system. Instead of
// implementing this twice, one for <Path> and again for <Query>, we do
// this once here. This is okay since we know that this handles the
// 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.previous {
self.inner.write_str("&")?;
self.inner.write_char(self.delimiter)?;
}
} else if self.fresh && self.delimiter == '&' {
if self.previous {
self.inner.write_char(self.delimiter)?;
}
if !self.prefixes.is_empty() {
@ -216,7 +252,123 @@ impl<'i, 'f: 'i> Formatter<'i, 'f> {
self.fresh = false;
self.previous = true;
self.inner.write_str(s)
self.inner.write_str(string.as_ref())
}
/// Writes the unnamed value `value`. Any nested names are prefixed as
/// necessary.
///
/// Refreshes `self` before and after the value is written.
///
/// # Example
///
/// ```rust
/// # extern crate rocket;
/// use std::fmt;
///
/// use rocket::http::uri::{Formatter, UriDisplay, UriPart, Path, Query};
///
/// struct Foo(usize);
///
/// impl<P: UriPart> UriDisplay<P> for Foo {
/// fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
/// f.write_value(&self.0)
/// }
/// }
///
/// let foo = Foo(123);
///
/// let uri_string = format!("{}", &foo as &UriDisplay<Path>);
/// assert_eq!(uri_string, "123");
///
/// let uri_string = format!("{}", &foo as &UriDisplay<Query>);
/// assert_eq!(uri_string, "123");
/// ```
#[inline]
pub fn write_value<T: UriDisplay<P>>(&mut self, value: T) -> fmt::Result {
self.refreshed(|f| UriDisplay::fmt(&value, f))
}
/// Refreshes the formatter.
///
/// After refreshing, [`write_raw()`] will prefix any nested names as well
/// as insert a separator.
///
/// [`write_raw()`]: Formatter::write_raw()
///
/// # Example
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// use std::fmt;
///
/// use rocket::http::uri::{Formatter, UriDisplay, Query, Path};
///
/// struct Foo;
///
/// impl UriDisplay<Query> for Foo {
/// fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
/// f.write_raw("a")?;
/// f.write_raw("raw")?;
/// f.refresh();
/// f.write_raw("format")
/// }
/// }
///
/// let uri_string = format!("{}", &Foo as &UriDisplay<Query>);
/// assert_eq!(uri_string, "araw&format");
///
///// #[derive(UriDisplayQuery)]
///// struct Message {
///// inner: Foo,
///// }
/////
///// let msg = Message { inner: Foo };
///// let uri_string = format!("{}", &msg as &UriDisplay);
///// assert_eq!(uri_string, "inner=araw&inner=format");
///
/// impl UriDisplay<Path> for Foo {
/// fn fmt(&self, f: &mut Formatter<Path>) -> fmt::Result {
/// f.write_raw("a")?;
/// f.write_raw("raw")?;
/// f.refresh();
/// f.write_raw("format")
/// }
/// }
///
/// let uri_string = format!("{}", &Foo as &UriDisplay<Path>);
/// assert_eq!(uri_string, "araw/format");
/// ```
#[inline(always)]
pub fn refresh(&mut self) {
self.fresh = true;
}
}
impl<'i> Formatter<'i, Query> {
fn with_prefix<F>(&mut self, prefix: &str, f: F) -> fmt::Result
where F: FnOnce(&mut Self) -> fmt::Result
{
// The `prefix` string is pushed in a `StackVec` for use by recursive
// (nested) calls to `write_raw`. The string is pushed here and then
// popped here. `self.prefixes` is modified nowhere else, and no strings
// leak from the the vector. As a result, it is impossible for a
// `prefix` to be accessed incorrectly as:
//
// * Rust _guarantees_ it exists for the lifetime of this method
// * it is only reachable while this method's stack is active because
// it is popped before this method returns
// * thus, at any point that it's reachable, it's valid
//
// Said succinctly: this `prefixes` stack shadows a subset of the
// `with_prefix` stack precisely, making it reachable to other code.
let prefix: &'static str = unsafe { ::std::mem::transmute(prefix) };
self.prefixes.push(prefix);
let result = f(self);
self.prefixes.pop();
result
}
/// Writes the named value `value` by prefixing `name` followed by `=` to
@ -231,100 +383,99 @@ impl<'i, 'f: 'i> Formatter<'i, 'f> {
/// # extern crate rocket;
/// use std::fmt;
///
/// use rocket::http::uri::{Formatter, UriDisplay};
/// use rocket::http::uri::{Formatter, UriDisplay, Query};
///
/// struct Foo {
/// name: usize
/// }
///
/// impl UriDisplay for Foo {
/// fn fmt(&self, f: &mut Formatter) -> fmt::Result {
/// // Note: This is identical to what #[derive(UriDisplayQuery)] would
/// // generate! In practice, _always_ use the derive.
/// impl UriDisplay<Query> for Foo {
/// fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
/// f.write_named_value("name", &self.name)
/// }
/// }
///
/// let foo = Foo { name: 123 };
/// let uri_string = format!("{}", &foo as &UriDisplay);
/// let uri_string = format!("{}", &foo as &UriDisplay<Query>);
/// assert_eq!(uri_string, "name=123");
/// ```
#[inline]
pub fn write_named_value<T: UriDisplay>(&mut self, name: &str, value: T) -> fmt::Result {
pub fn write_named_value<T: UriDisplay<Query>>(&mut self, name: &str, value: T) -> fmt::Result {
self.refreshed(|f| f.with_prefix(name, |f| f.write_value(value)))
}
/// Writes the unnamed value `value`. Any nested names are prefixed as
/// necessary.
///
/// Refreshes `self` before and after the value is written.
///
/// # Example
///
/// ```rust
/// # extern crate rocket;
/// use std::fmt;
///
/// use rocket::http::uri::{Formatter, UriDisplay};
///
/// struct Foo(usize);
///
/// impl UriDisplay for Foo {
/// fn fmt(&self, f: &mut Formatter) -> fmt::Result {
/// f.write_value(&self.0)
/// }
/// }
///
/// let foo = Foo(123);
/// let uri_string = format!("{}", &foo as &UriDisplay);
/// assert_eq!(uri_string, "123");
/// ```
#[inline]
pub fn write_value<T: UriDisplay>(&mut self, value: T) -> fmt::Result {
self.refreshed(|f| UriDisplay::fmt(&value, f))
}
/// Refreshes the formatter.
///
/// After refreshing, [`write_raw()`] will prefix any nested names as well
/// as insert an `&` separator.
///
/// [`write_raw()`]: Formatter::write_raw()
///
/// # Example
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// use std::fmt;
///
/// use rocket::http::uri::{Formatter, UriDisplay};
///
/// struct Foo;
///
/// impl UriDisplay for Foo {
/// fn fmt(&self, f: &mut Formatter) -> fmt::Result {
/// f.write_raw("a")?;
/// f.write_raw("raw")?;
/// f.refresh();
/// f.write_raw("format")
/// }
/// }
///
/// #[derive(UriDisplay)]
/// struct Message {
/// inner: Foo,
/// }
///
/// let msg = Message { inner: Foo };
/// let uri_string = format!("{}", &msg as &UriDisplay);
/// assert_eq!(uri_string, "inner=araw&inner=format");
/// ```
#[inline(always)]
pub fn refresh(&mut self) {
self.fresh = true;
}
}
impl<'f, 'i: 'f> fmt::Write for Formatter<'f, 'i> {
impl<'i, P: UriPart> fmt::Write for Formatter<'i, P> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write_raw(s)
}
}
// Used by code generation.
#[doc(hidden)]
pub enum UriArgumentsKind<A> {
Static(&'static str),
Dynamic(A)
}
// Used by code generation.
#[doc(hidden)]
pub enum UriQueryArgument<'a> {
Raw(&'a str),
NameValue(&'a str, &'a dyn UriDisplay<Query>),
Value(&'a dyn UriDisplay<Query>)
}
// Used by code generation.
#[doc(hidden)]
pub struct UriArguments<'a> {
pub path: UriArgumentsKind<&'a [&'a dyn UriDisplay<Path>]>,
pub query: Option<UriArgumentsKind<&'a [UriQueryArgument<'a>]>>,
}
// Used by code generation.
impl<'a> UriArguments<'a> {
#[doc(hidden)]
pub fn into_origin(self) -> Origin<'static> {
use std::borrow::Cow;
use self::{UriArgumentsKind::*, UriQueryArgument::*};
let path: Cow<'static, str> = match self.path {
Static(path) => path.into(),
Dynamic(args) => {
let mut string = String::from("/");
{
let mut formatter = Formatter::<Path>::new(&mut string);
for value in args {
let _ = formatter.write_value(value);
}
}
string.into()
}
};
let query: Option<Cow<'static, str>> = self.query.map(|query| match query {
Static(query) => query.into(),
Dynamic(args) => {
let mut string = String::new();
{
let mut f = Formatter::<Query>::new(&mut string);
for arg in args {
let _ = match arg {
Raw(v) => f.write_raw(v),
NameValue(n, v) => f.write_named_value(n, v),
Value(v) => f.write_value(v),
};
}
}
string.into()
}
});
Origin::new(path, query)
}
}

View File

@ -1,7 +1,7 @@
use std::path::{Path, PathBuf};
use RawStr;
use uri::UriDisplay;
use uri::{self, UriPart, UriDisplay};
/// Conversion trait for parameters used in [`uri!`] invocations.
///
@ -9,6 +9,8 @@ use uri::UriDisplay;
/// [`UriDisplay`]. As such, this trait typically does not need to be implemented.
/// Instead, implement [`UriDisplay`].
///
/// # Overview
///
/// 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
@ -20,8 +22,15 @@ use uri::UriDisplay;
/// implementation, provided by Rocket, allows an `&str` to be used in a `uri!`
/// invocation for route URI parameters declared as `String`:
///
/// ```rust,ignore
/// impl<'a> FromUriParam<&'a str> for String { type Target = &'a str; }
/// ```rust
/// # extern crate rocket;
/// # use rocket::http::uri::{FromUriParam, UriPart};
/// # struct S;
/// # type String = S;
/// impl<'a, P: UriPart> FromUriParam<P, &'a str> for String {
/// type Target = &'a str;
/// # fn from_uri_param(s: &'a str) -> Self::Target { "hi" }
/// }
/// ```
///
/// Because the [`FromUriParam::Target`] type is the same as the input type, the
@ -29,21 +38,34 @@ use uri::UriDisplay;
/// place of a `String` without penalty. A similar no-op conversion exists for
/// [`&RawStr`](RawStr):
///
/// ```rust,ignore
/// impl<'a, 'b> FromUriParam<&'a str> for &'b RawStr { type Target = &'a str; }
/// ```rust
/// # extern crate rocket;
/// # use rocket::http::uri::{FromUriParam, UriPart};
/// # struct S;
/// # type RawStr = S;
/// impl<'a, 'b, P: UriPart> FromUriParam<P, &'a str> for &'b RawStr {
/// type Target = &'a str;
/// # fn from_uri_param(s: &'a str) -> Self::Target { "hi" }
/// }
/// ```
///
/// # Provided Implementations
///
/// See [Foreign Impls](#foreign-impls) for implementations provided by Rocket.
///
/// # Implementing
///
/// This trait should only be implemented when you'd like to allow a type
/// different from the route's declared type to be used in its place in a `uri!`
/// invocation. For instance, if the route has a type of `T` and you'd like to
/// use a type of `S` in a `uri!` invocation, you'd implement `FromUriParam<T>
/// for S`.
/// use a type of `S` in a `uri!` invocation, you'd implement `FromUriParam<P,
/// T> for S` where `P` is `Path` for conversions valid in the path part of a
/// URI, `Uri` for conversions valid in the query part of a URI, or `P: UriPart`
/// when a conversion is valid in either case.
///
/// This is typically only warranted for owned-value types with
/// corresponding reference types: `String` and `&str`, for instance. In this
/// case, it's desirable to allow an `&str` to be used in place of a `String`.
/// This is typically only warranted for owned-value types with corresponding
/// reference types: `String` and `&str`, for instance. In this case, it's
/// desirable to allow an `&str` to be used in place of a `String`.
///
/// When implementing `FromUriParam`, be aware that Rocket will use the
/// [`UriDisplay`] implementation of [`FromUriParam::Target`], _not_ of the
@ -51,16 +73,17 @@ use uri::UriDisplay;
///
/// # Example
///
/// The following example implements `FromUriParam<(&str, &str)>` for a `User`
/// type. The implementation allows an `(&str, &str)` type to be used in a
/// `uri!` invocation where a `User` type is expected.
/// The following example implements `FromUriParam<Query, (&str, &str)>` for a
/// `User` type. The implementation allows an `(&str, &str)` type to be used in
/// a `uri!` invocation where a `User` type is expected in the query part of the
/// URI.
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// use std::fmt;
///
/// use rocket::http::RawStr;
/// use rocket::http::uri::{Formatter, UriDisplay, FromUriParam};
/// use rocket::http::uri::{Formatter, UriDisplay, FromUriParam, Query};
///
/// #[derive(FromForm)]
/// struct User<'a> {
@ -68,14 +91,14 @@ use uri::UriDisplay;
/// nickname: String,
/// }
///
/// impl<'a> UriDisplay for User<'a> {
/// fn fmt(&self, f: &mut Formatter) -> fmt::Result {
/// impl<'a> UriDisplay<Query> for User<'a> {
/// fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
/// f.write_named_value("name", &self.name)?;
/// f.write_named_value("nickname", &self.nickname)
/// }
/// }
///
/// impl<'a, 'b> FromUriParam<(&'a str, &'b str)> for User<'a> {
/// impl<'a, 'b> FromUriParam<Query, (&'a str, &'b str)> for User<'a> {
/// type Target = User<'a>;
///
/// fn from_uri_param((name, nickname): (&'a str, &'b str)) -> User<'a> {
@ -92,19 +115,19 @@ use uri::UriDisplay;
/// # use std::fmt;
/// use rocket::http::RawStr;
/// use rocket::request::Form;
/// # use rocket::http::uri::{Formatter, UriDisplay, FromUriParam};
/// # use rocket::http::uri::{Formatter, UriDisplay, FromUriParam, Query};
/// #
/// # #[derive(FromForm)]
/// # struct User<'a> { name: &'a RawStr, nickname: String, }
/// #
/// # impl<'a> UriDisplay for User<'a> {
/// # fn fmt(&self, f: &mut Formatter) -> fmt::Result {
/// # impl<'a> UriDisplay<Query> for User<'a> {
/// # fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
/// # f.write_named_value("name", &self.name)?;
/// # f.write_named_value("nickname", &self.nickname)
/// # }
/// # }
/// #
/// # impl<'a, 'b> FromUriParam<(&'a str, &'b str)> for User<'a> {
/// # impl<'a, 'b> FromUriParam<Query, (&'a str, &'b str)> for User<'a> {
/// # type Target = User<'a>;
/// # fn from_uri_param((name, nickname): (&'a str, &'b str)) -> User<'a> {
/// # User { name: name.into(), nickname: nickname.to_string() }
@ -122,9 +145,9 @@ use uri::UriDisplay;
/// [`uri!`]: ::rocket_codegen::uri
/// [`UriDisplay`]: uri::UriDisplay
/// [`FromUriParam::Target`]: uri::FromUriParam::Target
pub trait FromUriParam<T> {
pub trait FromUriParam<P: UriPart, T> {
/// The resulting type of this conversion.
type Target: UriDisplay;
type Target: UriDisplay<P>;
/// Converts a value of type `T` into a value of type `Self::Target`. The
/// resulting value of type `Self::Target` will be rendered into a URI using
@ -132,61 +155,61 @@ pub trait FromUriParam<T> {
fn from_uri_param(param: T) -> Self::Target;
}
impl<T: UriDisplay> FromUriParam<T> for T {
impl<P: UriPart, T: UriDisplay<P>> FromUriParam<P, 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 {
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 }
}
impl<'a, T: UriDisplay> FromUriParam<&'a mut T> for 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 }
}
/// A no cost conversion allowing an `&str` to be used in place of a `String`.
impl<'a> FromUriParam<&'a str> for 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 }
}
/// A no cost conversion allowing an `&str` to be used in place of an `&RawStr`.
impl<'a, 'b> FromUriParam<&'a str> for &'b 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 }
}
/// A no cost conversion allowing a `String` to be used in place of an `&RawStr`.
impl<'a> FromUriParam<String> for &'a RawStr {
impl<'a, P: UriPart> FromUriParam<P, String> for &'a RawStr {
type Target = String;
#[inline(always)]
fn from_uri_param(param: String) -> String { param }
}
/// A no cost conversion allowing a `String` to be used in place of an `&str`.
impl<'a> FromUriParam<String> for &'a 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<&'a Path> for 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 }
}
/// A no cost conversion allowing an `&str` to be used in place of a `PathBuf`.
impl<'a> FromUriParam<&'a str> for PathBuf {
impl<'a> FromUriParam<uri::Path, &'a str> for PathBuf {
type Target = &'a Path;
#[inline(always)]
@ -194,3 +217,25 @@ impl<'a> FromUriParam<&'a str> for PathBuf {
Path::new(param)
}
}
/// 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> {
type Target = T::Target;
#[inline(always)]
fn from_uri_param(param: A) -> Self::Target {
T::from_uri_param(param)
}
}
/// 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> {
type Target = T::Target;
#[inline(always)]
fn from_uri_param(param: A) -> Self::Target {
T::from_uri_param(param)
}
}

View File

@ -19,3 +19,63 @@ pub use self::uri_display::*;
pub use self::formatter::*;
pub use self::from_uri_param::*;
pub use self::segments::*;
mod private {
pub trait Sealed {}
impl Sealed for super::Path {}
impl Sealed for super::Query {}
}
/// Marker trait for types that mark a part of a URI.
///
/// This trait exists solely to categorize types that mark a part of the URI,
/// currently [`Path`] and [`Query`]. Said another way, types that implement
/// this trait are marker types that represent a part of a URI at the
/// type-level.
///
/// This trait is _sealed_: it cannot be implemented outside of Rocket.
///
/// # Usage
///
/// You will find this trait in traits like [`UriDisplay`] or structs like
/// [`Formatter`] as the bound on a generic parameter: `P: UriPart`. Because the
/// trait is sealed, the generic type is guaranteed to be instantiated as one of
/// [`Query`] or [`Path`], effectively creating two instances of the generic
/// items: `UriDisplay<Query>` and `UriDisplay<Path>`, and `Formatter<Query>`
/// and `Formatter<Path>`. Unlike having two distinct, non-generic traits, this
/// approach enables succinct, type-checked generic implementations of these
/// items.
///
/// [`Query`]: uri::Query
/// [`Path`]: uri::Path
/// [`UriDisplay`]: uri::UriDisplay
/// [`Formatter`]: uri::Formatter
pub trait UriPart: private::Sealed { }
/// Marker type indicating use of a type for the path [`UriPart`] of a URI.
///
/// In route URIs, this corresponds to all of the text before a `?`, if any, or
/// all of the text in the URI otherwise:
///
/// ```text
/// #[get("/home/<name>/<page>?<item>")]
/// ^------------------ Path
/// ```
///
/// [`UriPart`]: uri::UriPart
pub enum Path { }
/// Marker type indicating use of a type for the query [`UriPart`] of a URI.
///
/// In route URIs, this corresponds to all of the text after a `?`, if any.
///
/// ```text
/// #[get("/home/<name>/<page>?<item>&<form..>")]
/// ^-------------- Query
/// ```
///
/// [`UriPart`]: uri::UriPart
pub enum Query { }
impl UriPart for Path { }
impl UriPart for Query { }

View File

@ -1,10 +1,9 @@
use std::fmt;
use std::path::{Path, PathBuf};
use std::{fmt, path};
use std::borrow::Cow;
use percent_encoding::utf8_percent_encode;
use uri::{Uri, Formatter, UNSAFE_PATH_ENCODE_SET};
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!`.
@ -15,11 +14,48 @@ 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>`
///
/// The [`UriPart`] parameter `P` in `UriDisplay<P>` must be either [`Path`] or
/// [`Query`] (see the [`UriPart`] documentation for how this is enforced),
/// resulting in either `UriDisplay<Path>` or `UriDisplay<Query>`.
///
/// As the names might imply, the `Path` version of the trait is used when
/// displaying parameters in the path part of the URI while the `Query` version
/// is used when display parameters in the query part of the URI. These distinct
/// versions of the trait exist exactly to differentiate, at the type-level,
/// where in the URI a value is to be written to, allowing for type safety in
/// the face of differences between the two locations. For example, while it is
/// valid to use a value of `None` in the query part, omitting the parameter
/// entirely, doing so is _not_ valid in the path part. By differentiating in
/// the type system, both of these conditions can be enforced appropriately
/// through distinct implementations of `UriDisplay<Path>` and
/// `UriDisplay<Query>`.
///
/// Occasionally, the implementation of `UriDisplay` is independent of where the
/// parameter is to be displayed. When this is the case, the parameter may be
/// kept generic. That is, implementations can take the form:
///
/// ```rust
/// # extern crate rocket;
/// # use std::fmt;
/// # use rocket::http::uri::{UriPart, UriDisplay, Formatter};
/// # struct SomeType;
/// impl<P: UriPart> UriDisplay<P> for SomeType
/// # { fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result { Ok(()) } }
/// ```
///
/// [`UriPart`]: uri::UriPart
/// [`Path`]: uri::Path
/// [`Query`]: uri::Query
///
/// # 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.
/// 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.
///
/// To illustrate `UriDisplay`'s role in code generation for `uri!`, consider
/// the following route:
@ -49,25 +85,26 @@ use {RawStr, ext::Normalize};
/// ```
///
/// After verifying parameters and their types, Rocket will generate code
/// similar to the following:
/// similar (in spirit) to the following:
///
/// ```rust
/// # extern crate rocket;
/// # use rocket::http::uri::UriDisplay;
/// # use rocket::http::uri::{UriDisplay, Path, Query, Origin};
/// #
/// format!("/item/{}?track={}", &100 as &UriDisplay, &"inbound" as &UriDisplay);
/// Origin::parse(&format!("/item/{}?track={}",
/// &100 as &UriDisplay<Path>, &"inbound" as &UriDisplay<Query>));
/// ```
///
/// 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.
/// 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.
///
/// [`uri!`]: /rocket_codegen/#typed-uris-uri
///
/// # Provided Implementations
///
/// Rocket implements `UriDisplay` for several built-in types. Their behavior is
/// documented here.
/// Rocket implements `UriDisplay<P>` for all `P: UriPart` for several built-in
/// types.
///
/// * **i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32,
/// f64, bool, IpAddr, Ipv4Addr, Ipv6Addr**
@ -83,6 +120,47 @@ use {RawStr, ext::Normalize};
///
/// Uses the implementation of `UriDisplay` for `T`.
///
/// Rocket implements `UriDisplay<Path>` (but not `UriDisplay<Query>`) for
/// several built-in types.
///
/// * `T` for **`Option<T>`** _where_ **`T: UriDisplay<Path>`**
///
/// Uses the implementation of `UriDisplay` for `T::Target`.
///
/// When a type of `Option<T>` appears in a route path, use a type of `T` as
/// the parameter in `uri!`. Note that `Option<T>` itself _does not_
/// implement `UriDisplay<Path>`.
///
/// * `T` for **`Result<T, E>`** _where_ **`T: UriDisplay<Path>`**
///
/// Uses the implementation of `UriDisplay` for `T::Target`.
///
/// When a type of `Result<T, E>` appears in a route path, use a type of `T`
/// as the parameter in `uri!`. Note that `Result<T, E>` itself _does not_
/// implement `UriDisplay<Path>`.
///
/// Rocket implements `UriDisplay<Query>` (but not `UriDisplay<Path>`) for
/// several built-in types.
///
/// * **`Form<T>`, `LenientForm<T>`** _where_ **`T: FromUriParam + FromForm`**
///
/// Uses the implementation of `UriDisplay` for `T::Target`.
///
/// In general, when a type of `Form<T>` is to be displayed as part of a
/// URI's query, it suffices to derive `UriDisplay` for `T`. Note that any
/// type that can be converted into a `T` using [`FromUriParam`] can be used
/// in place of a `Form<T>` in a `uri!` invocation.
///
/// * **`Option<T>`** _where_ **`T: UriDisplay<Query>`**
///
/// If the `Option` is `Some`, uses the implementation of `UriDisplay` for
/// `T`. Otherwise, nothing is rendered.
///
/// * **`Result<T, E>`** _where_ **`T: UriDisplay<Query>`**
///
/// If the `Result` is `Ok`, uses the implementation of `UriDisplay` for
/// `T`. Otherwise, nothing is rendered.
///
/// # Deriving
///
/// Manually implementing `UriDisplay` should be done with care. For most use
@ -90,16 +168,25 @@ use {RawStr, ext::Normalize};
///
/// ```rust
/// # #[macro_use] extern crate rocket;
/// # use rocket::http::uri::UriDisplay;
/// #[derive(FromForm, UriDisplay)]
/// # use rocket::http::uri::{UriDisplay, Query, Path};
/// // Derives `UriDisplay<Query>`
/// #[derive(UriDisplayQuery)]
/// struct User {
/// name: String,
/// age: usize,
/// }
///
/// let user = User { name: "Michael Smith".into(), age: 31 };
/// let uri_string = format!("{}", &user as &UriDisplay);
/// let uri_string = format!("{}", &user as &UriDisplay<Query>);
/// assert_eq!(uri_string, "name=Michael%20Smith&age=31");
///
/// // Derives `UriDisplay<Path>`
/// #[derive(UriDisplayPath)]
/// struct Name(String);
///
/// let name = Name("Bob Smith".into());
/// let uri_string = format!("{}", &name as &UriDisplay<Path>);
/// assert_eq!(uri_string, "Bob%20Smith");
/// ```
///
/// As long as every field in the structure (or enum) implements `UriDisplay`,
@ -109,6 +196,8 @@ use {RawStr, ext::Normalize};
/// derive] documentation for full details.
///
/// [`UriDisplay` derive]: ../../../rocket_codegen/derive.UriDisplay.html
/// [`Formatter::write_named_value()`]: uri::Formatter::write_named_value()
/// [`Formatter::write_value()`]: uri::Formatter::write_value()
///
/// # Implementing
///
@ -127,12 +216,12 @@ use {RawStr, ext::Normalize};
/// ## 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. Note the custom parsing in the `FromParam`
/// implementation; as a result of this, a custom (reflexive) `UriDisplay`
/// implementation is required.
/// `FromParam` and `UriDisplay<Path>`. 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 path parameter type. Note the custom parsing in the
/// `FromParam` implementation; as a result of this, a custom (reflexive)
/// `UriDisplay` implementation is required.
///
/// ```rust
/// # #![feature(proc_macro_hygiene, decl_macro)]
@ -161,14 +250,15 @@ use {RawStr, ext::Normalize};
/// }
///
/// use std::fmt;
/// use rocket::http::uri::{Formatter, UriDisplay};
/// use rocket::http::uri::{Formatter, UriDisplay, Path};
/// 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. Prefixes the inner name with `name:`.
/// fn fmt(&self, f: &mut Formatter) -> fmt::Result {
/// 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.
/// // Prefixes the inner name with `name:`.
/// fn fmt(&self, f: &mut Formatter<Path>) -> fmt::Result {
/// f.write_value(&format!("name:{}", self.0))
/// }
/// }
@ -186,63 +276,67 @@ use {RawStr, ext::Normalize};
/// let uri = uri!(real: Name("Mike Smith".into()));
/// assert_eq!(uri.path(), "/name:Mike%20Smith");
/// ```
pub trait UriDisplay {
pub trait UriDisplay<P: UriPart> {
/// Formats `self` in a URI-safe manner using the given formatter.
fn fmt(&self, f: &mut Formatter) -> fmt::Result;
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result;
}
impl<'a> fmt::Display for &'a UriDisplay {
impl<'a> fmt::Display for &'a UriDisplay<Path> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
UriDisplay::fmt(*self, &mut Formatter::new(f))
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 UriDisplay for RawStr {
impl<P: UriPart> UriDisplay<P> for RawStr {
#[inline(always)]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
f.write_raw(&Uri::percent_encode(self.as_str()))
}
}
/// Percent-encodes the raw string.
impl UriDisplay for str {
impl<P: UriPart> UriDisplay<P> for str {
#[inline(always)]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
f.write_raw(&Uri::percent_encode(self))
}
}
/// Percent-encodes the raw string.
impl<'a> UriDisplay for Cow<'a, str> {
impl<'a, P: UriPart> UriDisplay<P> for Cow<'a, str> {
#[inline(always)]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
f.write_raw(&Uri::percent_encode(self))
}
}
/// Percent-encodes the raw string.
impl UriDisplay for String {
impl<P: UriPart> UriDisplay<P> for String {
#[inline(always)]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
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 for PathBuf {
impl UriDisplay<Path> for path::PathBuf {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let string = self.normalized_str();
let enc: Cow<str> = utf8_percent_encode(&string, UNSAFE_PATH_ENCODE_SET).into();
f.write_raw(&enc)
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 for Path {
impl UriDisplay<Path> for path::Path {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
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)
@ -252,9 +346,9 @@ impl UriDisplay for Path {
macro_rules! impl_with_display {
($($T:ty),+) => {$(
/// This implementation is identical to the `Display` implementation.
impl UriDisplay for $T {
impl<P: UriPart> UriDisplay<P> for $T {
#[inline(always)]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
use std::fmt::Write;
write!(f, "{}", self)
}
@ -274,9 +368,9 @@ impl_with_display! {
macro_rules! impl_for_ref {
($($T:ty),+) => {$(
/// Uses the implementation of `UriDisplay` for `T`.
impl<'a, T: UriDisplay + ?Sized> UriDisplay for $T {
impl<'a, P: UriPart, T: UriDisplay<P> + ?Sized> UriDisplay<P> for $T {
#[inline(always)]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
UriDisplay::fmt(*self, f)
}
}
@ -284,3 +378,25 @@ macro_rules! impl_for_ref {
}
impl_for_ref!(&'a mut T, &'a 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)?;
}
Ok(())
}
}
impl<E, T: UriDisplay<Query>> 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)?;
}
Ok(())
}
}

View File

@ -10,7 +10,7 @@ use config::Environment::*;
use config::{Result, ConfigBuilder, Environment, ConfigError, LoggingLevel};
use config::{Table, Value, Array, Datetime};
use http::Key;
use http::private::Key;
/// Structure for Rocket application configuration.
///

View File

@ -2,7 +2,7 @@ use std::fmt;
#[cfg(feature = "tls")]
use http::tls::{Certificate, PrivateKey};
use http::Key;
use http::private::Key;
use config::{Result, Config, Value, ConfigError, LoggingLevel};

View File

@ -3,7 +3,7 @@ use std::borrow::Cow;
use Rocket;
use local::LocalRequest;
use http::{Method, CookieJar};
use http::{Method, private::CookieJar};
use error::LaunchError;
/// A structure to construct requests for local dispatching.

View File

@ -3,7 +3,7 @@ use std::ops::Deref;
use outcome::Outcome::*;
use request::{Request, form::{FromForm, FormItems, FormDataError}};
use data::{Outcome, Transform, Transformed, Data, FromData};
use http::{Status, uri::FromUriParam};
use http::{Status, uri::{Query, FromUriParam}};
/// A data guard for parsing [`FromForm`] types strictly.
///
@ -219,7 +219,7 @@ impl<'f, T: FromForm<'f>> FromData<'f> for Form<T> {
}
}
impl<'f, A, T: FromUriParam<A> + FromForm<'f>> FromUriParam<A> for Form<T> {
impl<'f, A, T: FromUriParam<Query, A> + FromForm<'f>> FromUriParam<Query, A> for Form<T> {
type Target = T::Target;
#[inline(always)]

View File

@ -2,7 +2,7 @@ use std::ops::Deref;
use request::{Request, form::{Form, FormDataError, FromForm}};
use data::{Data, Transform, Transformed, FromData, Outcome};
use http::uri::FromUriParam;
use http::uri::{Query, FromUriParam};
/// A data gaurd for parsing [`FromForm`] types leniently.
///
@ -109,7 +109,7 @@ impl<'f, T: FromForm<'f>> FromData<'f> for LenientForm<T> {
}
}
impl<'f, A, T: FromUriParam<A> + FromForm<'f>> FromUriParam<A> for LenientForm<T> {
impl<'f, A, T: FromUriParam<Query, A> + FromForm<'f>> FromUriParam<Query, A> for LenientForm<T> {
type Target = T::Target;
#[inline(always)]

View File

@ -13,10 +13,10 @@ use request::{FromFormValue, FormItems, FormItem};
use rocket::Rocket;
use router::Route;
use config::{Config, Limits};
use http::uri::{Origin, Segments};
use http::{Method, Header, HeaderMap, Cookies, CookieJar};
use http::{RawStr, ContentType, Accept, MediaType, Indexed, SmallVec};
use http::hyper;
use http::{hyper, uri::{Origin, Segments}};
use http::{Method, Header, HeaderMap, Cookies};
use http::{RawStr, ContentType, Accept, MediaType};
use http::private::{Indexed, SmallVec, CookieJar};
type Indices = (usize, usize);