Overhealed route decorator. URI struct now understands query part.

This commit is contained in:
Sergio Benitez 2016-07-18 21:11:22 -07:00
parent 26b7b814f4
commit 92671a0cba
6 changed files with 188 additions and 205 deletions

View File

@ -4,7 +4,7 @@ use method::Method;
pub use hyper::server::Request as HyperRequest; pub use hyper::server::Request as HyperRequest;
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct Request<'a> { pub struct Request<'a> {
params: Option<Vec<&'a str>>, params: Option<Vec<&'a str>>,
pub method: Method, pub method: Method,

View File

@ -93,12 +93,12 @@ impl Rocket {
return handle_not_found(res); return handle_not_found(res);
} }
// Okay, we've got a route. Unwrap it, generate a request, and try to // Okay, we've got a route. Unwrap it, generate a request, and dispatch.
// dispatch.
println!("\t=> {}", Magenta.paint("Dispatching request."));
let route = route.unwrap(); let route = route.unwrap();
let params = route.get_params(uri); let params = route.get_params(uri);
let request = Request::new(method, uri, Some(params), &buf); let request = Request::new(method, uri, Some(params), &buf);
println!("\t=> {}", Magenta.paint("Dispatching request."));
let outcome = (route.handler)(request).respond(res); let outcome = (route.handler)(request).respond(res);
// TODO: keep trying lower ranked routes before dispatching a not found // TODO: keep trying lower ranked routes before dispatching a not found

View File

@ -5,15 +5,26 @@ use std::fmt::{self, Write};
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct URI<'a> { pub struct URI<'a> {
uri: &'a str,
path: &'a str, path: &'a str,
query: Option<&'a str>,
segment_count: Cell<Option<usize>> segment_count: Cell<Option<usize>>
} }
impl<'a> URI<'a> { 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 { URI {
segment_count: Cell::new(None), 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 { pub fn as_str(&self) -> &'a str {
self.path self.uri
} }
} }
impl<'a> fmt::Display for URI<'a> { impl<'a> fmt::Display for URI<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut last = '\0'; let mut last = '\0';
for c in self.path.chars() { for c in self.uri.chars() {
if !(c == '/' && last == '/') { f.write_char(c)?; } if !(c == '/' && last == '/') { f.write_char(c)?; }
last = c; last = c;
} }
@ -50,7 +61,7 @@ unsafe impl<'a> Sync for URI<'a> { /* It's safe! */ }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct URIBuf { pub struct URIBuf {
path: String, uri: String,
segment_count: Cell<Option<usize>> segment_count: Cell<Option<usize>>
} }
@ -65,25 +76,25 @@ impl URIBuf {
} }
pub fn segments(&self) -> Segments { pub fn segments(&self) -> Segments {
Segments(self.path.as_str()) self.as_uri_uncached().segments()
} }
fn as_uri_uncached(&self) -> URI { fn as_uri_uncached(&self) -> URI {
URI::new(self.path.as_str()) URI::new(self.uri.as_str())
} }
pub fn as_uri(&self) -> URI { 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.segment_count = self.segment_count.clone();
uri uri
} }
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
self.path.as_str() self.uri.as_str()
} }
pub fn to_string(&self) -> String { 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 { impl From<String> for URIBuf {
fn from(path: String) -> URIBuf { fn from(uri: String) -> URIBuf {
URIBuf { URIBuf {
segment_count: Cell::new(None), segment_count: Cell::new(None),
path: path, uri: uri,
} }
} }
} }
impl<'a> From<&'a str> for URIBuf { impl<'a> From<&'a str> for URIBuf {
fn from(path: &'a str) -> URIBuf { fn from(uri: &'a str) -> URIBuf {
URIBuf { URIBuf {
segment_count: Cell::new(None), segment_count: Cell::new(None),
path: path.to_string(), uri: uri.to_string(),
} }
} }
} }

View File

@ -17,17 +17,15 @@ struct Params {
} }
fn get_error_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params { fn get_error_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
assert_meta_item_list(ecx, meta_item, "error"); // 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(...)]");
// Ensure we can unwrap the k = v params.
let params = meta_item.node.get_list_items().unwrap();
// Now grab all of the required and optional parameters. // Now grab all of the required and optional parameters.
let req: [&'static str; 1] = ["code"]; let req: [&'static str; 1] = ["code"];
let kv_pairs = get_key_values(ecx, meta_item.span, &req, &[], &*params); let kv_pairs = get_key_values(ecx, meta_item.span, &req, &[], &*params);
// Ensure we have a code, just to keep parsing and generating errors. // 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() { let numeric_code = match c.node.parse() {
Ok(n) => n, Ok(n) => n,
Err(_) => { Err(_) => {

View File

@ -2,13 +2,10 @@ use super::{ROUTE_STRUCT_PREFIX, ROUTE_FN_PREFIX};
use utils::*; use utils::*;
use std::str::FromStr; 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::codemap::{Span, BytePos, /* DUMMY_SP, */ Spanned};
use syntax::tokenstream::TokenTree; use syntax::ast::{Stmt, Item, Expr, ItemKind, MetaItem, MetaItemKind, FnDecl};
use syntax::ast::{Ident, PatKind, Stmt};
use syntax::ast::{Item, Expr, ItemKind, MetaItem, MetaItemKind, FnDecl, Ty};
use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ptr::P; use syntax::ptr::P;
use syntax::print::pprust::{item_to_string, stmt_to_string}; 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)) (item, wrap_span(&*fn_decl, item.span))
} }
fn get_route_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params { // Parses the MetaItem derived from the route(...) macro.
// First, check that the macro was used in the #[route(a, b, ..)] form. fn parse_route(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
assert_meta_item_list(ecx, meta_item, "error"); // 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(...)]");
// Ensure we can unwrap the k = v params.
let params = meta_item.node.get_list_items().unwrap();
if params.len() < 1 { if params.len() < 1 {
bad_method_err(ecx, meta_item.span, "HTTP method parameter is missing."); bad_method_err(ecx, meta_item.span, "HTTP method parameter is missing.");
ecx.span_fatal(meta_item.span, "At least 2 arguments are required."); 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); 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. // 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) s.clone().map(String::from)
}); });
@ -107,7 +102,7 @@ fn get_route_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
.emit(); .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"); 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? // TODO: Put something like this in the library. Maybe as an iterator?
pub fn gen_params_hashset<'a>(ecx: &ExtCtxt, params: &Spanned<&'a str>) pub fn extract_params<'a>(ecx: &ExtCtxt, params: &Spanned<&'a str>)
-> HashSet<&'a str> { -> Vec<Spanned<&'a str>> {
let mut seen = HashSet::new(); let mut output_params = vec![];
let bad_match_err = "Parameter string is malformed."; let bad_match_err = "Parameter string is malformed.";
let mut start = 0; let mut start = 0;
@ -163,13 +140,7 @@ pub fn gen_params_hashset<'a>(ecx: &ExtCtxt, params: &Spanned<&'a str>)
if i > start + 1 { if i > start + 1 {
let param_name = &params.node[(start + 1)..i]; let param_name = &params.node[(start + 1)..i];
if seen.contains(param_name) { output_params.push(wrap_span(param_name, param_span))
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);
} else { } else {
ecx.span_err(param_span, "Parameter names cannot be empty."); ecx.span_err(param_span, "Parameter names cannot be empty.");
} }
@ -180,95 +151,52 @@ pub fn gen_params_hashset<'a>(ecx: &ExtCtxt, params: &Spanned<&'a str>)
} }
} }
seen output_params
} }
#[derive(Debug)] pub fn extract_params_from_kv<'a>(ecx: &ExtCtxt, params: &'a KVSpanned<String>)
struct SimpleArg { -> Vec<Spanned<&'a str>> {
name: String,
ty: P<Ty>,
span: Span
}
pub fn gen_kv_string_hashset<'a>(ecx: &ExtCtxt, params: &'a KVSpanned<String>)
-> HashSet<&'a str> {
let mut param_span = params.v_span; let mut param_span = params.v_span;
param_span.lo = params.v_span.lo + BytePos(1); param_span.lo = params.v_span.lo + BytePos(1);
let params = Spanned { extract_params(ecx, &Spanned {
span: param_span, span: param_span,
node: &*params.node node: &*params.node
}; })
gen_params_hashset(ecx, &params)
} }
impl SimpleArg { fn get_fn_params<'a, T: Iterator<Item=&'a Spanned<&'a str>>>(ecx: &ExtCtxt,
fn new<T: ToString>(name: T, ty: P<Ty>, sp: Span) -> SimpleArg { declared_params: T, fn_decl: &Spanned<&FnDecl>) -> Vec<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> {
debug!("FUNCTION: {:?}", fn_decl); 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 // First, check that all of the parameters are unique.
// params. If there are, get rid of one of them so we don't double error. let mut seen: HashMap<&str, &Spanned<&str>> = HashMap::new();
let new_external = external.clone(); for item in declared_params {
for param in path_params.intersection(&new_external) { if seen.contains_key(item.node) {
let msg = format!("'{}' appears as a parameter more than once.", param); let msg = format!(
external.remove(param); "\"{}\" was declared as a parameter more than once.", item.node);
ecx.span_err(dec_span, msg.as_str()); 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 // Ensure every param in the function declaration was declared by the user.
// each param name in the declaration to the result vector.
let mut result = vec![]; let mut result = vec![];
for arg in &fn_decl.node.inputs { for arg in &fn_decl.node.inputs {
let ident: &Ident = match arg.pat.node { let name = arg.pat.expect_ident(ecx, "Expected identifier.");
PatKind::Ident(_, ref ident, _) => &ident.node, if seen.remove(&*name.to_string()).is_none() {
_ => { let msg = format!("'{}' appears in the function declaration \
ecx.span_err(arg.pat.span, "Expected an identifier."); but does not appear as a parameter in the attribute.", name);
return result ecx.span_err(arg.pat.span, msg.as_str());
}
};
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());
} }
result.push(SimpleArg::new(name, arg.ty.clone(), arg.pat.span)); result.push(SimpleArg::new(name, arg.ty.clone(), arg.pat.span));
} }
// Ensure every param in `path` and `exclude` is in the function declaration. // Ensure every declared parameter is in the function declaration.
for item in path_params { for item in seen.values() {
let msg = format!("'{}' appears in the path string...", item); let msg = format!("'{}' was declared in the attribute...", item.node);
ecx.span_err(path.v_span, msg.as_str()); ecx.span_err(item.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 \ ecx.span_err(fn_decl.span, "...but does not appear in the function \
declaration."); declaration.");
} }
@ -277,40 +205,37 @@ fn get_fn_params<'a>(ecx: &ExtCtxt, dec_span: Span, path: &'a KVSpanned<String>,
} }
fn get_form_stmt(ecx: &ExtCtxt, fn_args: &mut Vec<SimpleArg>, 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 { if form_params.len() < 1 {
return None return None
} else if form_params.len() > 1 { } else if form_params.len() > 1 {
panic!("Allowed more than 1 form parameter!"); panic!("Allowed more than 1 form parameter!");
} }
let param_name = &form_params[0].node;
let param_ty; let (param_ty, param_ident) = {
let param_ident;
let param_name = form_params.iter().next().unwrap();
{
// Get the first item in the hashset, i.e., the form params variable name. // 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(); let fn_arg = fn_args.iter().filter(|a| &&*a.name == param_name).next();
if fn_arg.is_none() { if fn_arg.is_none() {
// This happens when a form parameter doesn't appear in the function. // This happens when a form parameter doesn't appear in the function.
// We should have already caught this, so just return None.
return None; return None;
} }
param_ty = fn_arg.unwrap().ty.clone(); (fn_arg.unwrap().ty.clone(), str_to_ident(param_name))
param_ident = str_to_ident(param_name); };
}
// Remove the paramter from the function arguments.
debug!("Form parameter variable: {}: {:?}", param_name, param_ty); debug!("Form parameter variable: {}: {:?}", param_name, param_ty);
let fn_arg_index = fn_args.iter().position(|a| &&*a.name == param_name).unwrap(); let fn_arg_index = fn_args.iter().position(|a| &&*a.name == param_name).unwrap();
fn_args.remove(fn_arg_index); fn_args.remove(fn_arg_index);
// The actual code we'll be inserting.
quote_stmt!(ecx, 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 $param_ident: $param_ty = {
let form_string = std::str::from_utf8(_req.data); let form_string = std::str::from_utf8(_req.data);
if form_string.is_err() { 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()) { 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! // FIXME: Compilation fails when parameters have the same name as the function!
pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem, pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
annotated: &Annotatable, push: &mut FnMut(Annotatable)) { 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 (item, fn_decl) = get_fn_decl(ecx, sp, annotated);
let route_params = get_route_params(ecx, meta_item);
// TODO: move this elsewhere // Parse and retrieve all of the parameters of the route.
let mut external_params = HashSet::new(); let route = parse_route(ecx, meta_item);
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);
}
let mut fn_params = get_fn_params(ecx, sp, &route_params.path, &fn_decl, // Get a list of the user declared parameters in `path` and `form`.
external_params.clone()); 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 // Create a comma seperated list (token tree) of the function parameters
// We pass this in to the user's function that we're wrapping. // We pass this in to the user's function that we're wrapping.
let fn_param_idents = token_separate(ecx, &fn_params, token::Comma); let fn_param_idents = token_separate(ecx, &fn_params, token::Comma);
// Generate the statements that will attempt to parse forms during run-time. // 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()); // Calling this function also remove the form parameter from fn_params.
let form_stmt = get_form_stmt(ecx, &mut fn_params, &form_param_hashset); 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))); form_stmt.as_ref().map(|s| debug!("Form stmt: {:?}", stmt_to_string(s)));
// Generate the statements that will attempt to parse the paramaters during // 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)); push(Annotatable::Item(route_fn_item));
let struct_name = prepend_ident(ROUTE_STRUCT_PREFIX, &item.ident); let struct_name = prepend_ident(ROUTE_STRUCT_PREFIX, &item.ident);
let path = route_params.path.node; let path = &route.path.node;
let method = method_variant_to_expr(ecx, route_params.method.node); let method = method_variant_to_expr(ecx, route.method.node);
push(Annotatable::Item(quote_item!(ecx, push(Annotatable::Item(quote_item!(ecx,
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
pub static $struct_name: rocket::StaticRouteInfo = rocket::StaticRouteInfo { pub static $struct_name: rocket::StaticRouteInfo = rocket::StaticRouteInfo {

View File

@ -1,7 +1,7 @@
use syntax::parse::{token}; use syntax::parse::{token};
use syntax::parse::token::Token; use syntax::parse::token::Token;
use syntax::tokenstream::TokenTree; 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::ext::base::{ExtCtxt};
use syntax::codemap::{Span, Spanned, BytePos, DUMMY_SP}; use syntax::codemap::{Span, Spanned, BytePos, DUMMY_SP};
use syntax::ext::quote::rt::ToTokens; use syntax::ext::quote::rt::ToTokens;
@ -50,22 +50,30 @@ pub fn dummy_span<T>(t: T) -> Spanned<T> {
} }
} }
#[inline] #[derive(Debug, Clone)]
pub fn dummy_kvspan<T>(t: T) -> KVSpanned<T> { 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 { KVSpanned {
k_span: DUMMY_SP, k_span: DUMMY_SP,
v_span: DUMMY_SP, v_span: DUMMY_SP,
p_span: DUMMY_SP, p_span: DUMMY_SP,
node: t, node: t,
} }
}
} }
#[derive(Debug, Clone)] impl<T: Default> Default for KVSpanned<T> {
pub struct KVSpanned<T> { fn default() -> KVSpanned<T> {
pub k_span: Span, KVSpanned::dummy(T::default())
pub v_span: Span, }
pub p_span: Span,
pub node: T
} }
impl<T: ToTokens> ToTokens for KVSpanned<T> { impl<T: ToTokens> ToTokens for KVSpanned<T> {
@ -147,27 +155,38 @@ pub fn token_separate<T: ToTokens>(ecx: &ExtCtxt, things: &[T],
output 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 { pub trait MetaItemExt {
fn is_list(&self) -> bool; fn expect_list<'a>(&'a self, ecx: &ExtCtxt, msg: &str) -> &'a Vec<P<MetaItem>>;
fn get_list_items(&self) -> Option<&Vec<P<MetaItem>>>; fn expect_word<'a>(&'a self, ecx: &ExtCtxt, msg: &str) -> &'a str;
} }
impl MetaItemExt for MetaItemKind { impl MetaItemExt for MetaItem {
fn is_list(&self) -> bool { fn expect_list<'a>(&'a self, ecx: &ExtCtxt, msg: &str) -> &'a Vec<P<MetaItem>> {
self.get_list_items().is_some() match self.node {
MetaItemKind::List(_, ref params) => params,
_ => ecx.span_fatal(self.span, msg)
}
} }
fn get_list_items(&self) -> Option<&Vec<P<MetaItem>>> { fn expect_word<'a>(&'a self, ecx: &ExtCtxt, msg: &str) -> &'a str {
match *self { match self.node {
MetaItemKind::List(_, ref params) => Some(params), MetaItemKind::Word(ref s) => &*s,
_ => None _ => 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> { #[derive(Debug)]
// for param in kv_params { pub struct SimpleArg {
// if let MetaItemKind::NameValue(ref name, ref value) = param.node { pub name: String,
// if &**name == key { pub ty: P<Ty>,
// if let LitKind::Str(ref string, _) = value.node { pub span: Span
// return Some(String::from(&**string)); }
// }
// } 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
// }