use crate::fqpath::*; use crate::utils::{ident_prefix, ident_suffix, snake_case_ident}; use proc_macro2::{Ident, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; use syn::{ parse_quote, spanned::Spanned, Attribute, Data, DataEnum, DataStruct, DeriveInput, Field, Meta, Variant, Visibility, }; macro_rules! err_message { ($spannable: expr, $($tok:tt)*) => { return quote_spanned!( $spannable.span() => compile_error!($($tok)*); ) }; } pub fn event_system(DeriveInput { data, ident, .. }: DeriveInput) -> TokenStream { let enum_data = match data { Data::Struct(d) => err_message!( d.struct_token, "Can't create an event system from an enum type" ), Data::Enum(data_enum) => data_enum, Data::Union(u) => err_message!( u.union_token, "Can't create an asset system from a union type" ), }; let structs = define_structs(&enum_data); let root = define_root_enum(&ident, &enum_data); let helper = define_helper_fn(&ident, &enum_data); let plugin = define_plugin(&ident, &enum_data); quote! { #structs #root #helper #plugin } } fn define_structs(data: &DataEnum) -> TokenStream { data.variants .iter() .map( |Variant { ident, attrs, fields, .. }| { let event_name = ident_suffix(ident, "Event"); let fields: TokenStream = fields .iter() .map(|fl @ Field { ident, ty, .. }| { let f = Field { ident: ident.clone(), ty: ty.clone(), vis: parse_quote!(pub), ..fl.clone() } .to_token_stream(); quote!(#f,) }) .collect(); quote! { #[derive(#FQClone, #FQDebug, #BevyEvent, #FQSerialize, #FQDeserialize)] pub struct #event_name { #fields } } }, ) .collect() } fn define_root_enum(name: &Ident, data: &DataEnum) -> TokenStream { let variants: TokenStream = data .variants .iter() .map(|Variant { ident, .. }| { let event = ident_suffix(ident, "Event"); quote!(#ident(#event),) }) .collect(); let conversions: TokenStream = data .variants .iter() .map(|Variant { ident, .. }| { let event = ident_suffix(ident, "Event"); quote! { impl #FQFrom<#event> for #name { fn from(value: #event) -> Self { #name::#ident(value) } } } }) .collect(); quote! { #[derive(#FQDebug, #FQClone, #BevyEvent, #FQSerialize, #FQDeserialize)] pub enum #name { #variants } #conversions } } fn define_helper_fn(name: &Ident, data: &DataEnum) -> TokenStream { let variants: TokenStream = data .variants .iter() .map(|Variant { ident, .. }| quote! { #name::#ident(value) => #send_event(world, value), }) .collect(); let fn_name = ident_prefix(&snake_case_ident(name), "dispatch_"); quote! { pub fn #fn_name(world: &mut #BevyWorld, event: #name) { match event { #variants } } } } fn define_plugin(name: &Ident, data: &DataEnum) -> TokenStream { let variants: TokenStream = data .variants .iter() .map(|Variant { ident, .. }| { let event = ident_suffix(ident, "Event"); quote!(app.add_event::<#event>();) }) .collect(); let plugin_name = ident_suffix(name, "Plugin"); quote! { pub struct #plugin_name; impl #BevyPlugin for #plugin_name { fn build(&self, app: &mut #BevyApp) { #variants } } } }