rocket_codegen/attribute/catch/
mod.rs1mod parse;
2
3use devise::ext::SpanDiagnosticExt;
4use devise::{Spanned, Result};
5use proc_macro2::{TokenStream, Span};
6
7use crate::http_codegen::Optional;
8use crate::syn_ext::ReturnTypeExt;
9use crate::exports::*;
10
11pub fn _catch(
12 args: proc_macro::TokenStream,
13 input: proc_macro::TokenStream
14) -> Result<TokenStream> {
15 let catch = parse::Attribute::parse(args.into(), input)?;
17
18 let user_catcher_fn = &catch.function;
20 let user_catcher_fn_name = &catch.function.sig.ident;
21 let vis = &catch.function.vis;
22 let status_code = Optional(catch.status.map(|s| s.code));
23 let deprecated = catch.function.attrs.iter().find(|a| a.path().is_ident("deprecated"));
24
25 if catch.function.sig.inputs.len() > 2 {
27 return Err(catch.function.sig.paren_token.span.join()
28 .error("invalid number of arguments: must be zero, one, or two")
29 .help("catchers optionally take `&Request` or `Status, &Request`"));
30 }
31
32 let return_type_span = catch.function.sig.output.ty()
34 .map(|ty| ty.span())
35 .unwrap_or_else(Span::call_site);
36
37 let codegen_args = &[__req, __status];
40 let inputs = catch.function.sig.inputs.iter().rev()
41 .zip(codegen_args.iter())
42 .map(|(fn_arg, codegen_arg)| match fn_arg {
43 syn::FnArg::Receiver(_) => codegen_arg.respanned(fn_arg.span()),
44 syn::FnArg::Typed(a) => codegen_arg.respanned(a.ty.span())
45 }).rev();
46
47 let dot_await = catch.function.sig.asyncness
49 .map(|a| quote_spanned!(a.span() => .await));
50
51 let catcher_response = quote_spanned!(return_type_span => {
52 let ___responder = #user_catcher_fn_name(#(#inputs),*) #dot_await;
53 #_response::Responder::respond_to(___responder, #__req)?
54 });
55
56 Ok(quote! {
58 #user_catcher_fn
59
60 #[doc(hidden)]
61 #[allow(nonstandard_style)]
62 #deprecated #vis struct #user_catcher_fn_name { }
64
65 #[allow(nonstandard_style, deprecated, clippy::style)]
67 impl #user_catcher_fn_name {
68 fn into_info(self) -> #_catcher::StaticInfo {
69 fn monomorphized_function<'__r>(
70 #__status: #Status,
71 #__req: &'__r #Request<'_>
72 ) -> #_catcher::BoxFuture<'__r> {
73 #_Box::pin(async move {
74 let __response = #catcher_response;
75 #Response::build()
76 .status(#__status)
77 .merge(__response)
78 .ok()
79 })
80 }
81
82 #_catcher::StaticInfo {
83 name: stringify!(#user_catcher_fn_name),
84 code: #status_code,
85 handler: monomorphized_function,
86 }
87 }
88
89 #[doc(hidden)]
90 pub fn into_catcher(self) -> #Catcher {
91 self.into_info().into()
92 }
93 }
94 })
95}
96
97pub fn catch_attribute(
98 args: proc_macro::TokenStream,
99 input: proc_macro::TokenStream
100) -> TokenStream {
101 _catch(args, input).unwrap_or_else(|d| d.emit_as_item_tokens())
102}