mod parse;
use devise::ext::SpanDiagnosticExt;
use devise::{Spanned, Result};
use proc_macro2::{TokenStream, Span};
use crate::http_codegen::Optional;
use crate::syn_ext::ReturnTypeExt;
use crate::exports::*;
pub fn _catch(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream
) -> Result<TokenStream> {
let catch = parse::Attribute::parse(args.into(), input)?;
let user_catcher_fn = &catch.function;
let user_catcher_fn_name = &catch.function.sig.ident;
let vis = &catch.function.vis;
let status_code = Optional(catch.status.map(|s| s.code));
let deprecated = catch.function.attrs.iter().find(|a| a.path().is_ident("deprecated"));
if catch.function.sig.inputs.len() > 2 {
return Err(catch.function.sig.paren_token.span.join()
.error("invalid number of arguments: must be zero, one, or two")
.help("catchers optionally take `&Request` or `Status, &Request`"));
}
let return_type_span = catch.function.sig.output.ty()
.map(|ty| ty.span())
.unwrap_or_else(Span::call_site);
let codegen_args = &[__req, __status];
let inputs = catch.function.sig.inputs.iter().rev()
.zip(codegen_args.iter())
.map(|(fn_arg, codegen_arg)| match fn_arg {
syn::FnArg::Receiver(_) => codegen_arg.respanned(fn_arg.span()),
syn::FnArg::Typed(a) => codegen_arg.respanned(a.ty.span())
}).rev();
let dot_await = catch.function.sig.asyncness
.map(|a| quote_spanned!(a.span() => .await));
let catcher_response = quote_spanned!(return_type_span => {
let ___responder = #user_catcher_fn_name(#(#inputs),*) #dot_await;
#_response::Responder::respond_to(___responder, #__req)?
});
Ok(quote! {
#user_catcher_fn
#[doc(hidden)]
#[allow(nonstandard_style)]
#deprecated #vis struct #user_catcher_fn_name { }
#[allow(nonstandard_style, deprecated, clippy::style)]
impl #user_catcher_fn_name {
fn into_info(self) -> #_catcher::StaticInfo {
fn monomorphized_function<'__r>(
#__status: #Status,
#__req: &'__r #Request<'_>
) -> #_catcher::BoxFuture<'__r> {
#_Box::pin(async move {
let __response = #catcher_response;
#Response::build()
.status(#__status)
.merge(__response)
.ok()
})
}
#_catcher::StaticInfo {
name: stringify!(#user_catcher_fn_name),
code: #status_code,
handler: monomorphized_function,
}
}
#[doc(hidden)]
pub fn into_catcher(self) -> #Catcher {
self.into_info().into()
}
}
})
}
pub fn catch_attribute(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream
) -> TokenStream {
_catch(args, input).unwrap_or_else(|d| d.emit_as_item_tokens())
}