sails_macros_core/event/
mod.rs

1use crate::sails_paths::sails_path_or_default;
2use args::{CratePathAttr, SAILS_PATH};
3use parity_scale_codec::Encode;
4use proc_macro_error::abort;
5use proc_macro2::TokenStream;
6use quote::quote;
7use syn::{Fields, ItemEnum, Path, parse::Parse};
8
9mod args;
10#[cfg(feature = "ethexe")]
11mod ethexe;
12
13pub fn event(attrs: TokenStream, input: TokenStream) -> TokenStream {
14    // Parse the input tokens into a syntax tree.
15    #[cfg_attr(not(feature = "ethexe"), allow(unused_mut))]
16    let mut input: ItemEnum = syn::parse2(input).unwrap_or_else(|err| {
17        abort!(
18            err.span(),
19            "`event` attribute can be applied to enums only: {}",
20            err
21        )
22    });
23
24    // Parse the attributes into a syntax tree.
25    let sails_path_attr = syn::parse2::<CratePathAttr>(attrs).ok();
26    let sails_path = &sails_path_or_default(sails_path_attr.map(|attr| attr.path()));
27
28    let event_impl = generate_sails_event_impl(&input, sails_path);
29
30    #[cfg(feature = "ethexe")]
31    let eth_event_impl = ethexe::generate_eth_event_impl(&input, sails_path);
32    #[cfg(feature = "ethexe")]
33    ethexe::process_indexed(&mut input);
34    #[cfg(not(feature = "ethexe"))]
35    let eth_event_impl = quote!();
36
37    quote! {
38        #input
39
40        #event_impl
41
42        #eth_event_impl
43    }
44}
45
46fn generate_sails_event_impl(input: &ItemEnum, sails_path: &Path) -> TokenStream {
47    // Parse the input enum
48    let enum_ident = &input.ident;
49    let variants = &input.variants;
50    // Check that the enum has at most 256 variants
51    if variants.len() > 256 {
52        abort!(
53            input,
54            "`event` enum can have at most 256 variants, but found {}",
55            variants.len()
56        )
57    }
58
59    // Build match arms for each variant
60    let mut match_arms = Vec::new();
61
62    for variant in variants {
63        let variant_ident = &variant.ident;
64        // Determine the pattern to match this variant, ignoring its fields:
65        let pattern = match &variant.fields {
66            Fields::Unit => {
67                // Unit variant: `Enum::Variant`
68                quote! { #enum_ident::#variant_ident }
69            }
70            Fields::Unnamed(_) => {
71                // Tuple variant: `Enum::Variant(..)`
72                quote! { #enum_ident::#variant_ident ( .. ) }
73            }
74            Fields::Named(_) => {
75                // Struct variant: `Enum::Variant { .. }`
76                quote! { #enum_ident::#variant_ident { .. } }
77            }
78        };
79        // Encode the variant identifier as a sequence of u8
80        let encoded_name = variant_ident.to_string().encode();
81
82        // Build the match arm: pattern => &[ ... ],
83        let arm = quote! {
84            #pattern => &[ #( #encoded_name ),* ]
85        };
86        match_arms.push(arm);
87    }
88
89    // Generate the impl block for `Event`
90    quote! {
91         impl #sails_path::SailsEvent for #enum_ident {
92            fn encoded_event_name(&self) -> &'static [u8] {
93                match self {
94                    #( #match_arms ),*
95                }
96            }
97
98            fn skip_bytes() -> usize {
99                1 // The first byte is reserved for the index of the event enum variant
100            }
101        }
102    }
103}
104
105pub fn derive_sails_event(input: TokenStream) -> TokenStream {
106    // Parse the input tokens into a syntax tree.
107    let input: ItemEnum = syn::parse2(input).unwrap_or_else(|err| {
108        abort!(
109            err.span(),
110            "`SailsEvent` can only be derived for enums: {}",
111            err
112        )
113    });
114
115    let sails_path_attr = input
116        .attrs
117        .iter()
118        .find(|attr| attr.path().is_ident(SAILS_PATH))
119        .map(|attr| {
120            attr.parse_args_with(CratePathAttr::parse)
121                .unwrap_or_else(|_| abort!(attr, "unexpected value for `crate` argument",))
122        });
123    let sails_path = &sails_path_or_default(sails_path_attr.map(|attr| attr.path()));
124
125    generate_sails_event_impl(&input, sails_path)
126}