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
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
151 changes: 58 additions & 93 deletions crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::derive_data::{EnumVariantFields, ReflectEnum};
use proc_macro2::Ident;
use proc_macro2::{Ident, TokenStream};
nicopap marked this conversation as resolved.
Show resolved Hide resolved
use quote::{quote, ToTokens};
use syn::Index;

/// Contains all data needed to construct all variants within an enum.
pub(crate) struct EnumVariantConstructors {
Expand All @@ -10,113 +11,77 @@ 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 :),
}
nicopap marked this conversation as resolved.
Show resolved Hide resolved
}
/// Gets the constructors for all variants in the given enum.
pub(crate) fn get_variant_constructors(
reflect_enum: &ReflectEnum,
ref_value: &Ident,
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<String> = Vec::with_capacity(variant_count);
let mut variant_constructors: Vec<proc_macro2::TokenStream> = 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 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 = field_indentifier(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}`"),
nicopap marked this conversation as resolved.
Show resolved Hide resolved
};
let unwrapper = if can_panic {
let expect_type = format!(
nicopap marked this conversation as resolved.
Show resolved Hide resolved
"field at index `{}` should be of type `{}`",
field_idx,
field_ty.to_token_stream()
"the {error_repr} field 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(#expect_type))
} 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 expect_field = format!("the {error_repr} field was not declared");
let accessor = quote!(#field_accessor .expect(#expect_field));
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
Loading