mirror of https://github.com/rwf2/Rocket.git
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:
parent
543b07a4ba
commit
b7db74144f
|
@ -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]
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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!()
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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() }
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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() { }
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -30,7 +30,6 @@ impl AsPtr for [u8] {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum Indexed<'a, T: ?Sized + ToOwned + 'a> {
|
||||
Indexed(usize, usize),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 { }
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue