Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use field access syntax in enum macros #1

Merged
merged 3 commits into from
Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
nicopap marked this conversation as resolved.
Show resolved Hide resolved
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