1use quote::quote;
2use syn::{spanned::Spanned, Item, ItemFn, ReturnType, Signature};
3
4#[cfg(not(feature = "rust_1_39"))]
9mod no_async;
10#[cfg(not(feature = "rust_1_39"))]
11use no_async::async_body;
12#[cfg(feature = "rust_1_39")]
13mod yes_async;
14#[cfg(feature = "rust_1_39")]
15use yes_async::async_body;
16
17pub fn body(
18 _attr: proc_macro::TokenStream,
19 item: proc_macro::TokenStream,
20) -> syn::Result<proc_macro2::TokenStream> {
21 let item = syn::parse::<Item>(item)?;
22
23 let f = match item {
24 Item::Fn(f) => f,
25 _ => {
26 return Err(syn::Error::new(
27 item.span(),
28 "`#[snafu::report]` may only be used on functions",
29 ))
30 }
31 };
32
33 let ItemFn {
34 attrs,
35 vis,
36 sig,
37 block,
38 } = f;
39
40 let Signature {
41 constness,
42 asyncness,
43 unsafety,
44 abi,
45 fn_token,
46 ident,
47 generics,
48 paren_token: _,
49 inputs,
50 variadic,
51 output,
52 } = sig;
53
54 let output_ty = match output {
55 ReturnType::Default => quote! { () },
56 ReturnType::Type(_, ty) => quote! { #ty },
57 };
58
59 let error_ty = quote! { <#output_ty as ::snafu::__InternalExtractErrorType>::Err };
60
61 let output = if cfg!(feature = "rust_1_61") {
62 quote! { -> ::snafu::Report<#error_ty> }
63 } else {
64 quote! { -> ::core::result::Result<(), ::snafu::Report<#error_ty>> }
65 };
66
67 let block = if asyncness.is_some() {
68 async_body(block)?
69 } else {
70 if cfg!(feature = "rust_1_61") {
71 quote! {
72 {
73 let __snafu_body = || #block;
74 <::snafu::Report<_> as ::core::convert::From<_>>::from(__snafu_body())
75 }
76 }
77 } else {
78 quote! {
79 {
80 let __snafu_body = || #block;
81 ::core::result::Result::map_err(__snafu_body(), ::snafu::Report::from_error)
82 }
83 }
84 }
85 };
86
87 Ok(quote! {
88 #(#attrs)*
89 #vis
90 #constness
91 #asyncness
92 #unsafety
93 #abi
94 #fn_token
95 #ident
96 #generics
97 (#inputs #variadic)
98 #output
99 #block
100 })
101}