mirror of https://github.com/rwf2/Rocket.git
Overhealed route decorator. URI struct now understands query part.
This commit is contained in:
parent
26b7b814f4
commit
92671a0cba
|
@ -4,7 +4,7 @@ use method::Method;
|
|||
|
||||
pub use hyper::server::Request as HyperRequest;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Request<'a> {
|
||||
params: Option<Vec<&'a str>>,
|
||||
pub method: Method,
|
||||
|
|
|
@ -93,12 +93,12 @@ impl Rocket {
|
|||
return handle_not_found(res);
|
||||
}
|
||||
|
||||
// Okay, we've got a route. Unwrap it, generate a request, and try to
|
||||
// dispatch.
|
||||
println!("\t=> {}", Magenta.paint("Dispatching request."));
|
||||
// Okay, we've got a route. Unwrap it, generate a request, and dispatch.
|
||||
let route = route.unwrap();
|
||||
let params = route.get_params(uri);
|
||||
let request = Request::new(method, uri, Some(params), &buf);
|
||||
|
||||
println!("\t=> {}", Magenta.paint("Dispatching request."));
|
||||
let outcome = (route.handler)(request).respond(res);
|
||||
|
||||
// TODO: keep trying lower ranked routes before dispatching a not found
|
||||
|
|
|
@ -5,15 +5,26 @@ use std::fmt::{self, Write};
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct URI<'a> {
|
||||
uri: &'a str,
|
||||
path: &'a str,
|
||||
query: Option<&'a str>,
|
||||
segment_count: Cell<Option<usize>>
|
||||
}
|
||||
|
||||
impl<'a> URI<'a> {
|
||||
pub fn new<T: AsRef<str> + ?Sized>(path: &'a T) -> URI<'a> {
|
||||
pub fn new<T: AsRef<str> + ?Sized>(uri: &'a T) -> URI<'a> {
|
||||
let uri = uri.as_ref();
|
||||
|
||||
let (path, query) = match uri.find('?') {
|
||||
Some(index) => (&uri[..index], Some(&uri[index..])),
|
||||
None => (uri, None)
|
||||
};
|
||||
|
||||
URI {
|
||||
segment_count: Cell::new(None),
|
||||
path: path.as_ref(),
|
||||
uri: uri,
|
||||
path: path,
|
||||
query: query,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,14 +41,14 @@ impl<'a> URI<'a> {
|
|||
}
|
||||
|
||||
pub fn as_str(&self) -> &'a str {
|
||||
self.path
|
||||
self.uri
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for URI<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut last = '\0';
|
||||
for c in self.path.chars() {
|
||||
for c in self.uri.chars() {
|
||||
if !(c == '/' && last == '/') { f.write_char(c)?; }
|
||||
last = c;
|
||||
}
|
||||
|
@ -50,7 +61,7 @@ unsafe impl<'a> Sync for URI<'a> { /* It's safe! */ }
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct URIBuf {
|
||||
path: String,
|
||||
uri: String,
|
||||
segment_count: Cell<Option<usize>>
|
||||
}
|
||||
|
||||
|
@ -65,25 +76,25 @@ impl URIBuf {
|
|||
}
|
||||
|
||||
pub fn segments(&self) -> Segments {
|
||||
Segments(self.path.as_str())
|
||||
self.as_uri_uncached().segments()
|
||||
}
|
||||
|
||||
fn as_uri_uncached(&self) -> URI {
|
||||
URI::new(self.path.as_str())
|
||||
URI::new(self.uri.as_str())
|
||||
}
|
||||
|
||||
pub fn as_uri(&self) -> URI {
|
||||
let mut uri = URI::new(self.path.as_str());
|
||||
let mut uri = URI::new(self.uri.as_str());
|
||||
uri.segment_count = self.segment_count.clone();
|
||||
uri
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.path.as_str()
|
||||
self.uri.as_str()
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
self.path.clone()
|
||||
self.uri.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,19 +107,19 @@ impl fmt::Display for URIBuf {
|
|||
}
|
||||
|
||||
impl From<String> for URIBuf {
|
||||
fn from(path: String) -> URIBuf {
|
||||
fn from(uri: String) -> URIBuf {
|
||||
URIBuf {
|
||||
segment_count: Cell::new(None),
|
||||
path: path,
|
||||
uri: uri,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for URIBuf {
|
||||
fn from(path: &'a str) -> URIBuf {
|
||||
fn from(uri: &'a str) -> URIBuf {
|
||||
URIBuf {
|
||||
segment_count: Cell::new(None),
|
||||
path: path.to_string(),
|
||||
uri: uri.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,17 +17,15 @@ struct Params {
|
|||
}
|
||||
|
||||
fn get_error_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
|
||||
assert_meta_item_list(ecx, meta_item, "error");
|
||||
|
||||
// Ensure we can unwrap the k = v params.
|
||||
let params = meta_item.node.get_list_items().unwrap();
|
||||
// Ensure we've been supplied with a k = v meta item. Error out if not.
|
||||
let params = meta_item.expect_list(ecx, "Bad use. Expected: #[error(...)]");
|
||||
|
||||
// Now grab all of the required and optional parameters.
|
||||
let req: [&'static str; 1] = ["code"];
|
||||
let kv_pairs = get_key_values(ecx, meta_item.span, &req, &[], &*params);
|
||||
|
||||
// Ensure we have a code, just to keep parsing and generating errors.
|
||||
let code = kv_pairs.get("code").map_or(dummy_kvspan(404), |c| {
|
||||
let code = kv_pairs.get("code").map_or(KVSpanned::dummy(404), |c| {
|
||||
let numeric_code = match c.node.parse() {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
|
|
|
@ -2,13 +2,10 @@ use super::{ROUTE_STRUCT_PREFIX, ROUTE_FN_PREFIX};
|
|||
use utils::*;
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use syntax::ext::quote::rt::ToTokens;
|
||||
use syntax::codemap::{Span, BytePos, /* DUMMY_SP, */ Spanned};
|
||||
use syntax::tokenstream::TokenTree;
|
||||
use syntax::ast::{Ident, PatKind, Stmt};
|
||||
use syntax::ast::{Item, Expr, ItemKind, MetaItem, MetaItemKind, FnDecl, Ty};
|
||||
use syntax::ast::{Stmt, Item, Expr, ItemKind, MetaItem, MetaItemKind, FnDecl};
|
||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::ptr::P;
|
||||
use syntax::print::pprust::{item_to_string, stmt_to_string};
|
||||
|
@ -54,12 +51,10 @@ pub fn get_fn_decl<'a>(ecx: &mut ExtCtxt, sp: Span, annotated: &'a Annotatable)
|
|||
(item, wrap_span(&*fn_decl, item.span))
|
||||
}
|
||||
|
||||
fn get_route_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
|
||||
// First, check that the macro was used in the #[route(a, b, ..)] form.
|
||||
assert_meta_item_list(ecx, meta_item, "error");
|
||||
|
||||
// Ensure we can unwrap the k = v params.
|
||||
let params = meta_item.node.get_list_items().unwrap();
|
||||
// Parses the MetaItem derived from the route(...) macro.
|
||||
fn parse_route(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
|
||||
// Ensure we've been supplied with a k = v meta item. Error out if not.
|
||||
let params = meta_item.expect_list(ecx, "Bad use. Expected: #[route(...)]");
|
||||
if params.len() < 1 {
|
||||
bad_method_err(ecx, meta_item.span, "HTTP method parameter is missing.");
|
||||
ecx.span_fatal(meta_item.span, "At least 2 arguments are required.");
|
||||
|
@ -89,7 +84,7 @@ fn get_route_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
|
|||
let kv_pairs = get_key_values(ecx, meta_item.span, &req, &opt, kv_params);
|
||||
|
||||
// Ensure we have a path, just to keep parsing and generating errors.
|
||||
let path = kv_pairs.get("path").map_or(dummy_kvspan("/".to_string()), |s| {
|
||||
let path = kv_pairs.get("path").map_or(KVSpanned::dummy("/".to_string()), |s| {
|
||||
s.clone().map(String::from)
|
||||
});
|
||||
|
||||
|
@ -107,7 +102,7 @@ fn get_route_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
|
|||
.emit();
|
||||
}
|
||||
|
||||
if f.node.chars().filter(|c| *c == '<').count() != 1 {
|
||||
if f.node.chars().filter(|c| *c == '<' || *c == '>').count() != 2 {
|
||||
ecx.span_err(f.p_span, "`form` must contain exactly one parameter");
|
||||
}
|
||||
|
||||
|
@ -121,29 +116,11 @@ fn get_route_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
|
|||
}
|
||||
}
|
||||
|
||||
// Is there a better way to do this? I need something with ToTokens for the
|
||||
// quote_expr macro that builds the route struct. I tried using
|
||||
// str_to_ident("rocket::Method::Options"), but this seems to miss the context,
|
||||
// and you get an 'ident not found' on compile. I also tried using the path expr
|
||||
// builder from ASTBuilder: same thing.
|
||||
fn method_variant_to_expr(ecx: &ExtCtxt, method: Method) -> P<Expr> {
|
||||
match method {
|
||||
Method::Options => quote_expr!(ecx, rocket::Method::Options),
|
||||
Method::Get => quote_expr!(ecx, rocket::Method::Get),
|
||||
Method::Post => quote_expr!(ecx, rocket::Method::Post),
|
||||
Method::Put => quote_expr!(ecx, rocket::Method::Put),
|
||||
Method::Delete => quote_expr!(ecx, rocket::Method::Delete),
|
||||
Method::Head => quote_expr!(ecx, rocket::Method::Head),
|
||||
Method::Trace => quote_expr!(ecx, rocket::Method::Trace),
|
||||
Method::Connect => quote_expr!(ecx, rocket::Method::Connect),
|
||||
Method::Patch => quote_expr!(ecx, rocket::Method::Patch),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Put something like this in the library. Maybe as an iterator?
|
||||
pub fn gen_params_hashset<'a>(ecx: &ExtCtxt, params: &Spanned<&'a str>)
|
||||
-> HashSet<&'a str> {
|
||||
let mut seen = HashSet::new();
|
||||
pub fn extract_params<'a>(ecx: &ExtCtxt, params: &Spanned<&'a str>)
|
||||
-> Vec<Spanned<&'a str>> {
|
||||
let mut output_params = vec![];
|
||||
let bad_match_err = "Parameter string is malformed.";
|
||||
|
||||
let mut start = 0;
|
||||
|
@ -163,13 +140,7 @@ pub fn gen_params_hashset<'a>(ecx: &ExtCtxt, params: &Spanned<&'a str>)
|
|||
|
||||
if i > start + 1 {
|
||||
let param_name = ¶ms.node[(start + 1)..i];
|
||||
if seen.contains(param_name) {
|
||||
let msg = format!("\"{}\" appears more than once in \
|
||||
the parameter string.", param_name);
|
||||
ecx.span_err(param_span, msg.as_str());
|
||||
}
|
||||
|
||||
seen.insert(param_name);
|
||||
output_params.push(wrap_span(param_name, param_span))
|
||||
} else {
|
||||
ecx.span_err(param_span, "Parameter names cannot be empty.");
|
||||
}
|
||||
|
@ -180,137 +151,91 @@ pub fn gen_params_hashset<'a>(ecx: &ExtCtxt, params: &Spanned<&'a str>)
|
|||
}
|
||||
}
|
||||
|
||||
seen
|
||||
output_params
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SimpleArg {
|
||||
name: String,
|
||||
ty: P<Ty>,
|
||||
span: Span
|
||||
}
|
||||
|
||||
pub fn gen_kv_string_hashset<'a>(ecx: &ExtCtxt, params: &'a KVSpanned<String>)
|
||||
-> HashSet<&'a str> {
|
||||
pub fn extract_params_from_kv<'a>(ecx: &ExtCtxt, params: &'a KVSpanned<String>)
|
||||
-> Vec<Spanned<&'a str>> {
|
||||
let mut param_span = params.v_span;
|
||||
param_span.lo = params.v_span.lo + BytePos(1);
|
||||
let params = Spanned {
|
||||
extract_params(ecx, &Spanned {
|
||||
span: param_span,
|
||||
node: &*params.node
|
||||
};
|
||||
|
||||
gen_params_hashset(ecx, ¶ms)
|
||||
})
|
||||
}
|
||||
|
||||
impl SimpleArg {
|
||||
fn new<T: ToString>(name: T, ty: P<Ty>, sp: Span) -> SimpleArg {
|
||||
SimpleArg { name: name.to_string(), ty: ty, span: sp }
|
||||
}
|
||||
|
||||
fn as_str(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for SimpleArg {
|
||||
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
|
||||
str_to_ident(self.as_str()).to_tokens(cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fn_params<'a>(ecx: &ExtCtxt, dec_span: Span, path: &'a KVSpanned<String>,
|
||||
fn_decl: &Spanned<&FnDecl>, mut external: HashSet<&'a str>)
|
||||
-> Vec<SimpleArg> {
|
||||
fn get_fn_params<'a, T: Iterator<Item=&'a Spanned<&'a str>>>(ecx: &ExtCtxt,
|
||||
declared_params: T, fn_decl: &Spanned<&FnDecl>) -> Vec<SimpleArg> {
|
||||
debug!("FUNCTION: {:?}", fn_decl);
|
||||
let mut path_params = gen_kv_string_hashset(ecx, &path);
|
||||
|
||||
// Ensure that there are no collisions between path parameters and external
|
||||
// params. If there are, get rid of one of them so we don't double error.
|
||||
let new_external = external.clone();
|
||||
for param in path_params.intersection(&new_external) {
|
||||
let msg = format!("'{}' appears as a parameter more than once.", param);
|
||||
external.remove(param);
|
||||
ecx.span_err(dec_span, msg.as_str());
|
||||
// First, check that all of the parameters are unique.
|
||||
let mut seen: HashMap<&str, &Spanned<&str>> = HashMap::new();
|
||||
for item in declared_params {
|
||||
if seen.contains_key(item.node) {
|
||||
let msg = format!(
|
||||
"\"{}\" was declared as a parameter more than once.", item.node);
|
||||
ecx.span_err(item.span, msg.as_str());
|
||||
} else {
|
||||
seen.insert(item.node, item);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure every param in the function declaration is in `path`. Also add
|
||||
// each param name in the declaration to the result vector.
|
||||
// Ensure every param in the function declaration was declared by the user.
|
||||
let mut result = vec![];
|
||||
for arg in &fn_decl.node.inputs {
|
||||
let ident: &Ident = match arg.pat.node {
|
||||
PatKind::Ident(_, ref ident, _) => &ident.node,
|
||||
_ => {
|
||||
ecx.span_err(arg.pat.span, "Expected an identifier.");
|
||||
return result
|
||||
}
|
||||
};
|
||||
|
||||
let name = ident.to_string();
|
||||
if !path_params.remove(name.as_str()) && !external.remove(name.as_str()) {
|
||||
let msg1 = format!("'{}' appears in the function declaration...", name);
|
||||
let msg2 = format!("...but does not appear as a parameter \
|
||||
(e.g., <{}>).", name);
|
||||
ecx.span_err(arg.pat.span, msg1.as_str());
|
||||
ecx.span_err(dec_span, msg2.as_str());
|
||||
let name = arg.pat.expect_ident(ecx, "Expected identifier.");
|
||||
if seen.remove(&*name.to_string()).is_none() {
|
||||
let msg = format!("'{}' appears in the function declaration \
|
||||
but does not appear as a parameter in the attribute.", name);
|
||||
ecx.span_err(arg.pat.span, msg.as_str());
|
||||
}
|
||||
|
||||
result.push(SimpleArg::new(name, arg.ty.clone(), arg.pat.span));
|
||||
}
|
||||
|
||||
// Ensure every param in `path` and `exclude` is in the function declaration.
|
||||
for item in path_params {
|
||||
let msg = format!("'{}' appears in the path string...", item);
|
||||
ecx.span_err(path.v_span, msg.as_str());
|
||||
ecx.span_err(fn_decl.span, "...but does not appear in the function \
|
||||
declration.");
|
||||
}
|
||||
|
||||
// FIXME: need the spans for the external params
|
||||
for item in external {
|
||||
let msg = format!("'{}' appears as a parameter...", item);
|
||||
ecx.span_err(dec_span, msg.as_str());
|
||||
ecx.span_err(fn_decl.span, "...but does not appear in the function \
|
||||
declaration.");
|
||||
// Ensure every declared parameter is in the function declaration.
|
||||
for item in seen.values() {
|
||||
let msg = format!("'{}' was declared in the attribute...", item.node);
|
||||
ecx.span_err(item.span, msg.as_str());
|
||||
ecx.span_err(fn_decl.span, "...but does not appear in the function \
|
||||
declaration.");
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn get_form_stmt(ecx: &ExtCtxt, fn_args: &mut Vec<SimpleArg>,
|
||||
form_params: &HashSet<&str>) -> Option<Stmt> {
|
||||
form_params: &[Spanned<&str>]) -> Option<Stmt> {
|
||||
if form_params.len() < 1 {
|
||||
return None
|
||||
} else if form_params.len() > 1 {
|
||||
panic!("Allowed more than 1 form parameter!");
|
||||
}
|
||||
|
||||
|
||||
let param_ty;
|
||||
let param_ident;
|
||||
let param_name = form_params.iter().next().unwrap();
|
||||
|
||||
{
|
||||
let param_name = &form_params[0].node;
|
||||
let (param_ty, param_ident) = {
|
||||
// Get the first item in the hashset, i.e., the form params variable name.
|
||||
let fn_arg = fn_args.iter().filter(|a| &&*a.name == param_name).next();
|
||||
if fn_arg.is_none() {
|
||||
// This happens when a form parameter doesn't appear in the function.
|
||||
// We should have already caught this, so just return None.
|
||||
return None;
|
||||
}
|
||||
|
||||
param_ty = fn_arg.unwrap().ty.clone();
|
||||
param_ident = str_to_ident(param_name);
|
||||
}
|
||||
(fn_arg.unwrap().ty.clone(), str_to_ident(param_name))
|
||||
};
|
||||
|
||||
// Remove the paramter from the function arguments.
|
||||
debug!("Form parameter variable: {}: {:?}", param_name, param_ty);
|
||||
let fn_arg_index = fn_args.iter().position(|a| &&*a.name == param_name).unwrap();
|
||||
fn_args.remove(fn_arg_index);
|
||||
|
||||
// The actual code we'll be inserting.
|
||||
quote_stmt!(ecx,
|
||||
// TODO: Actually get the form parameters to pass into from_form_string.
|
||||
// Alternatively, pass in some already parsed thing.
|
||||
let $param_ident: $param_ty = {
|
||||
let form_string = std::str::from_utf8(_req.data);
|
||||
if form_string.is_err() {
|
||||
return ::rocket::Response::not_found()
|
||||
return ::rocket::Response::server_error();
|
||||
};
|
||||
|
||||
match ::rocket::form::FromForm::from_form_string(form_string.unwrap()) {
|
||||
|
@ -324,30 +249,50 @@ fn get_form_stmt(ecx: &ExtCtxt, fn_args: &mut Vec<SimpleArg>,
|
|||
)
|
||||
}
|
||||
|
||||
// Is there a better way to do this? I need something with ToTokens for the
|
||||
// quote_expr macro that builds the route struct. I tried using
|
||||
// str_to_ident("rocket::Method::Options"), but this seems to miss the context,
|
||||
// and you get an 'ident not found' on compile. I also tried using the path expr
|
||||
// builder from ASTBuilder: same thing.
|
||||
fn method_variant_to_expr(ecx: &ExtCtxt, method: Method) -> P<Expr> {
|
||||
match method {
|
||||
Method::Options => quote_expr!(ecx, ::rocket::Method::Options),
|
||||
Method::Get => quote_expr!(ecx, ::rocket::Method::Get),
|
||||
Method::Post => quote_expr!(ecx, ::rocket::Method::Post),
|
||||
Method::Put => quote_expr!(ecx, ::rocket::Method::Put),
|
||||
Method::Delete => quote_expr!(ecx, ::rocket::Method::Delete),
|
||||
Method::Head => quote_expr!(ecx, ::rocket::Method::Head),
|
||||
Method::Trace => quote_expr!(ecx, ::rocket::Method::Trace),
|
||||
Method::Connect => quote_expr!(ecx, ::rocket::Method::Connect),
|
||||
Method::Patch => quote_expr!(ecx, ::rocket::Method::Patch),
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Compilation fails when parameters have the same name as the function!
|
||||
pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
||||
annotated: &Annotatable, push: &mut FnMut(Annotatable)) {
|
||||
// Get the encompassing item and function declaration for the annotated func.
|
||||
let (item, fn_decl) = get_fn_decl(ecx, sp, annotated);
|
||||
let route_params = get_route_params(ecx, meta_item);
|
||||
|
||||
// TODO: move this elsewhere
|
||||
let mut external_params = HashSet::new();
|
||||
let mut form_param_hashset = HashSet::new();
|
||||
if let Some(ref form) = route_params.form {
|
||||
form_param_hashset = gen_kv_string_hashset(ecx, form);
|
||||
external_params.extend(&form_param_hashset);
|
||||
}
|
||||
// Parse and retrieve all of the parameters of the route.
|
||||
let route = parse_route(ecx, meta_item);
|
||||
|
||||
let mut fn_params = get_fn_params(ecx, sp, &route_params.path, &fn_decl,
|
||||
external_params.clone());
|
||||
// Get a list of the user declared parameters in `path` and `form`.
|
||||
let path_params = extract_params_from_kv(ecx, &route.path);
|
||||
let form_thing = route.form.unwrap_or_default(); // Default is empty string.
|
||||
let form_params = extract_params_from_kv(ecx, &form_thing);
|
||||
|
||||
// Ensure the params match the function declaration and return the params.
|
||||
let all_params = path_params.iter().chain(form_params.iter());
|
||||
let mut fn_params = get_fn_params(ecx, all_params, &fn_decl);
|
||||
|
||||
// Create a comma seperated list (token tree) of the function parameters
|
||||
// We pass this in to the user's function that we're wrapping.
|
||||
let fn_param_idents = token_separate(ecx, &fn_params, token::Comma);
|
||||
|
||||
// Generate the statements that will attempt to parse forms during run-time.
|
||||
// let form_span = route_params.form.map_or(DUMMY_SP, |f| f.span.clone());
|
||||
let form_stmt = get_form_stmt(ecx, &mut fn_params, &form_param_hashset);
|
||||
// Calling this function also remove the form parameter from fn_params.
|
||||
let form_stmt = get_form_stmt(ecx, &mut fn_params, &form_params);
|
||||
form_stmt.as_ref().map(|s| debug!("Form stmt: {:?}", stmt_to_string(s)));
|
||||
|
||||
// Generate the statements that will attempt to parse the paramaters during
|
||||
|
@ -384,8 +329,8 @@ pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
|||
push(Annotatable::Item(route_fn_item));
|
||||
|
||||
let struct_name = prepend_ident(ROUTE_STRUCT_PREFIX, &item.ident);
|
||||
let path = route_params.path.node;
|
||||
let method = method_variant_to_expr(ecx, route_params.method.node);
|
||||
let path = &route.path.node;
|
||||
let method = method_variant_to_expr(ecx, route.method.node);
|
||||
push(Annotatable::Item(quote_item!(ecx,
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static $struct_name: rocket::StaticRouteInfo = rocket::StaticRouteInfo {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use syntax::parse::{token};
|
||||
use syntax::parse::token::Token;
|
||||
use syntax::tokenstream::TokenTree;
|
||||
use syntax::ast::{Path, Ident, MetaItem, MetaItemKind, LitKind};
|
||||
use syntax::ast::{Path, Ident, MetaItem, MetaItemKind, LitKind, Ty, self};
|
||||
use syntax::ext::base::{ExtCtxt};
|
||||
use syntax::codemap::{Span, Spanned, BytePos, DUMMY_SP};
|
||||
use syntax::ext::quote::rt::ToTokens;
|
||||
|
@ -50,22 +50,30 @@ pub fn dummy_span<T>(t: T) -> Spanned<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn dummy_kvspan<T>(t: T) -> KVSpanned<T> {
|
||||
KVSpanned {
|
||||
k_span: DUMMY_SP,
|
||||
v_span: DUMMY_SP,
|
||||
p_span: DUMMY_SP,
|
||||
node: t,
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KVSpanned<T> {
|
||||
pub k_span: Span, // Span for the key.
|
||||
pub v_span: Span, // Span for the value.
|
||||
pub p_span: Span, // Span for the full parameter.
|
||||
pub node: T // The value.
|
||||
}
|
||||
|
||||
impl<T> KVSpanned<T> {
|
||||
#[inline]
|
||||
pub fn dummy(t: T) -> KVSpanned<T> {
|
||||
KVSpanned {
|
||||
k_span: DUMMY_SP,
|
||||
v_span: DUMMY_SP,
|
||||
p_span: DUMMY_SP,
|
||||
node: t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KVSpanned<T> {
|
||||
pub k_span: Span,
|
||||
pub v_span: Span,
|
||||
pub p_span: Span,
|
||||
pub node: T
|
||||
impl<T: Default> Default for KVSpanned<T> {
|
||||
fn default() -> KVSpanned<T> {
|
||||
KVSpanned::dummy(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToTokens> ToTokens for KVSpanned<T> {
|
||||
|
@ -147,27 +155,38 @@ pub fn token_separate<T: ToTokens>(ecx: &ExtCtxt, things: &[T],
|
|||
output
|
||||
}
|
||||
|
||||
pub fn assert_meta_item_list(ecx: &ExtCtxt, meta_item: &MetaItem, s: &str) {
|
||||
if !meta_item.node.is_list() {
|
||||
let msg = format!("Incorrect use of macro. Expected: #[{}(...)]", s);
|
||||
ecx.span_fatal(meta_item.span, msg.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MetaItemExt {
|
||||
fn is_list(&self) -> bool;
|
||||
fn get_list_items(&self) -> Option<&Vec<P<MetaItem>>>;
|
||||
fn expect_list<'a>(&'a self, ecx: &ExtCtxt, msg: &str) -> &'a Vec<P<MetaItem>>;
|
||||
fn expect_word<'a>(&'a self, ecx: &ExtCtxt, msg: &str) -> &'a str;
|
||||
}
|
||||
|
||||
impl MetaItemExt for MetaItemKind {
|
||||
fn is_list(&self) -> bool {
|
||||
self.get_list_items().is_some()
|
||||
impl MetaItemExt for MetaItem {
|
||||
fn expect_list<'a>(&'a self, ecx: &ExtCtxt, msg: &str) -> &'a Vec<P<MetaItem>> {
|
||||
match self.node {
|
||||
MetaItemKind::List(_, ref params) => params,
|
||||
_ => ecx.span_fatal(self.span, msg)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_list_items(&self) -> Option<&Vec<P<MetaItem>>> {
|
||||
match *self {
|
||||
MetaItemKind::List(_, ref params) => Some(params),
|
||||
_ => None
|
||||
fn expect_word<'a>(&'a self, ecx: &ExtCtxt, msg: &str) -> &'a str {
|
||||
match self.node {
|
||||
MetaItemKind::Word(ref s) => &*s,
|
||||
_ => ecx.span_fatal(self.span, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PatExt {
|
||||
fn expect_ident<'a>(&'a self, ecx: &ExtCtxt, msg: &str) -> &'a Ident;
|
||||
}
|
||||
|
||||
impl PatExt for ast::Pat {
|
||||
fn expect_ident<'a>(&'a self, ecx: &ExtCtxt, msg: &str) -> &'a Ident {
|
||||
match self.node {
|
||||
ast::PatKind::Ident(_, ref ident, _) => &ident.node,
|
||||
_ => {
|
||||
ecx.span_fatal(self.span, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -198,16 +217,26 @@ pub fn prefix_paths(prefix: &str, paths: &mut Vec<Path>) {
|
|||
}
|
||||
}
|
||||
|
||||
// pub fn find_value_for(key: &str, kv_params: &[P<MetaItem>]) -> Option<String> {
|
||||
// for param in kv_params {
|
||||
// if let MetaItemKind::NameValue(ref name, ref value) = param.node {
|
||||
// if &**name == key {
|
||||
// if let LitKind::Str(ref string, _) = value.node {
|
||||
// return Some(String::from(&**string));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
#[derive(Debug)]
|
||||
pub struct SimpleArg {
|
||||
pub name: String,
|
||||
pub ty: P<Ty>,
|
||||
pub span: Span
|
||||
}
|
||||
|
||||
impl SimpleArg {
|
||||
pub fn new<T: ToString>(name: T, ty: P<Ty>, sp: Span) -> SimpleArg {
|
||||
SimpleArg { name: name.to_string(), ty: ty, span: sp }
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for SimpleArg {
|
||||
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
|
||||
token::str_to_ident(self.as_str()).to_tokens(cx)
|
||||
}
|
||||
}
|
||||
|
||||
// None
|
||||
// }
|
||||
|
|
Loading…
Reference in New Issue