From 0cf20290bee1ed5d6cfe0df418cdc24e8f3832e0 Mon Sep 17 00:00:00 2001 From: Nicola Papale Date: Sun, 26 Jun 2022 11:43:43 +0200 Subject: [PATCH] Clenaup a bit more the bevy_reflect enum code --- .../bevy_reflect_derive/src/enum_utility.rs | 45 +++++------- .../bevy_reflect_derive/src/impls/enums.rs | 70 +++++++++++-------- .../bevy_reflect_derive/src/utility.rs | 9 ++- 3 files changed, 65 insertions(+), 59 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs index f7410e675eacf..230e0876c6b6c 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs @@ -1,7 +1,9 @@ -use crate::derive_data::{EnumVariantFields, ReflectEnum}; -use proc_macro2::{Ident, TokenStream}; +use crate::{ + derive_data::{EnumVariantFields, ReflectEnum}, + utility::field_ident_or_indexed, +}; +use proc_macro2::Ident; use quote::{quote, ToTokens}; -use syn::Index; /// Contains all data needed to construct all variants within an enum. pub(crate) struct EnumVariantConstructors { @@ -11,13 +13,6 @@ pub(crate) struct EnumVariantConstructors { pub variant_constructors: Vec, } -fn field_indentifier(i: usize, ident: Option<&Ident>) -> TokenStream { - let tuple_accessor = Index::from(i); - match ident { - Some(ident) => quote!(#ident :), - None => quote!(#tuple_accessor :), - } -} /// Gets the constructors for all variants in the given enum. pub(crate) fn get_variant_constructors( reflect_enum: &ReflectEnum, @@ -26,8 +21,8 @@ pub(crate) fn get_variant_constructors( ) -> EnumVariantConstructors { let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); let variant_count = reflect_enum.variants().len(); - let mut variant_names: Vec = Vec::with_capacity(variant_count); - let mut variant_constructors: Vec = Vec::with_capacity(variant_count); + let mut variant_names = Vec::with_capacity(variant_count); + let mut variant_constructors = Vec::with_capacity(variant_count); for variant in reflect_enum.active_variants() { let ident = &variant.data.ident; @@ -41,24 +36,20 @@ pub(crate) fn get_variant_constructors( }; let mut reflect_index: usize = 0; let constructor_fields = fields.iter().enumerate().map(|(declar_index, field)| { - let field_ident = field_indentifier(declar_index, field.data.ident.as_ref()); + let field_ident = field_ident_or_indexed(declar_index, field.data.ident.as_ref()); let field_value = if field.attrs.ignore { quote! { Default::default() } } else { - let error_repr = match (&field.data.ident, reflect_index) { - (None, 0) => "1st".to_owned(), - (None, 1) => "2nd".to_owned(), - (None, 2) => "3rd".to_owned(), - // Assuming we have less than 21 fields - (None, n) => format!("{}th", n + 1), - (Some(name), _) => format!("`{name}`"), - }; + let error_repr = field.data.ident.as_ref().map_or_else( + || format!("at index {reflect_index}"), + |name| format!("`{name}`"), + ); let unwrapper = if can_panic { - let expect_type = format!( - "the {error_repr} field should be of type `{}`", + let type_err_message = format!( + "the field {error_repr} should be of type `{}`", field.data.ty.to_token_stream() ); - quote!(.expect(#expect_type)) + quote!(.expect(#type_err_message)) } else { quote!(?) }; @@ -70,14 +61,14 @@ pub(crate) fn get_variant_constructors( None => quote!(.field_at(#reflect_index)), }; reflect_index += 1; - let expect_field = format!("the {error_repr} field was not declared"); - let accessor = quote!(#field_accessor .expect(#expect_field)); + let missing_field_err_message = format!("the field {error_repr} was not declared"); + let accessor = quote!(#field_accessor .expect(#missing_field_err_message)); quote! { #bevy_reflect_path::FromReflect::from_reflect(#ref_value #accessor) #unwrapper } }; - quote! { #field_ident #field_value } + quote! { #field_ident : #field_value } }); variant_constructors.push(quote! { #variant_constructor { #( #constructor_fields ),* } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 33c9adcd23b51..db1e10bb2fcb4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -1,9 +1,9 @@ -use crate::derive_data::{EnumVariantFields, ReflectEnum}; +use crate::derive_data::{EnumVariantFields, ReflectEnum, StructField}; use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; use crate::impls::impl_typed; use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; -use quote::{format_ident, quote}; +use quote::quote; pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); @@ -275,29 +275,40 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden let name = ident.to_string(); let unit = reflect_enum.get_unit(ident); - // This is equivalent to a |fields: &Vec, to_run: |usize, usize, &StructField| -> TokenStream| - // closure. Issue is that the closure cannot itself accept another closure, because closures cannot vary in - // the concrete type they accept, and each closure has a different concrete type. - macro_rules! for_fields { - ($fields:expr, |$reflect_idx:ident, $declar_field:pat, $field:ident| $body:expr) => {{ - let mut field_info = Vec::new(); - let mut $reflect_idx: usize = 0; - for ($declar_field, $field) in $fields.iter().enumerate() { - if $field.attrs.ignore { - // Ignored field - continue; - } - field_info.push($body); - $reflect_idx += 1; + fn for_fields( + fields: &[StructField], + mut generate_for_field: impl FnMut(usize, usize, &StructField) -> proc_macro2::TokenStream, + ) -> (usize, Vec) { + let mut constructor_argument = Vec::new(); + let mut reflect_idx = 0; + for field in fields.iter() { + if field.attrs.ignore { + // Ignored field + continue; } - ($reflect_idx, field_info) - }}; + constructor_argument.push(generate_for_field(reflect_idx, field.index, field)); + reflect_idx += 1; + } + (reflect_idx, constructor_argument) } - let (variant_type, field_len, field_info) = match &variant.fields { - EnumVariantFields::Unit => ("Unit", 0usize, quote!(#name)), + let mut info_type = |variant, info_type, arguments| { + let variant = Ident::new(variant, Span::call_site()); + let info_type = Ident::new(info_type, Span::call_site()); + variant_info.push(quote! { + #bevy_reflect_path::VariantInfo::#variant( + #bevy_reflect_path::#info_type::new(#arguments) + ) + }); + variant + }; + let (variant, field_len) = match &variant.fields { + EnumVariantFields::Unit => { + let variant = info_type("Unit", "UnitVariantInfo", quote!(#name)); + (variant, 0usize) + } EnumVariantFields::Unnamed(fields) => { - let (field_len, field_info) = for_fields!(fields, |reflect_idx, declar, field| { + let (field_len, argument) = for_fields(fields, |reflect_idx, declar, field| { let declar_field = syn::Index::from(declar); enum_field_at.push(quote! { #unit { #declar_field : value, .. } if #ref_index == #reflect_idx => Some(value) @@ -305,10 +316,12 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden let field_ty = &field.data.ty; quote! { #bevy_reflect_path::UnnamedField::new::<#field_ty>(#reflect_idx) } }); - ("Tuple", field_len, quote!(#name, &[ #(#field_info),* ])) + let arguments = quote!(#name, &[ #(#argument),* ]); + let variant = info_type("Tuple", "TupleVariantInfo", arguments); + (variant, field_len) } EnumVariantFields::Named(fields) => { - let (field_len, field_info) = for_fields!(fields, |reflect_idx, _, field| { + let (field_len, argument) = for_fields(fields, |reflect_idx, _, field| { let field_ident = field.data.ident.as_ref().unwrap(); let field_name = field_ident.to_string(); enum_field.push(quote! { @@ -327,16 +340,11 @@ fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Iden let field_ty = &field.data.ty; quote! { #bevy_reflect_path::NamedField::new::<#field_ty, _>(#field_name) } }); - ("Struct", field_len, quote!(#name, &[ #(#field_info),* ])) + let arguments = quote!(#name, &[ #(#argument),* ]); + let variant = info_type("Struct", "StructVariantInfo", arguments); + (variant, field_len) } }; - let variant = Ident::new(variant_type, Span::call_site()); - let info_type = format_ident!("{}VariantInfo", variant_type); - variant_info.push(quote! { - #bevy_reflect_path::VariantInfo::#variant( - #bevy_reflect_path::#info_type::new(#field_info) - ) - }); enum_field_len.push(quote! { #unit{..} => #field_len }); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index a03541e934e26..9c79f40200fc7 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -2,7 +2,7 @@ use bevy_macro_utils::BevyManifest; use proc_macro2::{Ident, Span}; -use syn::Path; +use syn::{Member, Path}; /// Returns the correct path for `bevy_reflect`. pub(crate) fn get_bevy_reflect_path() -> Path { @@ -29,6 +29,13 @@ pub(crate) struct ResultSifter { errors: Option, } +pub(crate) fn field_ident_or_indexed(index: usize, ident: Option<&Ident>) -> Member { + ident.as_ref().map_or_else( + || Member::Unnamed(index.into()), + |&ident| Member::Named(ident.clone()), + ) +} + impl Default for ResultSifter { fn default() -> Self { Self {