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;
|
extern crate rocket;
|
||||||
|
|
||||||
|
mod files;
|
||||||
|
|
||||||
use rocket::Rocket;
|
use rocket::Rocket;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Error as IOError;
|
|
||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
|
use rocket::error::Error;
|
||||||
#[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))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[route(GET, path = "/user/<username>")]
|
#[route(GET, path = "/user/<username>")]
|
||||||
fn user_page(username: &str) -> String {
|
fn user_page(username: &str) -> String {
|
||||||
format!("This is {}'s page.", username)
|
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.
|
// TODO: Actually look at form parameters.
|
||||||
#[route(POST, path = "/login")]
|
// FIXME: fn login<'a>(user: UserLogin<'a>)
|
||||||
fn login() -> Result<Redirect, &'static str> {
|
#[route(POST, path = "/login", form = "<user>")]
|
||||||
if true {
|
fn login(user: UserLogin) -> Result<Redirect, String> {
|
||||||
Ok(Redirect::other("/user/some_name"))
|
match user.username {
|
||||||
} else {
|
"Sergio" => match user.password {
|
||||||
Err("Sorry, the username and password are invalid.")
|
"password" => Ok(Redirect::other("/user/some_name")),
|
||||||
|
_ => Err("Wrong password!".to_string())
|
||||||
|
},
|
||||||
|
_ => Err(format!("Unrecognized user, '{}'.", user.username))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let rocket = Rocket::new("localhost", 8000);
|
let mut rocket = Rocket::new("localhost", 8000);
|
||||||
rocket.mount_and_launch("/", routes![index, files, user_page, login]);
|
rocket.mount("/", routes![files::index, files::files, user_page, login]);
|
||||||
|
rocket.launch();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ extern crate rocket;
|
||||||
use rocket::Rocket;
|
use rocket::Rocket;
|
||||||
|
|
||||||
#[route(GET, path = "/users/<name>")]
|
#[route(GET, path = "/users/<name>")]
|
||||||
fn user(name: &str) -> Option<&'static str> {
|
fn user(name: &str, other: i8) -> Option<&'static str> {
|
||||||
if name == "Sergio" {
|
if name == "Sergio" {
|
||||||
Some("Hello, Sergio!")
|
Some("Hello, Sergio!")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub use router::Router;
|
||||||
pub use response::{Response, HypResponse, Responder, HypFresh};
|
pub use response::{Response, HypResponse, Responder, HypFresh};
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::io::Read;
|
||||||
use term_painter::ToStyle;
|
use term_painter::ToStyle;
|
||||||
use term_painter::Color::*;
|
use term_painter::Color::*;
|
||||||
use hyper::uri::RequestUri;
|
use hyper::uri::RequestUri;
|
||||||
|
@ -60,12 +61,16 @@ pub struct Rocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HypHandler for 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>) {
|
res: HypResponse<'a, HypFresh>) {
|
||||||
println!("{} {:?} {:?}", White.paint("Incoming:"),
|
println!("{} {:?} {:?}", White.paint("Incoming:"),
|
||||||
Green.paint(&req.method), Blue.paint(&req.uri));
|
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 RequestUri::AbsolutePath(uri_string) = req.uri {
|
||||||
if let Some(method) = Method::from_hyp(req.method) {
|
if let Some(method) = Method::from_hyp(req.method) {
|
||||||
|
|
||||||
let uri_str = uri_string.as_str();
|
let uri_str = uri_string.as_str();
|
||||||
let route = self.router.route(method, uri_str);
|
let route = self.router.route(method, uri_str);
|
||||||
let mut response = route.map_or(Response::not_found(), |route| {
|
let mut response = route.map_or(Response::not_found(), |route| {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use super::{STRUCT_PREFIX, FN_PREFIX};
|
use super::{STRUCT_PREFIX, FN_PREFIX};
|
||||||
use utils::{prepend_ident, get_key_values};
|
use utils::*;
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use syntax::ext::quote::rt::ToTokens;
|
use syntax::ext::quote::rt::ToTokens;
|
||||||
use syntax::codemap::{Span, DUMMY_SP};
|
use syntax::codemap::{Span, BytePos, DUMMY_SP, Spanned};
|
||||||
use syntax::ast::{Ident, TokenTree, PatKind};
|
use syntax::ast::{self, Ident, TokenTree, PatKind, Stmt};
|
||||||
use syntax::ast::{Item, Expr, ItemKind, MetaItem, MetaItemKind, FnDecl};
|
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::ext::build::AstBuilder;
|
use syntax::ext::build::AstBuilder;
|
||||||
|
@ -20,8 +20,9 @@ use rocket::Method;
|
||||||
const DEBUG: bool = true;
|
const DEBUG: bool = true;
|
||||||
|
|
||||||
struct Params {
|
struct Params {
|
||||||
method: Method,
|
method: Spanned<Method>,
|
||||||
path: String
|
path: KVSpanned<String>,
|
||||||
|
form: Option<KVSpanned<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bad_item_fatal(ecx: &mut ExtCtxt, dec_sp: Span, i_sp: Span) -> ! {
|
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)
|
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.
|
// `annotated` is the AST object for the annotated item.
|
||||||
let item: &P<Item> = match annotated {
|
let item: &P<Item> = match annotated {
|
||||||
&Annotatable::Item(ref item) => item,
|
&Annotatable::Item(ref item) => item,
|
||||||
|
@ -46,11 +47,11 @@ fn get_fn_decl<'a>(ecx: &mut ExtCtxt, sp: Span, annotated: &'a Annotatable)
|
||||||
};
|
};
|
||||||
|
|
||||||
let fn_decl: &P<FnDecl> = match item.node {
|
let fn_decl: &P<FnDecl> = match item.node {
|
||||||
ItemKind::Fn(ref decl, _, _, _, _, _) => decl,
|
ItemKind::Fn(ref decl, _, _, _, _, _) => decl,
|
||||||
_ => bad_item_fatal(ecx, sp, item.span)
|
_ => 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 {
|
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.");
|
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
|
// Get the method and the rest of the k = v params.
|
||||||
// is valid. If it's not, issue an error but use "GET" to continue parsing.
|
|
||||||
let (method_param, kv_params) = params.split_first().unwrap();
|
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 {
|
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);
|
let message = format!("{} is not a valid method.", word);
|
||||||
bad_method_err(ecx, method_param.span, message.as_str())
|
bad_method_err(ecx, method_param.span, message.as_str())
|
||||||
})
|
});
|
||||||
|
|
||||||
|
Spanned { span: method_param.span, node: method }
|
||||||
} else {
|
} else {
|
||||||
bad_method_err(ecx, method_param.span, "Invalid parameter. Expected a
|
let method = bad_method_err(ecx, method_param.span, "Invalid parameter. \
|
||||||
valid HTTP method at this position.")
|
Expected a valid HTTP method at this position.");
|
||||||
|
dummy_span(method)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Now grab all of the required and optional parameters.
|
// Now grab all of the required and optional parameters.
|
||||||
let req: [&'static str; 1] = ["path"];
|
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);
|
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(String::from("/"), |s| {
|
let path = kv_pairs.get("path").map_or(dummy_kvspan("/".to_string()), |s| {
|
||||||
String::from(*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 {
|
Params {
|
||||||
method: method,
|
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,
|
// TODO: Put something like this in the library. Maybe as an iterator?
|
||||||
fn_decl: &FnDecl) -> Vec<String> {
|
pub fn gen_params_hashset<'a>(ecx: &ExtCtxt, params: &Spanned<&'a str>)
|
||||||
debug!("FUNCTION: {:?}", fn_decl);
|
-> HashSet<&'a str> {
|
||||||
|
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
let bad_match_err = "Path string is malformed.";
|
let bad_match_err = "Parameter string is malformed.";
|
||||||
let mut matching = false;
|
|
||||||
|
|
||||||
// Collect all of the params in the path and insert into HashSet.
|
|
||||||
// TODO: Move this logic into main library.
|
|
||||||
let mut start = 0;
|
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 {
|
match c {
|
||||||
'<' if !matching => {
|
'<' if !matching => {
|
||||||
matching = true;
|
matching = true;
|
||||||
|
@ -134,75 +159,177 @@ pub fn get_fn_params(ecx: &ExtCtxt, sp: Span, path: &str,
|
||||||
},
|
},
|
||||||
'>' if matching => {
|
'>' if matching => {
|
||||||
matching = false;
|
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);
|
seen.insert(param_name);
|
||||||
} else {
|
} 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(params.span, bad_match_err),
|
||||||
'>' if !matching => ecx.span_err(sp, 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
|
// Ensure every param in the function declaration is in `path`. Also add
|
||||||
// each param name in the declaration to the result vector.
|
// each param name in the declaration to the result vector.
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
for arg in &fn_decl.inputs {
|
for arg in &fn_decl.node.inputs {
|
||||||
let ident: &Ident = match arg.pat.node {
|
let ident: &Ident = match arg.pat.node {
|
||||||
PatKind::Ident(_, ref ident, _) => &ident.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
|
return result
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = ident.to_string();
|
let name = ident.to_string();
|
||||||
if !seen.remove(name.as_str()) {
|
if !path_params.remove(name.as_str()) && !external.remove(name.as_str()) {
|
||||||
let msg = format!("'{}' appears in the function declaration but \
|
let msg1 = format!("'{}' appears in the function declaration...", name);
|
||||||
not in the path string.", name);
|
let msg2 = format!("...but does not appear as a parameter \
|
||||||
ecx.span_err(sp, msg.as_str());
|
(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.
|
// Ensure every param in `path` and `exclude` is in the function declaration.
|
||||||
for item in seen {
|
for item in path_params {
|
||||||
let msg = format!("'{}' appears in the path string but not in the \
|
let msg = format!("'{}' appears in the path string...", item);
|
||||||
function declaration.", item);
|
ecx.span_err(path.v_span, msg.as_str());
|
||||||
ecx.span_err(sp, 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
|
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,
|
pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
||||||
annotated: &Annotatable, push: &mut FnMut(Annotatable)) {
|
annotated: &Annotatable, push: &mut FnMut(Annotatable)) {
|
||||||
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);
|
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);
|
// TODO: move this elsewhere
|
||||||
debug!("Function Declaration: {:?}", fn_decl);
|
let mut external_params = HashSet::new();
|
||||||
|
let mut form_param_hashset = HashSet::new();
|
||||||
let mut fn_param_exprs = vec![];
|
if let Some(ref form) = route_params.form {
|
||||||
for (i, param) in fn_params.iter().enumerate() {
|
form_param_hashset = gen_kv_string_hashset(ecx, form);
|
||||||
let param_ident = str_to_ident(param.as_str());
|
external_params.extend(&form_param_hashset);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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![];
|
let mut fn_param_idents: Vec<TokenTree> = vec![];
|
||||||
for i in 0..fn_params.len() {
|
for i in 0..fn_params.len() {
|
||||||
let tokens = str_to_ident(fn_params[i].as_str()).to_tokens(ecx);
|
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);
|
debug!("Final Params: {:?}", fn_params);
|
||||||
let route_fn_name = prepend_ident(FN_PREFIX, &item.ident);
|
let route_fn_name = prepend_ident(FN_PREFIX, &item.ident);
|
||||||
let fn_name = item.ident;
|
let fn_name = item.ident;
|
||||||
let route_fn_item = quote_item!(ecx,
|
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
|
$fn_param_exprs
|
||||||
let result = $fn_name($fn_param_idents);
|
let result = $fn_name($fn_param_idents);
|
||||||
rocket::Response::new(result)
|
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));
|
push(Annotatable::Item(route_fn_item));
|
||||||
|
|
||||||
let struct_name = prepend_ident(STRUCT_PREFIX, &item.ident);
|
let struct_name = prepend_ident(STRUCT_PREFIX, &item.ident);
|
||||||
let path = route_params.path;
|
let path = route_params.path.node;
|
||||||
let method = method_variant_to_expr(ecx, route_params.method);
|
let method = method_variant_to_expr(ecx, route_params.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::Route = rocket::Route {
|
pub static $struct_name: rocket::Route = rocket::Route {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use syntax::parse::{token};
|
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::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 syntax::ptr::P;
|
||||||
|
|
||||||
use std::collections::{HashSet, HashMap};
|
use std::collections::{HashSet, HashMap};
|
||||||
|
@ -20,6 +21,7 @@ macro_rules! debug {
|
||||||
if DEBUG {
|
if DEBUG {
|
||||||
println!("{}:{}", file!(), line!());
|
println!("{}:{}", file!(), line!());
|
||||||
println!($($message)*);
|
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())
|
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],
|
pub fn get_key_values<'b>(ecx: &mut ExtCtxt, sp: Span, required: &[&str],
|
||||||
optional: &[&str], kv_params: &'b [P<MetaItem>])
|
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 seen = HashSet::new();
|
||||||
let mut kv_pairs = HashMap::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 let MetaItemKind::NameValue(ref name, ref value) = param.node {
|
||||||
if required.contains(&&**name) || optional.contains(&&**name) {
|
if required.contains(&&**name) || optional.contains(&&**name) {
|
||||||
if seen.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);
|
ecx.span_err(param.span, &msg);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
seen.insert(&**name);
|
seen.insert(&**name);
|
||||||
if let LitKind::Str(ref string, _) = value.node {
|
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 {
|
} else {
|
||||||
ecx.span_err(param.span, "Value must be a string.");
|
ecx.span_err(value.span, "Value must be a string.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let msg = format!("'{}' is not a valid parameter.", &**name);
|
let msg = format!("'{}' is not a valid parameter.", &**name);
|
||||||
|
|
Loading…
Reference in New Issue