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