Game stats that reset every frame, inspired by immediate mode GUI.
gamedev bevy stats
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Stated on rewriting basic struct derive.

Currently does not work as `darling` doesn't support "naked" attributes. Will need to implement manually.

+54 -34
+54 -34
immediate_stats_macros/src/lib.rs
··· 1 1 #[cfg(feature = "bevy_butler")] 2 2 mod bevy_butler; 3 3 4 + use darling::FromField; 4 5 use proc_macro_error::{ 5 6 emit_call_site_error, emit_call_site_warning, emit_warning, proc_macro_error, 6 7 }; 7 8 use proc_macro2::{Span, TokenStream}; 8 9 use quote::{ToTokens, quote}; 9 - use syn::{Data, DataEnum, DataStruct, DeriveInput, Field, Ident, Index, parse_macro_input}; 10 + use syn::spanned::Spanned; 11 + use syn::{Data, DataEnum, DataStruct, DeriveInput, Field, Ident, Index, Type, parse_macro_input}; 10 12 11 - #[proc_macro_derive( 12 - StatContainer, 13 - attributes(stat, stat_ignore, add_component, add_resource) 14 - )] 13 + #[proc_macro_derive(StatContainer, attributes(stat, stat_ignore, add_component))] 15 14 #[proc_macro_error] 16 15 pub fn stat_container_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream { 17 16 let tree: DeriveInput = parse_macro_input!(item as DeriveInput); 18 17 19 18 let struct_name = &tree.ident; 20 19 21 - let method = match tree.data.clone() { 22 - Data::Struct(s) => stat_container_struct(s), 20 + let method_contents = match tree.data.clone() { 21 + Data::Struct(s) => struct_fields(s).unwrap(), 23 22 Data::Enum(e) => stat_container_enum(e), 24 23 Data::Union(_) => { 25 24 emit_call_site_error!("This trait cannot be derived from unions."); ··· 27 26 } 28 27 }; 29 28 30 - let result = quote! { 29 + let method = quote! { 31 30 impl StatContainer for #struct_name { 32 - #method 31 + fn reset_modifiers(&mut self) { 32 + #method_contents 33 + } 33 34 } 34 35 }; 35 36 ··· 38 39 let systems = bevy_butler::register_systems(tree); 39 40 40 41 match systems { 41 - Ok(systems) => quote! { #result #systems }.into(), 42 + Ok(systems) => quote! { #method #systems }.into(), 42 43 Err(e) => e.write_errors().into(), 43 44 } 44 45 } 45 46 46 47 #[cfg(not(feature = "bevy_butler"))] 47 - result.into() 48 + method.into() 48 49 } 49 50 50 - fn stat_container_struct(s: DataStruct) -> TokenStream { 51 - match get_members_from_fields(s.fields) { 52 - MemberVec::Named(names) => { 53 - quote! { 54 - fn reset_modifiers(&mut self) { 55 - #(self.#names.reset_modifiers();)* 56 - } 57 - } 58 - } 59 - MemberVec::Unnamed(nums) => { 60 - quote! { 61 - fn reset_modifiers(&mut self) { 62 - #(self.#nums.reset_modifiers();)* 63 - } 51 + #[derive(FromField)] // Todo Implement manually to allow for naked attributes. 52 + #[darling(attributes(stat, stat_ignore))] 53 + struct FieldState { 54 + ident: Option<Ident>, 55 + ty: Type, 56 + #[darling(default, rename = "stat")] 57 + include: bool, 58 + #[darling(default, rename = "stat")] 59 + exclude: bool, 60 + } 61 + 62 + fn struct_fields(s: DataStruct) -> darling::Result<TokenStream> { 63 + let mut tokens = TokenStream::new(); 64 + for (index, field) in s.fields.iter().enumerate() { 65 + let field_state = FieldState::from_field(&field)?; 66 + 67 + let is_stat_type = field_state 68 + .ty 69 + .to_token_stream() 70 + .to_string() 71 + .contains("Stat"); 72 + 73 + if (field_state.include || is_stat_type) && !field_state.exclude { 74 + if let Some(ident) = field_state.ident { 75 + tokens.extend(quote! { 76 + self.#ident.reset_modifiers(); 77 + }); 78 + } else { 79 + let index = Index::from(index); 80 + tokens.extend(quote! { 81 + self.#index.reset_modifiers(); 82 + }); 64 83 } 65 84 } 66 - MemberVec::None => { 67 - emit_call_site_warning!( 68 - "Unused derive. Consider adding `#[stat]` to a field that implements `StatContainer`." 85 + 86 + if field_state.include && field_state.exclude { 87 + emit_warning!( 88 + field.span(), 89 + "`stat` attribute is overruled by `stat_ignore` attribute." 69 90 ); 70 - TokenStream::new() 71 91 } 72 92 } 93 + 94 + Ok(tokens) 73 95 } 74 96 75 97 fn stat_container_enum(e: DataEnum) -> TokenStream { ··· 123 145 } 124 146 125 147 quote! { 126 - fn reset_modifiers(&mut self) { 127 - match self { 128 - #(#cases)* 129 - _ => {}, 130 - } 148 + match self { 149 + #(#cases)* 150 + _ => {}, 131 151 } 132 152 } 133 153 }