evento_macro/
lib.rs

1#![forbid(unsafe_code)]
2
3extern crate proc_macro;
4extern crate proc_macro2;
5
6#[macro_use]
7extern crate proc_macro_error;
8
9use convert_case::{Case, Casing};
10use proc_macro::TokenStream;
11use proc_macro2::TokenStream as TokenStream2;
12use quote::{format_ident, quote};
13use sha3::{Digest, Sha3_256};
14use syn::{parse_macro_input, DeriveInput, FieldsNamed};
15
16#[proc_macro_error]
17#[proc_macro_derive(Aggregate)]
18pub fn aggregate_derive(input: TokenStream) -> TokenStream {
19    let DeriveInput { ident, data, .. } = parse_macro_input!(input);
20
21    let syn::Data::Struct(s) = data else {
22        abort!(ident, "Derive Aggregate only available on struct");
23    };
24
25    let mut hasher = Sha3_256::new();
26    hasher.update(ident.to_string());
27
28    if let syn::Fields::Named(FieldsNamed { named, .. }) = s.fields {
29        for field in named {
30            if let Some(ident) = field.ident {
31                hasher.update(ident.to_string());
32            }
33
34            if let syn::Type::Path(ty_path) = field.ty {
35                let idents_of_path =
36                    ty_path
37                        .path
38                        .segments
39                        .iter()
40                        .fold(String::new(), |mut acc, v| {
41                            acc.push_str(&v.ident.to_string());
42                            acc
43                        });
44
45                hasher.update(idents_of_path);
46            }
47        }
48    };
49
50    let name = ident.to_string().to_case(Case::Kebab).to_string();
51    let version = format!("{:x}", hasher.finalize());
52
53    quote! {
54        impl Aggregate for #ident {
55            fn aggregate_type() -> &'static str {
56                #name
57            }
58
59            fn aggregate_version() -> &'static str {
60                #version
61            }
62        }
63    }
64    .into()
65}
66
67#[proc_macro_error]
68#[proc_macro_derive(PublisherEvent)]
69pub fn publisher_event_derive(input: TokenStream) -> TokenStream {
70    let DeriveInput { ident, data, .. } = parse_macro_input!(input);
71
72    let syn::Data::Enum(s) = data else {
73        abort!(ident, "Derive PublisherEvent only available on Enum");
74    };
75
76    let mut variants = TokenStream2::default();
77
78    for variant in s.variants {
79        let v_ident = format_ident!("{}", variant.ident);
80        variants.extend::<TokenStream2>(quote! {
81            impl PublisherEvent for #v_ident {
82                type Output = #ident;
83
84                fn event_name() -> Self::Output {
85                    #ident::#v_ident
86                }
87            }
88        })
89    }
90
91    quote! {
92        impl From<#ident> for String {
93            fn from(e: #ident) -> Self {
94                e.to_string()
95            }
96        }
97
98        #variants
99    }
100    .into()
101}