mirror of https://github.com/rwf2/Rocket.git
Major progress towards form support.
This commit is contained in:
parent
fb8fdc3bc2
commit
d477c18062
|
@ -0,0 +1,13 @@
|
|||
use rocket;
|
||||
use std::fs::File;
|
||||
use std::io::Error as IOError;
|
||||
|
||||
#[route(GET, path = "/")]
|
||||
pub fn index() -> File {
|
||||
File::open("static/index.html").unwrap()
|
||||
}
|
||||
|
||||
#[route(GET, path = "/<file>")]
|
||||
pub fn files(file: &str) -> Result<File, IOError> {
|
||||
File::open(format!("static/{}", file))
|
||||
}
|
|
@ -3,37 +3,51 @@
|
|||
|
||||
extern crate rocket;
|
||||
|
||||
mod files;
|
||||
|
||||
use rocket::Rocket;
|
||||
use std::fs::File;
|
||||
use std::io::Error as IOError;
|
||||
use rocket::response::Redirect;
|
||||
|
||||
#[route(GET, path = "/")]
|
||||
fn index() -> File {
|
||||
File::open("static/index.html").unwrap()
|
||||
}
|
||||
|
||||
#[route(GET, path = "/<file>")]
|
||||
fn files(file: &str) -> Result<File, IOError> {
|
||||
File::open(format!("static/{}", file))
|
||||
}
|
||||
use rocket::error::Error;
|
||||
|
||||
#[route(GET, path = "/user/<username>")]
|
||||
fn user_page(username: &str) -> String {
|
||||
format!("This is {}'s page.", username)
|
||||
}
|
||||
|
||||
// #[derive(FormItem)] // FIXME: Make that happen.
|
||||
struct UserLogin<'a> {
|
||||
username: &'a str,
|
||||
password: &'a str
|
||||
}
|
||||
|
||||
trait FormItem: Sized {
|
||||
fn from_form_string(s: &str) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
impl<'a> FormItem for UserLogin<'a> {
|
||||
fn from_form_string(s: &str) -> Result<Self, Error> {
|
||||
Ok(UserLogin {
|
||||
username: "this",
|
||||
password: "that"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Actually look at form parameters.
|
||||
#[route(POST, path = "/login")]
|
||||
fn login() -> Result<Redirect, &'static str> {
|
||||
if true {
|
||||
Ok(Redirect::other("/user/some_name"))
|
||||
} else {
|
||||
Err("Sorry, the username and password are invalid.")
|
||||
// FIXME: fn login<'a>(user: UserLogin<'a>)
|
||||
#[route(POST, path = "/login", form = "<user>")]
|
||||
fn login(user: UserLogin) -> Result<Redirect, String> {
|
||||
match user.username {
|
||||
"Sergio" => match user.password {
|
||||
"password" => Ok(Redirect::other("/user/some_name")),
|
||||
_ => Err("Wrong password!".to_string())
|
||||
},
|
||||
_ => Err(format!("Unrecognized user, '{}'.", user.username))
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rocket = Rocket::new("localhost", 8000);
|
||||
rocket.mount_and_launch("/", routes![index, files, user_page, login]);
|
||||
let mut rocket = Rocket::new("localhost", 8000);
|
||||
rocket.mount("/", routes![files::index, files::files, user_page, login]);
|
||||
rocket.launch();
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ extern crate rocket;
|
|||
use rocket::Rocket;
|
||||
|
||||
#[route(GET, path = "/users/<name>")]
|
||||
fn user(name: &str) -> Option<&'static str> {
|
||||
fn user(name: &str, other: i8) -> Option<&'static str> {
|
||||
if name == "Sergio" {
|
||||
Some("Hello, Sergio!")
|
||||
} else {
|
||||
|
|
|
@ -19,6 +19,7 @@ pub use router::Router;
|
|||
pub use response::{Response, HypResponse, Responder, HypFresh};
|
||||
|
||||
use std::fmt;
|
||||
use std::io::Read;
|
||||
use term_painter::ToStyle;
|
||||
use term_painter::Color::*;
|
||||
use hyper::uri::RequestUri;
|
||||
|
@ -60,12 +61,16 @@ pub struct Rocket {
|
|||
}
|
||||
|
||||
impl HypHandler for Rocket {
|
||||
fn handle<'a, 'k>(&'a self, req: HypRequest<'a, 'k>,
|
||||
fn handle<'a, 'k>(&'a self, mut req: HypRequest<'a, 'k>,
|
||||
res: HypResponse<'a, HypFresh>) {
|
||||
println!("{} {:?} {:?}", White.paint("Incoming:"),
|
||||
Green.paint(&req.method), Blue.paint(&req.uri));
|
||||
let mut buf = vec![];
|
||||
req.read_to_end(&mut buf); // FIXME: Simple DOS attack here.
|
||||
|
||||
if let RequestUri::AbsolutePath(uri_string) = req.uri {
|
||||
if let Some(method) = Method::from_hyp(req.method) {
|
||||
|
||||
let uri_str = uri_string.as_str();
|
||||
let route = self.router.route(method, uri_str);
|
||||
let mut response = route.map_or(Response::not_found(), |route| {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use super::{STRUCT_PREFIX, FN_PREFIX};
|
||||
use utils::{prepend_ident, get_key_values};
|
||||
use utils::*;
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use syntax::ext::quote::rt::ToTokens;
|
||||
use syntax::codemap::{Span, DUMMY_SP};
|
||||
use syntax::ast::{Ident, TokenTree, PatKind};
|
||||
use syntax::ast::{Item, Expr, ItemKind, MetaItem, MetaItemKind, FnDecl};
|
||||
use syntax::codemap::{Span, BytePos, DUMMY_SP, Spanned};
|
||||
use syntax::ast::{self, Ident, TokenTree, PatKind, Stmt};
|
||||
use syntax::ast::{Item, Expr, ItemKind, MetaItem, MetaItemKind, FnDecl, Ty};
|
||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::ptr::P;
|
||||
use syntax::ext::build::AstBuilder;
|
||||
|
@ -20,8 +20,9 @@ use rocket::Method;
|
|||
const DEBUG: bool = true;
|
||||
|
||||
struct Params {
|
||||
method: Method,
|
||||
path: String
|
||||
method: Spanned<Method>,
|
||||
path: KVSpanned<String>,
|
||||
form: Option<KVSpanned<String>>,
|
||||
}
|
||||
|
||||
fn bad_item_fatal(ecx: &mut ExtCtxt, dec_sp: Span, i_sp: Span) -> ! {
|
||||
|
@ -37,7 +38,7 @@ fn bad_method_err(ecx: &mut ExtCtxt, dec_sp: Span, message: &str) -> Method {
|
|||
}
|
||||
|
||||
fn get_fn_decl<'a>(ecx: &mut ExtCtxt, sp: Span, annotated: &'a Annotatable)
|
||||
-> (&'a P<Item>, &'a P<FnDecl>) {
|
||||
-> (&'a P<Item>, Spanned<&'a FnDecl>) {
|
||||
// `annotated` is the AST object for the annotated item.
|
||||
let item: &P<Item> = match annotated {
|
||||
&Annotatable::Item(ref item) => item,
|
||||
|
@ -50,7 +51,7 @@ fn get_fn_decl<'a>(ecx: &mut ExtCtxt, sp: Span, annotated: &'a Annotatable)
|
|||
_ => bad_item_fatal(ecx, sp, item.span)
|
||||
};
|
||||
|
||||
(item, fn_decl)
|
||||
(item, wrap_span(&*fn_decl, item.span))
|
||||
}
|
||||
|
||||
fn get_route_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
|
||||
|
@ -67,32 +68,59 @@ fn get_route_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
|
|||
ecx.span_fatal(meta_item.span, "At least 2 arguments are required.");
|
||||
}
|
||||
|
||||
// Get the method and the rest of the k = v params. Ensure method parameter
|
||||
// is valid. If it's not, issue an error but use "GET" to continue parsing.
|
||||
// Get the method and the rest of the k = v params.
|
||||
let (method_param, kv_params) = params.split_first().unwrap();
|
||||
|
||||
// Ensure method parameter is valid. If it's not, issue an error but use
|
||||
// "GET" to continue parsing. method :: Spanned<Method>.
|
||||
let method = if let MetaItemKind::Word(ref word) = method_param.node {
|
||||
Method::from_str(word).unwrap_or_else(|_| {
|
||||
let method = Method::from_str(word).unwrap_or_else(|_| {
|
||||
let message = format!("{} is not a valid method.", word);
|
||||
bad_method_err(ecx, method_param.span, message.as_str())
|
||||
})
|
||||
});
|
||||
|
||||
Spanned { span: method_param.span, node: method }
|
||||
} else {
|
||||
bad_method_err(ecx, method_param.span, "Invalid parameter. Expected a
|
||||
valid HTTP method at this position.")
|
||||
let method = bad_method_err(ecx, method_param.span, "Invalid parameter. \
|
||||
Expected a valid HTTP method at this position.");
|
||||
dummy_span(method)
|
||||
};
|
||||
|
||||
// Now grab all of the required and optional parameters.
|
||||
let req: [&'static str; 1] = ["path"];
|
||||
let opt: [&'static str; 0] = [];
|
||||
let opt: [&'static str; 1] = ["form"];
|
||||
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(String::from("/"), |s| {
|
||||
String::from(*s)
|
||||
let path = kv_pairs.get("path").map_or(dummy_kvspan("/".to_string()), |s| {
|
||||
s.clone().map(|str_string| String::from(str_string))
|
||||
});
|
||||
|
||||
// If there's a form parameter, ensure method is POST.
|
||||
let form = kv_pairs.get("form").map_or(None, |f| {
|
||||
if method.node != Method::Post {
|
||||
ecx.span_err(f.p_span, "Use of `form` requires a POST method...");
|
||||
let message = format!("...but {} was found instead.", method.node);
|
||||
ecx.span_err(method_param.span, message.as_str());
|
||||
}
|
||||
|
||||
if !(f.node.starts_with('<') && f.node.ends_with('>')) {
|
||||
ecx.struct_span_err(f.p_span, "`form` cannot contain arbitrary text")
|
||||
.help("`form` must be exactly one parameter: \"<param>\"")
|
||||
.emit();
|
||||
}
|
||||
|
||||
if f.node.chars().filter(|c| *c == '<').count() != 1 {
|
||||
ecx.span_err(f.p_span, "`form` must contain exactly one parameter");
|
||||
}
|
||||
|
||||
Some(f.clone().map(|str_string| String::from(str_string)))
|
||||
});
|
||||
|
||||
Params {
|
||||
method: method,
|
||||
path: path
|
||||
path: path,
|
||||
form: form
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,18 +143,15 @@ fn method_variant_to_expr(ecx: &ExtCtxt, method: Method) -> P<Expr> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_fn_params(ecx: &ExtCtxt, sp: Span, path: &str,
|
||||
fn_decl: &FnDecl) -> Vec<String> {
|
||||
debug!("FUNCTION: {:?}", fn_decl);
|
||||
|
||||
// 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();
|
||||
let bad_match_err = "Path string is malformed.";
|
||||
let mut matching = false;
|
||||
let bad_match_err = "Parameter string is malformed.";
|
||||
|
||||
// Collect all of the params in the path and insert into HashSet.
|
||||
// TODO: Move this logic into main library.
|
||||
let mut start = 0;
|
||||
for (i, c) in path.char_indices() {
|
||||
let mut matching = false;
|
||||
for (i, c) in params.node.char_indices() {
|
||||
match c {
|
||||
'<' if !matching => {
|
||||
matching = true;
|
||||
|
@ -134,75 +159,177 @@ pub fn get_fn_params(ecx: &ExtCtxt, sp: Span, path: &str,
|
|||
},
|
||||
'>' if matching => {
|
||||
matching = false;
|
||||
if start + 1 < i {
|
||||
let param_name = &path[(start + 1)..i];
|
||||
|
||||
let mut param_span = params.span.clone();
|
||||
param_span.lo = params.span.lo + BytePos(start as u32);
|
||||
param_span.hi = params.span.lo + BytePos((i + 1) as u32);
|
||||
|
||||
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);
|
||||
} else {
|
||||
ecx.span_err(sp, "Parameter cannot be empty.");
|
||||
ecx.span_err(param_span, "Parameter names cannot be empty.");
|
||||
}
|
||||
},
|
||||
'<' if matching => ecx.span_err(sp, bad_match_err),
|
||||
'>' if !matching => ecx.span_err(sp, bad_match_err),
|
||||
'<' if matching => ecx.span_err(params.span, bad_match_err),
|
||||
'>' if !matching => ecx.span_err(params.span, bad_match_err),
|
||||
_ => { /* ... */ }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should stay here, though.
|
||||
seen
|
||||
}
|
||||
|
||||
#[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> {
|
||||
let mut param_span = params.v_span.clone();
|
||||
param_span.lo = params.v_span.lo + BytePos(1);
|
||||
let params = 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()
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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());
|
||||
}
|
||||
|
||||
// Ensure every param in the function declaration is in `path`. Also add
|
||||
// each param name in the declaration to the result vector.
|
||||
let mut result = vec![];
|
||||
for arg in &fn_decl.inputs {
|
||||
for arg in &fn_decl.node.inputs {
|
||||
let ident: &Ident = match arg.pat.node {
|
||||
PatKind::Ident(_, ref ident, _) => &ident.node,
|
||||
_ => {
|
||||
ecx.span_err(sp, "Expected an identifier."); // FIXME: fn span.
|
||||
ecx.span_err(arg.pat.span, "Expected an identifier.");
|
||||
return result
|
||||
}
|
||||
};
|
||||
|
||||
let name = ident.to_string();
|
||||
if !seen.remove(name.as_str()) {
|
||||
let msg = format!("'{}' appears in the function declaration but \
|
||||
not in the path string.", name);
|
||||
ecx.span_err(sp, msg.as_str());
|
||||
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(name);
|
||||
result.push(SimpleArg::new(name, arg.ty.clone(), arg.pat.span.clone()));
|
||||
}
|
||||
|
||||
// Ensure every param in `path` is in the function declaration.
|
||||
for item in seen {
|
||||
let msg = format!("'{}' appears in the path string but not in the \
|
||||
function declaration.", item);
|
||||
ecx.span_err(sp, msg.as_str());
|
||||
// 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.");
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn get_form_stmt(ecx: &ExtCtxt, fn_args: &mut Vec<SimpleArg>,
|
||||
form_params: &HashSet<&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();
|
||||
|
||||
{
|
||||
// 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.
|
||||
return None;
|
||||
}
|
||||
|
||||
param_ty = fn_arg.unwrap().ty.clone();
|
||||
param_ident = str_to_ident(param_name);
|
||||
}
|
||||
|
||||
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);
|
||||
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 = match FormItem::from_form_string("test string") {
|
||||
Ok(v) => v,
|
||||
Err(_) => return rocket::Response::not_found()
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
let (item, fn_decl) = get_fn_decl(ecx, sp, annotated);
|
||||
let route_params = get_route_params(ecx, meta_item);
|
||||
let fn_params = get_fn_params(ecx, sp, &route_params.path, &fn_decl);
|
||||
|
||||
debug!("Path: {:?}", route_params.path);
|
||||
debug!("Function Declaration: {:?}", fn_decl);
|
||||
|
||||
let mut fn_param_exprs = vec![];
|
||||
for (i, param) in fn_params.iter().enumerate() {
|
||||
let param_ident = str_to_ident(param.as_str());
|
||||
let param_fn_item = quote_stmt!(ecx,
|
||||
let $param_ident = match _req.get_param($i) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return rocket::Response::not_found()
|
||||
};
|
||||
).unwrap();
|
||||
|
||||
debug!("Param FN: {:?}", stmt_to_string(¶m_fn_item));
|
||||
fn_param_exprs.push(param_fn_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);
|
||||
}
|
||||
|
||||
let mut fn_params = get_fn_params(ecx, sp, &route_params.path, &fn_decl,
|
||||
external_params.clone());
|
||||
|
||||
// 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 mut fn_param_idents: Vec<TokenTree> = vec![];
|
||||
for i in 0..fn_params.len() {
|
||||
let tokens = str_to_ident(fn_params[i].as_str()).to_tokens(ecx);
|
||||
|
@ -212,11 +339,34 @@ pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
|||
}
|
||||
}
|
||||
|
||||
// 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, &mut form_param_hashset);
|
||||
form_stmt.as_ref().map(|s| debug!("Form stmt: {:?}", stmt_to_string(s)));
|
||||
|
||||
// Generate the statements that will attempt to parse the paramaters during
|
||||
// run-time.
|
||||
let mut fn_param_exprs = vec![];
|
||||
for (i, param) in fn_params.iter().enumerate() {
|
||||
let param_ident = str_to_ident(param.as_str());
|
||||
let param_ty = ¶m.ty;
|
||||
let param_fn_item = quote_stmt!(ecx,
|
||||
let $param_ident: $param_ty = match _req.get_param($i) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return rocket::Response::not_found()
|
||||
};
|
||||
).unwrap();
|
||||
|
||||
debug!("Param FN: {:?}", stmt_to_string(¶m_fn_item));
|
||||
fn_param_exprs.push(param_fn_item);
|
||||
}
|
||||
|
||||
debug!("Final Params: {:?}", fn_params);
|
||||
let route_fn_name = prepend_ident(FN_PREFIX, &item.ident);
|
||||
let fn_name = item.ident;
|
||||
let route_fn_item = quote_item!(ecx,
|
||||
fn $route_fn_name<'a>(_req: rocket::Request) -> rocket::Response<'a> {
|
||||
fn $route_fn_name<'rocket>(_req: rocket::Request) -> rocket::Response<'rocket> {
|
||||
$form_stmt
|
||||
$fn_param_exprs
|
||||
let result = $fn_name($fn_param_idents);
|
||||
rocket::Response::new(result)
|
||||
|
@ -227,8 +377,8 @@ pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
|||
push(Annotatable::Item(route_fn_item));
|
||||
|
||||
let struct_name = prepend_ident(STRUCT_PREFIX, &item.ident);
|
||||
let path = route_params.path;
|
||||
let method = method_variant_to_expr(ecx, route_params.method);
|
||||
let path = route_params.path.node;
|
||||
let method = method_variant_to_expr(ecx, route_params.method.node);
|
||||
push(Annotatable::Item(quote_item!(ecx,
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static $struct_name: rocket::Route = rocket::Route {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use syntax::parse::{token};
|
||||
use syntax::ast::{Ident, MetaItem, MetaItemKind, LitKind};
|
||||
use syntax::ast::{Ident, MetaItem, MetaItemKind, LitKind, TokenTree};
|
||||
use syntax::ext::base::{ExtCtxt};
|
||||
use syntax::codemap::Span;
|
||||
use syntax::codemap::{Span, Spanned, BytePos, DUMMY_SP};
|
||||
use syntax::ext::quote::rt::ToTokens;
|
||||
use syntax::ptr::P;
|
||||
|
||||
use std::collections::{HashSet, HashMap};
|
||||
|
@ -20,6 +21,7 @@ macro_rules! debug {
|
|||
if DEBUG {
|
||||
println!("{}:{}", file!(), line!());
|
||||
println!($($message)*);
|
||||
println!("");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -37,9 +39,60 @@ pub fn append_ident<T: ToString>(ident: &Ident, other: T) -> Ident {
|
|||
token::str_to_ident(new_ident.as_str())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn wrap_span<T>(t: T, span: Span) -> Spanned<T> {
|
||||
Spanned {
|
||||
span: span,
|
||||
node: t,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn dummy_span<T>(t: T) -> Spanned<T> {
|
||||
Spanned {
|
||||
span: DUMMY_SP,
|
||||
node: 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,
|
||||
pub v_span: Span,
|
||||
pub p_span: Span,
|
||||
pub node: T
|
||||
}
|
||||
|
||||
impl<T: ToTokens> ToTokens for KVSpanned<T> {
|
||||
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
|
||||
self.node.to_tokens(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> KVSpanned<T> {
|
||||
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> KVSpanned<U> {
|
||||
KVSpanned {
|
||||
k_span: self.k_span,
|
||||
v_span: self.v_span,
|
||||
p_span: self.p_span,
|
||||
node: f(self.node),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_key_values<'b>(ecx: &mut ExtCtxt, sp: Span, required: &[&str],
|
||||
optional: &[&str], kv_params: &'b [P<MetaItem>])
|
||||
-> HashMap<&'b str, &'b str> {
|
||||
-> HashMap<&'b str, KVSpanned<&'b str>> {
|
||||
let mut seen = HashSet::new();
|
||||
let mut kv_pairs = HashMap::new();
|
||||
|
||||
|
@ -48,16 +101,23 @@ pub fn get_key_values<'b>(ecx: &mut ExtCtxt, sp: Span, required: &[&str],
|
|||
if let MetaItemKind::NameValue(ref name, ref value) = param.node {
|
||||
if required.contains(&&**name) || optional.contains(&&**name) {
|
||||
if seen.contains(&**name) {
|
||||
let msg = format!("'{}' cannot be set twice.", &**name);
|
||||
let msg = format!("'{}' parameter appears twice.", &**name);
|
||||
ecx.span_err(param.span, &msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
seen.insert(&**name);
|
||||
if let LitKind::Str(ref string, _) = value.node {
|
||||
kv_pairs.insert(&**name, &**string);
|
||||
let mut k_span = param.span;
|
||||
k_span.hi = k_span.lo + BytePos(name.len() as u32);
|
||||
kv_pairs.insert(&**name, KVSpanned {
|
||||
node: &**string,
|
||||
k_span: k_span,
|
||||
p_span: param.span,
|
||||
v_span: value.span,
|
||||
});
|
||||
} else {
|
||||
ecx.span_err(param.span, "Value must be a string.");
|
||||
ecx.span_err(value.span, "Value must be a string.");
|
||||
}
|
||||
} else {
|
||||
let msg = format!("'{}' is not a valid parameter.", &**name);
|
||||
|
|
Loading…
Reference in New Issue