mirror of https://github.com/rwf2/Rocket.git
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:
parent
bceb1ecfb6
commit
95a8a51b76
|
@ -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
|
|
||||||
```
|
|
||||||
|
|
|
@ -6,10 +6,9 @@ extern crate lazy_static;
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
extern crate tera;
|
extern crate tera;
|
||||||
|
|
||||||
mod static_files;
|
|
||||||
|
|
||||||
use rocket::Rocket;
|
use rocket::Rocket;
|
||||||
use rocket::response::{Cookied, Redirect};
|
use rocket::response::{Cookied, Redirect};
|
||||||
|
use rocket::Method;
|
||||||
|
|
||||||
lazy_static!(static ref TERA: tera::Tera = tera::Tera::new("templates/**/*"););
|
lazy_static!(static ref TERA: tera::Tera = tera::Tera::new("templates/**/*"););
|
||||||
|
|
||||||
|
@ -31,13 +30,13 @@ fn submit(message: Message) -> Cookied<Redirect> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[route(GET, path = "/")]
|
#[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))
|
TERA.render("index.html", ctxt(None))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut rocket = Rocket::new("127.0.0.1", 8000);
|
let mut rocket = Rocket::new("127.0.0.1", 8000);
|
||||||
rocket.mount("/", static_files::routes());
|
|
||||||
rocket.mount("/", routes![submit, index]);
|
rocket.mount("/", routes![submit, index]);
|
||||||
rocket.launch();
|
rocket.launch();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]
|
|
||||||
}
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
@ -2,8 +2,6 @@ use error::Error;
|
||||||
use param::FromParam;
|
use param::FromParam;
|
||||||
use method::Method;
|
use method::Method;
|
||||||
|
|
||||||
pub use hyper::server::Request as HyperRequest;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Request<'a> {
|
pub struct Request<'a> {
|
||||||
params: Option<Vec<&'a str>>,
|
params: Option<Vec<&'a str>>,
|
|
@ -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,
|
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);
|
debug!("FUNCTION: {:?}", fn_decl);
|
||||||
|
|
||||||
// First, check that all of the parameters are unique.
|
// 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.
|
// Ensure every param in the function declaration was declared by the user.
|
||||||
let mut result = vec![];
|
|
||||||
for arg in &fn_decl.node.inputs {
|
for arg in &fn_decl.node.inputs {
|
||||||
let name = arg.pat.expect_ident(ecx, "Expected identifier.");
|
let name = arg.pat.expect_ident(ecx, "Expected identifier.");
|
||||||
if seen.remove(&*name.to_string()).is_none() {
|
let arg = SimpleArg::new(name, arg.ty.clone(), arg.pat.span);
|
||||||
let msg = format!("'{}' appears in the function declaration \
|
if seen.remove(&*name.to_string()).is_some() {
|
||||||
but does not appear as a parameter in the attribute.", name);
|
user_params.push(UserParam::new(arg, true));
|
||||||
ecx.span_err(arg.pat.span, msg.as_str());
|
} else {
|
||||||
|
user_params.push(UserParam::new(arg, false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push(SimpleArg::new(name, arg.ty.clone(), arg.pat.span));
|
// Emit an error on every attribute param that didn't match in fn params.
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure every declared parameter is in the function declaration.
|
|
||||||
for item in seen.values() {
|
for item in seen.values() {
|
||||||
let msg = format!("'{}' was declared in the attribute...", item.node);
|
let msg = format!("'{}' was declared in the attribute...", item.node);
|
||||||
ecx.span_err(item.span, msg.as_str());
|
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.");
|
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> {
|
form_params: &[Spanned<&str>]) -> Option<Stmt> {
|
||||||
if form_params.len() < 1 {
|
if form_params.len() < 1 {
|
||||||
return None;
|
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.
|
// Ensure the params match the function declaration and return the params.
|
||||||
let all_params = path_params.iter().chain(form_params.iter());
|
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
|
// 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, &user_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.
|
||||||
// Calling this function also remove the form parameter from fn_params.
|
// 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)));
|
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
|
||||||
// run-time.
|
// run-time.
|
||||||
let mut fn_param_exprs = vec![];
|
let mut fn_param_exprs = vec![];
|
||||||
for (i, param) in fn_params.iter().enumerate() {
|
for (i, param) in user_params.iter().enumerate() {
|
||||||
let param_ident = str_to_ident(param.as_str());
|
let ident = str_to_ident(param.as_str());
|
||||||
let param_ty = ¶m.ty;
|
let ty = ¶m.ty;
|
||||||
let param_fn_item = quote_stmt!(ecx,
|
let param_fn_item =
|
||||||
let $param_ident: $param_ty = match _req.get_param($i) {
|
if param.declared {
|
||||||
|
quote_stmt!(ecx,
|
||||||
|
let $ident: $ty = match _req.get_param($i) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(_) => return ::rocket::Response::forward()
|
Err(_) => return ::rocket::Response::forward()
|
||||||
};
|
};
|
||||||
).unwrap();
|
).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()
|
||||||
|
};
|
||||||
|
|
||||||
debug!("Param FN: {:?}", stmt_to_string(¶m_fn_item));
|
debug!("Param FN: {:?}", stmt_to_string(¶m_fn_item));
|
||||||
fn_param_exprs.push(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 route_fn_name = prepend_ident(ROUTE_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,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue