|
| 1 | +use bevy_macro_utils::{get_lit_str, Symbol}; |
| 2 | +use proc_macro::TokenStream; |
| 3 | +use quote::{quote, ToTokens}; |
| 4 | +use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Path, Result}; |
| 5 | + |
| 6 | +pub fn derive_event(input: TokenStream) -> TokenStream { |
| 7 | + let mut ast = parse_macro_input!(input as DeriveInput); |
| 8 | + let bevy_ecs_path: Path = crate::bevy_ecs_path(); |
| 9 | + |
| 10 | + ast.generics |
| 11 | + .make_where_clause() |
| 12 | + .predicates |
| 13 | + .push(parse_quote! { Self: Send + Sync + 'static }); |
| 14 | + |
| 15 | + let attrs = match parse_event_attr(&ast) { |
| 16 | + Ok(attrs) => attrs, |
| 17 | + Err(e) => return e.into_compile_error().into(), |
| 18 | + }; |
| 19 | + |
| 20 | + let missed = attrs.missed; |
| 21 | + |
| 22 | + let warn_missed = match missed { |
| 23 | + Missed::Ignore => quote! { const WARN_MISSED: bool = false; }, |
| 24 | + Missed::DebugWarn => quote! { |
| 25 | + #[cfg(debug_assertions)] |
| 26 | + const WARN_MISSED: bool = true; |
| 27 | + #[cfg(not(debug_assertions))] |
| 28 | + const WARN_MISSED: bool = false; |
| 29 | + }, |
| 30 | + Missed::Warn => quote! { const WARN_MISSED:bool = true; }, |
| 31 | + }; |
| 32 | + |
| 33 | + let struct_name = &ast.ident; |
| 34 | + let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); |
| 35 | + |
| 36 | + TokenStream::from(quote! { |
| 37 | + impl #impl_generics #bevy_ecs_path::event::Event for #struct_name #type_generics #where_clause { |
| 38 | + #warn_missed |
| 39 | + } |
| 40 | + }) |
| 41 | +} |
| 42 | + |
| 43 | +struct Attrs { |
| 44 | + missed: Missed, |
| 45 | +} |
| 46 | + |
| 47 | +enum Missed { |
| 48 | + Ignore, |
| 49 | + DebugWarn, |
| 50 | + Warn, |
| 51 | +} |
| 52 | + |
| 53 | +pub const EVENT: Symbol = Symbol("event"); |
| 54 | +pub const MISSED: Symbol = Symbol("missed"); |
| 55 | + |
| 56 | +const IGNORE: &str = "ignore"; |
| 57 | +const DEBUG_WARN: &str = "debug_warn"; |
| 58 | +const WARN: &str = "warn"; |
| 59 | + |
| 60 | +fn parse_event_attr(ast: &DeriveInput) -> Result<Attrs> { |
| 61 | + let meta_items = bevy_macro_utils::parse_attrs(ast, EVENT)?; |
| 62 | + |
| 63 | + let mut attrs = Attrs { |
| 64 | + missed: Missed::DebugWarn, |
| 65 | + }; |
| 66 | + |
| 67 | + for meta in meta_items { |
| 68 | + use syn::{ |
| 69 | + Meta::NameValue, |
| 70 | + NestedMeta::{Lit, Meta}, |
| 71 | + }; |
| 72 | + match meta { |
| 73 | + Meta(NameValue(m)) if m.path == MISSED => { |
| 74 | + attrs.missed = match get_lit_str(MISSED, &m.lit)?.value().as_str() { |
| 75 | + IGNORE => Missed::Ignore, |
| 76 | + DEBUG_WARN => Missed::DebugWarn, |
| 77 | + WARN => Missed::Warn, |
| 78 | + e => { |
| 79 | + return Err(Error::new_spanned( |
| 80 | + m.lit, |
| 81 | + format!( |
| 82 | + "Invalid missed event behaviour `{e}`, expected '{IGNORE}', '{DEBUG_WARN}', or '{WARN}'.", |
| 83 | + ), |
| 84 | + )) |
| 85 | + } |
| 86 | + } |
| 87 | + } |
| 88 | + Meta(meta_item) => { |
| 89 | + return Err(Error::new_spanned( |
| 90 | + meta_item.path(), |
| 91 | + format!( |
| 92 | + "unknown event attribute `{}`", |
| 93 | + meta_item.path().into_token_stream() |
| 94 | + ), |
| 95 | + )); |
| 96 | + } |
| 97 | + Lit(lit) => { |
| 98 | + return Err(Error::new_spanned( |
| 99 | + lit, |
| 100 | + "unexpected literal in event attribute", |
| 101 | + )) |
| 102 | + } |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + Ok(attrs) |
| 107 | +} |
0 commit comments