croncat_errors_macro/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Error, Ident, Variant};
6
7#[proc_macro_attribute]
8pub fn croncat_error(_attrs: TokenStream, input: TokenStream) -> TokenStream {
9    let mut input = parse_macro_input!(input as DeriveInput);
10    let enum_name = input.ident.clone();
11
12    // Ensure they've placed the attribute macro above the derive
13    if !input.attrs.iter().any(|attr| attr.path.is_ident("derive")) {
14        let msg = "😻 Please move the `#[croncat_error]` macro above the `#[derive]` attribute with the `Error` trait. 😻";
15        let error = Error::new_spanned(enum_name, msg).to_compile_error();
16        return TokenStream::from(quote! {
17            #error
18        });
19    }
20
21    let mut is_croncat_error_present = false;
22    if let Data::Enum(DataEnum {
23        ref mut variants, ..
24    }) = input.data
25    {
26        // See if they already have CronCatError variant
27        for variant in variants.iter() {
28            if variant.ident == Ident::new("CronCatError", variant.ident.span()) {
29                is_croncat_error_present = true;
30            }
31        }
32
33        if !is_croncat_error_present {
34            // Add the CronCat variant, which looks like:
35            // #[error("CronCat error: {err:?}")]
36            // CronCatError {
37            //   err: CronCatContractError
38            // }
39            let croncat_error_variant: Variant = syn::parse_quote! {
40                #[error("CronCat error: {err:?}")]
41                CronCatError {
42                    err: croncat_integration_utils::error::CronCatContractError,
43                }
44            };
45            variants.push(croncat_error_variant);
46        }
47    }
48
49    // Add an impl for error propagation. Looks like:
50    // impl From<CronCatContractError> for ContractError {
51    //   fn from(error: CronCatContractError) -> Self {
52    //     ContractError::CronCatError {
53    //       err: error,
54    //     }
55    //   }
56    // }
57    let expanded = quote! {
58        #input
59
60        impl From<croncat_integration_utils::error::CronCatContractError> for #enum_name {
61            fn from(error: croncat_integration_utils::error::CronCatContractError) -> Self {
62                #enum_name::CronCatError {
63                    err: error,
64                }
65            }
66        }
67    };
68
69    TokenStream::from(expanded)
70}