oasis_macros/
event_derive.rs

1#[proc_macro_derive(Event, attributes(indexed))]
2pub fn event_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
3    let input = parse_macro_input!(input as syn::DeriveInput);
4    let event_name = &input.ident;
5    let generics = &input.generics;
6
7    let fields = match input.data {
8        syn::Data::Struct(syn::DataStruct { fields, .. }) => fields,
9        _ => {
10            err!(input: "an `Event` must be a struct.");
11            return proc_macro::TokenStream::new();
12        }
13    };
14
15    fn is_indexed(field: &syn::Field) -> bool {
16        field.attrs.iter().any(|attr| attr.path.is_ident("indexed"))
17    }
18
19    let indexed_field_idents = match fields {
20        syn::Fields::Named(syn::FieldsNamed { named, .. }) => named
21            .iter()
22            .filter_map(|field| {
23                if is_indexed(field) {
24                    Some(syn::Member::Named(field.ident.as_ref().unwrap().clone()))
25                } else {
26                    None
27                }
28            })
29            .collect(),
30        syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) => unnamed
31            .iter()
32            .enumerate()
33            .filter_map(|(i, field)| {
34                if is_indexed(field) {
35                    Some(syn::Member::Unnamed(syn::Index {
36                        index: i as u32,
37                        span: proc_macro2::Span::call_site(),
38                    }))
39                } else {
40                    None
41                }
42            })
43            .collect(),
44        syn::Fields::Unit => Vec::new(),
45    };
46
47    let impl_wrapper_ident = format_ident!("_IMPL_EVENT_FOR_{}", event_name);
48
49    proc_macro::TokenStream::from(quote! {
50        #[allow(non_upper_case_globals)]
51        const #impl_wrapper_ident: () = {
52            use oasis_std::{abi::*, exe::{encode_event_topic, Event}};
53
54            impl#generics Event for #event_name#generics  {
55                fn emit(&self) {
56                    let topics: &[[u8; 32]] = &[
57                        encode_event_topic(&stringify!(#event_name)),
58                        #(encode_event_topic(&self.#indexed_field_idents)),*
59                    ];
60                    let topic_refs: Vec<&[u8]> = topics.iter().map(|t| t.as_ref()).collect();
61                    oasis_std::backend::emit(&topic_refs, &self.try_to_vec().unwrap());
62                }
63            }
64        };
65    })
66}