diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 6640496fc2cbd..e34a1f84e91f4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -15,8 +15,8 @@ use syn::{ parse_macro_input, punctuated::Punctuated, token::{Comma, Paren, Where}, - Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Index, Member, Meta, NestedMeta, - Path, + Attribute, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Index, + Member, Meta, NestedMeta, Path, Token, Variant, }; #[derive(Default)] @@ -41,9 +41,15 @@ enum DeriveType { Struct, TupleStruct, UnitStruct, + Enum, Value, } +enum Items<'a> { + Fields(&'a Punctuated), + Variants(&'a Punctuated), +} + static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; @@ -51,29 +57,35 @@ static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); let unit_struct_punctuated = Punctuated::new(); - let (fields, mut derive_type) = match &ast.data { + let (items, mut derive_type) = match &ast.data { Data::Struct(DataStruct { fields: Fields::Named(fields), .. - }) => (&fields.named, DeriveType::Struct), + }) => (Items::Fields(&fields.named), DeriveType::Struct), Data::Struct(DataStruct { fields: Fields::Unnamed(fields), .. - }) => (&fields.unnamed, DeriveType::TupleStruct), + }) => (Items::Fields(&fields.unnamed), DeriveType::TupleStruct), Data::Struct(DataStruct { fields: Fields::Unit, .. - }) => (&unit_struct_punctuated, DeriveType::UnitStruct), - _ => (&unit_struct_punctuated, DeriveType::Value), + }) => ( + Items::Fields(&unit_struct_punctuated), + DeriveType::UnitStruct, + ), + Data::Enum(DataEnum { variants, .. }) => (Items::Variants(variants), DeriveType::Enum), + _ => (Items::Fields(&unit_struct_punctuated), DeriveType::Value), }; - - let fields_and_args = fields + let attrs: Vec<&Vec> = match items { + Items::Fields(fields) => fields.iter().map(|field| &field.attrs).collect(), + Items::Variants(variants) => variants.iter().map(|variant| &variant.attrs).collect(), + }; + let args = attrs .iter() .enumerate() - .map(|(i, f)| { + .map(|(i, attrs)| { ( - f, - f.attrs + attrs .iter() .find(|a| *a.path.get_ident().as_ref().unwrap() == REFLECT_ATTRIBUTE_NAME) .map(|a| { @@ -93,18 +105,18 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { i, ) }) - .collect::, usize)>>(); - let active_fields = fields_and_args + .collect::, usize)>>(); + let active_items = args .iter() - .filter(|(_field, attrs, _i)| { + .filter(|(attrs, _i)| { attrs.is_none() || match attrs.as_ref().unwrap().ignore { Some(ignore) => !ignore, None => true, } }) - .map(|(f, _attr, i)| (*f, *i)) - .collect::>(); + .map(|(_attr, i)| *i) + .collect::>(); let modules = get_modules(); let bevy_reflect_path = get_path(&modules.bevy_reflect); @@ -137,22 +149,46 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { ); match derive_type { - DeriveType::Struct | DeriveType::UnitStruct => impl_struct( - type_name, - &ast.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, - &active_fields, - ), - DeriveType::TupleStruct => impl_tuple_struct( - type_name, - &ast.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_attrs, - &active_fields, - ), + DeriveType::Struct | DeriveType::UnitStruct => { + let active_fields = match items { + Items::Fields(fields) => fields, + Items::Variants(_) => { + unreachable!() + } + } + .iter() + .zip(active_items.iter()) + .map(|(field, i)| (field, *i)) + .collect::>(); + impl_struct( + type_name, + &ast.generics, + get_type_registration_impl, + &bevy_reflect_path, + &reflect_attrs, + &active_fields, + ) + } + DeriveType::TupleStruct => { + let active_fields = match items { + Items::Fields(fields) => fields, + Items::Variants(_) => { + unreachable!() + } + } + .iter() + .zip(active_items.iter()) + .map(|(field, i)| (field, *i)) + .collect::>(); + impl_tuple_struct( + type_name, + &ast.generics, + get_type_registration_impl, + &bevy_reflect_path, + &reflect_attrs, + &active_fields, + ) + } DeriveType::Value => impl_value( type_name, &ast.generics, @@ -160,6 +196,24 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { &bevy_reflect_path, &reflect_attrs, ), + DeriveType::Enum => { + let active_variants = match items { + Items::Fields(_) => unreachable!(), + Items::Variants(variants) => variants, + } + .iter() + .zip(active_items.iter()) + .map(|(variant, i)| (variant, *i)) + .collect::>(); + impl_enum( + type_name, + &ast.generics, + get_type_registration_impl, + &bevy_reflect_path, + &reflect_attrs, + &active_variants, + ) + } } } @@ -494,12 +548,12 @@ fn impl_value( } #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { // FIXME let value = value.any(); if let Some(value) = value.downcast_ref::() { *self = value.clone(); } else { - panic!("Value is not {}.", std::any::type_name::()); + panic!("Attempted to apply non-enum type to enum type."); } } @@ -539,6 +593,570 @@ fn impl_value( } }) } + +fn impl_enum( + enum_name: &Ident, + generics: &Generics, + get_type_registration_impl: proc_macro2::TokenStream, + bevy_reflect_path: &Path, + reflect_attrs: &ReflectAttrs, + active_variants: &[(&Variant, usize)], +) -> TokenStream { + let mut variant_indices = Vec::new(); + let mut struct_wrappers = Vec::new(); + let mut tuple_wrappers = Vec::new(); + let mut variant_names = Vec::new(); + let mut variant_idents = Vec::new(); + let mut variant_and_fields_idents = Vec::new(); + let mut reflect_variants = Vec::new(); + let mut reflect_variants_mut = Vec::new(); + for (variant, variant_index) in active_variants.iter() { + let ident = &variant.ident; + let variant_name = format!("{}::{}", enum_name, variant.ident); + let variant_ident = { + match &variant.fields { + Fields::Named(_struct_fields) => { + quote!(#enum_name::#ident {..}) + } + Fields::Unnamed(tuple) => { + let tuple_fields = &tuple.unnamed; + if tuple_fields.len() == 1 { + quote!(#enum_name::#ident (_)) + } else { + quote!(#enum_name::#ident (..)) + } + } + Fields::Unit => { + quote!(#enum_name::#ident) + } + } + }; + let variant_and_fields_ident = { + match &variant.fields { + Fields::Named(struct_fields) => { + let field_names = struct_fields + .named + .iter() + .map(|field| field.ident.as_ref().unwrap()) + .collect::>(); + quote!(#enum_name::#ident {#(#field_names,)*}) + } + Fields::Unnamed(tuple_fields) => { + let field_names = (0..tuple_fields.unnamed.len()) + .map(|i| Ident::new(format!("t{}", i).as_str(), Span::call_site())) + .collect::>(); + if tuple_fields.unnamed.len() == 1 { + quote!(#enum_name::#ident (new_type)) + } else { + quote!(#enum_name::#ident (#(#field_names,)*)) + } + } + Fields::Unit => { + quote!(#enum_name::#ident) + } + } + }; + let wrapper_ident = if let Fields::Named(_) | Fields::Unnamed(_) = &variant.fields { + Ident::new( + format!("{}{}Wrapper", enum_name, variant.ident).as_str(), + Span::call_site(), + ) + } else { + Ident::new("unused", Span::call_site()) + }; + let wrapper_name = match &variant.fields { + Fields::Named(struct_fields) => { + quote!(#struct_fields).to_string() + } + Fields::Unnamed(tuple_fields) => { + quote!(#tuple_fields).to_string() + } + Fields::Unit => "unused".to_string(), + }; + let reflect_variant = { + match &variant.fields { + Fields::Named(_struct_fields) => { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &Self, &#wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariant::Struct(wrapper_ref as &dyn Struct) + }) + } + Fields::Unnamed(tuple_fields) => { + if tuple_fields.unnamed.len() == 1 { + quote!(#bevy_reflect_path::EnumVariant::NewType(new_type as &dyn #bevy_reflect_path::Reflect)) + } else { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &Self, &#wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariant::Tuple(wrapper_ref as &dyn Tuple) + }) + } + } + Fields::Unit => { + quote!(#bevy_reflect_path::EnumVariant::Unit) + } + } + }; + let reflect_variant_mut = { + match &variant.fields { + Fields::Named(_struct_fields) => { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &mut Self, &mut #wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariantMut::Struct(wrapper_ref as &mut dyn Struct) + }) + } + Fields::Unnamed(tuple) => { + let tuple_fields = &tuple.unnamed; + if tuple_fields.len() == 1 { + quote!(#bevy_reflect_path::EnumVariantMut::NewType(new_type as &mut dyn #bevy_reflect_path::Reflect)) + } else { + quote!({ + let wrapper_ref = unsafe { std::mem::transmute::< &mut Self, &mut #wrapper_ident >(self) }; + #bevy_reflect_path::EnumVariantMut::Tuple(wrapper_ref as &mut dyn Tuple) + }) + } + } + Fields::Unit => { + quote!(#bevy_reflect_path::EnumVariantMut::Unit) + } + } + }; + match &variant.fields { + Fields::Named(struct_fields) => { + struct_wrappers.push(( + wrapper_ident, + wrapper_name, + variant_index, + variant_name.clone(), + variant_ident.clone(), + variant_and_fields_ident.clone(), + struct_fields.clone(), + )); + } + Fields::Unnamed(tuple_fields) => { + if tuple_fields.unnamed.len() > 1 { + tuple_wrappers.push(( + wrapper_ident, + wrapper_name, + variant_index, + variant_name.clone(), + variant_ident.clone(), + variant_and_fields_ident.clone(), + tuple_fields.clone(), + )); + } + } + Fields::Unit => {} + } + variant_indices.push(variant_index); + variant_names.push(variant_name); + variant_idents.push(variant_ident); + variant_and_fields_idents.push(variant_and_fields_ident); + reflect_variants.push(reflect_variant); + reflect_variants_mut.push(reflect_variant_mut); + } + let hash_fn = reflect_attrs.get_hash_impl(&bevy_reflect_path); + let serialize_fn = reflect_attrs.get_serialize_impl(&bevy_reflect_path); + let partial_eq_fn = match reflect_attrs.reflect_partial_eq { + TraitImpl::NotImplemented => quote! { + use #bevy_reflect_path::Enum; + #bevy_reflect_path::enum_partial_eq(self, value) + }, + TraitImpl::Implemented | TraitImpl::Custom(_) => reflect_attrs.get_partial_eq_impl(), + }; + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let mut token_stream = TokenStream::from(quote! { + #get_type_registration_impl + + impl #impl_generics #bevy_reflect_path::Enum for #enum_name#ty_generics #where_clause { + fn variant(&self) -> #bevy_reflect_path::EnumVariant<'_> { + match self { + #(#variant_and_fields_idents => #reflect_variants,)* + } + } + + fn variant_mut(&mut self) -> #bevy_reflect_path::EnumVariantMut<'_> { + match self { + #(#variant_and_fields_idents => #reflect_variants_mut,)* + } + } + + fn variant_info(&self) -> #bevy_reflect_path::VariantInfo<'_> { + let index = match self { + #(#variant_idents => #variant_indices,)* + }; + #bevy_reflect_path::VariantInfo { + index, + name: self.get_index_name(index).unwrap(), + } + } + + fn get_index_name(&self, index: usize) -> Option<&'_ str> { + match index { + #(#variant_indices => Some(#variant_names),)* + _ => None, + } + } + + fn get_index_from_name(&self, name: &str) -> Option { + match name { + #(#variant_names => Some(#variant_indices),)* + _ => None, + } + } + + fn iter_variants_info(&self) -> #bevy_reflect_path::VariantInfoIter<'_> { + #bevy_reflect_path::VariantInfoIter::new(self) + } + } + + impl #impl_generics #bevy_reflect_path::Reflect for #enum_name#ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn any(&self) -> &dyn std::any::Any { + self + } + #[inline] + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + #[inline] + fn clone_value(&self) -> Box { + use #bevy_reflect_path::Enum; + Box::new(self.clone()) // FIXME: should it be clone_dynamic? + } + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { // FIXME + use #bevy_reflect_path::Enum; + let value = value.any(); + if let Some(value) = value.downcast_ref::() { + *self = value.clone(); + } else { + panic!("Attempted to apply non-enum type to enum type."); + } + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Enum(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Enum(self) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + #serialize_fn + } + + fn reflect_hash(&self) -> Option { + #hash_fn + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #partial_eq_fn + } + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + } + }); + for ( + wrapper_ident, + wrapper_name, + variant_index, + variant_name, + _variant_ident, + variant_and_fields_ident, + fields, + ) in struct_wrappers + { + let mut field_names = Vec::new(); + let mut field_idents = Vec::new(); + let mut field_indices = Vec::new(); + for (i, field) in fields.named.iter().enumerate() { + field_names.push(field.ident.as_ref().unwrap().to_string()); + field_idents.push(field.ident.clone()); + field_indices.push(i); + } + let fields_len = field_indices.len(); + let mut match_fields = quote!(); + for (i, variant_ident) in variant_idents.iter().enumerate() { + if i == *variant_index { + match_fields.extend(quote!( + #variant_and_fields_ident => (#(#field_idents,)*), + )); + } else { + match_fields.extend(quote!( + #variant_ident => unreachable!(), + )); + } + } + let match_fields_mut = quote!(let (#(#field_idents,)*) = match &mut self.0 { + #match_fields + };); + let match_fields = quote!(let (#(#field_idents,)*) = match &self.0 { + #match_fields + };); + token_stream.extend(TokenStream::from(quote! { + #[repr(transparent)] + pub struct #wrapper_ident(TestEnum); + impl #bevy_reflect_path::Reflect for #wrapper_ident { + fn type_name(&self) -> &str { + #wrapper_name + } + + fn any(&self) -> &dyn std::any::Any { + self.0.any() + } + + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self.0.any_mut() + } + + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + self.0.apply(value); + } + + fn set(&mut self, value: Box) -> Result<(), Box> { + self.0.set(value) + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Struct(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Struct(self) + } + + fn clone_value(&self) -> Box { + self.0.clone_value() + } + + fn reflect_hash(&self) -> Option { + self.0.reflect_hash() + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + self.0.reflect_partial_eq(value) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + self.0.serializable() + } + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + } + impl #bevy_reflect_path::Struct for #wrapper_ident { + fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { + #match_fields + match name { + #(#field_names => Some(#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + #match_fields_mut + match name { + #(#field_names => Some(#field_idents),)* + _ => None, + } + } + + fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + #match_fields + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + #match_fields_mut + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + fn name_at(&self, index: usize) -> Option<&str> { + match index { + #(#field_indices => Some(#field_names),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #fields_len + } + + fn iter_fields(&self) -> bevy::reflect::FieldIter { + FieldIter::new(self) + } + + fn clone_dynamic(&self) -> bevy::reflect::DynamicStruct { + #match_fields + let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); + dynamic.set_name(self.type_name().to_string()); + #(dynamic.insert_boxed(#field_names, #field_idents.clone_value());)* + dynamic + } + } + })); + } + for ( + wrapper_ident, + wrapper_name, + variant_index, + variant_name, + _variant_ident, + variant_and_fields_ident, + fields, + ) in tuple_wrappers + { + let mut field_names = Vec::new(); + let mut field_idents = Vec::new(); + let mut field_indices = Vec::new(); + for (index, _field) in fields.unnamed.iter().enumerate() { + let field_name = format!("t{}", index); // FIXME: done in 2 places + let field_ident = Ident::new(field_name.as_str(), Span::call_site()); + field_names.push(field_name); + field_idents.push(field_ident); + field_indices.push(index); + } + let fields_len = field_indices.len(); + let mut match_fields = quote!(); + for (i, variant_ident) in variant_idents.iter().enumerate() { + if i == *variant_index { + match_fields.extend(quote!( + #variant_and_fields_ident => (#(#field_idents,)*), + )); + } else { + match_fields.extend(quote!( + #variant_ident => unreachable!(), + )); + } + } + let match_fields_mut = quote!(let (#(#field_idents,)*) = match &mut self.0 { + #match_fields + };); + let match_fields = quote!(let (#(#field_idents,)*) = match &self.0 { + #match_fields + };); + token_stream.extend(TokenStream::from(quote! { + #[repr(transparent)] + pub struct #wrapper_ident(TestEnum); + impl #bevy_reflect_path::Reflect for #wrapper_ident { + fn type_name(&self) -> &str { + #wrapper_name + } + + fn any(&self) -> &dyn std::any::Any { + self.0.any() + } + + fn any_mut(&mut self) -> &mut dyn std::any::Any { + self.0.any_mut() + } + + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + self.0.apply(value); + } + + fn set(&mut self, value: Box) -> Result<(), Box> { + self.0.set(value) + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Tuple(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Tuple(self) + } + + fn clone_value(&self) -> Box { + self.0.clone_value() + } + + fn reflect_hash(&self) -> Option { + self.0.reflect_hash() + } + + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + self.0.reflect_partial_eq(value) + } + + fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { + self.0.serializable() + } + + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + } + impl #bevy_reflect_path::Tuple for #wrapper_ident { + fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + #match_fields + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + #match_fields_mut + match index { + #(#field_indices => Some(#field_idents),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #fields_len + } + + fn iter_fields(&self) -> bevy::reflect::TupleFieldIter { + TupleFieldIter::new(self) + } + + fn clone_dynamic(&self) -> bevy::reflect::DynamicTuple { + #match_fields + let mut dynamic = #bevy_reflect_path::DynamicTuple::default(); + #(dynamic.insert_boxed(#field_idents.clone_value());)* + dynamic + } + } + })); + } + token_stream +} struct ReflectDef { type_name: Ident, generics: Generics, diff --git a/crates/bevy_reflect/src/enum_trait.rs b/crates/bevy_reflect/src/enum_trait.rs index 8dadd82dd0f05..16eb90c7f49fc 100644 --- a/crates/bevy_reflect/src/enum_trait.rs +++ b/crates/bevy_reflect/src/enum_trait.rs @@ -1,4 +1,4 @@ -use crate::{Reflect, Struct, Tuple}; +use crate::{Reflect, ReflectRef, Struct, Tuple}; pub trait Enum: Reflect { fn variant(&self) -> EnumVariant<'_>; @@ -8,6 +8,7 @@ pub trait Enum: Reflect { fn get_index_name(&self, index: usize) -> Option<&str>; fn get_index_from_name(&self, name: &str) -> Option; } +#[derive(PartialEq, Eq)] pub struct VariantInfo<'a> { pub index: usize, pub name: &'a str, @@ -15,29 +16,27 @@ pub struct VariantInfo<'a> { pub struct VariantInfoIter<'a> { pub(crate) value: &'a dyn Enum, pub(crate) index: usize, - pub(crate) len: usize, +} +impl<'a> VariantInfoIter<'a> { + pub fn new(value: &'a dyn Enum) -> Self { + Self { value, index: 0 } + } } impl<'a> Iterator for VariantInfoIter<'a> { type Item = VariantInfo<'a>; fn next(&mut self) -> Option { - if self.index == self.len { - return None; - } - let item = VariantInfo { - index: self.index, - name: self.value.get_index_name(self.index).unwrap(), - }; + let value = self + .value + .get_index_name(self.index) + .map(|name| VariantInfo { + index: self.index, + name, + }); self.index += 1; - Some(item) - } - - fn size_hint(&self) -> (usize, Option) { - let size = self.len - self.index; - (size, Some(size)) + value } } -impl<'a> ExactSizeIterator for VariantInfoIter<'a> {} pub enum EnumVariant<'a> { Unit, @@ -51,3 +50,54 @@ pub enum EnumVariantMut<'a> { Tuple(&'a mut dyn Tuple), Struct(&'a mut dyn Struct), } + +#[inline] +pub fn enum_partial_eq(enum_a: &E, reflect_b: &dyn Reflect) -> Option { + let enum_b = if let ReflectRef::Enum(e) = reflect_b.reflect_ref() { + e + } else { + return Some(false); + }; + + if enum_a.variant_info() != enum_b.variant_info() { + return Some(false); + } + + let variant_b = enum_b.variant(); + match enum_a.variant() { + EnumVariant::Unit => { + if let EnumVariant::Unit = variant_b { + } else { + return Some(false); + } + } + EnumVariant::NewType(t_a) => { + if let EnumVariant::NewType(t_b) = variant_b { + if let Some(false) | None = t_b.reflect_partial_eq(t_a) { + return Some(false); + } + } else { + return Some(false); + } + } + EnumVariant::Tuple(t_a) => { + if let EnumVariant::Tuple(t_b) = variant_b { + if let Some(false) | None = t_b.reflect_partial_eq(t_a.as_reflect()) { + return Some(false); + } + } else { + return Some(false); + } + } + EnumVariant::Struct(s_a) => { + if let EnumVariant::Struct(s_b) = variant_b { + if let Some(false) | None = s_b.reflect_partial_eq(s_a.as_reflect()) { + return Some(false); + } + } else { + return Some(false); + } + } + } + Some(true) +}