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;
|
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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(_) => {
|
||||||
|
|
|
@ -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 = ¶ms.node[(start + 1)..i];
|
let param_name = ¶ms.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, ¶ms)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
|
|
@ -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
|
|
||||||
// }
|
|
||||||
|
|
Loading…
Reference in New Issue