From 084481a84e47d2ee451ec4d04a12a8f7135e92f9 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Mon, 28 Aug 2017 20:14:59 -0700 Subject: [PATCH] Initial implementation of typed URIs. This is a breaking change. All Rocket applications using code generation must now additionally declare usage of the 'decl_macro' feature. --- codegen/Cargo.toml | 1 + codegen/src/decorators/route.rs | 62 ++++- codegen/src/lib.rs | 16 +- codegen/src/macros/mod.rs | 59 +++-- codegen/src/macros/uri.rs | 145 +++++++++++ codegen/src/parser/mod.rs | 4 +- codegen/src/parser/param.rs | 93 +++---- codegen/src/parser/route.rs | 21 +- codegen/src/parser/uri_macro.rs | 241 ++++++++++++++++++ codegen/src/utils/mod.rs | 64 +++-- codegen/src/utils/parser_ext.rs | 45 +++- codegen/src/utils/span_ext.rs | 18 +- .../compile-fail/absolute-mount-paths.rs | 2 +- .../tests/compile-fail/bad-attribute-form.rs | 2 +- .../tests/compile-fail/bad-attribute-param.rs | 2 +- codegen/tests/compile-fail/bad-error-fn.rs | 2 +- .../tests/compile-fail/bad-ident-argument.rs | 2 +- .../bad-value-types-in-attribute.rs | 2 +- .../tests/compile-fail/data-without-post.rs | 2 +- codegen/tests/compile-fail/decorate-enum.rs | 2 +- codegen/tests/compile-fail/decorate-impl.rs | 2 +- codegen/tests/compile-fail/decorate-struct.rs | 2 +- codegen/tests/compile-fail/decorate-trait.rs | 2 +- codegen/tests/compile-fail/empty-segments.rs | 2 +- codegen/tests/compile-fail/form-field-attr.rs | 2 +- codegen/tests/compile-fail/ignored_params.rs | 2 +- .../compile-fail/malformed-media-type.rs | 2 +- .../compile-fail/malformed-param-list.rs | 2 +- .../compile-fail/phantom-declared-param.rs | 2 +- .../tests/compile-fail/route-bad-method.rs | 2 +- .../compile-fail/route-invalid-method.rs | 2 +- .../compile-fail/unknown-attribute-param.rs | 2 +- .../tests/compile-fail/unknown-media-type.rs | 2 +- codegen/tests/run-pass/complete-decorator.rs | 2 +- codegen/tests/run-pass/custom-content-type.rs | 2 +- codegen/tests/run-pass/derive_form.rs | 2 +- codegen/tests/run-pass/dynamic-paths.rs | 2 +- codegen/tests/run-pass/empty-fn.rs | 2 +- codegen/tests/run-pass/empty_form.rs | 2 +- codegen/tests/run-pass/error-handler.rs | 2 +- codegen/tests/run-pass/form-field-rename.rs | 2 +- codegen/tests/run-pass/instanced-mounting.rs | 2 +- .../tests/run-pass/issue-1-colliding-names.rs | 2 +- codegen/tests/run-pass/methods.rs | 2 +- codegen/tests/run-pass/rank_decorator.rs | 2 +- .../refactored_rocket_no_lint_errors.rs | 2 +- codegen/tests/run-pass/segments.rs | 2 +- codegen/tests/run-pass/type-alias-lints.rs | 2 +- examples/config/src/main.rs | 2 +- examples/config/tests/development.rs | 2 +- examples/config/tests/production.rs | 2 +- examples/config/tests/staging.rs | 2 +- examples/content_types/src/main.rs | 2 +- examples/cookies/src/main.rs | 2 +- examples/errors/src/main.rs | 2 +- examples/fairings/src/main.rs | 2 +- examples/form_kitchen_sink/src/main.rs | 2 +- examples/form_validation/src/main.rs | 2 +- examples/handlebars_templates/src/main.rs | 2 +- examples/hello_person/src/main.rs | 2 +- examples/hello_world/src/main.rs | 2 +- examples/json/src/main.rs | 2 +- examples/managed_queue/src/main.rs | 2 +- examples/msgpack/src/main.rs | 2 +- examples/optional_redirect/src/main.rs | 2 +- examples/pastebin/src/main.rs | 2 +- examples/query_params/src/main.rs | 2 +- examples/ranking/src/main.rs | 2 +- examples/raw_sqlite/src/main.rs | 2 +- examples/raw_upload/src/main.rs | 2 +- examples/redirect/src/main.rs | 2 +- examples/request_guard/src/main.rs | 2 +- examples/session/src/main.rs | 2 +- examples/state/src/main.rs | 2 +- examples/static_files/src/main.rs | 2 +- examples/stream/src/main.rs | 2 +- examples/testing/src/main.rs | 2 +- examples/tls/src/main.rs | 2 +- examples/todo/src/main.rs | 2 +- examples/uuid/src/main.rs | 2 +- lib/src/catcher.rs | 2 +- lib/src/data/from_data.rs | 2 +- lib/src/http/cookies.rs | 4 +- lib/src/http/uri.rs | 43 +++- lib/src/lib.rs | 6 +- lib/src/local/mod.rs | 2 +- lib/src/request/form/form.rs | 14 +- lib/src/request/form/from_form.rs | 4 +- lib/src/request/form/lenient.rs | 6 +- lib/src/request/from_request.rs | 2 +- lib/src/request/param.rs | 6 +- lib/src/request/state.rs | 2 +- lib/src/response/flash.rs | 2 +- lib/src/response/responder.rs | 2 +- lib/src/rocket.rs | 12 +- lib/tests/form_method-issue-45.rs | 2 +- lib/tests/form_value_decoding-issue-82.rs | 2 +- lib/tests/head_handling.rs | 2 +- lib/tests/limits.rs | 2 +- lib/tests/precise-content-type-matching.rs | 2 +- lib/tests/query-and-non-query-dont-collide.rs | 2 +- lib/tests/redirect_from_catcher-issue-113.rs | 2 +- lib/tests/remote-rewrite.rs | 2 +- lib/tests/responder_lifetime-issue-345.rs | 2 +- lib/tests/route_guard.rs | 2 +- lib/tests/segments-issues-41-86.rs | 2 +- lib/tests/strict_and_lenient_forms.rs | 2 +- 107 files changed, 803 insertions(+), 235 deletions(-) create mode 100644 codegen/src/macros/uri.rs create mode 100644 codegen/src/parser/uri_macro.rs diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 1ba1f007..67db3621 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -16,6 +16,7 @@ plugin = true [dependencies] rocket = { version = "0.4.0-dev", path = "../lib/" } +ordermap = "0.2" log = "0.3" [dev-dependencies] diff --git a/codegen/src/decorators/route.rs b/codegen/src/decorators/route.rs index 6d59ec25..6e4c2a9f 100644 --- a/codegen/src/decorators/route.rs +++ b/codegen/src/decorators/route.rs @@ -1,14 +1,14 @@ use std::collections::HashSet; use std::fmt::Display; -use ::{ROUTE_STRUCT_PREFIX, ROUTE_FN_PREFIX, PARAM_PREFIX}; +use ::{ROUTE_STRUCT_PREFIX, ROUTE_FN_PREFIX, PARAM_PREFIX, URI_INFO_MACRO_PREFIX}; use ::{ROUTE_ATTR, ROUTE_INFO_ATTR}; use parser::{Param, RouteParams}; use utils::*; -use syntax::codemap::{Span, Spanned}; +use syntax::codemap::{Span, Spanned, dummy_spanned}; use syntax::tokenstream::TokenTree; -use syntax::ast::{Arg, Ident, Stmt, Expr, MetaItem, Path}; +use syntax::ast::{Arg, Ident, Item, Stmt, Expr, MetaItem, Path}; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; use syntax::parse::token; @@ -43,7 +43,8 @@ impl RouteParams { fn missing_declared_err(&self, ecx: &ExtCtxt, arg: &Spanned) { let (fn_span, fn_name) = (self.annotated_fn.span(), self.annotated_fn.ident()); ecx.struct_span_err(arg.span, &format!("unused dynamic parameter: `{}`", arg.node)) - .span_note(fn_span, &format!("expected argument named `{}` in `{}`", arg.node, fn_name)) + .span_note(fn_span, &format!("expected argument named `{}` in `{}` handler", + arg.node, fn_name)) .emit(); } @@ -127,10 +128,12 @@ impl RouteParams { // an error/debug message if parsing a parameter fails. fn generate_param_statements(&self, ecx: &ExtCtxt) -> Vec { let mut fn_param_statements = vec![]; + let params = Param::parse_many(ecx, self.uri.node.path(), self.uri.span.trim(1)) + .unwrap_or_else(|mut diag| { diag.emit(); vec![] }); // Generate a statement for every declared paramter in the path. let mut declared_set = HashSet::new(); - for (i, param) in self.path_params(ecx).enumerate() { + for (i, param) in params.iter().enumerate() { declared_set.insert(param.ident().name); let ty = match self.annotated_fn.find_input(¶m.ident().name) { Some(arg) => strip_ty_lifetimes(arg.ty.clone()), @@ -142,7 +145,7 @@ impl RouteParams { // Note: the `None` case shouldn't happen if a route is matched. let ident = param.ident().prepend(PARAM_PREFIX); - let expr = match param { + let expr = match *param { Param::Single(_) => quote_expr!(ecx, match __req.get_param_str($i) { Some(s) => <$ty as ::rocket::request::FromParam>::from_param(s), None => return ::rocket::Outcome::Forward(__data) @@ -213,6 +216,49 @@ impl RouteParams { sep_by_tok(ecx, &args, token::Comma) } + fn generate_uri_macro(&self, ecx: &ExtCtxt) -> P { + let macro_args = parse_as_tokens(ecx, "$($token:tt)*"); + let macro_exp = parse_as_tokens(ecx, "$($token)*"); + let macro_name = self.annotated_fn.ident().prepend(URI_INFO_MACRO_PREFIX); + + // What we return if we find an inconsistency throughout. + let dummy = quote_item!(ecx, pub macro $macro_name($macro_args) { }).unwrap(); + + // Hacky check to see if the user's URI was valid. + if self.uri.span == dummy_spanned(()).span { + return dummy + } + + // Extract the route uri path and paramters from the uri. + let route_path = self.uri.node.to_string(); + let params = match Param::parse_many(ecx, &route_path, self.uri.span.trim(1)) { + Ok(params) => params, + Err(mut diag) => { + diag.cancel(); + return dummy; + } + }; + + // Generate the list of arguments for the URI. + let mut fn_uri_args = vec![]; + for param in ¶ms { + if let Some(arg) = self.annotated_fn.find_input(¶m.ident().name) { + let (pat, ty) = (&arg.pat, &arg.ty); + fn_uri_args.push(quote_tokens!(ecx, $pat: $ty)) + } else { + return dummy; + } + } + + // Generate the call to the internal URI macro with all the info. + let args = sep_by_tok(ecx, &fn_uri_args, token::Comma); + quote_item!(ecx, + pub macro $macro_name($macro_args) { + rocket_internal_uri!($route_path, ($args), $macro_exp) + } + ).expect("consistent uri macro item") + } + fn explode(&self, ecx: &ExtCtxt) -> (InternedString, &str, Path, P, P) { let name = self.annotated_fn.ident().name.as_str(); let path = &self.uri.node.as_str(); @@ -242,6 +288,7 @@ fn generic_route_decorator(known_method: Option>, let query_statement = route.generate_query_statement(ecx); let data_statement = route.generate_data_statement(ecx); let fn_arguments = route.generate_fn_arguments(ecx); + let uri_macro = route.generate_uri_macro(ecx); // Generate and emit the wrapping function with the Rocket handler signature. let user_fn_name = route.annotated_fn.ident(); @@ -288,6 +335,9 @@ fn generic_route_decorator(known_method: Option>, let route_attr = quote_attr!(ecx, #[$attr_name($struct_name)]); attach_and_emit(&mut output, route_attr, annotated); + // Emit the per-route URI macro. + emit_item(&mut output, uri_macro); + output } diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index a638dbd3..aefcf134 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -148,6 +148,7 @@ extern crate syntax; extern crate syntax_ext; extern crate rustc_plugin; extern crate rocket; +extern crate ordermap; #[macro_use] mod utils; mod parser; @@ -166,6 +167,7 @@ const ROUTE_STRUCT_PREFIX: &'static str = "static_rocket_route_info_for_"; const CATCH_STRUCT_PREFIX: &'static str = "static_rocket_catch_info_for_"; const ROUTE_FN_PREFIX: &'static str = "rocket_route_fn_"; const CATCH_FN_PREFIX: &'static str = "rocket_catch_fn_"; +const URI_INFO_MACRO_PREFIX: &'static str = "rocket_uri_for_"; const ROUTE_ATTR: &'static str = "rocket_route"; const ROUTE_INFO_ATTR: &'static str = "rocket_route_info"; @@ -188,6 +190,12 @@ macro_rules! register_derives { ) } +macro_rules! register_macros { + ($reg:expr, $($n:expr => $f:ident),+) => ( + $($reg.register_macro($n, macros::$f);)+ + ) +} + /// Compiler hook for Rust to register plugins. #[plugin_registrar] pub fn plugin_registrar(reg: &mut Registry) { @@ -196,8 +204,12 @@ pub fn plugin_registrar(reg: &mut Registry) { ::rocket::logger::init(::rocket::config::LoggingLevel::Debug); } - reg.register_macro("routes", macros::routes); - reg.register_macro("errors", macros::errors); + register_macros!(reg, + "routes" => routes, + "errors" => errors, + "uri" => uri, + "rocket_internal_uri" => uri_internal + ); register_derives!(reg, "derive_FromForm" => from_form_derive diff --git a/codegen/src/macros/mod.rs b/codegen/src/macros/mod.rs index 1cf6649d..59426ab7 100644 --- a/codegen/src/macros/mod.rs +++ b/codegen/src/macros/mod.rs @@ -1,3 +1,5 @@ +mod uri; + use {ROUTE_STRUCT_PREFIX, CATCH_STRUCT_PREFIX}; use utils::{sep_by_tok, ParserExt, IdentExt}; @@ -8,39 +10,48 @@ use syntax::ext::base::{DummyResult, ExtCtxt, MacResult, MacEager}; use syntax::parse::token::Token; use syntax::ptr::P; +pub use self::uri::{uri, uri_internal}; + +#[inline] +pub fn prefix_path(prefix: &str, path: &mut Path) { + let last = path.segments.len() - 1; + let last_seg = &mut path.segments[last]; + last_seg.identifier = last_seg.identifier.prepend(prefix); +} + #[inline] pub fn prefix_paths(prefix: &str, paths: &mut Vec) { for p in paths { - let last = p.segments.len() - 1; - let last_seg = &mut p.segments[last]; - last_seg.identifier = last_seg.identifier.prepend(prefix); + prefix_path(prefix, p); } } -pub fn prefixing_vec_macro(prefix: &str, - mut to_expr: F, - ecx: &mut ExtCtxt, - sp: Span, - args: &[TokenTree]) - -> Box - where F: FnMut(&ExtCtxt, Path) -> P +pub fn prefixing_vec_macro( + prefix: &str, + mut to_expr: F, + ecx: &mut ExtCtxt, + sp: Span, + args: &[TokenTree]) -> Box +where F: FnMut(&ExtCtxt, Path) -> P { let mut parser = ecx.new_parser_from_tts(args); - let paths = parser.parse_paths(); - if let Ok(mut paths) = paths { - // Prefix each path terminator and build up the P for each path. - prefix_paths(prefix, &mut paths); - let path_exprs: Vec> = paths.into_iter() - .map(|path| to_expr(ecx, path)) - .collect(); + match parser.parse_paths() { + Ok(mut paths) => { + // Prefix each path terminator and build up the P for each path. + prefix_paths(prefix, &mut paths); + let path_exprs: Vec> = paths.into_iter() + .map(|path| to_expr(ecx, path)) + .collect(); - // Now put them all in one vector and return the thing. - let path_list = sep_by_tok(ecx, &path_exprs, Token::Comma); - let output = quote_expr!(ecx, vec![$path_list]).unwrap(); - MacEager::expr(P(output)) - } else { - paths.unwrap_err().emit(); - DummyResult::expr(sp) + // Now put them all in one vector and return the thing. + let path_list = sep_by_tok(ecx, &path_exprs, Token::Comma); + let output = quote_expr!(ecx, vec![$path_list]).unwrap(); + MacEager::expr(P(output)) + } + Err(mut e) => { + e.emit(); + DummyResult::expr(sp) + } } } diff --git a/codegen/src/macros/uri.rs b/codegen/src/macros/uri.rs new file mode 100644 index 00000000..7bb5c455 --- /dev/null +++ b/codegen/src/macros/uri.rs @@ -0,0 +1,145 @@ +extern crate syntax_pos; + +use std::fmt::Display; + +use URI_INFO_MACRO_PREFIX; +use super::prefix_path; + +use parser::{UriParams, InternalUriParams, Validation}; + +use syntax::codemap::Span; +use syntax::ext::base::{DummyResult, ExtCtxt, MacEager, MacResult}; +use syntax::tokenstream::{TokenStream, TokenTree}; +use syntax::ast::{self, Ident}; +use syntax::parse::PResult; +use syntax::ext::build::AstBuilder; +use syntax::ptr::P; + +/// FIXME: Implement `MultiSpan::From>`. +use self::syntax_pos::MultiSpan as MS; + +pub fn uri( + ecx: &mut ExtCtxt, + sp: Span, + args: &[TokenTree], +) -> Box { + // Generate the path to the internal macro. + let mut parser = ecx.new_parser_from_tts(args); + let (_, mut macro_path) = try_parse!(sp, UriParams::parse_prelude(&mut parser)); + prefix_path(URI_INFO_MACRO_PREFIX, &mut macro_path); + + // It's incredibly important we use `sp` as the Span for the generated code + // so that errors from the `internal` call show up on the user's code. + let expr = parser.mk_mac_expr(sp, + ast::Mac_ { + path: macro_path, + tts: args.to_vec().into_iter().collect::().into(), + }, + ::syntax::util::ThinVec::new(), + ); + + MacEager::expr(expr) +} + +fn extract_exprs<'a>( + ecx: &ExtCtxt<'a>, + internal: &InternalUriParams, +) -> PResult<'a, Vec>> { + let route_name = &internal.uri_params.route_path; + match internal.validate() { + Validation::Ok(exprs) => Ok(exprs), + Validation::Unnamed(expected, actual) => { + let mut diag = ecx.struct_span_err(internal.uri_params.args_span(), + &format!("`{}` route uri expects {} but {} supplied", + route_name, p!(expected, "parameter"), p!(actual, "was"))); + + if expected > 0 { + let ps = p!("parameter", expected); + diag.note(&format!("expected {}: {}", ps, internal.fn_args_str())); + } + + Err(diag) + } + Validation::Named(missing, extra, dup) => { + let ps = p!("parameter", internal.fn_args.len()); + let e = &format!("invalid {} for `{}` route uri", ps, route_name); + let mut diag = ecx.struct_span_err(internal.uri_params.args_span(), e); + diag.note(&format!("expected {}: {}", ps, internal.fn_args_str())); + + fn join>(iter: T) -> (&'static str, String) { + let items: Vec<_> = iter.map(|i| format!("`{}`", i)).collect(); + (p!("parameter", items.len()), items.join(", ")) + } + + if !extra.is_empty() { + let (ps, msg) = join(extra.iter().map(|id| id.node)); + let sp: Vec<_> = extra.iter().map(|ident| ident.span).collect(); + diag.span_help(MS::from_spans(sp), &format!("unknown {}: {}", ps, msg)); + } + + if !dup.is_empty() { + let (ps, msg) = join(dup.iter().map(|id| id.node)); + let sp: Vec<_> = dup.iter().map(|ident| ident.span).collect(); + diag.span_help(MS::from_spans(sp), &format!("duplicate {}: {}", ps, msg)); + } + + if !missing.is_empty() { + let (ps, msg) = join(missing.iter()); + diag.help(&format!("missing {}: {}", ps, msg)); + } + + Err(diag) + } + } +} + +#[allow(unused_imports)] +pub fn uri_internal( + ecx: &mut ExtCtxt, + sp: Span, + tt: &[TokenTree], +) -> Box { + // Parse the internal invocation and the user's URI param expressions. + let mut parser = ecx.new_parser_from_tts(tt); + let internal = try_parse!(sp, InternalUriParams::parse(ecx, &mut parser)); + let exprs = try_parse!(sp, extract_exprs(ecx, &internal)); + + // Generate the statements to typecheck each parameter. First, the mount. + let mut argument_stmts = vec![]; + let mut format_assign_tokens = vec![]; + if let Some(mount_point) = internal.uri_params.mount_point { + // TODO: Should all expressions, not just string literals, be allowed? + // let as_ref = ecx.expr_method_call(span, expr, Ident::from_str("as_ref"), v![]); + let mount_string = mount_point.node; + argument_stmts.push(ecx.stmt_let_typed( + mount_point.span, + false, + Ident::from_str("mount"), + quote_ty!(ecx, &str), + quote_expr!(ecx, $mount_string), + )); + + format_assign_tokens.push(quote_tokens!(ecx, mount = mount,)); + } else { + format_assign_tokens.push(quote_tokens!(ecx, mount = "/",)); + } + + // Now the user's parameters. + for (i, &(ident, ref ty)) in internal.fn_args.iter().enumerate() { + let (span, expr) = (exprs[i].span, exprs[i].clone()); + let into = ecx.expr_method_call(span, expr, Ident::from_str("into"), vec![]); + let stmt = ecx.stmt_let_typed(span, false, ident.node, ty.clone(), into); + + argument_stmts.push(stmt); + format_assign_tokens.push(quote_tokens!(ecx, + $ident = &$ident as &::rocket::http::uri::UriDisplay, + )); + } + + let user_path = internal.uri.node.replace('<', "{").replace('>', "}"); + let route_uri = "{mount}".to_string() + &user_path; + MacEager::expr(quote_expr!(ecx, { + $argument_stmts + ::rocket::http::uri::URI::from(format!($route_uri, $format_assign_tokens)) + })) +} diff --git a/codegen/src/parser/mod.rs b/codegen/src/parser/mod.rs index cb152019..bdab9f0c 100644 --- a/codegen/src/parser/mod.rs +++ b/codegen/src/parser/mod.rs @@ -4,9 +4,11 @@ mod error; mod param; mod function; mod uri; +mod uri_macro; pub use self::keyvalue::KVSpanned; pub use self::route::RouteParams; pub use self::error::ErrorParams; -pub use self::param::{Param, ParamIter}; +pub use self::param::Param; pub use self::function::Function; +pub use self::uri_macro::{Args, InternalUriParams, UriParams, Validation}; diff --git a/codegen/src/parser/param.rs b/codegen/src/parser/param.rs index 4d0d3449..88247ef9 100644 --- a/codegen/src/parser/param.rs +++ b/codegen/src/parser/param.rs @@ -1,8 +1,8 @@ use syntax::ast::Ident; +use syntax::codemap::{Span, Spanned}; use syntax::ext::base::ExtCtxt; -use syntax::codemap::{Span, Spanned, BytePos}; - -use utils::span; +use utils::SpanExt; +use syntax::parse::PResult; #[derive(Debug)] pub enum Param { @@ -22,64 +22,45 @@ impl Param { Param::Single(ref ident) | Param::Many(ref ident) => &ident.node, } } -} -pub struct ParamIter<'s, 'a, 'c: 'a> { - ctxt: &'a ExtCtxt<'c>, - span: Span, - string: &'s str, -} + pub fn parse_many<'a>( + ecx: &ExtCtxt<'a>, + mut string: &str, + mut span: Span + ) -> PResult<'a, Vec> { + let err = |sp, msg| { Err(ecx.struct_span_err(sp, msg)) }; -impl<'s, 'a, 'c: 'a> ParamIter<'s, 'a, 'c> { - pub fn new(c: &'a ExtCtxt<'c>, s: &'s str, p: Span) -> ParamIter<'s, 'a, 'c> { - ParamIter { ctxt: c, span: p, string: s } - } -} + let mut params = vec![]; + loop { + // Find the start and end indexes for the next parameter, if any. + let (start, end) = match string.find('<') { + Some(i) => match string.find('>') { + Some(j) if j > i => (i, j), + Some(_) => return err(span, "malformed parameters"), + None => return err(span, "malformed parameters") + }, + _ => return Ok(params) + }; -impl<'s, 'a, 'c> Iterator for ParamIter<'s, 'a, 'c> { - type Item = Param; + // Calculate the parameter's ident and span. + let param_span = span.expand(start, end + 1); + let full_param = &string[(start + 1)..end]; + let (is_many, param) = if full_param.ends_with("..") { + (true, &full_param[..(full_param.len() - 2)]) + } else { + (false, full_param) + }; - fn next(&mut self) -> Option { - let err = |ecx: &ExtCtxt, sp: Span, msg: &str| { - ecx.span_err(sp, msg); - None - }; + // Advance the string and span. + string = &string[(end + 1)..]; + span = span.trim_left(end + 1); - // Find the start and end indexes for the next parameter, if any. - let (start, end) = match self.string.find('<') { - Some(i) => match self.string.find('>') { - Some(j) => (i, j), - None => return err(self.ctxt, self.span, "malformed parameters") - }, - _ => return None, - }; - - // Ensure we found a valid parameter. - if end <= start { - return err(self.ctxt, self.span, "malformed parameters"); - } - - // Calculate the parameter's ident. - let full_param = &self.string[(start + 1)..end]; - let (is_many, param) = if full_param.ends_with("..") { - (true, &full_param[..(full_param.len() - 2)]) - } else { - (false, full_param) - }; - - let mut param_span = self.span; - param_span.lo = self.span.lo + BytePos(start as u32); - param_span.hi = self.span.lo + BytePos((end + 1) as u32); - - // Advance the string and span. - self.string = &self.string[(end + 1)..]; - self.span.lo = self.span.lo + BytePos((end + 1) as u32); - - let spanned_ident = span(Ident::from_str(param), param_span); - if is_many { - Some(Param::Many(spanned_ident)) - } else { - Some(Param::Single(spanned_ident)) + let spanned_ident = param_span.wrap(Ident::from_str(param)); + if is_many { + params.push(Param::Many(spanned_ident)) + } else { + params.push(Param::Single(spanned_ident)) + } } } } diff --git a/codegen/src/parser/route.rs b/codegen/src/parser/route.rs index e6d305e4..482cc8a7 100644 --- a/codegen/src/parser/route.rs +++ b/codegen/src/parser/route.rs @@ -6,7 +6,7 @@ use syntax::ext::base::{ExtCtxt, Annotatable}; use syntax::codemap::{Span, Spanned, dummy_spanned}; use utils::{span, MetaItemExt, SpanExt, is_valid_ident}; -use super::{Function, ParamIter}; +use super::Function; use super::keyvalue::KVSpanned; use super::uri::validate_uri; use rocket::http::{Method, MediaType}; @@ -33,12 +33,13 @@ impl RouteParams { /// Parses the route attribute from the given decorator context. If the /// parse is not successful, this function exits early with the appropriate /// error message to the user. - pub fn from(ecx: &mut ExtCtxt, - sp: Span, - known_method: Option>, - meta_item: &MetaItem, - annotated: &Annotatable) - -> RouteParams { + pub fn from( + ecx: &mut ExtCtxt, + sp: Span, + known_method: Option>, + meta_item: &MetaItem, + annotated: &Annotatable + ) -> RouteParams { let function = Function::from(annotated).unwrap_or_else(|item_sp| { ecx.span_err(sp, "this attribute can only be used on functions..."); ecx.span_fatal(item_sp, "...but was applied to the item above."); @@ -125,12 +126,6 @@ impl RouteParams { annotated_fn: function, } } - - pub fn path_params<'s, 'a, 'c: 'a>(&'s self, - ecx: &'a ExtCtxt<'c>) - -> ParamIter<'s, 'a, 'c> { - ParamIter::new(ecx, self.uri.node.path(), self.uri.span.trim(1)) - } } fn is_valid_method(method: Method) -> bool { diff --git a/codegen/src/parser/uri_macro.rs b/codegen/src/parser/uri_macro.rs new file mode 100644 index 00000000..faa6bef8 --- /dev/null +++ b/codegen/src/parser/uri_macro.rs @@ -0,0 +1,241 @@ +use utils::{self, ParserExt, SpanExt}; + +use syntax::codemap::{Spanned, Span}; +use syntax::ext::base::ExtCtxt; +use syntax::symbol::InternedString; +use syntax::ast::{self, Expr, Name, Ident, Path}; +use syntax::parse::PResult; +use syntax::parse::token::{DelimToken, Token}; +use syntax::parse::common::SeqSep; +use syntax::parse::parser::{Parser, PathStyle}; +use syntax::print::pprust::ty_to_string; +use syntax::ptr::P; + +use ordermap::OrderMap; + +#[derive(Debug)] +enum Arg { + Unnamed(P), + Named(Spanned, P), +} + +#[derive(Debug)] +pub enum Args { + Unnamed(Vec>), + Named(Vec<(Spanned, P)>), +} + +#[derive(Debug)] +pub struct UriParams { + pub mount_point: Option>, + pub route_path: Path, + pub arguments: Option>, +} + +#[derive(Debug)] +pub struct InternalUriParams { + pub uri: Spanned, + pub fn_args: Vec<(Spanned, P)>, + pub uri_params: UriParams, +} + +impl Arg { + fn is_named(&self) -> bool { + match *self { + Arg::Named(..) => true, + Arg::Unnamed(_) => false, + } + } + + fn unnamed(self) -> P { + match self { + Arg::Unnamed(expr) => expr, + _ => panic!("Called Arg::unnamed() on an Arg::named!"), + } + } + + fn named(self) -> (Spanned, P) { + match self { + Arg::Named(ident, expr) => (ident, expr), + _ => panic!("Called Arg::named() on an Arg::Unnamed!"), + } + } +} + +impl UriParams { + // Parses the mount point, if any, and route identifier. + pub fn parse_prelude<'a>( + parser: &mut Parser<'a> + ) -> PResult<'a, (Option>, Path)> { + // Parse the mount point and suffixing ',', if any. + let mount_point = match parser.parse_optional_str() { + Some((symbol, _, _)) => { + let string = symbol.as_str(); + let span = parser.prev_span; + parser.expect(&Token::Comma)?; + Some(span.wrap(string)) + } + None => None, + }; + + // Parse the route identifier, which must always exist. + let route_path = parser.parse_path(PathStyle::Mod)?; + Ok((mount_point, route_path)) + } + + /// The Span to use when referring to all of the arguments. + pub fn args_span(&self) -> Span { + match self.arguments { + Some(ref args) => args.span, + None => self.route_path.span + } + } + + pub fn parse<'a>( ecx: &'a ExtCtxt, parser: &mut Parser<'a>,) -> PResult<'a, UriParams> { + // Parse the mount point and suffixing ',', if any. + let (mount_point, route_path) = Self::parse_prelude(parser)?; + + // If there are no arguments, finish early. + if !parser.eat(&Token::Colon) { + parser.expect(&Token::Eof)?; + let arguments = None; + return Ok(UriParams { mount_point, route_path, arguments, }); + } + + // Parse arguments. + let mut args_span = parser.span; + let comma = SeqSep::trailing_allowed(Token::Comma); + let arguments = parser.parse_seq_to_end(&Token::Eof, comma, |parser| { + let has_key = parser.look_ahead(1, |token| *token == Token::Eq); + + if has_key { + let inner_ident = parser.parse_ident()?; + let ident = parser.prev_span.wrap(inner_ident); + parser.expect(&Token::Eq)?; + + let expr = parser.parse_expr()?; + Ok(Arg::Named(ident, expr)) + } else { + let expr = parser.parse_expr()?; + Ok(Arg::Unnamed(expr)) + } + })?; + + // Set the end of the args_span to be the end of the args. + args_span.hi = parser.prev_span.hi; + + // A 'colon' was used but there are no arguments. + if arguments.is_empty() { + return Err(ecx.struct_span_err(parser.prev_span, + "expected argument list after `:`")); + } + + // Ensure that both types of arguments were not used at once. + let (mut homogeneous_args, mut prev_named) = (true, None); + for arg in arguments.iter() { + match prev_named { + Some(prev_named) => homogeneous_args = prev_named == arg.is_named(), + None => prev_named = Some(arg.is_named()), + } + } + + if !homogeneous_args { + return Err(ecx.struct_span_err(args_span, + "named and unnamed parameters cannot be mixed")); + } + + // Create the `Args` enum, which properly types one-kind-of-argument-ness. + let args = if prev_named.unwrap() { + Args::Named(arguments.into_iter().map(|arg| arg.named()).collect()) + } else { + Args::Unnamed(arguments.into_iter().map(|arg| arg.unnamed()).collect()) + }; + + let arguments = Some(args_span.wrap(args)); + Ok(UriParams { mount_point, route_path, arguments, }) + } +} + +pub enum Validation { + // Number expected, what we actually got. + Unnamed(usize, usize), + // (Missing, Extra, Duplicate) + Named(Vec, Vec>, Vec>), + // Everything is okay. + Ok(Vec>) +} + +impl InternalUriParams { + pub fn parse<'a>( + ecx: &'a ExtCtxt, + parser: &mut Parser<'a>, + ) -> PResult<'a, InternalUriParams> { + let uri_str = parser.parse_str_lit().map(|(s, _)| s.as_str().to_string())?; + let uri = parser.prev_span.wrap(uri_str); + parser.expect(&Token::Comma)?; + + let start = Token::OpenDelim(DelimToken::Paren); + let end = Token::CloseDelim(DelimToken::Paren); + let comma = SeqSep::trailing_allowed(Token::Comma); + let fn_args = parser + .parse_seq(&start, &end, comma, |parser| { + let param = parser.parse_ident_inc_pat()?; + parser.expect(&Token::Colon)?; + let ty = utils::strip_ty_lifetimes(parser.parse_ty()?); + Ok((param, ty)) + })? + .node; + + parser.expect(&Token::Comma)?; + let uri_params = UriParams::parse(ecx, parser)?; + Ok(InternalUriParams { uri, fn_args, uri_params, }) + } + + pub fn fn_args_str(&self) -> String { + self.fn_args.iter() + .map(|&(ident, ref ty)| format!("{}: {}", ident.node, ty_to_string(&ty))) + .collect::>() + .join(", ") + } + + pub fn validate(&self) -> Validation { + let unnamed = |args: &Vec>| -> Validation { + let (expected, actual) = (self.fn_args.len(), args.len()); + if expected != actual { Validation::Unnamed(expected, actual) } + else { Validation::Ok(args.clone()) } + }; + + match self.uri_params.arguments { + None => unnamed(&vec![]), + Some(Spanned { node: Args::Unnamed(ref args), .. }) => unnamed(args), + Some(Spanned { node: Args::Named(ref args), .. }) => { + let mut params: OrderMap>> = self.fn_args.iter() + .map(|&(ident, _)| (ident.node.name, None)) + .collect(); + + let (mut extra, mut dup) = (vec![], vec![]); + for &(ident, ref expr) in args { + match params.get_mut(&ident.node.name) { + Some(ref entry) if entry.is_some() => dup.push(ident), + Some(entry) => *entry = Some(expr.clone()), + None => extra.push(ident), + } + } + + let (mut missing, mut exprs) = (vec![], vec![]); + for (name, expr) in params.into_iter() { + match expr { + Some(expr) => exprs.push(expr), + None => missing.push(name) + } + } + + if (extra.len() + dup.len() + missing.len()) == 0 { + Validation::Ok(exprs) + } else { + Validation::Named(missing, extra, dup) + } + } + } + } +} diff --git a/codegen/src/utils/mod.rs b/codegen/src/utils/mod.rs index cb9960d6..29e67ae6 100644 --- a/codegen/src/utils/mod.rs +++ b/codegen/src/utils/mod.rs @@ -70,23 +70,11 @@ pub fn attach_and_emit(out: &mut Vec, attr: Attribute, to: Annotata } } -macro_rules! quote_enum { - ($ecx:expr, $var:expr => $(::$root:ident)+ - { $($variant:ident),+ ; $($extra:pat => $result:expr),* }) => ({ - use syntax::codemap::DUMMY_SP; - use syntax::ast::Ident; - use $(::$root)+::*; - let root_idents = vec![$(Ident::from_str(stringify!($root))),+]; - match $var { - $($variant => { - let variant = Ident::from_str(stringify!($variant)); - let mut idents = root_idents.clone(); - idents.push(variant); - $ecx.path_global(DUMMY_SP, idents) - })+ - $($extra => $result),* - } - }) +pub fn parse_as_tokens(ecx: &ExtCtxt, string: &str) -> Vec { + use syntax::parse::parse_stream_from_source_str as parse_stream; + + let stream = parse_stream("<_>".into(), string.into(), ecx.parse_sess, None); + stream.into_trees().collect() } pub struct TyLifetimeRemover; @@ -140,3 +128,45 @@ pub fn is_valid_ident>(s: S) -> bool { true } + +macro_rules! quote_enum { + ($ecx:expr, $var:expr => $(::$root:ident)+ + { $($variant:ident),+ ; $($extra:pat => $result:expr),* }) => ({ + use syntax::codemap::DUMMY_SP; + use syntax::ast::Ident; + use $(::$root)+::*; + let root_idents = vec![$(Ident::from_str(stringify!($root))),+]; + match $var { + $($variant => { + let variant = Ident::from_str(stringify!($variant)); + let mut idents = root_idents.clone(); + idents.push(variant); + $ecx.path_global(DUMMY_SP, idents) + })+ + $($extra => $result),* + } + }) +} + +macro_rules! try_parse { + ($sp:expr, $parse:expr) => ( + match $parse { + Ok(v) => v, + Err(mut e) => { e.emit(); return DummyResult::expr($sp); } + } + ) +} + +macro_rules! p { + ("parameter", $num:expr) => ( + if $num == 1 { "parameter" } else { "parameters" } + ); + + ($num:expr, "was") => ( + if $num == 1 { "1 was".into() } else { format!("{} were", $num) } + ); + + ($num:expr, "parameter") => ( + if $num == 1 { "1 parameter".into() } else { format!("{} parameters", $num) } + ) +} diff --git a/codegen/src/utils/parser_ext.rs b/codegen/src/utils/parser_ext.rs index 44f18fa1..9a0ae1ae 100644 --- a/codegen/src/utils/parser_ext.rs +++ b/codegen/src/utils/parser_ext.rs @@ -1,11 +1,22 @@ +use super::SpanExt; + use syntax::parse::parser::{PathStyle, Parser}; use syntax::parse::PResult; -use syntax::ast::Path; +use syntax::ast::{self, Path, StrStyle, Ident}; use syntax::parse::token::Token::{Eof, Comma}; use syntax::parse::common::SeqSep; +use syntax::codemap::Spanned; +use syntax::symbol::Symbol; pub trait ParserExt<'a> { + // Parse a comma-seperated list of paths: `a::b, b::c`. fn parse_paths(&mut self) -> PResult<'a, Vec>; + + // Just like `parse_str` but takes into account interpolated expressions. + fn parse_str_lit(&mut self) -> PResult<'a, (Symbol, StrStyle)>; + + // Like `parse_ident` but also looks for an `ident` in a `Pat`. + fn parse_ident_inc_pat(&mut self) -> PResult<'a, Spanned>; } impl<'a> ParserExt<'a> for Parser<'a> { @@ -14,4 +25,36 @@ impl<'a> ParserExt<'a> for Parser<'a> { SeqSep::trailing_allowed(Comma), |p| p.parse_path(PathStyle::Mod)) } + + fn parse_str_lit(&mut self) -> PResult<'a, (Symbol, StrStyle)> { + self.parse_str() + .or_else(|mut e| { + let expr = self.parse_expr().map_err(|i| { e.cancel(); i })?; + let string_lit = match expr.node { + ast::ExprKind::Lit(ref lit) => match lit.node { + ast::LitKind::Str(symbol, style) => (symbol, style), + _ => return Err(e) + } + _ => return Err(e) + }; + + e.cancel(); + Ok(string_lit) + }) + } + + fn parse_ident_inc_pat(&mut self) -> PResult<'a, Spanned> { + self.parse_ident() + .map(|ident| self.prev_span.wrap(ident)) + .or_else(|mut e| { + let pat = self.parse_pat().map_err(|i| { e.cancel(); i })?; + let ident = match pat.node { + ast::PatKind::Ident(_, ident, _) => ident, + _ => return Err(e) + }; + + e.cancel(); + Ok(ident) + }) + } } diff --git a/codegen/src/utils/span_ext.rs b/codegen/src/utils/span_ext.rs index 932e69ba..1564df3c 100644 --- a/codegen/src/utils/span_ext.rs +++ b/codegen/src/utils/span_ext.rs @@ -1,4 +1,4 @@ -use syntax::codemap::{Span, BytePos}; +use syntax::codemap::{Span, Spanned, BytePos}; pub trait SpanExt { /// Trim the span on the left and right by `length`. @@ -15,6 +15,12 @@ pub trait SpanExt { // Trim from the left so that the span is `length` in size. fn shorten_upto(self, length: usize) -> Span; + + // Wrap `T` into a `Spanned` with `self` as the span. + fn wrap(self, node: T) -> Spanned; + + /// Expand the span on the left by `left` and right by `right`. + fn expand(self, left: usize, right: usize) -> Span; } impl SpanExt for Span { @@ -43,4 +49,14 @@ impl SpanExt for Span { self.hi = self.hi - BytePos(length); self } + + fn wrap(self, node: T) -> Spanned { + Spanned { node: node, span: self } + } + + fn expand(mut self, left: usize, right: usize) -> Span { + self.lo = self.lo + BytePos(left as u32); + self.hi = self.lo + BytePos(right as u32); + self + } } diff --git a/codegen/tests/compile-fail/absolute-mount-paths.rs b/codegen/tests/compile-fail/absolute-mount-paths.rs index 96637bde..026a0573 100644 --- a/codegen/tests/compile-fail/absolute-mount-paths.rs +++ b/codegen/tests/compile-fail/absolute-mount-paths.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] #[get("a")] //~ ERROR absolute diff --git a/codegen/tests/compile-fail/bad-attribute-form.rs b/codegen/tests/compile-fail/bad-attribute-form.rs index 2315262b..b22a66c6 100644 --- a/codegen/tests/compile-fail/bad-attribute-form.rs +++ b/codegen/tests/compile-fail/bad-attribute-form.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/bad-attribute-param.rs b/codegen/tests/compile-fail/bad-attribute-param.rs index 2bb7a0c1..d04311b8 100644 --- a/codegen/tests/compile-fail/bad-attribute-param.rs +++ b/codegen/tests/compile-fail/bad-attribute-param.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/bad-error-fn.rs b/codegen/tests/compile-fail/bad-error-fn.rs index 053926ab..7ef8525d 100644 --- a/codegen/tests/compile-fail/bad-error-fn.rs +++ b/codegen/tests/compile-fail/bad-error-fn.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/bad-ident-argument.rs b/codegen/tests/compile-fail/bad-ident-argument.rs index 4b544f02..5bcfaec7 100644 --- a/codegen/tests/compile-fail/bad-ident-argument.rs +++ b/codegen/tests/compile-fail/bad-ident-argument.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/bad-value-types-in-attribute.rs b/codegen/tests/compile-fail/bad-value-types-in-attribute.rs index 0f6b4a47..8c1f29bb 100644 --- a/codegen/tests/compile-fail/bad-value-types-in-attribute.rs +++ b/codegen/tests/compile-fail/bad-value-types-in-attribute.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/data-without-post.rs b/codegen/tests/compile-fail/data-without-post.rs index 44ea2b04..30c6f46d 100644 --- a/codegen/tests/compile-fail/data-without-post.rs +++ b/codegen/tests/compile-fail/data-without-post.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/decorate-enum.rs b/codegen/tests/compile-fail/decorate-enum.rs index 0998e244..2c21f6ca 100644 --- a/codegen/tests/compile-fail/decorate-enum.rs +++ b/codegen/tests/compile-fail/decorate-enum.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/decorate-impl.rs b/codegen/tests/compile-fail/decorate-impl.rs index 7d0b88ca..b6938705 100644 --- a/codegen/tests/compile-fail/decorate-impl.rs +++ b/codegen/tests/compile-fail/decorate-impl.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/decorate-struct.rs b/codegen/tests/compile-fail/decorate-struct.rs index 066a13da..04b30ee2 100644 --- a/codegen/tests/compile-fail/decorate-struct.rs +++ b/codegen/tests/compile-fail/decorate-struct.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/decorate-trait.rs b/codegen/tests/compile-fail/decorate-trait.rs index 2757e14c..d482a349 100644 --- a/codegen/tests/compile-fail/decorate-trait.rs +++ b/codegen/tests/compile-fail/decorate-trait.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/empty-segments.rs b/codegen/tests/compile-fail/empty-segments.rs index 13b1a690..d64b7c20 100644 --- a/codegen/tests/compile-fail/empty-segments.rs +++ b/codegen/tests/compile-fail/empty-segments.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] #[get("/a/b/c//d")] //~ ERROR paths cannot contain empty segments diff --git a/codegen/tests/compile-fail/form-field-attr.rs b/codegen/tests/compile-fail/form-field-attr.rs index f6118707..d814d039 100644 --- a/codegen/tests/compile-fail/form-field-attr.rs +++ b/codegen/tests/compile-fail/form-field-attr.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] #[derive(FromForm)] diff --git a/codegen/tests/compile-fail/ignored_params.rs b/codegen/tests/compile-fail/ignored_params.rs index 5cb68a44..2fd78ffd 100644 --- a/codegen/tests/compile-fail/ignored_params.rs +++ b/codegen/tests/compile-fail/ignored_params.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] #[get("/")] //~ ERROR unused dynamic parameter: `name` diff --git a/codegen/tests/compile-fail/malformed-media-type.rs b/codegen/tests/compile-fail/malformed-media-type.rs index 37596b8f..cd4d843c 100644 --- a/codegen/tests/compile-fail/malformed-media-type.rs +++ b/codegen/tests/compile-fail/malformed-media-type.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/malformed-param-list.rs b/codegen/tests/compile-fail/malformed-param-list.rs index cfadb5ec..5fa55276 100644 --- a/codegen/tests/compile-fail/malformed-param-list.rs +++ b/codegen/tests/compile-fail/malformed-param-list.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] #[get("/><")] //~ ERROR malformed diff --git a/codegen/tests/compile-fail/phantom-declared-param.rs b/codegen/tests/compile-fail/phantom-declared-param.rs index 8914593c..253e5e0f 100644 --- a/codegen/tests/compile-fail/phantom-declared-param.rs +++ b/codegen/tests/compile-fail/phantom-declared-param.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] #[get("/")] //~ ERROR unused dynamic parameter: `param` diff --git a/codegen/tests/compile-fail/route-bad-method.rs b/codegen/tests/compile-fail/route-bad-method.rs index f8b955f8..98c85ce0 100644 --- a/codegen/tests/compile-fail/route-bad-method.rs +++ b/codegen/tests/compile-fail/route-bad-method.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/route-invalid-method.rs b/codegen/tests/compile-fail/route-invalid-method.rs index e15fa8c6..560e0712 100644 --- a/codegen/tests/compile-fail/route-invalid-method.rs +++ b/codegen/tests/compile-fail/route-invalid-method.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/unknown-attribute-param.rs b/codegen/tests/compile-fail/unknown-attribute-param.rs index 5106fad8..4f9a7fbe 100644 --- a/codegen/tests/compile-fail/unknown-attribute-param.rs +++ b/codegen/tests/compile-fail/unknown-attribute-param.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/compile-fail/unknown-media-type.rs b/codegen/tests/compile-fail/unknown-media-type.rs index 32426dc4..1744aef6 100644 --- a/codegen/tests/compile-fail/unknown-media-type.rs +++ b/codegen/tests/compile-fail/unknown-media-type.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/complete-decorator.rs b/codegen/tests/run-pass/complete-decorator.rs index 07a2bd04..589a72db 100644 --- a/codegen/tests/run-pass/complete-decorator.rs +++ b/codegen/tests/run-pass/complete-decorator.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/custom-content-type.rs b/codegen/tests/run-pass/custom-content-type.rs index f114a35b..52b7b433 100644 --- a/codegen/tests/run-pass/custom-content-type.rs +++ b/codegen/tests/run-pass/custom-content-type.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/derive_form.rs b/codegen/tests/run-pass/derive_form.rs index ded29fbf..b449b305 100644 --- a/codegen/tests/run-pass/derive_form.rs +++ b/codegen/tests/run-pass/derive_form.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/dynamic-paths.rs b/codegen/tests/run-pass/dynamic-paths.rs index 82c4d872..f723091d 100644 --- a/codegen/tests/run-pass/dynamic-paths.rs +++ b/codegen/tests/run-pass/dynamic-paths.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/empty-fn.rs b/codegen/tests/run-pass/empty-fn.rs index 2b3b6add..5c35e18c 100644 --- a/codegen/tests/run-pass/empty-fn.rs +++ b/codegen/tests/run-pass/empty-fn.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/empty_form.rs b/codegen/tests/run-pass/empty_form.rs index 56d1cae9..b4457730 100644 --- a/codegen/tests/run-pass/empty_form.rs +++ b/codegen/tests/run-pass/empty_form.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/error-handler.rs b/codegen/tests/run-pass/error-handler.rs index e268a462..a650b710 100644 --- a/codegen/tests/run-pass/error-handler.rs +++ b/codegen/tests/run-pass/error-handler.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/form-field-rename.rs b/codegen/tests/run-pass/form-field-rename.rs index ec99d7a8..431d1735 100644 --- a/codegen/tests/run-pass/form-field-rename.rs +++ b/codegen/tests/run-pass/form-field-rename.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/instanced-mounting.rs b/codegen/tests/run-pass/instanced-mounting.rs index e43ff335..457a7c40 100644 --- a/codegen/tests/run-pass/instanced-mounting.rs +++ b/codegen/tests/run-pass/instanced-mounting.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] #![allow(dead_code)] diff --git a/codegen/tests/run-pass/issue-1-colliding-names.rs b/codegen/tests/run-pass/issue-1-colliding-names.rs index 6c97c895..43334a57 100644 --- a/codegen/tests/run-pass/issue-1-colliding-names.rs +++ b/codegen/tests/run-pass/issue-1-colliding-names.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/methods.rs b/codegen/tests/run-pass/methods.rs index 1fab7dd5..ba3f5c8d 100644 --- a/codegen/tests/run-pass/methods.rs +++ b/codegen/tests/run-pass/methods.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/rank_decorator.rs b/codegen/tests/run-pass/rank_decorator.rs index b0d3729c..9a4dfa84 100644 --- a/codegen/tests/run-pass/rank_decorator.rs +++ b/codegen/tests/run-pass/rank_decorator.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/refactored_rocket_no_lint_errors.rs b/codegen/tests/run-pass/refactored_rocket_no_lint_errors.rs index be2326be..9d6e37b2 100644 --- a/codegen/tests/run-pass/refactored_rocket_no_lint_errors.rs +++ b/codegen/tests/run-pass/refactored_rocket_no_lint_errors.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] #![allow(dead_code, unused_variables)] diff --git a/codegen/tests/run-pass/segments.rs b/codegen/tests/run-pass/segments.rs index 8e7ec45a..26f25894 100644 --- a/codegen/tests/run-pass/segments.rs +++ b/codegen/tests/run-pass/segments.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/codegen/tests/run-pass/type-alias-lints.rs b/codegen/tests/run-pass/type-alias-lints.rs index d9ead934..4cac9924 100644 --- a/codegen/tests/run-pass/type-alias-lints.rs +++ b/codegen/tests/run-pass/type-alias-lints.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] #![allow(dead_code, unused_variables)] diff --git a/examples/config/src/main.rs b/examples/config/src/main.rs index c12fbd40..e64ffe62 100644 --- a/examples/config/src/main.rs +++ b/examples/config/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/config/tests/development.rs b/examples/config/tests/development.rs index ff6d347e..aa6f750a 100644 --- a/examples/config/tests/development.rs +++ b/examples/config/tests/development.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/config/tests/production.rs b/examples/config/tests/production.rs index c6ea3bb3..ff24ed8b 100644 --- a/examples/config/tests/production.rs +++ b/examples/config/tests/production.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/config/tests/staging.rs b/examples/config/tests/staging.rs index c8aab066..d06a2972 100644 --- a/examples/config/tests/staging.rs +++ b/examples/config/tests/staging.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/content_types/src/main.rs b/examples/content_types/src/main.rs index c705e95b..0ba79db3 100644 --- a/examples/content_types/src/main.rs +++ b/examples/content_types/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/cookies/src/main.rs b/examples/cookies/src/main.rs index aeb8a671..4e196559 100644 --- a/examples/cookies/src/main.rs +++ b/examples/cookies/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket_contrib; diff --git a/examples/errors/src/main.rs b/examples/errors/src/main.rs index d2780f65..07210db8 100644 --- a/examples/errors/src/main.rs +++ b/examples/errors/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/fairings/src/main.rs b/examples/fairings/src/main.rs index 1355d850..330b137b 100644 --- a/examples/fairings/src/main.rs +++ b/examples/fairings/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/form_kitchen_sink/src/main.rs b/examples/form_kitchen_sink/src/main.rs index 4d8a23ea..c05eea91 100644 --- a/examples/form_kitchen_sink/src/main.rs +++ b/examples/form_kitchen_sink/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/form_validation/src/main.rs b/examples/form_validation/src/main.rs index 45dfd151..163094be 100644 --- a/examples/form_validation/src/main.rs +++ b/examples/form_validation/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/handlebars_templates/src/main.rs b/examples/handlebars_templates/src/main.rs index 660aae5c..a1f1b77c 100644 --- a/examples/handlebars_templates/src/main.rs +++ b/examples/handlebars_templates/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket_contrib; diff --git a/examples/hello_person/src/main.rs b/examples/hello_person/src/main.rs index a4769efe..dc07dc82 100644 --- a/examples/hello_person/src/main.rs +++ b/examples/hello_person/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs index 524574ad..ec85671a 100644 --- a/examples/hello_world/src/main.rs +++ b/examples/hello_world/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/json/src/main.rs b/examples/json/src/main.rs index e02aa6fc..d0352e86 100644 --- a/examples/json/src/main.rs +++ b/examples/json/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/managed_queue/src/main.rs b/examples/managed_queue/src/main.rs index 7dd09c4e..1c0d73cc 100644 --- a/examples/managed_queue/src/main.rs +++ b/examples/managed_queue/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate crossbeam; diff --git a/examples/msgpack/src/main.rs b/examples/msgpack/src/main.rs index ffff2400..aa750550 100644 --- a/examples/msgpack/src/main.rs +++ b/examples/msgpack/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/optional_redirect/src/main.rs b/examples/optional_redirect/src/main.rs index 693ae1ec..601e4ef6 100644 --- a/examples/optional_redirect/src/main.rs +++ b/examples/optional_redirect/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/pastebin/src/main.rs b/examples/pastebin/src/main.rs index 2a95e527..1f4a6cd2 100644 --- a/examples/pastebin/src/main.rs +++ b/examples/pastebin/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/query_params/src/main.rs b/examples/query_params/src/main.rs index 3c48a4bd..9a779d85 100644 --- a/examples/query_params/src/main.rs +++ b/examples/query_params/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/ranking/src/main.rs b/examples/ranking/src/main.rs index ab1d6f6a..9b59cff5 100644 --- a/examples/ranking/src/main.rs +++ b/examples/ranking/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/raw_sqlite/src/main.rs b/examples/raw_sqlite/src/main.rs index 9392da11..9fc82363 100644 --- a/examples/raw_sqlite/src/main.rs +++ b/examples/raw_sqlite/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/raw_upload/src/main.rs b/examples/raw_upload/src/main.rs index 8005f03d..0fef0dbf 100644 --- a/examples/raw_upload/src/main.rs +++ b/examples/raw_upload/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/redirect/src/main.rs b/examples/redirect/src/main.rs index bfbf4104..066957ec 100644 --- a/examples/redirect/src/main.rs +++ b/examples/redirect/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/request_guard/src/main.rs b/examples/request_guard/src/main.rs index ef987d38..475d2c4e 100644 --- a/examples/request_guard/src/main.rs +++ b/examples/request_guard/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/session/src/main.rs b/examples/session/src/main.rs index da57043c..a4525c67 100644 --- a/examples/session/src/main.rs +++ b/examples/session/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket_contrib; diff --git a/examples/state/src/main.rs b/examples/state/src/main.rs index 7fe08aaf..67f26c97 100644 --- a/examples/state/src/main.rs +++ b/examples/state/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/static_files/src/main.rs b/examples/static_files/src/main.rs index a47f80ff..b5d92c2b 100644 --- a/examples/static_files/src/main.rs +++ b/examples/static_files/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/stream/src/main.rs b/examples/stream/src/main.rs index 34916be7..625b5ac2 100644 --- a/examples/stream/src/main.rs +++ b/examples/stream/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 73bd0341..ab6bf9ad 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/tls/src/main.rs b/examples/tls/src/main.rs index 524574ad..ec85671a 100644 --- a/examples/tls/src/main.rs +++ b/examples/tls/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/todo/src/main.rs b/examples/todo/src/main.rs index 2a082108..498f4121 100644 --- a/examples/todo/src/main.rs +++ b/examples/todo/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive, const_fn)] +#![feature(plugin, decl_macro, custom_derive, const_fn)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/examples/uuid/src/main.rs b/examples/uuid/src/main.rs index 2e6c02ac..1b4386e6 100644 --- a/examples/uuid/src/main.rs +++ b/examples/uuid/src/main.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] #[macro_use] diff --git a/lib/src/catcher.rs b/lib/src/catcher.rs index 1739a7ff..e7d71d00 100644 --- a/lib/src/catcher.rs +++ b/lib/src/catcher.rs @@ -35,7 +35,7 @@ use yansi::Color::*; /// declared using the `error` decorator, as follows: /// /// ```rust -/// #![feature(plugin)] +/// #![feature(plugin, decl_macro)] /// #![plugin(rocket_codegen)] /// /// extern crate rocket; diff --git a/lib/src/data/from_data.rs b/lib/src/data/from_data.rs index 22b98362..710dc59f 100644 --- a/lib/src/data/from_data.rs +++ b/lib/src/data/from_data.rs @@ -156,7 +156,7 @@ impl<'a, S, E> IntoOutcome for Result { /// ```rust /// # #![allow(unused_attributes)] /// # #![allow(unused_variables)] -/// # #![feature(plugin)] +/// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// # diff --git a/lib/src/http/cookies.rs b/lib/src/http/cookies.rs index 523ca631..e884bdc5 100644 --- a/lib/src/http/cookies.rs +++ b/lib/src/http/cookies.rs @@ -35,7 +35,7 @@ use http::Header; /// a handler to retrieve the value of a "message" cookie. /// /// ```rust -/// # #![feature(plugin)] +/// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::http::Cookies; @@ -56,7 +56,7 @@ use http::Header; /// [private cookie]: /rocket/http/enum.Cookies.html#private-cookies /// /// ```rust -/// # #![feature(plugin)] +/// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// # diff --git a/lib/src/http/uri.rs b/lib/src/http/uri.rs index eeac2335..f5950efb 100644 --- a/lib/src/http/uri.rs +++ b/lib/src/http/uri.rs @@ -1,11 +1,13 @@ //! Borrowed and owned string types for absolute URIs. -use std::convert::From; use std::fmt; +use std::convert::From; use std::borrow::Cow; use std::str::Utf8Error; use std::sync::atomic::{AtomicIsize, Ordering}; +use http::RawStr; + use url; /// Index (start, end) into a string, to prevent borrowing. @@ -346,6 +348,45 @@ impl<'a> fmt::Display for URI<'a> { } } +pub trait UriDisplay { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result; +} + +impl UriDisplay for T { + #[inline(always)] + default fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ::fmt(self, f) + } +} + +impl<'a> fmt::Display for &'a UriDisplay { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + (**self).fmt(f) + } +} + +impl<'a> UriDisplay for &'a RawStr { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", URI::percent_encode((*self).as_str())) + } +} + +impl<'a> UriDisplay for &'a str { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", URI::percent_encode(self)) + } +} + +impl<'a> UriDisplay for String { + #[inline(always)] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", URI::percent_encode(self.as_str())) + } +} + /// Iterator over the segments of an absolute URI path. Skips empty segments. /// /// ### Examples diff --git a/lib/src/lib.rs b/lib/src/lib.rs index f96f11bf..e92f7df1 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -2,7 +2,7 @@ #![feature(conservative_impl_trait)] #![feature(drop_types_in_const)] #![feature(const_fn)] -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![feature(never_type)] #![feature(try_trait)] @@ -55,7 +55,7 @@ //! Then, add the following to the top of your `main.rs` file: //! //! ```rust -//! #![feature(plugin)] +//! #![feature(plugin, decl_macro)] //! # #![allow(unused_attributes)] //! #![plugin(rocket_codegen)] //! @@ -66,7 +66,7 @@ //! write Rocket applications. Here's a simple example to get you started: //! //! ```rust -//! #![feature(plugin)] +//! #![feature(plugin, decl_macro)] //! #![plugin(rocket_codegen)] //! //! extern crate rocket; diff --git a/lib/src/local/mod.rs b/lib/src/local/mod.rs index b5ce997e..82ff85a7 100644 --- a/lib/src/local/mod.rs +++ b/lib/src/local/mod.rs @@ -67,7 +67,7 @@ //! consider the following complete "Hello, world!" application, with testing. //! //! ```rust -//! #![feature(plugin)] +//! #![feature(plugin, decl_macro)] //! #![plugin(rocket_codegen)] //! //! extern crate rocket; diff --git a/lib/src/request/form/form.rs b/lib/src/request/form/form.rs index 8ad71b00..19695cac 100644 --- a/lib/src/request/form/form.rs +++ b/lib/src/request/form/form.rs @@ -49,7 +49,7 @@ use request::form::{FromForm, FormItems}; /// The simplest data structure with a reference into form data looks like this: /// /// ```rust -/// # #![feature(plugin, custom_derive)] +/// # #![feature(plugin, decl_macro, custom_derive)] /// # #![allow(deprecated, dead_code, unused_attributes)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; @@ -65,7 +65,7 @@ use request::form::{FromForm, FormItems}; /// a string. A handler for this type can be written as: /// /// ```rust -/// # #![feature(plugin, custom_derive)] +/// # #![feature(plugin, decl_macro, custom_derive)] /// # #![allow(deprecated, unused_attributes)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; @@ -91,7 +91,7 @@ use request::form::{FromForm, FormItems}; /// The owned analog of the `UserInput` type above is: /// /// ```rust -/// # #![feature(plugin, custom_derive)] +/// # #![feature(plugin, decl_macro, custom_derive)] /// # #![allow(deprecated, dead_code, unused_attributes)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; @@ -105,7 +105,7 @@ use request::form::{FromForm, FormItems}; /// The handler is written similarly: /// /// ```rust -/// # #![feature(plugin, custom_derive)] +/// # #![feature(plugin, decl_macro, custom_derive)] /// # #![allow(deprecated, unused_attributes)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; @@ -170,7 +170,7 @@ impl<'f, T: FromForm<'f> + 'f> Form<'f, T> { /// # Example /// /// ```rust - /// # #![feature(plugin, custom_derive)] + /// # #![feature(plugin, decl_macro, custom_derive)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::request::Form; @@ -198,7 +198,7 @@ impl<'f, T: FromForm<'f> + 'f> Form<'f, T> { /// # Example /// /// ```rust - /// # #![feature(plugin, custom_derive)] + /// # #![feature(plugin, decl_macro, custom_derive)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::request::Form; @@ -269,7 +269,7 @@ impl<'f, T: FromForm<'f> + 'static> Form<'f, T> { /// # Example /// /// ```rust - /// # #![feature(plugin, custom_derive)] + /// # #![feature(plugin, decl_macro, custom_derive)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::request::Form; diff --git a/lib/src/request/form/from_form.rs b/lib/src/request/form/from_form.rs index 29fc3972..08b79bc4 100644 --- a/lib/src/request/form/from_form.rs +++ b/lib/src/request/form/from_form.rs @@ -14,7 +14,7 @@ use request::FormItems; /// validation. /// /// ```rust -/// #![feature(plugin, custom_derive)] +/// #![feature(plugin, decl_macro, custom_derive)] /// #![plugin(rocket_codegen)] /// # #![allow(deprecated, dead_code, unused_attributes)] /// @@ -34,7 +34,7 @@ use request::FormItems; /// data via the `data` parameter and `Form` type. /// /// ```rust -/// # #![feature(plugin, custom_derive)] +/// # #![feature(plugin, decl_macro, custom_derive)] /// # #![allow(deprecated, dead_code, unused_attributes)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; diff --git a/lib/src/request/form/lenient.rs b/lib/src/request/form/lenient.rs index e4ed3d11..1e00f837 100644 --- a/lib/src/request/form/lenient.rs +++ b/lib/src/request/form/lenient.rs @@ -56,7 +56,7 @@ impl<'f, T: FromForm<'f> + 'f> LenientForm<'f, T> { /// # Example /// /// ```rust - /// # #![feature(plugin, custom_derive)] + /// # #![feature(plugin, decl_macro, custom_derive)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::request::LenientForm; @@ -84,7 +84,7 @@ impl<'f, T: FromForm<'f> + 'f> LenientForm<'f, T> { /// # Example /// /// ```rust - /// # #![feature(plugin, custom_derive)] + /// # #![feature(plugin, decl_macro, custom_derive)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::request::LenientForm; @@ -114,7 +114,7 @@ impl<'f, T: FromForm<'f> + 'static> LenientForm<'f, T> { /// # Example /// /// ```rust - /// # #![feature(plugin, custom_derive)] + /// # #![feature(plugin, decl_macro, custom_derive)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::request::LenientForm; diff --git a/lib/src/request/from_request.rs b/lib/src/request/from_request.rs index 49192164..1cdc9323 100644 --- a/lib/src/request/from_request.rs +++ b/lib/src/request/from_request.rs @@ -167,7 +167,7 @@ impl IntoOutcome for Result { /// `senstive` handler. /// /// ```rust -/// # #![feature(plugin)] +/// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// # diff --git a/lib/src/request/param.rs b/lib/src/request/param.rs index 7a002506..b3bc85d6 100644 --- a/lib/src/request/param.rs +++ b/lib/src/request/param.rs @@ -20,7 +20,7 @@ use http::RawStr; /// handler for the dynamic `"/"` path: /// /// ```rust -/// # #![feature(plugin)] +/// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// #[get("/")] @@ -56,7 +56,7 @@ use http::RawStr; /// parameter as follows: /// /// ```rust -/// # #![feature(plugin)] +/// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// # use rocket::http::RawStr; @@ -163,7 +163,7 @@ use http::RawStr; /// dynamic path segment: /// /// ```rust -/// # #![feature(plugin)] +/// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// # use rocket::request::FromParam; diff --git a/lib/src/request/state.rs b/lib/src/request/state.rs index 775cfe1f..29a1c3b0 100644 --- a/lib/src/request/state.rs +++ b/lib/src/request/state.rs @@ -21,7 +21,7 @@ use http::Status; /// following example does just this: /// /// ```rust -/// # #![feature(plugin)] +/// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::State; diff --git a/lib/src/response/flash.rs b/lib/src/response/flash.rs index 8899d057..17d37da7 100644 --- a/lib/src/response/flash.rs +++ b/lib/src/response/flash.rs @@ -46,7 +46,7 @@ const FLASH_COOKIE_NAME: &'static str = "_flash"; /// message on both the request and response sides. /// /// ```rust -/// # #![feature(plugin)] +/// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # /// # extern crate rocket; diff --git a/lib/src/response/responder.rs b/lib/src/response/responder.rs index 2f7122f0..da3c8ef1 100644 --- a/lib/src/response/responder.rs +++ b/lib/src/response/responder.rs @@ -137,7 +137,7 @@ use request::Request; /// following `Responder` implementation accomplishes this: /// /// ```rust -/// # #![feature(plugin)] +/// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// # diff --git a/lib/src/rocket.rs b/lib/src/rocket.rs index 3eb9db2e..70f90352 100644 --- a/lib/src/rocket.rs +++ b/lib/src/rocket.rs @@ -448,7 +448,7 @@ impl Rocket { /// dispatched to the `hi` route. /// /// ```rust - /// # #![feature(plugin)] + /// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// # @@ -514,7 +514,7 @@ impl Rocket { /// # Examples /// /// ```rust - /// #![feature(plugin)] + /// #![feature(plugin, decl_macro)] /// #![plugin(rocket_codegen)] /// /// extern crate rocket; @@ -573,7 +573,7 @@ impl Rocket { /// # Example /// /// ```rust - /// # #![feature(plugin)] + /// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::State; @@ -609,7 +609,7 @@ impl Rocket { /// # Example /// /// ```rust - /// # #![feature(plugin)] + /// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::Rocket; @@ -711,7 +711,7 @@ impl Rocket { /// # Example /// /// ```rust - /// # #![feature(plugin)] + /// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::Rocket; @@ -748,7 +748,7 @@ impl Rocket { /// # Example /// /// ```rust - /// # #![feature(plugin)] + /// # #![feature(plugin, decl_macro)] /// # #![plugin(rocket_codegen)] /// # extern crate rocket; /// use rocket::Rocket; diff --git a/lib/tests/form_method-issue-45.rs b/lib/tests/form_method-issue-45.rs index 4cc0dad4..2a9d7f9d 100644 --- a/lib/tests/form_method-issue-45.rs +++ b/lib/tests/form_method-issue-45.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/lib/tests/form_value_decoding-issue-82.rs b/lib/tests/form_value_decoding-issue-82.rs index 5ad1c849..99d7b4ab 100644 --- a/lib/tests/form_value_decoding-issue-82.rs +++ b/lib/tests/form_value_decoding-issue-82.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/lib/tests/head_handling.rs b/lib/tests/head_handling.rs index 4e20c9f2..26910dc6 100644 --- a/lib/tests/head_handling.rs +++ b/lib/tests/head_handling.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/lib/tests/limits.rs b/lib/tests/limits.rs index 1aa97f10..c3f7d2d3 100644 --- a/lib/tests/limits.rs +++ b/lib/tests/limits.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/lib/tests/precise-content-type-matching.rs b/lib/tests/precise-content-type-matching.rs index 93e67e65..c3be3159 100644 --- a/lib/tests/precise-content-type-matching.rs +++ b/lib/tests/precise-content-type-matching.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/lib/tests/query-and-non-query-dont-collide.rs b/lib/tests/query-and-non-query-dont-collide.rs index 18ad64a8..244539c9 100644 --- a/lib/tests/query-and-non-query-dont-collide.rs +++ b/lib/tests/query-and-non-query-dont-collide.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/lib/tests/redirect_from_catcher-issue-113.rs b/lib/tests/redirect_from_catcher-issue-113.rs index a497e51c..2b6f71b5 100644 --- a/lib/tests/redirect_from_catcher-issue-113.rs +++ b/lib/tests/redirect_from_catcher-issue-113.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/lib/tests/remote-rewrite.rs b/lib/tests/remote-rewrite.rs index 2af7ef61..a5f3bdac 100644 --- a/lib/tests/remote-rewrite.rs +++ b/lib/tests/remote-rewrite.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/lib/tests/responder_lifetime-issue-345.rs b/lib/tests/responder_lifetime-issue-345.rs index ebea90c0..468f36cb 100644 --- a/lib/tests/responder_lifetime-issue-345.rs +++ b/lib/tests/responder_lifetime-issue-345.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] #![allow(dead_code)] // This test is only here so that we can ensure it compiles. diff --git a/lib/tests/route_guard.rs b/lib/tests/route_guard.rs index 32518076..c23bb420 100644 --- a/lib/tests/route_guard.rs +++ b/lib/tests/route_guard.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/lib/tests/segments-issues-41-86.rs b/lib/tests/segments-issues-41-86.rs index 804a3ec4..3a5a843f 100644 --- a/lib/tests/segments-issues-41-86.rs +++ b/lib/tests/segments-issues-41-86.rs @@ -1,4 +1,4 @@ -#![feature(plugin)] +#![feature(plugin, decl_macro)] #![plugin(rocket_codegen)] extern crate rocket; diff --git a/lib/tests/strict_and_lenient_forms.rs b/lib/tests/strict_and_lenient_forms.rs index 853320e2..b98fe015 100644 --- a/lib/tests/strict_and_lenient_forms.rs +++ b/lib/tests/strict_and_lenient_forms.rs @@ -1,4 +1,4 @@ -#![feature(plugin, custom_derive)] +#![feature(plugin, decl_macro, custom_derive)] #![plugin(rocket_codegen)] extern crate rocket;