Skip to content

Commit

Permalink
Clenaup a bit more the bevy_reflect enum code
Browse files Browse the repository at this point in the history
  • Loading branch information
nicopap committed Jun 26, 2022
1 parent cf1c27e commit 0cf2029
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 59 deletions.
45 changes: 18 additions & 27 deletions crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -11,13 +13,6 @@ pub(crate) struct EnumVariantConstructors {
pub variant_constructors: Vec<proc_macro2::TokenStream>,
}

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,
Expand All @@ -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<String> = Vec::with_capacity(variant_count);
let mut variant_constructors: Vec<proc_macro2::TokenStream> = 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;
Expand All @@ -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!(?)
};
Expand All @@ -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 ),* }
Expand Down
70 changes: 39 additions & 31 deletions crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand Down Expand Up @@ -275,40 +275,53 @@ 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<StructField>, 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<proc_macro2::TokenStream>) {
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)
});
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! {
Expand All @@ -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
});
Expand Down
9 changes: 8 additions & 1 deletion crates/bevy_reflect/bevy_reflect_derive/src/utility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -29,6 +29,13 @@ pub(crate) struct ResultSifter<T> {
errors: Option<syn::Error>,
}

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<T> Default for ResultSifter<T> {
fn default() -> Self {
Self {
Expand Down

0 comments on commit 0cf2029

Please sign in to comment.