Skip to content

Commit

Permalink
Merge pull request #1 from nicopap/reflect-enum-some-cleanup
Browse files Browse the repository at this point in the history
Use field access syntax in enum macros
  • Loading branch information
MrGVSV authored Jun 27, 2022
2 parents 81e356c + 9f10360 commit 1f19587
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 231 deletions.
30 changes: 10 additions & 20 deletions crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,28 +187,18 @@ impl<'a> ReflectDerive<'a> {
.iter()
.enumerate()
.map(|(index, variant)| -> Result<EnumVariant, syn::Error> {
let attrs = parse_field_attrs(&variant.attrs)?;
let fields = Self::collect_struct_fields(&variant.fields)?;

Ok(match variant.fields {
Fields::Named(..) => EnumVariant {
data: variant,
fields: EnumVariantFields::Named(fields),
attrs,
index,
},
Fields::Unnamed(..) => EnumVariant {
data: variant,
fields: EnumVariantFields::Unnamed(fields),
attrs,
index,
},
Fields::Unit => EnumVariant {
data: variant,
fields: EnumVariantFields::Unit,
attrs,
index,
},
let fields = match variant.fields {
Fields::Named(..) => EnumVariantFields::Named(fields),
Fields::Unnamed(..) => EnumVariantFields::Unnamed(fields),
Fields::Unit => EnumVariantFields::Unit,
};
Ok(EnumVariant {
fields,
attrs: parse_field_attrs(&variant.attrs)?,
data: variant,
index,
})
})
.fold(
Expand Down
144 changes: 50 additions & 94 deletions crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::derive_data::{EnumVariantFields, ReflectEnum};
use crate::{
derive_data::{EnumVariantFields, ReflectEnum},
utility::ident_or_index,
};
use proc_macro2::Ident;
use quote::{quote, ToTokens};

Expand All @@ -17,106 +20,59 @@ pub(crate) fn get_variant_constructors(
can_panic: bool,
) -> EnumVariantConstructors {
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
let mut variant_names: Vec<String> = Vec::new();
let mut variant_constructors: Vec<proc_macro2::TokenStream> = Vec::new();
let variant_count = reflect_enum.variants().len();
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;
let name = ident.to_string();
let unit = reflect_enum.get_unit(ident);

match &variant.fields {
EnumVariantFields::Unit => {
variant_constructors.push(quote! {
#unit
});
}
EnumVariantFields::Unnamed(fields) => {
let mut variant_apply = Vec::new();
let mut field_idx: usize = 0;
for field in fields.iter() {
if field.attrs.ignore {
// Ignored field -> use default value
variant_apply.push(quote! {
Default::default()
});
continue;
}

let field_ty = &field.data.ty;
let expect_field = format!("field at index `{}` should exist", field_idx);
let expect_type = format!(
"field at index `{}` should be of type `{}`",
field_idx,
field_ty.to_token_stream()
let variant_constructor = reflect_enum.get_unit(ident);

let fields = match &variant.fields {
EnumVariantFields::Unit => &[],
EnumVariantFields::Named(fields) => fields.as_slice(),
EnumVariantFields::Unnamed(fields) => fields.as_slice(),
};
let mut reflect_index: usize = 0;
let constructor_fields = fields.iter().enumerate().map(|(declar_index, field)| {
let field_ident = ident_or_index(field.data.ident.as_ref(), declar_index);
let field_value = if field.attrs.ignore {
quote! { Default::default() }
} else {
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 type_err_message = format!(
"the field {error_repr} should be of type `{}`",
field.data.ty.to_token_stream()
);

let unwrapper = if can_panic {
quote!(.expect(#expect_type))
} else {
quote!(?)
};

variant_apply.push(quote! {
#bevy_reflect_path::FromReflect::from_reflect(
#ref_value
.field_at(#field_idx)
.expect(#expect_field)
)
#unwrapper
});

field_idx += 1;
}

variant_constructors.push(quote! {
#unit( #(#variant_apply),* )
});
}
EnumVariantFields::Named(fields) => {
let mut variant_apply = Vec::new();
for field in fields.iter() {
let field_ident = field.data.ident.as_ref().unwrap();

if field.attrs.ignore {
// Ignored field -> use default value
variant_apply.push(quote! {
#field_ident: Default::default()
});
continue;
quote!(.expect(#type_err_message))
} else {
quote!(?)
};
let field_accessor = match &field.data.ident {
Some(ident) => {
let name = ident.to_string();
quote!(.field(#name))
}

let field_name = field_ident.to_string();
let field_ty = &field.data.ty;
let expect_field = format!("field with name `{}` should exist", field_name);
let expect_type = format!(
"field with name `{}` should be of type `{}`",
field_name,
field_ty.to_token_stream()
);

let unwrapper = if can_panic {
quote!(.expect(#expect_type))
} else {
quote!(?)
};

variant_apply.push(quote! {
#field_ident: #bevy_reflect_path::FromReflect::from_reflect(
#ref_value
.field(#field_name)
.expect(#expect_field)
)
#unwrapper
});
None => quote!(.field_at(#reflect_index)),
};
reflect_index += 1;
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
}

variant_constructors.push(quote! {
#unit{ #(#variant_apply),* }
});
}
}

};
quote! { #field_ident : #field_value }
});
variant_constructors.push(quote! {
#variant_constructor { #( #constructor_fields ),* }
});
variant_names.push(name);
}

Expand Down
149 changes: 58 additions & 91 deletions crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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 crate::utility;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::quote;
Expand Down Expand Up @@ -262,126 +261,94 @@ struct EnumImpls {
fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Ident) -> EnumImpls {
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();

let mut variant_info: Vec<proc_macro2::TokenStream> = Vec::new();
let mut enum_field: Vec<proc_macro2::TokenStream> = Vec::new();
let mut enum_field_at: Vec<proc_macro2::TokenStream> = Vec::new();
let mut enum_index_of: Vec<proc_macro2::TokenStream> = Vec::new();
let mut enum_name_at: Vec<proc_macro2::TokenStream> = Vec::new();
let mut enum_field_len: Vec<proc_macro2::TokenStream> = Vec::new();
let mut enum_variant_name: Vec<proc_macro2::TokenStream> = Vec::new();
let mut enum_variant_type: Vec<proc_macro2::TokenStream> = Vec::new();
let mut variant_info = Vec::new();
let mut enum_field = Vec::new();
let mut enum_field_at = Vec::new();
let mut enum_index_of = Vec::new();
let mut enum_name_at = Vec::new();
let mut enum_field_len = Vec::new();
let mut enum_variant_name = Vec::new();
let mut enum_variant_type = Vec::new();

for variant in reflect_enum.active_variants() {
let ident = &variant.data.ident;
let name = ident.to_string();
let unit = reflect_enum.get_unit(ident);

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;
}
constructor_argument.push(generate_for_field(reflect_idx, field.index, field));
reflect_idx += 1;
}
(reflect_idx, constructor_argument)
}
let mut add_fields_branch = |variant, info_type, arguments, field_len| {
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)
)
});
enum_field_len.push(quote! {
#unit{..} => #field_len
});
enum_variant_name.push(quote! {
#unit{..} => #name
});
enum_variant_type.push(quote! {
#unit{..} => #bevy_reflect_path::VariantType::#variant
});
};
match &variant.fields {
EnumVariantFields::Unit => {
variant_info.push(quote! {
#bevy_reflect_path::VariantInfo::Unit(
#bevy_reflect_path::UnitVariantInfo::new(#name)
)
});
enum_variant_name.push(quote! {
#unit => #name
});
enum_variant_type.push(quote! {
#unit => #bevy_reflect_path::VariantType::Unit
});
add_fields_branch("Unit", "UnitVariantInfo", quote!(#name), 0usize);
}
EnumVariantFields::Unnamed(fields) => {
let mut field_info = Vec::new();
let mut field_idx: usize = 0;
for field in fields.iter() {
if field.attrs.ignore {
// Ignored field
continue;
}

let empties = utility::underscores(field_idx);
let (field_len, argument) = for_fields(fields, |reflect_idx, declar, field| {
let declar_field = syn::Index::from(declar);
enum_field_at.push(quote! {
#unit( #empties value, .. ) if #ref_index == #field_idx => Some(value)
#unit { #declar_field : value, .. } if #ref_index == #reflect_idx => Some(value)
});

let field_ty = &field.data.ty;
field_info.push(quote! {
#bevy_reflect_path::UnnamedField::new::<#field_ty>(#field_idx)
});

field_idx += 1;
}

let field_len = field_idx;
enum_field_len.push(quote! {
#unit(..) => #field_len
});
enum_variant_name.push(quote! {
#unit(..) => #name
});
enum_variant_type.push(quote! {
#unit(..) => #bevy_reflect_path::VariantType::Tuple
});
variant_info.push(quote! {
#bevy_reflect_path::VariantInfo::Tuple(
#bevy_reflect_path::TupleVariantInfo::new(#name, &[
#(#field_info),*
])
)
quote! { #bevy_reflect_path::UnnamedField::new::<#field_ty>(#reflect_idx) }
});
let arguments = quote!(#name, &[ #(#argument),* ]);
add_fields_branch("Tuple", "TupleVariantInfo", arguments, field_len);
}
EnumVariantFields::Named(fields) => {
let mut field_info = Vec::new();
let mut field_idx: usize = 0;
for field in fields.iter() {
let (field_len, argument) = for_fields(fields, |reflect_idx, _, field| {
let field_ident = field.data.ident.as_ref().unwrap();

if field.attrs.ignore {
// Ignored field
continue;
}

let field_name = field_ident.to_string();
enum_field.push(quote! {
#unit{ #field_ident, .. } if #ref_name == #field_name => Some(#field_ident)
});
enum_field_at.push(quote! {
#unit{ #field_ident, .. } if #ref_index == #field_idx => Some(#field_ident)
#unit{ #field_ident, .. } if #ref_index == #reflect_idx => Some(#field_ident)
});
enum_index_of.push(quote! {
#unit{ .. } if #ref_name == #field_name => Some(#field_idx)
#unit{ .. } if #ref_name == #field_name => Some(#reflect_idx)
});
enum_name_at.push(quote! {
#unit{ .. } if #ref_index == #field_idx => Some(#field_name)
#unit{ .. } if #ref_index == #reflect_idx => Some(#field_name)
});

let field_ty = &field.data.ty;
field_info.push(quote! {
#bevy_reflect_path::NamedField::new::<#field_ty, _>(#field_name)
});

field_idx += 1;
}

let field_len = field_idx;
enum_field_len.push(quote! {
#unit{..} => #field_len
});
enum_variant_name.push(quote! {
#unit{..} => #name
});
enum_variant_type.push(quote! {
#unit{..} => #bevy_reflect_path::VariantType::Struct
});
variant_info.push(quote! {
#bevy_reflect_path::VariantInfo::Struct(
#bevy_reflect_path::StructVariantInfo::new(#name, &[
#(#field_info),*
])
)
quote! { #bevy_reflect_path::NamedField::new::<#field_ty, _>(#field_name) }
});
let arguments = quote!(#name, &[ #(#argument),* ]);
add_fields_branch("Struct", "StructVariantInfo", arguments, field_len);
}
}
};
}

EnumImpls {
Expand Down
Loading

0 comments on commit 1f19587

Please sign in to comment.