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 1 commit
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
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));
nicopap marked this conversation as resolved.
Show resolved Hide resolved
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
};
nicopap marked this conversation as resolved.
Show resolved Hide resolved
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 {
MrGVSV marked this conversation as resolved.
Show resolved Hide resolved
ident.as_ref().map_or_else(
|| Member::Unnamed(index.into()),
|&ident| Member::Named(ident.clone()),
)
nicopap marked this conversation as resolved.
Show resolved Hide resolved
}
nicopap marked this conversation as resolved.
Show resolved Hide resolved

impl<T> Default for ResultSifter<T> {
fn default() -> Self {
Self {
Expand Down