Allow error handlers to take 0, 1, or 2 parameters.

fixes #13
This commit is contained in:
Sergio Benitez 2016-10-04 14:38:47 -07:00
parent 63d31e8082
commit d4f9525b22
5 changed files with 92 additions and 8 deletions

View File

@ -2,10 +2,50 @@ use utils::*;
use ::{CATCH_STRUCT_PREFIX, CATCH_FN_PREFIX};
use syntax::codemap::{Span};
use syntax::ast::{MetaItem};
use syntax::ast::{MetaItem, Ident, TyKind};
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::tokenstream::TokenTree;
use syntax::parse::token::{self, str_to_ident};
use parser::ErrorParams;
const ERR_PARAM: &'static str = "_error";
const REQ_PARAM: &'static str = "_request";
trait ErrorGenerateExt {
fn generate_fn_arguments(&self, &ExtCtxt, Ident, Ident) -> Vec<TokenTree>;
}
impl ErrorGenerateExt for ErrorParams {
fn generate_fn_arguments(&self, ecx: &ExtCtxt, err: Ident, req: Ident)
-> Vec<TokenTree> {
let arg_help = "error handlers can take either a `rocket::Error` or \
`rocket::Request` type, or both.";
// Retrieve the params from the user's handler and check the number.
let input_args = &self.annotated_fn.decl().inputs;
if input_args.len() > 2 {
let sp = self.annotated_fn.span();
ecx.struct_span_err(sp, "error handlers can have at most 2 arguments")
.help(arg_help).emit()
}
// (Imperfectly) inspect the types to figure which params to pass in.
let args = input_args.iter().map(|arg| &arg.ty).filter_map(|ty| {
match ty.node {
TyKind::Rptr(..) => Some(req.clone()),
TyKind::Path(..) => Some(err.clone()),
_ => {
ecx.struct_span_err(ty.span, "unexpected error handler argument")
.help(arg_help).emit();
None
}
}
}).collect::<Vec<_>>();
sep_by_tok(ecx, &args, token::Comma)
}
}
pub fn error_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
annotated: &Annotatable, push: &mut FnMut(Annotatable)) {
let error = ErrorParams::from(ecx, sp, meta_item, annotated);
@ -13,11 +53,14 @@ pub fn error_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
let user_fn_name = error.annotated_fn.ident();
let catch_fn_name = user_fn_name.prepend(CATCH_FN_PREFIX);
let code = error.code.node;
let (err_ident, req_ident) = (str_to_ident(ERR_PARAM), str_to_ident(REQ_PARAM));
let fn_arguments = error.generate_fn_arguments(ecx, err_ident, req_ident);
emit_item(push, quote_item!(ecx,
fn $catch_fn_name<'rocket>(err: ::rocket::Error,
req: &'rocket ::rocket::Request<'rocket>)
fn $catch_fn_name<'rocket>($err_ident: ::rocket::Error,
$req_ident: &'rocket ::rocket::Request<'rocket>)
-> ::rocket::Response<'rocket> {
let result = $user_fn_name(err, req);
let result = $user_fn_name($fn_arguments);
rocket::Response::with_raw_status($code, result)
}
).expect("catch function"));

View File

@ -0,0 +1,18 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
use rocket::{Error, Request};
#[error(404)]
fn err_a(_a: Error, _b: Request, _c: Error) -> &'static str { "hi" }
//~^ ERROR: can have at most 2
#[error(404)]
fn err_b(_a: (isize, usize)) -> &'static str { "hi" }
//~^ ERROR: unexpected error handler argument
fn main() {
}

View File

@ -0,0 +1,24 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
use rocket::{Error, Request};
#[error(404)]
fn err0() -> &'static str { "hi" }
#[error(404)]
fn err1a(_err: Error) -> &'static str { "hi" }
#[error(404)]
fn err1b(_req: &Request) -> &'static str { "hi" }
#[error(404)]
fn err2(_err: Error, _req: &Request) -> &'static str { "hi" }
fn main() {
rocket::ignite()
.catch(errors![err0, err1a, err1b, err2]);
}

View File

@ -2,7 +2,6 @@
#![plugin(rocket_codegen)]
extern crate rocket;
use rocket::{Error, Request};
#[get("/hello/<name>/<age>")]
fn hello(name: &str, age: i8) -> String {
@ -10,10 +9,10 @@ fn hello(name: &str, age: i8) -> String {
}
#[error(404)]
fn not_found<'r>(_error: Error, request: &'r Request<'r>) -> String {
fn not_found(req: &rocket::Request) -> String {
format!("<p>Sorry, but '{}' is not a valid path!</p>
<p>Try visiting /hello/&lt;name&gt;/&lt;age&gt; instead.</p>",
request.uri)
req.uri)
}
fn main() {

View File

@ -2,7 +2,7 @@
pub enum Error {
BadMethod,
BadParse,
NoRoute, // FIXME: Add a chain of routes attempted.
NoRoute, // TODO: Add a chain of routes attempted.
Internal,
NoKey,
}