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}