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.
This commit is contained in:
Sergio Benitez 2017-08-28 20:14:59 -07:00
parent 322c02f654
commit 084481a84e
107 changed files with 803 additions and 235 deletions

View File

@ -16,6 +16,7 @@ plugin = true
[dependencies] [dependencies]
rocket = { version = "0.4.0-dev", path = "../lib/" } rocket = { version = "0.4.0-dev", path = "../lib/" }
ordermap = "0.2"
log = "0.3" log = "0.3"
[dev-dependencies] [dev-dependencies]

View File

@ -1,14 +1,14 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::Display; 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 ::{ROUTE_ATTR, ROUTE_INFO_ATTR};
use parser::{Param, RouteParams}; use parser::{Param, RouteParams};
use utils::*; use utils::*;
use syntax::codemap::{Span, Spanned}; use syntax::codemap::{Span, Spanned, dummy_spanned};
use syntax::tokenstream::TokenTree; 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::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder; use syntax::ext::build::AstBuilder;
use syntax::parse::token; use syntax::parse::token;
@ -43,7 +43,8 @@ impl RouteParams {
fn missing_declared_err<T: Display>(&self, ecx: &ExtCtxt, arg: &Spanned<T>) { fn missing_declared_err<T: Display>(&self, ecx: &ExtCtxt, arg: &Spanned<T>) {
let (fn_span, fn_name) = (self.annotated_fn.span(), self.annotated_fn.ident()); 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)) 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(); .emit();
} }
@ -127,10 +128,12 @@ impl RouteParams {
// an error/debug message if parsing a parameter fails. // an error/debug message if parsing a parameter fails.
fn generate_param_statements(&self, ecx: &ExtCtxt) -> Vec<Stmt> { fn generate_param_statements(&self, ecx: &ExtCtxt) -> Vec<Stmt> {
let mut fn_param_statements = 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. // Generate a statement for every declared paramter in the path.
let mut declared_set = HashSet::new(); 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); declared_set.insert(param.ident().name);
let ty = match self.annotated_fn.find_input(&param.ident().name) { let ty = match self.annotated_fn.find_input(&param.ident().name) {
Some(arg) => strip_ty_lifetimes(arg.ty.clone()), 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. // Note: the `None` case shouldn't happen if a route is matched.
let ident = param.ident().prepend(PARAM_PREFIX); 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) { Param::Single(_) => quote_expr!(ecx, match __req.get_param_str($i) {
Some(s) => <$ty as ::rocket::request::FromParam>::from_param(s), Some(s) => <$ty as ::rocket::request::FromParam>::from_param(s),
None => return ::rocket::Outcome::Forward(__data) None => return ::rocket::Outcome::Forward(__data)
@ -213,6 +216,49 @@ impl RouteParams {
sep_by_tok(ecx, &args, token::Comma) sep_by_tok(ecx, &args, token::Comma)
} }
fn generate_uri_macro(&self, ecx: &ExtCtxt) -> P<Item> {
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 &params {
if let Some(arg) = self.annotated_fn.find_input(&param.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<Expr>, P<Expr>) { fn explode(&self, ecx: &ExtCtxt) -> (InternedString, &str, Path, P<Expr>, P<Expr>) {
let name = self.annotated_fn.ident().name.as_str(); let name = self.annotated_fn.ident().name.as_str();
let path = &self.uri.node.as_str(); let path = &self.uri.node.as_str();
@ -242,6 +288,7 @@ fn generic_route_decorator(known_method: Option<Spanned<Method>>,
let query_statement = route.generate_query_statement(ecx); let query_statement = route.generate_query_statement(ecx);
let data_statement = route.generate_data_statement(ecx); let data_statement = route.generate_data_statement(ecx);
let fn_arguments = route.generate_fn_arguments(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. // Generate and emit the wrapping function with the Rocket handler signature.
let user_fn_name = route.annotated_fn.ident(); let user_fn_name = route.annotated_fn.ident();
@ -288,6 +335,9 @@ fn generic_route_decorator(known_method: Option<Spanned<Method>>,
let route_attr = quote_attr!(ecx, #[$attr_name($struct_name)]); let route_attr = quote_attr!(ecx, #[$attr_name($struct_name)]);
attach_and_emit(&mut output, route_attr, annotated); attach_and_emit(&mut output, route_attr, annotated);
// Emit the per-route URI macro.
emit_item(&mut output, uri_macro);
output output
} }

View File

@ -148,6 +148,7 @@ extern crate syntax;
extern crate syntax_ext; extern crate syntax_ext;
extern crate rustc_plugin; extern crate rustc_plugin;
extern crate rocket; extern crate rocket;
extern crate ordermap;
#[macro_use] mod utils; #[macro_use] mod utils;
mod parser; 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 CATCH_STRUCT_PREFIX: &'static str = "static_rocket_catch_info_for_";
const ROUTE_FN_PREFIX: &'static str = "rocket_route_fn_"; const ROUTE_FN_PREFIX: &'static str = "rocket_route_fn_";
const CATCH_FN_PREFIX: &'static str = "rocket_catch_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_ATTR: &'static str = "rocket_route";
const ROUTE_INFO_ATTR: &'static str = "rocket_route_info"; 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. /// Compiler hook for Rust to register plugins.
#[plugin_registrar] #[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) { 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); ::rocket::logger::init(::rocket::config::LoggingLevel::Debug);
} }
reg.register_macro("routes", macros::routes); register_macros!(reg,
reg.register_macro("errors", macros::errors); "routes" => routes,
"errors" => errors,
"uri" => uri,
"rocket_internal_uri" => uri_internal
);
register_derives!(reg, register_derives!(reg,
"derive_FromForm" => from_form_derive "derive_FromForm" => from_form_derive

View File

@ -1,3 +1,5 @@
mod uri;
use {ROUTE_STRUCT_PREFIX, CATCH_STRUCT_PREFIX}; use {ROUTE_STRUCT_PREFIX, CATCH_STRUCT_PREFIX};
use utils::{sep_by_tok, ParserExt, IdentExt}; 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::parse::token::Token;
use syntax::ptr::P; 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] #[inline]
pub fn prefix_paths(prefix: &str, paths: &mut Vec<Path>) { pub fn prefix_paths(prefix: &str, paths: &mut Vec<Path>) {
for p in paths { for p in paths {
let last = p.segments.len() - 1; prefix_path(prefix, p);
let last_seg = &mut p.segments[last];
last_seg.identifier = last_seg.identifier.prepend(prefix);
} }
} }
pub fn prefixing_vec_macro<F>(prefix: &str, pub fn prefixing_vec_macro<F>(
mut to_expr: F, prefix: &str,
ecx: &mut ExtCtxt, mut to_expr: F,
sp: Span, ecx: &mut ExtCtxt,
args: &[TokenTree]) sp: Span,
-> Box<MacResult + 'static> args: &[TokenTree]) -> Box<MacResult + 'static>
where F: FnMut(&ExtCtxt, Path) -> P<Expr> where F: FnMut(&ExtCtxt, Path) -> P<Expr>
{ {
let mut parser = ecx.new_parser_from_tts(args); let mut parser = ecx.new_parser_from_tts(args);
let paths = parser.parse_paths(); match parser.parse_paths() {
if let Ok(mut paths) = paths { Ok(mut paths) => {
// Prefix each path terminator and build up the P<Expr> for each path. // Prefix each path terminator and build up the P<Expr> for each path.
prefix_paths(prefix, &mut paths); prefix_paths(prefix, &mut paths);
let path_exprs: Vec<P<Expr>> = paths.into_iter() let path_exprs: Vec<P<Expr>> = paths.into_iter()
.map(|path| to_expr(ecx, path)) .map(|path| to_expr(ecx, path))
.collect(); .collect();
// Now put them all in one vector and return the thing. // Now put them all in one vector and return the thing.
let path_list = sep_by_tok(ecx, &path_exprs, Token::Comma); let path_list = sep_by_tok(ecx, &path_exprs, Token::Comma);
let output = quote_expr!(ecx, vec![$path_list]).unwrap(); let output = quote_expr!(ecx, vec![$path_list]).unwrap();
MacEager::expr(P(output)) MacEager::expr(P(output))
} else { }
paths.unwrap_err().emit(); Err(mut e) => {
DummyResult::expr(sp) e.emit();
DummyResult::expr(sp)
}
} }
} }

145
codegen/src/macros/uri.rs Normal file
View File

@ -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<Vec<Span>>`.
use self::syntax_pos::MultiSpan as MS;
pub fn uri(
ecx: &mut ExtCtxt,
sp: Span,
args: &[TokenTree],
) -> Box<MacResult + 'static> {
// 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::<TokenStream>().into(),
},
::syntax::util::ThinVec::new(),
);
MacEager::expr(expr)
}
fn extract_exprs<'a>(
ecx: &ExtCtxt<'a>,
internal: &InternalUriParams,
) -> PResult<'a, Vec<P<ast::Expr>>> {
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<S: Display, T: Iterator<Item = S>>(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<MacResult + 'static> {
// 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))
}))
}

View File

@ -4,9 +4,11 @@ mod error;
mod param; mod param;
mod function; mod function;
mod uri; mod uri;
mod uri_macro;
pub use self::keyvalue::KVSpanned; pub use self::keyvalue::KVSpanned;
pub use self::route::RouteParams; pub use self::route::RouteParams;
pub use self::error::ErrorParams; pub use self::error::ErrorParams;
pub use self::param::{Param, ParamIter}; pub use self::param::Param;
pub use self::function::Function; pub use self::function::Function;
pub use self::uri_macro::{Args, InternalUriParams, UriParams, Validation};

View File

@ -1,8 +1,8 @@
use syntax::ast::Ident; use syntax::ast::Ident;
use syntax::codemap::{Span, Spanned};
use syntax::ext::base::ExtCtxt; use syntax::ext::base::ExtCtxt;
use syntax::codemap::{Span, Spanned, BytePos}; use utils::SpanExt;
use syntax::parse::PResult;
use utils::span;
#[derive(Debug)] #[derive(Debug)]
pub enum Param { pub enum Param {
@ -22,64 +22,45 @@ impl Param {
Param::Single(ref ident) | Param::Many(ref ident) => &ident.node, Param::Single(ref ident) | Param::Many(ref ident) => &ident.node,
} }
} }
}
pub struct ParamIter<'s, 'a, 'c: 'a> { pub fn parse_many<'a>(
ctxt: &'a ExtCtxt<'c>, ecx: &ExtCtxt<'a>,
span: Span, mut string: &str,
string: &'s str, mut span: Span
} ) -> PResult<'a, Vec<Param>> {
let err = |sp, msg| { Err(ecx.struct_span_err(sp, msg)) };
impl<'s, 'a, 'c: 'a> ParamIter<'s, 'a, 'c> { let mut params = vec![];
pub fn new(c: &'a ExtCtxt<'c>, s: &'s str, p: Span) -> ParamIter<'s, 'a, 'c> { loop {
ParamIter { ctxt: c, span: p, string: s } // 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> { // Calculate the parameter's ident and span.
type Item = Param; 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<Param> { // Advance the string and span.
let err = |ecx: &ExtCtxt, sp: Span, msg: &str| { string = &string[(end + 1)..];
ecx.span_err(sp, msg); span = span.trim_left(end + 1);
None
};
// Find the start and end indexes for the next parameter, if any. let spanned_ident = param_span.wrap(Ident::from_str(param));
let (start, end) = match self.string.find('<') { if is_many {
Some(i) => match self.string.find('>') { params.push(Param::Many(spanned_ident))
Some(j) => (i, j), } else {
None => return err(self.ctxt, self.span, "malformed parameters") params.push(Param::Single(spanned_ident))
}, }
_ => 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))
} }
} }
} }

View File

@ -6,7 +6,7 @@ use syntax::ext::base::{ExtCtxt, Annotatable};
use syntax::codemap::{Span, Spanned, dummy_spanned}; use syntax::codemap::{Span, Spanned, dummy_spanned};
use utils::{span, MetaItemExt, SpanExt, is_valid_ident}; use utils::{span, MetaItemExt, SpanExt, is_valid_ident};
use super::{Function, ParamIter}; use super::Function;
use super::keyvalue::KVSpanned; use super::keyvalue::KVSpanned;
use super::uri::validate_uri; use super::uri::validate_uri;
use rocket::http::{Method, MediaType}; use rocket::http::{Method, MediaType};
@ -33,12 +33,13 @@ impl RouteParams {
/// Parses the route attribute from the given decorator context. If the /// Parses the route attribute from the given decorator context. If the
/// parse is not successful, this function exits early with the appropriate /// parse is not successful, this function exits early with the appropriate
/// error message to the user. /// error message to the user.
pub fn from(ecx: &mut ExtCtxt, pub fn from(
sp: Span, ecx: &mut ExtCtxt,
known_method: Option<Spanned<Method>>, sp: Span,
meta_item: &MetaItem, known_method: Option<Spanned<Method>>,
annotated: &Annotatable) meta_item: &MetaItem,
-> RouteParams { annotated: &Annotatable
) -> RouteParams {
let function = Function::from(annotated).unwrap_or_else(|item_sp| { let function = Function::from(annotated).unwrap_or_else(|item_sp| {
ecx.span_err(sp, "this attribute can only be used on functions..."); ecx.span_err(sp, "this attribute can only be used on functions...");
ecx.span_fatal(item_sp, "...but was applied to the item above."); ecx.span_fatal(item_sp, "...but was applied to the item above.");
@ -125,12 +126,6 @@ impl RouteParams {
annotated_fn: function, 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 { fn is_valid_method(method: Method) -> bool {

View File

@ -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<Expr>),
Named(Spanned<Ident>, P<Expr>),
}
#[derive(Debug)]
pub enum Args {
Unnamed(Vec<P<Expr>>),
Named(Vec<(Spanned<Ident>, P<Expr>)>),
}
#[derive(Debug)]
pub struct UriParams {
pub mount_point: Option<Spanned<InternedString>>,
pub route_path: Path,
pub arguments: Option<Spanned<Args>>,
}
#[derive(Debug)]
pub struct InternalUriParams {
pub uri: Spanned<String>,
pub fn_args: Vec<(Spanned<ast::Ident>, P<ast::Ty>)>,
pub uri_params: UriParams,
}
impl Arg {
fn is_named(&self) -> bool {
match *self {
Arg::Named(..) => true,
Arg::Unnamed(_) => false,
}
}
fn unnamed(self) -> P<Expr> {
match self {
Arg::Unnamed(expr) => expr,
_ => panic!("Called Arg::unnamed() on an Arg::named!"),
}
}
fn named(self) -> (Spanned<Ident>, P<Expr>) {
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<Spanned<InternedString>>, 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<Name>, Vec<Spanned<Ident>>, Vec<Spanned<Ident>>),
// Everything is okay.
Ok(Vec<P<Expr>>)
}
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::<Vec<_>>()
.join(", ")
}
pub fn validate(&self) -> Validation {
let unnamed = |args: &Vec<P<Expr>>| -> 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<Name, Option<P<Expr>>> = 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)
}
}
}
}
}

View File

@ -70,23 +70,11 @@ pub fn attach_and_emit(out: &mut Vec<Annotatable>, attr: Attribute, to: Annotata
} }
} }
macro_rules! quote_enum { pub fn parse_as_tokens(ecx: &ExtCtxt, string: &str) -> Vec<TokenTree> {
($ecx:expr, $var:expr => $(::$root:ident)+ use syntax::parse::parse_stream_from_source_str as parse_stream;
{ $($variant:ident),+ ; $($extra:pat => $result:expr),* }) => ({
use syntax::codemap::DUMMY_SP; let stream = parse_stream("<_>".into(), string.into(), ecx.parse_sess, None);
use syntax::ast::Ident; stream.into_trees().collect()
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 struct TyLifetimeRemover; pub struct TyLifetimeRemover;
@ -140,3 +128,45 @@ pub fn is_valid_ident<S: AsRef<str>>(s: S) -> bool {
true 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) }
)
}

View File

@ -1,11 +1,22 @@
use super::SpanExt;
use syntax::parse::parser::{PathStyle, Parser}; use syntax::parse::parser::{PathStyle, Parser};
use syntax::parse::PResult; 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::token::Token::{Eof, Comma};
use syntax::parse::common::SeqSep; use syntax::parse::common::SeqSep;
use syntax::codemap::Spanned;
use syntax::symbol::Symbol;
pub trait ParserExt<'a> { pub trait ParserExt<'a> {
// Parse a comma-seperated list of paths: `a::b, b::c`.
fn parse_paths(&mut self) -> PResult<'a, Vec<Path>>; fn parse_paths(&mut self) -> PResult<'a, Vec<Path>>;
// 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<Ident>>;
} }
impl<'a> ParserExt<'a> for Parser<'a> { impl<'a> ParserExt<'a> for Parser<'a> {
@ -14,4 +25,36 @@ impl<'a> ParserExt<'a> for Parser<'a> {
SeqSep::trailing_allowed(Comma), SeqSep::trailing_allowed(Comma),
|p| p.parse_path(PathStyle::Mod)) |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<Ident>> {
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)
})
}
} }

View File

@ -1,4 +1,4 @@
use syntax::codemap::{Span, BytePos}; use syntax::codemap::{Span, Spanned, BytePos};
pub trait SpanExt { pub trait SpanExt {
/// Trim the span on the left and right by `length`. /// 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. // Trim from the left so that the span is `length` in size.
fn shorten_upto(self, length: usize) -> Span; fn shorten_upto(self, length: usize) -> Span;
// Wrap `T` into a `Spanned<T>` with `self` as the span.
fn wrap<T>(self, node: T) -> Spanned<T>;
/// Expand the span on the left by `left` and right by `right`.
fn expand(self, left: usize, right: usize) -> Span;
} }
impl SpanExt for Span { impl SpanExt for Span {
@ -43,4 +49,14 @@ impl SpanExt for Span {
self.hi = self.hi - BytePos(length); self.hi = self.hi - BytePos(length);
self self
} }
fn wrap<T>(self, node: T) -> Spanned<T> {
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
}
} }

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
#[get("a")] //~ ERROR absolute #[get("a")] //~ ERROR absolute

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
#[get("/a/b/c//d")] //~ ERROR paths cannot contain empty segments #[get("/a/b/c//d")] //~ ERROR paths cannot contain empty segments

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
#[derive(FromForm)] #[derive(FromForm)]

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
#[get("/<name>")] //~ ERROR unused dynamic parameter: `name` #[get("/<name>")] //~ ERROR unused dynamic parameter: `name`

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
#[get("/><")] //~ ERROR malformed #[get("/><")] //~ ERROR malformed

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
#[get("/<param>")] //~ ERROR unused dynamic parameter: `param` #[get("/<param>")] //~ ERROR unused dynamic parameter: `param`

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
#![allow(dead_code)] #![allow(dead_code)]

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
#![allow(dead_code, unused_variables)] #![allow(dead_code, unused_variables)]

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
#![allow(dead_code, unused_variables)] #![allow(dead_code, unused_variables)]

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket_contrib; extern crate rocket_contrib;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket_contrib; extern crate rocket_contrib;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate crossbeam; extern crate crossbeam;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket_contrib; extern crate rocket_contrib;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive, const_fn)] #![feature(plugin, decl_macro, custom_derive, const_fn)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
#[macro_use] #[macro_use]

View File

@ -35,7 +35,7 @@ use yansi::Color::*;
/// declared using the `error` decorator, as follows: /// declared using the `error` decorator, as follows:
/// ///
/// ```rust /// ```rust
/// #![feature(plugin)] /// #![feature(plugin, decl_macro)]
/// #![plugin(rocket_codegen)] /// #![plugin(rocket_codegen)]
/// ///
/// extern crate rocket; /// extern crate rocket;

View File

@ -156,7 +156,7 @@ impl<'a, S, E> IntoOutcome<S, (Status, E), Data> for Result<S, E> {
/// ```rust /// ```rust
/// # #![allow(unused_attributes)] /// # #![allow(unused_attributes)]
/// # #![allow(unused_variables)] /// # #![allow(unused_variables)]
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// # /// #

View File

@ -35,7 +35,7 @@ use http::Header;
/// a handler to retrieve the value of a "message" cookie. /// a handler to retrieve the value of a "message" cookie.
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::http::Cookies; /// use rocket::http::Cookies;
@ -56,7 +56,7 @@ use http::Header;
/// [private cookie]: /rocket/http/enum.Cookies.html#private-cookies /// [private cookie]: /rocket/http/enum.Cookies.html#private-cookies
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// # /// #

View File

@ -1,11 +1,13 @@
//! Borrowed and owned string types for absolute URIs. //! Borrowed and owned string types for absolute URIs.
use std::convert::From;
use std::fmt; use std::fmt;
use std::convert::From;
use std::borrow::Cow; use std::borrow::Cow;
use std::str::Utf8Error; use std::str::Utf8Error;
use std::sync::atomic::{AtomicIsize, Ordering}; use std::sync::atomic::{AtomicIsize, Ordering};
use http::RawStr;
use url; use url;
/// Index (start, end) into a string, to prevent borrowing. /// 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<T: fmt::Display> UriDisplay for T {
#[inline(always)]
default fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Display>::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. /// Iterator over the segments of an absolute URI path. Skips empty segments.
/// ///
/// ### Examples /// ### Examples

View File

@ -2,7 +2,7 @@
#![feature(conservative_impl_trait)] #![feature(conservative_impl_trait)]
#![feature(drop_types_in_const)] #![feature(drop_types_in_const)]
#![feature(const_fn)] #![feature(const_fn)]
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![feature(never_type)] #![feature(never_type)]
#![feature(try_trait)] #![feature(try_trait)]
@ -55,7 +55,7 @@
//! Then, add the following to the top of your `main.rs` file: //! Then, add the following to the top of your `main.rs` file:
//! //!
//! ```rust //! ```rust
//! #![feature(plugin)] //! #![feature(plugin, decl_macro)]
//! # #![allow(unused_attributes)] //! # #![allow(unused_attributes)]
//! #![plugin(rocket_codegen)] //! #![plugin(rocket_codegen)]
//! //!
@ -66,7 +66,7 @@
//! write Rocket applications. Here's a simple example to get you started: //! write Rocket applications. Here's a simple example to get you started:
//! //!
//! ```rust //! ```rust
//! #![feature(plugin)] //! #![feature(plugin, decl_macro)]
//! #![plugin(rocket_codegen)] //! #![plugin(rocket_codegen)]
//! //!
//! extern crate rocket; //! extern crate rocket;

View File

@ -67,7 +67,7 @@
//! consider the following complete "Hello, world!" application, with testing. //! consider the following complete "Hello, world!" application, with testing.
//! //!
//! ```rust //! ```rust
//! #![feature(plugin)] //! #![feature(plugin, decl_macro)]
//! #![plugin(rocket_codegen)] //! #![plugin(rocket_codegen)]
//! //!
//! extern crate rocket; //! extern crate rocket;

View File

@ -49,7 +49,7 @@ use request::form::{FromForm, FormItems};
/// The simplest data structure with a reference into form data looks like this: /// The simplest data structure with a reference into form data looks like this:
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin, custom_derive)] /// # #![feature(plugin, decl_macro, custom_derive)]
/// # #![allow(deprecated, dead_code, unused_attributes)] /// # #![allow(deprecated, dead_code, unused_attributes)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
@ -65,7 +65,7 @@ use request::form::{FromForm, FormItems};
/// a string. A handler for this type can be written as: /// a string. A handler for this type can be written as:
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin, custom_derive)] /// # #![feature(plugin, decl_macro, custom_derive)]
/// # #![allow(deprecated, unused_attributes)] /// # #![allow(deprecated, unused_attributes)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
@ -91,7 +91,7 @@ use request::form::{FromForm, FormItems};
/// The owned analog of the `UserInput` type above is: /// The owned analog of the `UserInput` type above is:
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin, custom_derive)] /// # #![feature(plugin, decl_macro, custom_derive)]
/// # #![allow(deprecated, dead_code, unused_attributes)] /// # #![allow(deprecated, dead_code, unused_attributes)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
@ -105,7 +105,7 @@ use request::form::{FromForm, FormItems};
/// The handler is written similarly: /// The handler is written similarly:
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin, custom_derive)] /// # #![feature(plugin, decl_macro, custom_derive)]
/// # #![allow(deprecated, unused_attributes)] /// # #![allow(deprecated, unused_attributes)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
@ -170,7 +170,7 @@ impl<'f, T: FromForm<'f> + 'f> Form<'f, T> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin, custom_derive)] /// # #![feature(plugin, decl_macro, custom_derive)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::request::Form; /// use rocket::request::Form;
@ -198,7 +198,7 @@ impl<'f, T: FromForm<'f> + 'f> Form<'f, T> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin, custom_derive)] /// # #![feature(plugin, decl_macro, custom_derive)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::request::Form; /// use rocket::request::Form;
@ -269,7 +269,7 @@ impl<'f, T: FromForm<'f> + 'static> Form<'f, T> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin, custom_derive)] /// # #![feature(plugin, decl_macro, custom_derive)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::request::Form; /// use rocket::request::Form;

View File

@ -14,7 +14,7 @@ use request::FormItems;
/// validation. /// validation.
/// ///
/// ```rust /// ```rust
/// #![feature(plugin, custom_derive)] /// #![feature(plugin, decl_macro, custom_derive)]
/// #![plugin(rocket_codegen)] /// #![plugin(rocket_codegen)]
/// # #![allow(deprecated, dead_code, unused_attributes)] /// # #![allow(deprecated, dead_code, unused_attributes)]
/// ///
@ -34,7 +34,7 @@ use request::FormItems;
/// data via the `data` parameter and `Form` type. /// data via the `data` parameter and `Form` type.
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin, custom_derive)] /// # #![feature(plugin, decl_macro, custom_derive)]
/// # #![allow(deprecated, dead_code, unused_attributes)] /// # #![allow(deprecated, dead_code, unused_attributes)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;

View File

@ -56,7 +56,7 @@ impl<'f, T: FromForm<'f> + 'f> LenientForm<'f, T> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin, custom_derive)] /// # #![feature(plugin, decl_macro, custom_derive)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::request::LenientForm; /// use rocket::request::LenientForm;
@ -84,7 +84,7 @@ impl<'f, T: FromForm<'f> + 'f> LenientForm<'f, T> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin, custom_derive)] /// # #![feature(plugin, decl_macro, custom_derive)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::request::LenientForm; /// use rocket::request::LenientForm;
@ -114,7 +114,7 @@ impl<'f, T: FromForm<'f> + 'static> LenientForm<'f, T> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin, custom_derive)] /// # #![feature(plugin, decl_macro, custom_derive)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::request::LenientForm; /// use rocket::request::LenientForm;

View File

@ -167,7 +167,7 @@ impl<S, E> IntoOutcome<S, (Status, E), ()> for Result<S, E> {
/// `senstive` handler. /// `senstive` handler.
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// # /// #

View File

@ -20,7 +20,7 @@ use http::RawStr;
/// handler for the dynamic `"/<id>"` path: /// handler for the dynamic `"/<id>"` path:
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// #[get("/<id>")] /// #[get("/<id>")]
@ -56,7 +56,7 @@ use http::RawStr;
/// parameter as follows: /// parameter as follows:
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// # use rocket::http::RawStr; /// # use rocket::http::RawStr;
@ -163,7 +163,7 @@ use http::RawStr;
/// dynamic path segment: /// dynamic path segment:
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// # use rocket::request::FromParam; /// # use rocket::request::FromParam;

View File

@ -21,7 +21,7 @@ use http::Status;
/// following example does just this: /// following example does just this:
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::State; /// use rocket::State;

View File

@ -46,7 +46,7 @@ const FLASH_COOKIE_NAME: &'static str = "_flash";
/// message on both the request and response sides. /// message on both the request and response sides.
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # /// #
/// # extern crate rocket; /// # extern crate rocket;

View File

@ -137,7 +137,7 @@ use request::Request;
/// following `Responder` implementation accomplishes this: /// following `Responder` implementation accomplishes this:
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// # /// #

View File

@ -448,7 +448,7 @@ impl Rocket {
/// dispatched to the `hi` route. /// dispatched to the `hi` route.
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// # /// #
@ -514,7 +514,7 @@ impl Rocket {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// #![feature(plugin)] /// #![feature(plugin, decl_macro)]
/// #![plugin(rocket_codegen)] /// #![plugin(rocket_codegen)]
/// ///
/// extern crate rocket; /// extern crate rocket;
@ -573,7 +573,7 @@ impl Rocket {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::State; /// use rocket::State;
@ -609,7 +609,7 @@ impl Rocket {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::Rocket; /// use rocket::Rocket;
@ -711,7 +711,7 @@ impl Rocket {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::Rocket; /// use rocket::Rocket;
@ -748,7 +748,7 @@ impl Rocket {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # #![feature(plugin)] /// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)] /// # #![plugin(rocket_codegen)]
/// # extern crate rocket; /// # extern crate rocket;
/// use rocket::Rocket; /// use rocket::Rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive)] #![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

View File

@ -1,4 +1,4 @@
#![feature(plugin)] #![feature(plugin, decl_macro)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate rocket; extern crate rocket;

Some files were not shown because too many files have changed in this diff Show More