snafu_derive/
report.rs

1use quote::quote;
2use syn::{spanned::Spanned, Item, ItemFn, ReturnType, Signature};
3
4// In versions of Rust before 1.39, we can't use the `.await` keyword
5// in *our* source code, even if we never generate it in the *output*
6// source code. Hiding it behind a conditionally-compiled module
7// works.
8#[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}