bevy_yoetz_macros/suggestion/
variant.rs

1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{parse_quote, Error};
4
5use super::field::{FieldConfig, FieldRole};
6use super::suggestion_enum::SuggestionEnumData;
7
8pub struct SuggestionVariantData<'a> {
9    pub parent: &'a SuggestionEnumData,
10    pub name: syn::Ident,
11    pub strategy_name: syn::Ident,
12    pub fields: syn::Fields,
13    pub fields_config: Vec<FieldConfig>,
14}
15
16impl<'a> SuggestionVariantData<'a> {
17    pub fn new(parent: &'a SuggestionEnumData, variant: &syn::Variant) -> Result<Self, Error> {
18        let mut fields = variant.fields.clone();
19        let fields_config = fields
20            .iter_mut()
21            .map(FieldConfig::new_for)
22            .collect::<Result<Vec<_>, _>>()?;
23        Ok(Self {
24            parent,
25            name: variant.ident.clone(),
26            strategy_name: syn::Ident::new(
27                &format!("{}{}", parent.name, variant.ident,),
28                variant.ident.span(),
29            ),
30            fields,
31            fields_config,
32        })
33    }
34
35    fn semicolon_if_needed(&self) -> Option<syn::token::Semi> {
36        if matches!(self.fields, syn::Fields::Named(..)) {
37            None
38        } else {
39            Some(Default::default())
40        }
41    }
42
43    pub fn emit_strategy_code(&self) -> Result<TokenStream, Error> {
44        let strategy_name = &self.strategy_name;
45        let mut fields = self.fields.clone();
46        for (field, config) in fields.iter_mut().zip(self.fields_config.iter()) {
47            field.vis = self.parent.visibility.clone();
48            if config.role.unwrap() == FieldRole::Key {
49                field.attrs.push(parse_quote!(#[allow(dead_code)]))
50            }
51        }
52        let visibility = &self.parent.visibility;
53        let semicolon = self.semicolon_if_needed();
54        let extra_derives = &self.parent.strategy_structs_config.derive;
55        Ok(quote! {
56            #[derive(bevy::ecs::component::Component, #(#extra_derives),*)]
57            #visibility struct #strategy_name #fields #semicolon
58        })
59    }
60
61    pub fn iter_fields_with_configs(&self) -> impl Iterator<Item = (&syn::Field, &FieldConfig)> {
62        self.fields.iter().zip(&self.fields_config)
63    }
64
65    pub fn iter_key_fields(&self) -> impl Iterator<Item = &syn::Field> {
66        self.iter_fields_with_configs()
67            .filter_map(|(field, config)| {
68                if config.role.unwrap() == FieldRole::Key {
69                    Some(field)
70                } else {
71                    None
72                }
73            })
74    }
75
76    pub fn emit_key_enum_variant(&self) -> Result<TokenStream, Error> {
77        let name = &self.name;
78        let fields = match &self.fields {
79            syn::Fields::Named(named) => syn::Fields::Named(syn::FieldsNamed {
80                brace_token: named.brace_token,
81                named: self.iter_key_fields().cloned().collect(),
82            }),
83            syn::Fields::Unnamed(unnamed) => {
84                return Err(Error::new_spanned(
85                    unnamed,
86                    "tuple variants are currently unsupported for YoetzSuggestion, \
87                    and are resuseved for future features",
88                ));
89            }
90            syn::Fields::Unit => syn::Fields::Unit,
91        };
92        Ok(quote! {
93            #name #fields
94        })
95    }
96}