Added FromRequest and modified macro to use it: any parameters not declared by the user in the attributes will automatically be retrieved using FromRequest.

This commit is contained in:
Sergio Benitez 2016-08-08 03:10:23 -07:00
parent bceb1ecfb6
commit 95a8a51b76
8 changed files with 129 additions and 64 deletions

View File

@ -1,16 +0,0 @@
Rocket Todo Example
===================
Before running this example, you'll need to ensure there's a database file
present. You can do this with Diesel.
Running migration with Diesel
-----------------------------
Just run the following commands in your shell:
```
cargo install diesel_cli # installs the diesel CLI tools
DATABASE_URL=db/db.sql diesel migration run # create db/db.sql
```

View File

@ -6,10 +6,9 @@ extern crate lazy_static;
extern crate rocket;
extern crate tera;
mod static_files;
use rocket::Rocket;
use rocket::response::{Cookied, Redirect};
use rocket::Method;
lazy_static!(static ref TERA: tera::Tera = tera::Tera::new("templates/**/*"););
@ -31,13 +30,13 @@ fn submit(message: Message) -> Cookied<Redirect> {
}
#[route(GET, path = "/")]
fn index() -> tera::TeraResult<String> {
fn index(method: Method) -> tera::TeraResult<String> {
println!("Method is: {}", method);
TERA.render("index.html", ctxt(None))
}
fn main() {
let mut rocket = Rocket::new("127.0.0.1", 8000);
rocket.mount("/", static_files::routes());
rocket.mount("/", routes![submit, index]);
rocket.launch();
}

View File

@ -1,19 +0,0 @@
use std::fs::File;
use std::io;
use rocket;
#[route(GET, path = "/<top>/<file>")]
fn all_level_one(top: &str, file: &str) -> io::Result<File> {
let file = format!("static/{}/{}", top, file);
File::open(file)
}
#[route(GET, path = "/<file>")]
fn all(file: &str) -> io::Result<File> {
let file = format!("static/{}", file);
File::open(file)
}
pub fn routes() -> Vec<rocket::Route> {
routes![all_level_one, all]
}

View File

@ -0,0 +1,46 @@
use request::*;
use method::Method;
use std::fmt::Debug;
pub trait FromRequest<'r, 'c>: Sized {
type Error: Debug;
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error>;
}
impl<'r, 'c> FromRequest<'r, 'c> for &'r Request<'c> {
type Error = ();
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
Ok(request)
}
}
impl<'r, 'c> FromRequest<'r, 'c> for Method {
type Error = &'static str;
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
Ok(request.method)
}
}
impl<'r, 'c, T: FromRequest<'r, 'c>> FromRequest<'r, 'c> for Option<T> {
type Error = ();
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
let opt = match T::from_request(request) {
Ok(v) => Some(v),
Err(_) => None
};
Ok(opt)
}
}
impl<'r, 'c, T: FromRequest<'r, 'c>> FromRequest<'r, 'c> for Result<T, T::Error> {
type Error = ();
fn from_request(request: &'r Request<'c>) -> Result<Self, Self::Error> {
Ok(T::from_request(request))
}
}

6
lib/src/request/mod.rs Normal file
View File

@ -0,0 +1,6 @@
mod request;
mod from_request;
pub use hyper::server::Request as HyperRequest;
pub use self::request::Request;
pub use self::from_request::FromRequest;

View File

@ -2,8 +2,6 @@ use error::Error;
use param::FromParam;
use method::Method;
pub use hyper::server::Request as HyperRequest;
#[derive(Clone, Debug)]
pub struct Request<'a> {
params: Option<Vec<&'a str>>,

View File

@ -163,8 +163,12 @@ pub fn extract_params_from_kv<'a>(ecx: &ExtCtxt, params: &'a KVSpanned<String>)
})
}
// Analyzes the declared parameters against the function declaration. Returns
// two vectors. The first is the set of parameters declared by the user, and
// the second is the set of parameters not declared by the user.
fn get_fn_params<'a, T: Iterator<Item=&'a Spanned<&'a str>>>(ecx: &ExtCtxt,
declared_params: T, fn_decl: &Spanned<&FnDecl>) -> Vec<SimpleArg> {
declared_params: T, fn_decl: &Spanned<&FnDecl>)
-> Vec<UserParam> {
debug!("FUNCTION: {:?}", fn_decl);
// First, check that all of the parameters are unique.
@ -179,20 +183,20 @@ fn get_fn_params<'a, T: Iterator<Item=&'a Spanned<&'a str>>>(ecx: &ExtCtxt,
}
}
let mut user_params = vec![];
// Ensure every param in the function declaration was declared by the user.
let mut result = vec![];
for arg in &fn_decl.node.inputs {
let name = arg.pat.expect_ident(ecx, "Expected identifier.");
if seen.remove(&*name.to_string()).is_none() {
let msg = format!("'{}' appears in the function declaration \
but does not appear as a parameter in the attribute.", name);
ecx.span_err(arg.pat.span, msg.as_str());
let arg = SimpleArg::new(name, arg.ty.clone(), arg.pat.span);
if seen.remove(&*name.to_string()).is_some() {
user_params.push(UserParam::new(arg, true));
} else {
user_params.push(UserParam::new(arg, false));
}
result.push(SimpleArg::new(name, arg.ty.clone(), arg.pat.span));
}
// Ensure every declared parameter is in the function declaration.
// Emit an error on every attribute param that didn't match in fn params.
for item in seen.values() {
let msg = format!("'{}' was declared in the attribute...", item.node);
ecx.span_err(item.span, msg.as_str());
@ -200,10 +204,10 @@ fn get_fn_params<'a, T: Iterator<Item=&'a Spanned<&'a str>>>(ecx: &ExtCtxt,
declaration.");
}
result
user_params
}
fn get_form_stmt(ecx: &ExtCtxt, fn_args: &mut Vec<SimpleArg>,
fn get_form_stmt(ecx: &ExtCtxt, fn_args: &mut Vec<UserParam>,
form_params: &[Spanned<&str>]) -> Option<Stmt> {
if form_params.len() < 1 {
return None;
@ -281,35 +285,52 @@ pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
// 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);
let mut user_params = get_fn_params(ecx, all_params, &fn_decl);
// Create a comma seperated list (token tree) of the function parameters
// We pass this in to the user's function that we're wrapping.
let fn_param_idents = token_separate(ecx, &fn_params, token::Comma);
let fn_param_idents = token_separate(ecx, &user_params, token::Comma);
// Generate the statements that will attempt to parse forms during run-time.
// Calling this function also remove the form parameter from fn_params.
let form_stmt = get_form_stmt(ecx, &mut fn_params, &form_params);
let form_stmt = get_form_stmt(ecx, &mut user_params, &form_params);
form_stmt.as_ref().map(|s| debug!("Form stmt: {:?}", stmt_to_string(s)));
// Generate the statements that will attempt to parse the paramaters during
// 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 = &param.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::forward()
for (i, param) in user_params.iter().enumerate() {
let ident = str_to_ident(param.as_str());
let ty = &param.ty;
let param_fn_item =
if param.declared {
quote_stmt!(ecx,
let $ident: $ty = match _req.get_param($i) {
Ok(v) => v,
Err(_) => return ::rocket::Response::forward()
};
).unwrap()
} else {
quote_stmt!(ecx,
let $ident: $ty = match
<$ty as ::rocket::request::FromRequest>::from_request(&_req) {
Ok(v) => v,
Err(e) => {
// TODO: Add $ident and $ty to the string.
// TODO: Add some kind of loggin facility in Rocket
// to get the formatting right (IE, so it idents
// correctly).
println!("Failed to parse: {:?}", e);
return ::rocket::Response::forward();
}
};
).unwrap()
};
).unwrap();
debug!("Param FN: {:?}", stmt_to_string(&param_fn_item));
fn_param_exprs.push(param_fn_item);
}
debug!("Final Params: {:?}", fn_params);
let route_fn_name = prepend_ident(ROUTE_FN_PREFIX, &item.ident);
let fn_name = item.ident;
let route_fn_item = quote_item!(ecx,

View File

@ -1,3 +1,5 @@
use std::ops::Deref;
use syntax::parse::{token};
use syntax::parse::token::Token;
use syntax::tokenstream::TokenTree;
@ -240,3 +242,31 @@ impl ToTokens for SimpleArg {
}
}
pub struct UserParam {
pub arg: SimpleArg,
pub declared: bool
}
impl UserParam {
pub fn new(arg: SimpleArg, declared: bool) -> UserParam {
UserParam {
arg: arg,
declared: declared
}
}
}
impl Deref for UserParam {
type Target = SimpleArg;
fn deref(&self) -> &SimpleArg {
&self.arg
}
}
impl ToTokens for UserParam {
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
self.arg.to_tokens(cx)
}
}