···11+use crate::FieldOptions;
22+use proc_macro2::{Span, TokenStream};
33+use quote::quote;
44+use syn::{DataEnum, Ident, Variant};
55+66+/// Returns a match statement that can be used to reset an enum.
77+pub fn reset_enum(body: &DataEnum) -> TokenStream {
88+ body.variants
99+ .iter()
1010+ .map(|variant| {
1111+ let cases = reset_variant(&variant);
1212+ quote! {
1313+ match self {
1414+ #cases
1515+ _ => {}
1616+ }
1717+ }
1818+ })
1919+ .flatten()
2020+ .collect()
2121+}
2222+2323+/// Returns a case that can be used to reset the variant.
2424+/// If there are no stat fields, the result will be empty.
2525+fn reset_variant(variant: &Variant) -> TokenStream {
2626+ // List of all identifiers that need to be reset.
2727+ let names: Vec<Ident> = variant
2828+ .fields
2929+ .iter()
3030+ .enumerate()
3131+ .filter_map(|(index, field)| {
3232+ let options = FieldOptions::from_field(field);
3333+3434+ if options.is_stat() {
3535+ return Some(match options.ident {
3636+ Some(ident) => ident,
3737+ None => get_ident_from_index(index),
3838+ });
3939+ }
4040+4141+ None
4242+ })
4343+ .collect();
4444+4545+ if names.is_empty() {
4646+ return TokenStream::new();
4747+ }
4848+4949+ let is_named = variant.fields.iter().next().unwrap().ident.is_some();
5050+ let ident = &variant.ident;
5151+5252+ if is_named {
5353+ quote! {
5454+ Self::#ident { #(#names,)* .. } => {
5555+ #(#names.reset_modifiers();)*
5656+ },
5757+ }
5858+ } else {
5959+ quote! {
6060+ Self::#ident ( #(#names,)* .. ) => {
6161+ #(#names.reset_modifiers();)*
6262+ },
6363+ }
6464+ }
6565+}
6666+6767+/// Generates an alphabetic identifier from an index.
6868+fn get_ident_from_index(index: usize) -> Ident {
6969+ Ident::new(
7070+ format!("{}", ('a' as u8 + index as u8) as char).as_str(),
7171+ Span::call_site(),
7272+ )
7373+}
+35
immediate_stats_macros/src/derive_struct.rs
···11+use crate::FieldOptions;
22+use proc_macro2::TokenStream;
33+use quote::quote;
44+use syn::{DataStruct, Field, Index};
55+66+/// Returns the code that can be used to reset a struct's stat fields.
77+pub fn reset_struct(body: &DataStruct) -> TokenStream {
88+ body.fields
99+ .iter()
1010+ .enumerate()
1111+ .map(|(index, field)| reset_struct_field(field, index))
1212+ .flatten()
1313+ .collect()
1414+}
1515+1616+/// Returns the method call that can be used to reset a stat field.
1717+/// If the field is not a stat, the result will be empty.
1818+/// The `index` is used for tuple/unnamed fields.
1919+fn reset_struct_field(field: &Field, index: usize) -> TokenStream {
2020+ let options = FieldOptions::from_field(&field);
2121+2222+ if options.is_stat() {
2323+ return match options.ident {
2424+ Some(ident) => {
2525+ quote! { self.#ident.reset_modifiers(); }
2626+ }
2727+ None => {
2828+ let index = Index::from(index);
2929+ quote! { self.#index.reset_modifiers(); }
3030+ }
3131+ };
3232+ }
3333+3434+ TokenStream::new()
3535+}
+12-110
immediate_stats_macros/src/lib.rs
···11#[cfg(feature = "bevy_butler")]
22mod bevy_butler;
33+mod derive_enum;
44+mod derive_struct;
3544-use darling::{Error, FromField};
66+use darling::Error;
57use proc_macro_error::{emit_call_site_error, emit_warning, proc_macro_error};
66-use proc_macro2::{Span, TokenStream};
78use quote::{ToTokens, quote};
89use syn::spanned::Spanned;
99-use syn::{Data, DeriveInput, Field, Fields, Ident, Index, Variant, parse_macro_input};
1010+use syn::{Data, DeriveInput, Field, Ident, parse_macro_input};
10111112#[proc_macro_derive(StatContainer, attributes(stat, stat_ignore, add_component))]
1213#[proc_macro_error]
···1516 let ident = &tree.ident;
16171718 let method_contents = match tree.data.clone() {
1818- Data::Struct(s) => reset_struct(&s.fields),
1919- Data::Enum(e) => reset_variants(e.variants.iter()),
1919+ Data::Struct(s) => derive_struct::reset_struct(&s),
2020+ Data::Enum(e) => derive_enum::reset_enum(&e),
2021 Data::Union(_) => {
2122 emit_call_site_error!("This trait cannot be derived from unions.");
2223 return proc_macro::TokenStream::new();
···4142 method.into()
4243}
43444545+/// Represents the options that a field could have.
4446#[derive(Default)]
4547struct FieldOptions {
4648 ident: Option<Ident>,
4949+ /// True if the field's type contains the word "Stat".
4750 stat_type: bool,
5151+ /// True if the field has the `#[stat]` attribute.
4852 include: bool,
5353+ /// True if the field has the `#[stat_ignore]` attribute.
4954 exclude: bool,
5055}
5156···62676368 (self.include || self.stat_type) && !self.exclude
6469 }
6565-}
66706767-impl FromField for FieldOptions {
6868- fn from_field(field: &Field) -> darling::Result<Self> {
7171+ fn from_field(field: &Field) -> Self {
6972 let mut options = FieldOptions {
7073 ident: field.ident.clone(),
7174 ..Self::default()
···8487 }
8588 }
86898787- Ok(options)
8888- }
8989-}
9090-9191-/// Returns the code that can be used to reset a struct's stat fields.
9292-fn reset_struct(fields: &Fields) -> TokenStream {
9393- fields
9494- .iter()
9595- .enumerate()
9696- .map(|(index, field)| reset_struct_field(field, index))
9797- .flatten()
9898- .collect()
9999-}
100100-101101-/// Returns the method call that can be used to reset a stat field.
102102-/// If the field is not a stat, the result will be empty.
103103-/// The `index` is used for tuple/unnamed fields.
104104-fn reset_struct_field(field: &Field, index: usize) -> TokenStream {
105105- let options = FieldOptions::from_field(&field).unwrap();
106106-107107- if options.is_stat() {
108108- return match options.ident {
109109- Some(ident) => {
110110- quote! { self.#ident.reset_modifiers(); }
111111- }
112112- None => {
113113- let index = Index::from(index);
114114- quote! { self.#index.reset_modifiers(); }
115115- }
116116- };
117117- }
118118-119119- TokenStream::new()
120120-}
121121-122122-/// Returns a match statement that can be used to reset an enum.
123123-fn reset_variants<'a, T>(variants: T) -> TokenStream
124124-where
125125- T: Iterator<Item = &'a Variant>,
126126-{
127127- variants
128128- .map(|variant| {
129129- let cases = reset_variant(&variant);
130130- quote! {
131131- match self {
132132- #cases
133133- _ => {}
134134- }
135135- }
136136- })
137137- .flatten()
138138- .collect()
139139-}
140140-141141-/// Returns a case that can be used to reset the variant.
142142-/// If there are no stat fields, the result will be empty.
143143-fn reset_variant(variant: &Variant) -> TokenStream {
144144- let names: Vec<Ident> = variant
145145- .fields
146146- .iter()
147147- .enumerate()
148148- .filter_map(|(index, field)| {
149149- let options = FieldOptions::from_field(field).unwrap();
150150-151151- if options.is_stat() {
152152- return Some(match options.ident {
153153- Some(ident) => ident,
154154- None => get_ident_from_index(index),
155155- });
156156- }
157157-158158- None
159159- })
160160- .collect();
161161-162162- if names.is_empty() {
163163- return TokenStream::new();
9090+ options
16491 }
165165-166166- let is_named = variant.fields.iter().next().unwrap().ident.is_some();
167167- let ident = &variant.ident;
168168-169169- if is_named {
170170- quote! {
171171- Self::#ident { #(#names,)* .. } => {
172172- #(#names.reset_modifiers();)*
173173- },
174174- }
175175- } else {
176176- quote! {
177177- Self::#ident ( #(#names,)* .. ) => {
178178- #(#names.reset_modifiers();)*
179179- },
180180- }
181181- }
182182-}
183183-184184-/// Generates an alphabetic identifier from an index.
185185-fn get_ident_from_index(index: usize) -> Ident {
186186- Ident::new(
187187- format!("{}", ('a' as u8 + index as u8) as char).as_str(),
188188- Span::call_site(),
189189- )
19092}