use ::{CATCH_STRUCT_PREFIX, CATCH_FN_PREFIX, CATCHER_ATTR};
use parser::CatchParams;
use utils::*;
use syntax::source_map::{Span};
use syntax::ast::{MetaItem, Ident, TyKind};
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::tokenstream::TokenTree;
use syntax::parse::token;
const ERR_PARAM: &'static str = "__err";
const REQ_PARAM: &'static str = "__req";
trait CatchGenerateExt {
fn generate_fn_arguments(&self, &ExtCtxt, Ident, Ident) -> Vec<TokenTree>;
}
impl CatchGenerateExt for CatchParams {
fn generate_fn_arguments(&self, ecx: &ExtCtxt, err: Ident, req: Ident)
-> Vec<TokenTree> {
let arg_help = "error catchers can take either a `rocket::Error`, \
`rocket::Request` type, or both.";
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 catchers can have at most 2 arguments")
.help(arg_help).emit()
}
let args = input_args.iter().map(|arg| &arg.ty).filter_map(|ty| {
match ty.node {
TyKind::Rptr(..) => Some(req),
TyKind::Path(..) => Some(err),
_ => {
ecx.struct_span_err(ty.span, "unknown error catcher argument")
.help(arg_help)
.emit();
None
}
}
}).collect::<Vec<_>>();
sep_by_tok(ecx, &args, token::Comma)
}
}
pub fn catch_decorator(
ecx: &mut ExtCtxt,
sp: Span,
meta_item: &MetaItem,
annotated: Annotatable
) -> Vec<Annotatable> {
let mut output = Vec::new();
let catch = CatchParams::from(ecx, sp, meta_item, &annotated);
let user_fn_name = catch.annotated_fn.ident();
let catch_fn_name = user_fn_name.prepend(CATCH_FN_PREFIX);
let code = catch.code.node;
let err_ident = Ident::from_str(ERR_PARAM);
let req_ident = Ident::from_str(REQ_PARAM);
let fn_arguments = catch.generate_fn_arguments(ecx, err_ident, req_ident);
emit_item(&mut output, quote_item!(ecx,
fn $catch_fn_name<'_b>($err_ident: ::rocket::Error,
$req_ident: &'_b ::rocket::Request)
-> ::rocket::response::Result<'_b> {
let user_response = $user_fn_name($fn_arguments);
let response = ::rocket::response::Responder::respond_to(user_response,
$req_ident)?;
let status = ::rocket::http::Status::raw($code);
::rocket::response::Response::build().status(status).merge(response).ok()
}
).expect("catch function"));
let struct_name = user_fn_name.prepend(CATCH_STRUCT_PREFIX);
emit_item(&mut output, quote_item!(ecx,
#[allow(non_upper_case_globals)]
pub static $struct_name: ::rocket::StaticCatchInfo =
::rocket::StaticCatchInfo {
code: $code,
handler: $catch_fn_name
};
).expect("catch info struct"));
let attr_name = Ident::from_str(CATCHER_ATTR);
let catcher_attr = quote_attr!(ecx, #[$attr_name($struct_name)]);
attach_and_emit(&mut output, catcher_attr, annotated);
output
}
pub fn error_decorator(
ecx: &mut ExtCtxt,
sp: Span,
meta_item: &MetaItem,
annotated: Annotatable
) -> Vec<Annotatable> {
ecx.struct_span_warn(sp, "use of deprecated Rocket attribute `error` \
(deprecated since v0.3.15)")
.help("the `error` attribute was replaced by the `catch` attribute: \
`#[catch(..)]`")
.emit();
catch_decorator(ecx, sp, meta_item, annotated)
}