alloy_sol_macro_expander/expand/
error.rs

1//! [`ItemError`] expansion.
2
3use super::{expand_fields, expand_from_into_tuples, expand_tokenize, ExpCtxt};
4use alloy_sol_macro_input::{mk_doc, ContainsSolAttrs};
5use ast::ItemError;
6use proc_macro2::TokenStream;
7use quote::quote;
8use syn::Result;
9
10/// Expands an [`ItemError`]:
11///
12/// ```ignore (pseudo-code)
13/// pub struct #name {
14///     #(pub #parameter_name: #parameter_type,)*
15/// }
16///
17/// impl SolError for #name {
18///     ...
19/// }
20/// ```
21pub(super) fn expand(cx: &ExpCtxt<'_>, error: &ItemError) -> Result<TokenStream> {
22    let ItemError { parameters: params, .. } = error;
23    cx.assert_resolved(params)?;
24
25    let (sol_attrs, mut attrs) = error.split_attrs()?;
26    cx.derives(&mut attrs, params, true);
27    let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true);
28    let abi = sol_attrs.abi.or(cx.attrs.abi).unwrap_or(false);
29
30    let tokenize_impl = expand_tokenize(params, cx);
31
32    let name = cx.overloaded_name(error.into());
33    let signature = cx.error_signature(error);
34    let selector = crate::utils::selector(&signature);
35
36    let alloy_sol_types = &cx.crates.sol_types;
37
38    let converts = expand_from_into_tuples(&name.0, params, cx);
39    let fields = expand_fields(params, cx);
40    let doc = docs.then(|| {
41        let selector = hex::encode_prefixed(selector.array.as_slice());
42        mk_doc(format!(
43            "Custom error with signature `{signature}` and selector `{selector}`.\n\
44             ```solidity\n{error}\n```"
45        ))
46    });
47    let abi: Option<TokenStream> = abi.then(|| {
48        if_json! {
49            let error = super::to_abi::generate(error, cx);
50            quote! {
51                #[automatically_derived]
52                impl alloy_sol_types::JsonAbiExt for #name {
53                    type Abi = alloy_sol_types::private::alloy_json_abi::Error;
54
55                    #[inline]
56                    fn abi() -> Self::Abi {
57                        #error
58                    }
59                }
60            }
61        }
62    });
63    let tokens = quote! {
64        #(#attrs)*
65        #doc
66        #[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields)]
67        #[derive(Clone)]
68        pub struct #name {
69            #(#fields),*
70        }
71
72        #[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields, clippy::style)]
73        const _: () = {
74            use #alloy_sol_types as alloy_sol_types;
75
76            #converts
77
78            #[automatically_derived]
79            impl alloy_sol_types::SolError for #name {
80                type Parameters<'a> = UnderlyingSolTuple<'a>;
81                type Token<'a> = <Self::Parameters<'a> as alloy_sol_types::SolType>::Token<'a>;
82
83                const SIGNATURE: &'static str = #signature;
84                const SELECTOR: [u8; 4] = #selector;
85
86                #[inline]
87                fn new<'a>(tuple: <Self::Parameters<'a> as alloy_sol_types::SolType>::RustType) -> Self {
88                    tuple.into()
89                }
90
91                #[inline]
92                fn tokenize(&self) -> Self::Token<'_> {
93                    #tokenize_impl
94                }
95            }
96
97            #abi
98        };
99    };
100    Ok(tokens)
101}