es-entity-macros 0.10.36

Proc macros for es-entity
Documentation
use convert_case::{Case, Casing};
use darling::{FromDeriveInput, ToTokens};
use proc_macro2::TokenStream;
use quote::{TokenStreamExt, quote};

#[derive(Debug, Clone, FromDeriveInput)]
#[darling(attributes(es_event))]
pub struct EsEvent {
    ident: syn::Ident,
    data: darling::ast::Data<syn::Variant, ()>,
    id: syn::Type,
    #[darling(default, rename = "event_context")]
    event_ctx: Option<bool>,
}

pub fn derive(ast: syn::DeriveInput) -> darling::Result<proc_macro2::TokenStream> {
    let event = EsEvent::from_derive_input(&ast)?;
    Ok(quote!(#event))
}

impl ToTokens for EsEvent {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let ident = &self.ident;
        let id = &self.id;
        let event_context = {
            #[cfg(feature = "event-context")]
            {
                self.event_ctx.unwrap_or(true)
            }
            #[cfg(not(feature = "event-context"))]
            {
                self.event_ctx.unwrap_or(false)
            }
        };

        let match_arms = match &self.data {
            darling::ast::Data::Enum(variants) => {
                let arms: Vec<_> = variants
                    .iter()
                    .map(|v| {
                        let variant_ident = &v.ident;
                        let snake_name = variant_ident.to_string().to_case(Case::Snake);
                        quote! {
                            Self::#variant_ident { .. } => #snake_name,
                        }
                    })
                    .collect();
                quote! { #(#arms)* }
            }
            _ => panic!("EsEvent can only be derived for enums"),
        };

        tokens.append_all(quote! {
            impl es_entity::EsEvent for #ident {
                type EntityId = #id;

                fn event_context() -> bool {
                    #event_context
                }

                fn event_type(&self) -> &'static str {
                    match self {
                        #match_arms
                    }
                }
            }
        });
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn generates_event_type_match() {
        let input: syn::DeriveInput = syn::parse_quote! {
            #[es_event(id = "UserId")]
            enum UserEvent {
                Initialized { id: UserId, name: String },
                NameUpdated { name: String },
                Deactivated { reason: String },
                AccountClosed {},
            }
        };
        let event = EsEvent::from_derive_input(&input).unwrap();
        let mut tokens = TokenStream::new();
        event.to_tokens(&mut tokens);

        let expected = quote! {
            impl es_entity::EsEvent for UserEvent {
                type EntityId = UserId;

                fn event_context() -> bool {
                    false
                }

                fn event_type(&self) -> &'static str {
                    match self {
                        Self::Initialized { .. } => "initialized",
                        Self::NameUpdated { .. } => "name_updated",
                        Self::Deactivated { .. } => "deactivated",
                        Self::AccountClosed { .. } => "account_closed",
                    }
                }
            }
        };

        assert_eq!(tokens.to_string(), expected.to_string());
    }
}