From d6b3e57b8b3cdd516b3f5de4d7f8f7bbbab2a906 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 2 Aug 2022 22:14:41 +0000 Subject: [PATCH] bevy_reflect: Reflect enums (#4761) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ```
Effects on Option This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ```
##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions
View Section This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option = None; let my_enum = Box::new(foo) as Box; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums.
--- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente --- .../src/container_attributes.rs | 2 +- .../bevy_reflect_derive/src/derive_data.rs | 329 ++++++--- .../bevy_reflect_derive/src/enum_utility.rs | 84 +++ .../bevy_reflect_derive/src/from_reflect.rs | 79 ++- .../bevy_reflect_derive/src/impls.rs | 492 ------------- .../bevy_reflect_derive/src/impls/enums.rs | 363 ++++++++++ .../bevy_reflect_derive/src/impls/mod.rs | 11 + .../bevy_reflect_derive/src/impls/structs.rs | 201 ++++++ .../src/impls/tuple_structs.rs | 162 +++++ .../bevy_reflect_derive/src/impls/typed.rs | 38 + .../bevy_reflect_derive/src/impls/values.rs | 105 +++ .../bevy_reflect_derive/src/lib.rs | 115 ++-- .../bevy_reflect_derive/src/utility.rs | 77 ++- crates/bevy_reflect/src/enums/dynamic_enum.rs | 375 ++++++++++ crates/bevy_reflect/src/enums/enum_trait.rs | 280 ++++++++ crates/bevy_reflect/src/enums/helpers.rs | 121 ++++ crates/bevy_reflect/src/enums/mod.rs | 651 ++++++++++++++++++ crates/bevy_reflect/src/enums/variants.rs | 231 +++++++ crates/bevy_reflect/src/impls/std.rs | 292 +++++++- crates/bevy_reflect/src/lib.rs | 29 +- crates/bevy_reflect/src/reflect.rs | 8 +- crates/bevy_reflect/src/serde/de.rs | 200 +++++- crates/bevy_reflect/src/serde/mod.rs | 2 + crates/bevy_reflect/src/serde/ser.rs | 229 +++++- crates/bevy_reflect/src/struct_trait.rs | 5 + crates/bevy_reflect/src/type_info.rs | 7 +- crates/bevy_render/src/camera/camera.rs | 3 +- crates/bevy_render/src/camera/projection.rs | 11 +- crates/bevy_window/Cargo.toml | 1 + crates/bevy_window/src/window.rs | 4 +- examples/reflection/reflection_types.rs | 33 +- 31 files changed, 3816 insertions(+), 724 deletions(-) create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs delete mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs create mode 100644 crates/bevy_reflect/src/enums/dynamic_enum.rs create mode 100644 crates/bevy_reflect/src/enums/enum_trait.rs create mode 100644 crates/bevy_reflect/src/enums/helpers.rs create mode 100644 crates/bevy_reflect/src/enums/mod.rs create mode 100644 crates/bevy_reflect/src/enums/variants.rs diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 49190c83a239e..99513ed3acbd4 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -97,7 +97,7 @@ pub(crate) enum TraitImpl { /// /// > __Note:__ Registering a custom function only works for special traits. /// -#[derive(Default)] +#[derive(Default, Clone)] pub(crate) struct ReflectTraits { debug: TraitImpl, hash: TraitImpl, diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index 219fe8158bcac..422809d6db804 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -1,61 +1,112 @@ use crate::container_attributes::ReflectTraits; use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr}; -use crate::utility::get_bevy_reflect_path; -use crate::{REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; -use syn::{Data, DataStruct, DeriveInput, Field, Fields, Generics, Ident, Meta, Path}; - -pub(crate) enum DeriveType { - Struct, - TupleStruct, - UnitStruct, - Value, -} +use quote::quote; -/// Represents a field on a struct or tuple struct. -pub(crate) struct StructField<'a> { - /// The raw field. - pub data: &'a Field, - /// The reflection-based attributes on the field. - pub attrs: ReflectFieldAttr, - /// The index of this field within the struct. - pub index: usize, +use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Token, Type, Variant}; + +pub(crate) enum ReflectDerive<'a> { + Struct(ReflectStruct<'a>), + TupleStruct(ReflectStruct<'a>), + UnitStruct(ReflectStruct<'a>), + Enum(ReflectEnum<'a>), + Value(ReflectMeta<'a>), } -/// Data used by derive macros for `Reflect` and `FromReflect` +/// Metadata present on all reflected types, including name, generics, and attributes. /// /// # Example +/// /// ```ignore -/// // attrs +/// #[derive(Reflect)] +/// // traits /// // |----------------------------------------| /// #[reflect(PartialEq, Serialize, Deserialize, Default)] /// // type_name generics /// // |-------------------||----------| +/// struct ThingThatImReflecting {/* ... */} +/// ``` +pub(crate) struct ReflectMeta<'a> { + /// The registered traits for this type. + traits: ReflectTraits, + /// The name of this type. + type_name: &'a Ident, + /// The generics defined on this type. + generics: &'a Generics, + /// A cached instance of the path to the `bevy_reflect` crate. + bevy_reflect_path: Path, +} + +/// Struct data used by derive macros for `Reflect` and `FromReflect`. +/// +/// # Example +/// +/// ```ignore +/// #[derive(Reflect)] +/// #[reflect(PartialEq, Serialize, Deserialize, Default)] /// struct ThingThatImReflecting { /// x: T1, // | /// y: T2, // |- fields /// z: T3 // | /// } /// ``` -pub(crate) struct ReflectDeriveData<'a> { - derive_type: DeriveType, - traits: ReflectTraits, - type_name: &'a Ident, - generics: &'a Generics, +pub(crate) struct ReflectStruct<'a> { + meta: ReflectMeta<'a>, fields: Vec>, - bevy_reflect_path: Path, } -impl<'a> ReflectDeriveData<'a> { - pub fn from_input(input: &'a DeriveInput) -> Result { - let mut output = Self { - type_name: &input.ident, - derive_type: DeriveType::Value, - generics: &input.generics, - fields: Vec::new(), - traits: ReflectTraits::default(), - bevy_reflect_path: get_bevy_reflect_path(), - }; +/// Enum data used by derive macros for `Reflect` and `FromReflect`. +/// +/// # Example +/// +/// ```ignore +/// #[derive(Reflect)] +/// #[reflect(PartialEq, Serialize, Deserialize, Default)] +/// enum ThingThatImReflecting { +/// A(T1), // | +/// B, // |- variants +/// C { foo: T2, bar: T3 } // | +/// } +/// ``` +pub(crate) struct ReflectEnum<'a> { + meta: ReflectMeta<'a>, + variants: Vec>, +} +/// Represents a field on a struct or tuple struct. +pub(crate) struct StructField<'a> { + /// The raw field. + pub data: &'a Field, + /// The reflection-based attributes on the field. + pub attrs: ReflectFieldAttr, + /// The index of this field within the struct. + pub index: usize, +} + +/// Represents a variant on an enum. +pub(crate) struct EnumVariant<'a> { + /// The raw variant. + pub data: &'a Variant, + /// The fields within this variant. + pub fields: EnumVariantFields<'a>, + /// The reflection-based attributes on the variant. + pub attrs: ReflectFieldAttr, + /// The index of this variant within the enum. + #[allow(dead_code)] + pub index: usize, +} + +pub(crate) enum EnumVariantFields<'a> { + Named(Vec>), + Unnamed(Vec>), + Unit, +} + +impl<'a> ReflectDerive<'a> { + pub fn from_input(input: &'a DeriveInput) -> Result { + let mut traits = ReflectTraits::default(); // Should indicate whether `#[reflect_value]` was used let mut force_reflect_value = false; @@ -68,95 +119,105 @@ impl<'a> ReflectDeriveData<'a> { if let Some(ident) = meta_list.path.get_ident() { if ident == REFLECT_ATTRIBUTE_NAME { - output.traits = ReflectTraits::from_nested_metas(&meta_list.nested); + traits = ReflectTraits::from_nested_metas(&meta_list.nested); } else if ident == REFLECT_VALUE_ATTRIBUTE_NAME { force_reflect_value = true; - output.traits = ReflectTraits::from_nested_metas(&meta_list.nested); + traits = ReflectTraits::from_nested_metas(&meta_list.nested); } } } - let fields = match &input.data { - Data::Struct(DataStruct { - fields: Fields::Named(fields), - .. - }) => { - if !force_reflect_value { - output.derive_type = DeriveType::Struct; - } - &fields.named - } - Data::Struct(DataStruct { - fields: Fields::Unnamed(fields), - .. - }) => { - if !force_reflect_value { - output.derive_type = DeriveType::TupleStruct; - } - &fields.unnamed - } - Data::Struct(DataStruct { - fields: Fields::Unit, - .. - }) => { - if !force_reflect_value { - output.derive_type = DeriveType::UnitStruct; + let meta = ReflectMeta::new(&input.ident, &input.generics, traits); + + if force_reflect_value { + return Ok(Self::Value(meta)); + } + + return match &input.data { + Data::Struct(data) => { + let reflect_struct = ReflectStruct { + meta, + fields: Self::collect_struct_fields(&data.fields)?, + }; + + match data.fields { + Fields::Named(..) => Ok(Self::Struct(reflect_struct)), + Fields::Unnamed(..) => Ok(Self::TupleStruct(reflect_struct)), + Fields::Unit => Ok(Self::UnitStruct(reflect_struct)), } - return Ok(output); } - _ => { - return Ok(output); + Data::Enum(data) => { + let reflect_enum = ReflectEnum { + meta, + variants: Self::collect_enum_variants(&data.variants)?, + }; + Ok(Self::Enum(reflect_enum)) } + Data::Union(..) => Err(syn::Error::new( + input.span(), + "reflection not supported for unions", + )), }; + } - let mut errors: Option = None; - output.fields = fields + fn collect_struct_fields(fields: &'a Fields) -> Result>, syn::Error> { + let sifter: utility::ResultSifter> = fields .iter() .enumerate() - .map(|(index, field)| { - let attrs = parse_field_attrs(&field.attrs).unwrap_or_else(|err| { - if let Some(ref mut errors) = errors { - errors.combine(err); - } else { - errors = Some(err); - } - ReflectFieldAttr::default() - }); - - StructField { + .map(|(index, field)| -> Result { + let attrs = parse_field_attrs(&field.attrs)?; + Ok(StructField { index, attrs, data: field, - } + }) }) - .collect::>(); - if let Some(errs) = errors { - return Err(errs); - } + .fold( + utility::ResultSifter::default(), + utility::ResultSifter::fold, + ); - Ok(output) + sifter.finish() } - /// Get an iterator over the active fields - pub fn active_fields(&self) -> impl Iterator> { - self.fields.iter().filter(|field| !field.attrs.ignore) - } + fn collect_enum_variants( + variants: &'a Punctuated, + ) -> Result>, syn::Error> { + let sifter: utility::ResultSifter> = variants + .iter() + .enumerate() + .map(|(index, variant)| -> Result { + let fields = Self::collect_struct_fields(&variant.fields)?; - /// Get an iterator over the ignored fields - pub fn ignored_fields(&self) -> impl Iterator> { - self.fields.iter().filter(|field| field.attrs.ignore) - } + 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( + utility::ResultSifter::default(), + utility::ResultSifter::fold, + ); - /// Get a collection of all active types - pub fn active_types(&self) -> Vec { - self.active_fields() - .map(|field| field.data.ty.clone()) - .collect::>() + sifter.finish() } +} - /// The [`DeriveType`] of this struct. - pub fn derive_type(&self) -> &DeriveType { - &self.derive_type +impl<'a> ReflectMeta<'a> { + pub fn new(type_name: &'a Ident, generics: &'a Generics, traits: ReflectTraits) -> Self { + Self { + traits, + type_name, + generics, + bevy_reflect_path: utility::get_bevy_reflect_path(), + } } /// The registered reflect traits on this struct. @@ -174,12 +235,6 @@ impl<'a> ReflectDeriveData<'a> { self.generics } - /// The complete set of fields in this struct. - #[allow(dead_code)] - pub fn fields(&self) -> &[StructField<'a>] { - &self.fields - } - /// The cached `bevy_reflect` path. pub fn bevy_reflect_path(&self) -> &Path { &self.bevy_reflect_path @@ -195,3 +250,63 @@ impl<'a> ReflectDeriveData<'a> { ) } } + +impl<'a> ReflectStruct<'a> { + /// Access the metadata associated with this struct definition. + pub fn meta(&self) -> &ReflectMeta<'a> { + &self.meta + } + + /// Get an iterator over the active fields. + pub fn active_fields(&self) -> impl Iterator> { + self.fields.iter().filter(|field| !field.attrs.ignore) + } + + /// Get an iterator over the ignored fields. + pub fn ignored_fields(&self) -> impl Iterator> { + self.fields.iter().filter(|field| field.attrs.ignore) + } + + /// Get a collection of all active types. + pub fn active_types(&self) -> impl Iterator { + self.active_fields().map(|field| &field.data.ty) + } + + /// The complete set of fields in this struct. + #[allow(dead_code)] + pub fn fields(&self) -> &[StructField<'a>] { + &self.fields + } +} + +impl<'a> ReflectEnum<'a> { + /// Access the metadata associated with this enum definition. + pub fn meta(&self) -> &ReflectMeta<'a> { + &self.meta + } + + /// Get an iterator over the active variants. + pub fn active_variants(&self) -> impl Iterator> { + self.variants.iter().filter(|variant| !variant.attrs.ignore) + } + + /// Get an iterator over the ignored variants. + #[allow(dead_code)] + pub fn ignored_variants(&self) -> impl Iterator> { + self.variants.iter().filter(|variant| variant.attrs.ignore) + } + + /// Returns the given ident as a qualified unit variant of this enum. + pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream { + let name = self.meta.type_name; + quote! { + #name::#variant + } + } + + /// The complete set of variants in this enum. + #[allow(dead_code)] + pub fn variants(&self) -> &[EnumVariant<'a>] { + &self.variants + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs new file mode 100644 index 0000000000000..427db8a377e57 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs @@ -0,0 +1,84 @@ +use crate::{ + derive_data::{EnumVariantFields, ReflectEnum}, + utility::ident_or_index, +}; +use proc_macro2::Ident; +use quote::{quote, ToTokens}; + +/// Contains all data needed to construct all variants within an enum. +pub(crate) struct EnumVariantConstructors { + /// The names of each variant as a string. + pub variant_names: Vec, + /// The stream of tokens that will construct each variant. + pub variant_constructors: Vec, +} + +/// 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 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 variant_constructor = reflect_enum.get_unit(ident); + + let fields = match &variant.fields { + EnumVariantFields::Unit => &[], + EnumVariantFields::Named(fields) | 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() + ); + quote!(.expect(#type_err_message)) + } else { + quote!(?) + }; + let field_accessor = match &field.data.ident { + Some(ident) => { + let name = ident.to_string(); + quote!(.field(#name)) + } + 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 + } + }; + quote! { #field_ident : #field_value } + }); + variant_constructors.push(quote! { + #variant_constructor { #( #constructor_fields ),* } + }); + variant_names.push(name); + } + + EnumVariantConstructors { + variant_names, + variant_constructors, + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs index b4c9c345f26eb..73e4cb6451108 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -1,28 +1,28 @@ use crate::container_attributes::REFLECT_DEFAULT; +use crate::derive_data::ReflectEnum; +use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; use crate::field_attributes::DefaultBehavior; -use crate::ReflectDeriveData; +use crate::{ReflectMeta, ReflectStruct}; use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; -use syn::{Field, Generics, Ident, Index, Lit, LitInt, LitStr, Member, Path}; +use syn::{Field, Ident, Index, Lit, LitInt, LitStr, Member}; /// Implements `FromReflect` for the given struct -pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { - impl_struct_internal(derive_data, false) +pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { + impl_struct_internal(reflect_struct, false) } /// Implements `FromReflect` for the given tuple struct -pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { - impl_struct_internal(derive_data, true) +pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { + impl_struct_internal(reflect_struct, true) } /// Implements `FromReflect` for the given value type -pub(crate) fn impl_value( - type_name: &Ident, - generics: &Generics, - bevy_reflect_path: &Path, -) -> TokenStream { - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); +pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { + let type_name = meta.type_name(); + let bevy_reflect_path = meta.bevy_reflect_path(); + let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); TokenStream::from(quote! { impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> Option { @@ -32,6 +32,35 @@ pub(crate) fn impl_value( }) } +/// Implements `FromReflect` for the given enum type +pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { + let type_name = reflect_enum.meta().type_name(); + let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); + + let ref_value = Ident::new("__param0", Span::call_site()); + let EnumVariantConstructors { + variant_names, + variant_constructors, + } = get_variant_constructors(reflect_enum, &ref_value, false); + + let (impl_generics, ty_generics, where_clause) = + reflect_enum.meta().generics().split_for_impl(); + TokenStream::from(quote! { + impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { + fn from_reflect(#ref_value: &dyn #bevy_reflect_path::Reflect) -> Option { + if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #ref_value.reflect_ref() { + match #ref_value.variant_name() { + #(#variant_names => Some(#variant_constructors),)* + name => panic!("variant with name `{}` does not exist on enum `{}`", name, std::any::type_name::()), + } + } else { + None + } + } + } + }) +} + /// Container for a struct's members (field name or index) and their /// corresponding values. struct MemberValuePair(Vec, Vec); @@ -42,10 +71,10 @@ impl MemberValuePair { } } -fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> TokenStream { - let struct_name = derive_data.type_name(); - let generics = derive_data.generics(); - let bevy_reflect_path = derive_data.bevy_reflect_path(); +fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> TokenStream { + let struct_name = reflect_struct.meta().type_name(); + let generics = reflect_struct.meta().generics(); + let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); let ref_struct = Ident::new("__ref_struct", Span::call_site()); let ref_struct_type = if is_tuple { @@ -54,11 +83,11 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke Ident::new("Struct", Span::call_site()) }; - let field_types = derive_data.active_types(); + let field_types = reflect_struct.active_types(); let MemberValuePair(active_members, active_values) = - get_active_fields(derive_data, &ref_struct, &ref_struct_type, is_tuple); + get_active_fields(reflect_struct, &ref_struct, &ref_struct_type, is_tuple); - let constructor = if derive_data.traits().contains(REFLECT_DEFAULT) { + let constructor = if reflect_struct.meta().traits().contains(REFLECT_DEFAULT) { quote!( let mut __this = Self::default(); #( @@ -71,7 +100,7 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke ) } else { let MemberValuePair(ignored_members, ignored_values) = - get_ignored_fields(derive_data, is_tuple); + get_ignored_fields(reflect_struct, is_tuple); quote!( Some( @@ -115,9 +144,9 @@ fn impl_struct_internal(derive_data: &ReflectDeriveData, is_tuple: bool) -> Toke /// /// Each value of the `MemberValuePair` is a token stream that generates a /// a default value for the ignored field. -fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> MemberValuePair { +fn get_ignored_fields(reflect_struct: &ReflectStruct, is_tuple: bool) -> MemberValuePair { MemberValuePair::new( - derive_data + reflect_struct .ignored_fields() .map(|field| { let member = get_ident(field.data, field.index, is_tuple); @@ -138,15 +167,15 @@ fn get_ignored_fields(derive_data: &ReflectDeriveData, is_tuple: bool) -> Member /// Each value of the `MemberValuePair` is a token stream that generates a /// closure of type `fn() -> Option` where `T` is that field's type. fn get_active_fields( - derive_data: &ReflectDeriveData, + reflect_struct: &ReflectStruct, dyn_struct_name: &Ident, struct_type: &Ident, is_tuple: bool, ) -> MemberValuePair { - let bevy_reflect_path = derive_data.bevy_reflect_path(); + let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); MemberValuePair::new( - derive_data + reflect_struct .active_fields() .map(|field| { let member = get_ident(field.data, field.index, is_tuple); diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs deleted file mode 100644 index 38680a35adf38..0000000000000 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs +++ /dev/null @@ -1,492 +0,0 @@ -use crate::container_attributes::ReflectTraits; -use crate::ReflectDeriveData; -use proc_macro::TokenStream; -use proc_macro2::Ident; -use quote::quote; -use syn::{Generics, Index, Member, Path}; - -/// Implements `Struct`, `GetTypeRegistration`, and `Reflect` for the given derive data. -pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { - let bevy_reflect_path = derive_data.bevy_reflect_path(); - let struct_name = derive_data.type_name(); - - let field_names = derive_data - .active_fields() - .map(|field| { - field - .data - .ident - .as_ref() - .map(|i| i.to_string()) - .unwrap_or_else(|| field.index.to_string()) - }) - .collect::>(); - let field_idents = derive_data - .active_fields() - .map(|field| { - field - .data - .ident - .as_ref() - .map(|ident| Member::Named(ident.clone())) - .unwrap_or_else(|| Member::Unnamed(Index::from(field.index))) - }) - .collect::>(); - let field_types = derive_data - .active_fields() - .map(|field| field.data.ty.clone()) - .collect::>(); - let field_count = field_idents.len(); - let field_indices = (0..field_count).collect::>(); - - let hash_fn = derive_data.traits().get_hash_impl(bevy_reflect_path); - let partial_eq_fn = derive_data - .traits() - .get_partial_eq_impl(bevy_reflect_path) - .unwrap_or_else(|| { - quote! { - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #bevy_reflect_path::struct_partial_eq(self, value) - } - } - }); - let debug_fn = derive_data.traits().get_debug_impl(); - - let typed_impl = impl_typed( - struct_name, - derive_data.generics(), - quote! { - let fields: [#bevy_reflect_path::NamedField; #field_count] = [ - #(#bevy_reflect_path::NamedField::new::<#field_types, _>(#field_names),)* - ]; - let info = #bevy_reflect_path::StructInfo::new::(&fields); - #bevy_reflect_path::TypeInfo::Struct(info) - }, - bevy_reflect_path, - ); - - let get_type_registration_impl = derive_data.get_type_registration(); - let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); - - TokenStream::from(quote! { - #get_type_registration_impl - - #typed_impl - - impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause { - fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { - match name { - #(#field_names => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match name { - #(#field_names => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn name_at(&self, index: usize) -> Option<&str> { - match index { - #(#field_indices => Some(#field_names),)* - _ => None, - } - } - - fn field_len(&self) -> usize { - #field_count - } - - fn iter_fields(&self) -> #bevy_reflect_path::FieldIter { - #bevy_reflect_path::FieldIter::new(self) - } - - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { - let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); - dynamic.set_name(self.type_name().to_string()); - #(dynamic.insert_boxed(#field_names, self.#field_idents.clone_value());)* - dynamic - } - } - - impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { - ::type_info() - } - - #[inline] - fn into_any(self: Box) -> Box { - self - } - - #[inline] - fn as_any(&self) -> &dyn std::any::Any { - self - } - - #[inline] - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - Box::new(#bevy_reflect_path::Struct::clone_dynamic(self)) - } - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() { - for (i, value) in struct_value.iter_fields().enumerate() { - let name = struct_value.name_at(i).unwrap(); - #bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value)); - } - } else { - panic!("Attempted to apply non-struct type to struct type."); - } - } - - 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) - } - - #hash_fn - - #partial_eq_fn - - #debug_fn - } - }) -} - -/// Implements `TupleStruct`, `GetTypeRegistration`, and `Reflect` for the given derive data. -pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream { - let bevy_reflect_path = derive_data.bevy_reflect_path(); - let struct_name = derive_data.type_name(); - let get_type_registration_impl = derive_data.get_type_registration(); - - let field_idents = derive_data - .active_fields() - .map(|field| Member::Unnamed(Index::from(field.index))) - .collect::>(); - let field_types = derive_data - .active_fields() - .map(|field| field.data.ty.clone()) - .collect::>(); - let field_count = field_idents.len(); - let field_indices = (0..field_count).collect::>(); - - let hash_fn = derive_data.traits().get_hash_impl(bevy_reflect_path); - let partial_eq_fn = derive_data - .traits() - .get_partial_eq_impl(bevy_reflect_path) - .unwrap_or_else(|| { - quote! { - fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { - #bevy_reflect_path::tuple_struct_partial_eq(self, value) - } - } - }); - let debug_fn = derive_data.traits().get_debug_impl(); - - let typed_impl = impl_typed( - struct_name, - derive_data.generics(), - quote! { - let fields: [#bevy_reflect_path::UnnamedField; #field_count] = [ - #(#bevy_reflect_path::UnnamedField::new::<#field_types>(#field_indices),)* - ]; - let info = #bevy_reflect_path::TupleStructInfo::new::(&fields); - #bevy_reflect_path::TypeInfo::TupleStruct(info) - }, - bevy_reflect_path, - ); - - let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); - TokenStream::from(quote! { - #get_type_registration_impl - - #typed_impl - - impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_clause { - fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&self.#field_idents),)* - _ => None, - } - } - - fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { - match index { - #(#field_indices => Some(&mut self.#field_idents),)* - _ => None, - } - } - - fn field_len(&self) -> usize { - #field_count - } - - fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter { - #bevy_reflect_path::TupleStructFieldIter::new(self) - } - - fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct { - let mut dynamic = #bevy_reflect_path::DynamicTupleStruct::default(); - dynamic.set_name(self.type_name().to_string()); - #(dynamic.insert_boxed(self.#field_idents.clone_value());)* - dynamic - } - } - - impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { - ::type_info() - } - - #[inline] - fn into_any(self: Box) -> Box { - self - } - - #[inline] - fn as_any(&self) -> &dyn std::any::Any { - self - } - - #[inline] - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - Box::new(#bevy_reflect_path::TupleStruct::clone_dynamic(self)) - } - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = value.reflect_ref() { - for (i, value) in struct_value.iter_fields().enumerate() { - #bevy_reflect_path::TupleStruct::field_mut(self, i).map(|v| v.apply(value)); - } - } else { - panic!("Attempted to apply non-TupleStruct type to TupleStruct type."); - } - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::TupleStruct(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::TupleStruct(self) - } - - #hash_fn - - #partial_eq_fn - - #debug_fn - } - }) -} - -/// Implements `GetTypeRegistration` and `Reflect` for the given type data. -pub(crate) fn impl_value( - type_name: &Ident, - generics: &Generics, - get_type_registration_impl: proc_macro2::TokenStream, - bevy_reflect_path: &Path, - reflect_traits: &ReflectTraits, -) -> TokenStream { - let hash_fn = reflect_traits.get_hash_impl(bevy_reflect_path); - let partial_eq_fn = reflect_traits.get_partial_eq_impl(bevy_reflect_path); - let debug_fn = reflect_traits.get_debug_impl(); - - let typed_impl = impl_typed( - type_name, - generics, - quote! { - let info = #bevy_reflect_path::ValueInfo::new::(); - #bevy_reflect_path::TypeInfo::Value(info) - }, - bevy_reflect_path, - ); - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - TokenStream::from(quote! { - #get_type_registration_impl - - #typed_impl - - impl #impl_generics #bevy_reflect_path::Reflect for #type_name #ty_generics #where_clause { - #[inline] - fn type_name(&self) -> &str { - std::any::type_name::() - } - - #[inline] - fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { - ::type_info() - } - - #[inline] - fn into_any(self: Box) -> Box { - self - } - - #[inline] - fn as_any(&self) -> &dyn std::any::Any { - self - } - - #[inline] - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - #[inline] - fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { - self - } - - #[inline] - fn clone_value(&self) -> Box { - Box::new(self.clone()) - } - - #[inline] - fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { - let value = value.as_any(); - if let Some(value) = value.downcast_ref::() { - *self = value.clone(); - } else { - panic!("Value is not {}.", std::any::type_name::()); - } - } - - #[inline] - fn set(&mut self, value: Box) -> Result<(), Box> { - *self = value.take()?; - Ok(()) - } - - fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { - #bevy_reflect_path::ReflectRef::Value(self) - } - - fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { - #bevy_reflect_path::ReflectMut::Value(self) - } - - #hash_fn - - #partial_eq_fn - - #debug_fn - } - }) -} - -fn impl_typed( - type_name: &Ident, - generics: &Generics, - generator: proc_macro2::TokenStream, - bevy_reflect_path: &Path, -) -> proc_macro2::TokenStream { - let is_generic = !generics.params.is_empty(); - - let static_generator = if is_generic { - quote! { - static CELL: #bevy_reflect_path::utility::GenericTypeInfoCell = #bevy_reflect_path::utility::GenericTypeInfoCell::new(); - CELL.get_or_insert::(|| { - #generator - }) - } - } else { - quote! { - static CELL: #bevy_reflect_path::utility::NonGenericTypeInfoCell = #bevy_reflect_path::utility::NonGenericTypeInfoCell::new(); - CELL.get_or_set(|| { - #generator - }) - } - }; - - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - quote! { - impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_clause { - fn type_info() -> &'static #bevy_reflect_path::TypeInfo { - #static_generator - } - } - } -} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs new file mode 100644 index 0000000000000..6140f853eee69 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -0,0 +1,363 @@ +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::quote; + +pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { + let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); + let enum_name = reflect_enum.meta().type_name(); + + let ref_name = Ident::new("__name_param", Span::call_site()); + let ref_index = Ident::new("__index_param", Span::call_site()); + let ref_value = Ident::new("__value_param", Span::call_site()); + + let EnumImpls { + variant_info, + enum_field, + enum_field_at, + enum_index_of, + enum_name_at, + enum_field_len, + enum_variant_name, + enum_variant_type, + } = generate_impls(reflect_enum, &ref_index, &ref_name); + + let EnumVariantConstructors { + variant_names, + variant_constructors, + } = get_variant_constructors(reflect_enum, &ref_value, true); + + let hash_fn = reflect_enum + .meta() + .traits() + .get_hash_impl(bevy_reflect_path) + .unwrap_or_else(|| { + quote! { + fn reflect_hash(&self) -> Option { + #bevy_reflect_path::enum_hash(self) + } + } + }); + let debug_fn = reflect_enum.meta().traits().get_debug_impl(); + let partial_eq_fn = reflect_enum + .meta() + .traits() + .get_partial_eq_impl(bevy_reflect_path) + .unwrap_or_else(|| { + quote! { + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #bevy_reflect_path::enum_partial_eq(self, value) + } + } + }); + + let typed_impl = impl_typed( + enum_name, + reflect_enum.meta().generics(), + quote! { + let variants = [#(#variant_info),*]; + let info = #bevy_reflect_path::EnumInfo::new::(&variants); + #bevy_reflect_path::TypeInfo::Enum(info) + }, + bevy_reflect_path, + ); + + let get_type_registration_impl = reflect_enum.meta().get_type_registration(); + let (impl_generics, ty_generics, where_clause) = + reflect_enum.meta().generics().split_for_impl(); + + TokenStream::from(quote! { + #get_type_registration_impl + + #typed_impl + + impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_clause { + fn field(&self, #ref_name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { + match self { + #(#enum_field,)* + _ => None, + } + } + + fn field_at(&self, #ref_index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + match self { + #(#enum_field_at,)* + _ => None, + } + } + + fn field_mut(&mut self, #ref_name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match self { + #(#enum_field,)* + _ => None, + } + } + + fn field_at_mut(&mut self, #ref_index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match self { + #(#enum_field_at,)* + _ => None, + } + } + + fn index_of(&self, #ref_name: &str) -> Option { + match self { + #(#enum_index_of,)* + _ => None, + } + } + + fn name_at(&self, #ref_index: usize) -> Option<&str> { + match self { + #(#enum_name_at,)* + _ => None, + } + } + + fn iter_fields(&self) -> #bevy_reflect_path::VariantFieldIter { + #bevy_reflect_path::VariantFieldIter::new(self) + } + + #[inline] + fn field_len(&self) -> usize { + match self { + #(#enum_field_len,)* + _ => 0, + } + } + + #[inline] + fn variant_name(&self) -> &str { + match self { + #(#enum_variant_name,)* + _ => unreachable!(), + } + } + + #[inline] + fn variant_type(&self) -> #bevy_reflect_path::VariantType { + match self { + #(#enum_variant_type,)* + _ => unreachable!(), + } + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicEnum { + #bevy_reflect_path::DynamicEnum::from_ref::(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 get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(#bevy_reflect_path::Enum::clone_dynamic(self)) + } + + #[inline] + fn set(&mut self, #ref_value: Box) -> Result<(), Box> { + *self = #ref_value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, #ref_value: &dyn #bevy_reflect_path::Reflect) { + if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #ref_value.reflect_ref() { + if #bevy_reflect_path::Enum::variant_name(self) == #ref_value.variant_name() { + // Same variant -> just update fields + match #ref_value.variant_type() { + #bevy_reflect_path::VariantType::Struct => { + for field in #ref_value.iter_fields() { + let name = field.name().unwrap(); + #bevy_reflect_path::Enum::field_mut(self, name).map(|v| v.apply(field.value())); + } + } + #bevy_reflect_path::VariantType::Tuple => { + for (index, field) in #ref_value.iter_fields().enumerate() { + #bevy_reflect_path::Enum::field_at_mut(self, index).map(|v| v.apply(field.value())); + } + } + _ => {} + } + } else { + // New variant -> perform a switch + match #ref_value.variant_name() { + #(#variant_names => { + *self = #variant_constructors + })* + name => panic!("variant with name `{}` does not exist on enum `{}`", name, std::any::type_name::()), + } + } + } else { + panic!("`{}` is not an enum", #ref_value.type_name()); + } + } + + 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) + } + + #hash_fn + + #partial_eq_fn + + #debug_fn + } + }) +} + +struct EnumImpls { + variant_info: Vec, + enum_field: Vec, + enum_field_at: Vec, + enum_index_of: Vec, + enum_name_at: Vec, + enum_field_len: Vec, + enum_variant_name: Vec, + enum_variant_type: Vec, +} + +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::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) { + 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 => { + add_fields_branch("Unit", "UnitVariantInfo", quote!(#name), 0usize); + } + EnumVariantFields::Unnamed(fields) => { + 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) } + }); + let arguments = quote!(#name, &[ #(#argument),* ]); + add_fields_branch("Tuple", "TupleVariantInfo", arguments, field_len); + } + EnumVariantFields::Named(fields) => { + 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! { + #unit{ #field_ident, .. } if #ref_name == #field_name => Some(#field_ident) + }); + enum_field_at.push(quote! { + #unit{ #field_ident, .. } if #ref_index == #reflect_idx => Some(#field_ident) + }); + enum_index_of.push(quote! { + #unit{ .. } if #ref_name == #field_name => Some(#reflect_idx) + }); + enum_name_at.push(quote! { + #unit{ .. } if #ref_index == #reflect_idx => Some(#field_name) + }); + + let field_ty = &field.data.ty; + quote! { #bevy_reflect_path::NamedField::new::<#field_ty, _>(#field_name) } + }); + let arguments = quote!(#name, &[ #(#argument),* ]); + add_fields_branch("Struct", "StructVariantInfo", arguments, field_len); + } + }; + } + + EnumImpls { + variant_info, + enum_field, + enum_field_at, + enum_index_of, + enum_name_at, + enum_field_len, + enum_variant_name, + enum_variant_type, + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs new file mode 100644 index 0000000000000..19523fbf806ba --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs @@ -0,0 +1,11 @@ +mod enums; +mod structs; +mod tuple_structs; +mod typed; +mod values; + +pub(crate) use enums::impl_enum; +pub(crate) use structs::impl_struct; +pub(crate) use tuple_structs::impl_tuple_struct; +pub(crate) use typed::impl_typed; +pub(crate) use values::impl_value; diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs new file mode 100644 index 0000000000000..4dca3a4feea16 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -0,0 +1,201 @@ +use crate::impls::impl_typed; +use crate::ReflectStruct; +use proc_macro::TokenStream; +use quote::quote; +use syn::{Index, Member}; + +/// Implements `Struct`, `GetTypeRegistration`, and `Reflect` for the given derive data. +pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { + let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); + let struct_name = reflect_struct.meta().type_name(); + + let field_names = reflect_struct + .active_fields() + .map(|field| { + field + .data + .ident + .as_ref() + .map(|i| i.to_string()) + .unwrap_or_else(|| field.index.to_string()) + }) + .collect::>(); + let field_idents = reflect_struct + .active_fields() + .map(|field| { + field + .data + .ident + .as_ref() + .map(|ident| Member::Named(ident.clone())) + .unwrap_or_else(|| Member::Unnamed(Index::from(field.index))) + }) + .collect::>(); + let field_types = reflect_struct.active_types(); + let field_count = field_idents.len(); + let field_indices = (0..field_count).collect::>(); + + let hash_fn = reflect_struct + .meta() + .traits() + .get_hash_impl(bevy_reflect_path); + let debug_fn = reflect_struct.meta().traits().get_debug_impl(); + let partial_eq_fn = reflect_struct.meta() + .traits() + .get_partial_eq_impl(bevy_reflect_path) + .unwrap_or_else(|| { + quote! { + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #bevy_reflect_path::struct_partial_eq(self, value) + } + } + }); + + let typed_impl = impl_typed( + struct_name, + reflect_struct.meta().generics(), + quote! { + let fields = [ + #(#bevy_reflect_path::NamedField::new::<#field_types, _>(#field_names),)* + ]; + let info = #bevy_reflect_path::StructInfo::new::(&fields); + #bevy_reflect_path::TypeInfo::Struct(info) + }, + bevy_reflect_path, + ); + + let get_type_registration_impl = reflect_struct.meta().get_type_registration(); + let (impl_generics, ty_generics, where_clause) = + reflect_struct.meta().generics().split_for_impl(); + + TokenStream::from(quote! { + #get_type_registration_impl + + #typed_impl + + impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_clause { + fn field(&self, name: &str) -> Option<&dyn #bevy_reflect_path::Reflect> { + match name { + #(#field_names => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match name { + #(#field_names => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn field_at(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn name_at(&self, index: usize) -> Option<&str> { + match index { + #(#field_indices => Some(#field_names),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #field_count + } + + fn iter_fields(&self) -> #bevy_reflect_path::FieldIter { + #bevy_reflect_path::FieldIter::new(self) + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicStruct { + let mut dynamic = #bevy_reflect_path::DynamicStruct::default(); + dynamic.set_name(self.type_name().to_string()); + #(dynamic.insert_boxed(#field_names, self.#field_idents.clone_value());)* + dynamic + } + } + + impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(#bevy_reflect_path::Struct::clone_dynamic(self)) + } + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + if let #bevy_reflect_path::ReflectRef::Struct(struct_value) = value.reflect_ref() { + for (i, value) in struct_value.iter_fields().enumerate() { + let name = struct_value.name_at(i).unwrap(); + #bevy_reflect_path::Struct::field_mut(self, name).map(|v| v.apply(value)); + } + } else { + panic!("Attempted to apply non-struct type to struct type."); + } + } + + 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) + } + + #hash_fn + + #partial_eq_fn + + #debug_fn + } + }) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs new file mode 100644 index 0000000000000..0ad33ba4bb8ce --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -0,0 +1,162 @@ +use crate::impls::impl_typed; +use crate::ReflectStruct; +use proc_macro::TokenStream; +use quote::quote; +use syn::{Index, Member}; + +/// Implements `TupleStruct`, `GetTypeRegistration`, and `Reflect` for the given derive data. +pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { + let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); + let struct_name = reflect_struct.meta().type_name(); + let get_type_registration_impl = reflect_struct.meta().get_type_registration(); + + let field_idents = reflect_struct + .active_fields() + .map(|field| Member::Unnamed(Index::from(field.index))) + .collect::>(); + let field_types = reflect_struct.active_types(); + let field_count = field_idents.len(); + let field_indices = (0..field_count).collect::>(); + + let hash_fn = reflect_struct + .meta() + .traits() + .get_hash_impl(bevy_reflect_path); + let debug_fn = reflect_struct.meta().traits().get_debug_impl(); + let partial_eq_fn = reflect_struct + .meta() + .traits() + .get_partial_eq_impl(bevy_reflect_path) + .unwrap_or_else(|| { + quote! { + fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { + #bevy_reflect_path::tuple_struct_partial_eq(self, value) + } + } + }); + + let typed_impl = impl_typed( + struct_name, + reflect_struct.meta().generics(), + quote! { + let fields = [ + #(#bevy_reflect_path::UnnamedField::new::<#field_types>(#field_idents),)* + ]; + let info = #bevy_reflect_path::TupleStructInfo::new::(&fields); + #bevy_reflect_path::TypeInfo::TupleStruct(info) + }, + bevy_reflect_path, + ); + + let (impl_generics, ty_generics, where_clause) = + reflect_struct.meta().generics().split_for_impl(); + + TokenStream::from(quote! { + #get_type_registration_impl + + #typed_impl + + impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_clause { + fn field(&self, index: usize) -> Option<&dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&self.#field_idents),)* + _ => None, + } + } + + fn field_mut(&mut self, index: usize) -> Option<&mut dyn #bevy_reflect_path::Reflect> { + match index { + #(#field_indices => Some(&mut self.#field_idents),)* + _ => None, + } + } + + fn field_len(&self) -> usize { + #field_count + } + + fn iter_fields(&self) -> #bevy_reflect_path::TupleStructFieldIter { + #bevy_reflect_path::TupleStructFieldIter::new(self) + } + + fn clone_dynamic(&self) -> #bevy_reflect_path::DynamicTupleStruct { + let mut dynamic = #bevy_reflect_path::DynamicTupleStruct::default(); + dynamic.set_name(self.type_name().to_string()); + #(dynamic.insert_boxed(self.#field_idents.clone_value());)* + dynamic + } + } + + impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(#bevy_reflect_path::TupleStruct::clone_dynamic(self)) + } + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + if let #bevy_reflect_path::ReflectRef::TupleStruct(struct_value) = value.reflect_ref() { + for (i, value) in struct_value.iter_fields().enumerate() { + #bevy_reflect_path::TupleStruct::field_mut(self, i).map(|v| v.apply(value)); + } + } else { + panic!("Attempted to apply non-TupleStruct type to TupleStruct type."); + } + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::TupleStruct(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::TupleStruct(self) + } + + #hash_fn + + #partial_eq_fn + + #debug_fn + } + }) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs new file mode 100644 index 0000000000000..76d36f5869bb1 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs @@ -0,0 +1,38 @@ +use proc_macro2::Ident; +use quote::quote; +use syn::{Generics, Path}; + +pub(crate) fn impl_typed( + type_name: &Ident, + generics: &Generics, + generator: proc_macro2::TokenStream, + bevy_reflect_path: &Path, +) -> proc_macro2::TokenStream { + let is_generic = !generics.params.is_empty(); + + let static_generator = if is_generic { + quote! { + static CELL: #bevy_reflect_path::utility::GenericTypeInfoCell = #bevy_reflect_path::utility::GenericTypeInfoCell::new(); + CELL.get_or_insert::(|| { + #generator + }) + } + } else { + quote! { + static CELL: #bevy_reflect_path::utility::NonGenericTypeInfoCell = #bevy_reflect_path::utility::NonGenericTypeInfoCell::new(); + CELL.get_or_set(|| { + #generator + }) + } + }; + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + quote! { + impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_clause { + fn type_info() -> &'static #bevy_reflect_path::TypeInfo { + #static_generator + } + } + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs new file mode 100644 index 0000000000000..be01b2214fd11 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -0,0 +1,105 @@ +use crate::impls::impl_typed; +use crate::ReflectMeta; +use proc_macro::TokenStream; +use quote::quote; + +/// Implements `GetTypeRegistration` and `Reflect` for the given type data. +pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { + let bevy_reflect_path = meta.bevy_reflect_path(); + let type_name = meta.type_name(); + + let hash_fn = meta.traits().get_hash_impl(bevy_reflect_path); + let partial_eq_fn = meta.traits().get_partial_eq_impl(bevy_reflect_path); + let debug_fn = meta.traits().get_debug_impl(); + + let typed_impl = impl_typed( + type_name, + meta.generics(), + quote! { + let info = #bevy_reflect_path::ValueInfo::new::(); + #bevy_reflect_path::TypeInfo::Value(info) + }, + bevy_reflect_path, + ); + + let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); + let get_type_registration_impl = meta.get_type_registration(); + + TokenStream::from(quote! { + #get_type_registration_impl + + #typed_impl + + impl #impl_generics #bevy_reflect_path::Reflect for #type_name #ty_generics #where_clause { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn std::any::Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn #bevy_reflect_path::Reflect { + self + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(self.clone()) + } + + #[inline] + fn apply(&mut self, value: &dyn #bevy_reflect_path::Reflect) { + let value = value.as_any(); + if let Some(value) = value.downcast_ref::() { + *self = value.clone(); + } else { + panic!("Value is not {}.", std::any::type_name::()); + } + } + + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef { + #bevy_reflect_path::ReflectRef::Value(self) + } + + fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut { + #bevy_reflect_path::ReflectMut::Value(self) + } + + #hash_fn + + #partial_eq_fn + + #debug_fn + } + }) +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index 7cf9fe560b004..d7a7515683a11 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -16,6 +16,7 @@ extern crate proc_macro; mod container_attributes; mod derive_data; +mod enum_utility; mod field_attributes; mod from_reflect; mod impls; @@ -25,11 +26,11 @@ mod trait_reflection; mod type_uuid; mod utility; -use crate::derive_data::ReflectDeriveData; -use derive_data::DeriveType; +use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct}; use proc_macro::TokenStream; use quote::quote; use reflect_value::ReflectValueDef; +use syn::spanned::Spanned; use syn::{parse_macro_input, DeriveInput}; pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; @@ -39,21 +40,18 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); - let derive_data = match ReflectDeriveData::from_input(&ast) { + let derive_data = match ReflectDerive::from_input(&ast) { Ok(data) => data, Err(err) => return err.into_compile_error().into(), }; - match derive_data.derive_type() { - DeriveType::Struct | DeriveType::UnitStruct => impls::impl_struct(&derive_data), - DeriveType::TupleStruct => impls::impl_tuple_struct(&derive_data), - DeriveType::Value => impls::impl_value( - derive_data.type_name(), - derive_data.generics(), - derive_data.get_type_registration(), - derive_data.bevy_reflect_path(), - derive_data.traits(), - ), + match derive_data { + ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => { + impls::impl_struct(&struct_data) + } + ReflectDerive::TupleStruct(struct_data) => impls::impl_tuple_struct(&struct_data), + ReflectDerive::Enum(meta) => impls::impl_enum(&meta), + ReflectDerive::Value(meta) => impls::impl_value(&meta), } } @@ -68,19 +66,18 @@ pub fn derive_reflect(input: TokenStream) -> TokenStream { pub fn derive_from_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); - let derive_data = match ReflectDeriveData::from_input(&ast) { + let derive_data = match ReflectDerive::from_input(&ast) { Ok(data) => data, Err(err) => return err.into_compile_error().into(), }; - match derive_data.derive_type() { - DeriveType::Struct | DeriveType::UnitStruct => from_reflect::impl_struct(&derive_data), - DeriveType::TupleStruct => from_reflect::impl_tuple_struct(&derive_data), - DeriveType::Value => from_reflect::impl_value( - derive_data.type_name(), - &ast.generics, - derive_data.bevy_reflect_path(), - ), + match derive_data { + ReflectDerive::Struct(struct_data) | ReflectDerive::UnitStruct(struct_data) => { + from_reflect::impl_struct(&struct_data) + } + ReflectDerive::TupleStruct(struct_data) => from_reflect::impl_tuple_struct(&struct_data), + ReflectDerive::Enum(meta) => from_reflect::impl_enum(&meta), + ReflectDerive::Value(meta) => from_reflect::impl_value(&meta), } } @@ -97,25 +94,12 @@ pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro] pub fn impl_reflect_value(input: TokenStream) -> TokenStream { - let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - - let bevy_reflect_path = utility::get_bevy_reflect_path(); - let ty = &reflect_value_def.type_name; - let reflect_traits = reflect_value_def.traits.unwrap_or_default(); - let registration_data = &reflect_traits.idents(); - let get_type_registration_impl = registration::impl_get_type_registration( - ty, - &bevy_reflect_path, - registration_data, - &reflect_value_def.generics, - ); - impls::impl_value( - ty, - &reflect_value_def.generics, - get_type_registration_impl, - &bevy_reflect_path, - &reflect_traits, - ) + let def = parse_macro_input!(input as ReflectValueDef); + impls::impl_value(&ReflectMeta::new( + &def.type_name, + &def.generics, + def.traits.unwrap_or_default(), + )) } /// A replacement for `#[derive(Reflect)]` to be used with foreign types which @@ -149,26 +133,47 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream { #[proc_macro] pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); - let derive_data = match ReflectDeriveData::from_input(&ast) { + let derive_data = match ReflectDerive::from_input(&ast) { Ok(data) => data, Err(err) => return err.into_compile_error().into(), }; - let impl_struct: proc_macro2::TokenStream = impls::impl_struct(&derive_data).into(); - let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct(&derive_data).into(); - - TokenStream::from(quote! { - #impl_struct - - #impl_from_struct - }) + match derive_data { + ReflectDerive::Struct(struct_data) => { + let impl_struct: proc_macro2::TokenStream = impls::impl_struct(&struct_data).into(); + let impl_from_struct: proc_macro2::TokenStream = + from_reflect::impl_struct(&struct_data).into(); + + TokenStream::from(quote! { + #impl_struct + + #impl_from_struct + }) + } + ReflectDerive::TupleStruct(..) => syn::Error::new( + ast.span(), + "impl_reflect_struct does not support tuple structs", + ) + .into_compile_error() + .into(), + ReflectDerive::UnitStruct(..) => syn::Error::new( + ast.span(), + "impl_reflect_struct does not support unit structs", + ) + .into_compile_error() + .into(), + _ => syn::Error::new(ast.span(), "impl_reflect_struct only supports structs") + .into_compile_error() + .into(), + } } #[proc_macro] pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { - let reflect_value_def = parse_macro_input!(input as ReflectValueDef); - - let bevy_reflect_path = utility::get_bevy_reflect_path(); - let ty = &reflect_value_def.type_name; - from_reflect::impl_value(ty, &reflect_value_def.generics, &bevy_reflect_path) + let def = parse_macro_input!(input as ReflectValueDef); + from_reflect::impl_value(&ReflectMeta::new( + &def.type_name, + &def.generics, + def.traits.unwrap_or_default(), + )) } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 34fbdf186cc94..f8894689a3858 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -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 { @@ -21,3 +21,78 @@ pub(crate) fn get_reflect_ident(name: &str) -> Ident { let reflected = format!("Reflect{}", name); Ident::new(&reflected, Span::call_site()) } + +/// Helper struct used to process an iterator of `Result, syn::Error>`, +/// combining errors into one along the way. +pub(crate) struct ResultSifter { + items: Vec, + errors: Option, +} + +/// Returns a `Member` made of `ident` or `index` if `ident` is None. +/// +/// Rust struct syntax allows for `Struct { foo: "string" }` with explicitly +/// named fields. It allows the `Struct { 0: "string" }` syntax when the struct +/// is declared as a tuple struct. +/// +/// ``` +/// # fn main() { +/// struct Foo { field: &'static str } +/// struct Bar(&'static str); +/// let Foo { field } = Foo { field: "hi" }; +/// let Bar { 0: field } = Bar { 0: "hello" }; +/// let Bar(field) = Bar("hello"); // more common syntax +/// # } +/// ``` +/// +/// This function helps field access in context where you are declaring either +/// a tuple struct or a struct with named fields. If you don't have a field name, +/// it means you need to access the struct through an index. +pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { + // TODO(Quality) when #4761 is merged, code that does this should be replaced + // by a call to `ident_or_index`. + ident.map_or_else( + || Member::Unnamed(index.into()), + |ident| Member::Named(ident.clone()), + ) +} + +impl Default for ResultSifter { + fn default() -> Self { + Self { + items: Vec::new(), + errors: None, + } + } +} + +impl ResultSifter { + /// Sift the given result, combining errors if necessary. + pub fn sift(&mut self, result: Result) { + match result { + Ok(data) => self.items.push(data), + Err(err) => { + if let Some(ref mut errors) = self.errors { + errors.combine(err); + } else { + self.errors = Some(err); + } + } + } + } + + /// Associated method that provides a convenient implementation for [`Iterator::fold`]. + pub fn fold(mut sifter: Self, result: Result) -> Self { + sifter.sift(result); + sifter + } + + /// Complete the sifting process and return the final result. + pub fn finish(self) -> Result, syn::Error> { + if let Some(errors) = self.errors { + Err(errors) + } else { + Ok(self.items) + } + } +} diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs new file mode 100644 index 0000000000000..d0f097e977b1e --- /dev/null +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -0,0 +1,375 @@ +use crate::utility::NonGenericTypeInfoCell; +use crate::{ + enum_debug, enum_hash, enum_partial_eq, DynamicInfo, DynamicStruct, DynamicTuple, Enum, + Reflect, ReflectMut, ReflectRef, Struct, Tuple, TypeInfo, Typed, VariantFieldIter, VariantType, +}; +use std::any::Any; +use std::fmt::Formatter; + +/// A dynamic representation of an enum variant. +pub enum DynamicVariant { + Unit, + Tuple(DynamicTuple), + Struct(DynamicStruct), +} + +impl Clone for DynamicVariant { + fn clone(&self) -> Self { + match self { + DynamicVariant::Unit => DynamicVariant::Unit, + DynamicVariant::Tuple(data) => DynamicVariant::Tuple(data.clone_dynamic()), + DynamicVariant::Struct(data) => DynamicVariant::Struct(data.clone_dynamic()), + } + } +} + +impl Default for DynamicVariant { + fn default() -> Self { + DynamicVariant::Unit + } +} + +impl From for DynamicVariant { + fn from(dyn_tuple: DynamicTuple) -> Self { + Self::Tuple(dyn_tuple) + } +} + +impl From for DynamicVariant { + fn from(dyn_struct: DynamicStruct) -> Self { + Self::Struct(dyn_struct) + } +} + +impl From<()> for DynamicVariant { + fn from(_: ()) -> Self { + Self::Unit + } +} + +/// A dynamic representation of an enum. +/// +/// This allows for enums to be configured at runtime. +/// +/// # Example +/// +/// ``` +/// # use bevy_reflect::{DynamicEnum, DynamicVariant, Reflect}; +/// +/// // The original enum value +/// let mut value: Option = Some(123); +/// +/// // Create a DynamicEnum to represent the new value +/// let mut dyn_enum = DynamicEnum::new( +/// Reflect::type_name(&value), +/// "None", +/// DynamicVariant::Unit +/// ); +/// +/// // Apply the DynamicEnum as a patch to the original value +/// value.apply(&dyn_enum); +/// +/// // Tada! +/// assert_eq!(None, value); +/// ``` +#[derive(Default)] +pub struct DynamicEnum { + name: String, + variant_name: String, + variant: DynamicVariant, +} + +impl DynamicEnum { + /// Create a new [`DynamicEnum`] to represent an enum at runtime. + /// + /// # Arguments + /// + /// * `name`: The type name of the enum + /// * `variant_name`: The name of the variant to set + /// * `variant`: The variant data + /// + pub fn new, V: Into>( + name: I, + variant_name: I, + variant: V, + ) -> Self { + Self { + name: name.into(), + variant_name: variant_name.into(), + variant: variant.into(), + } + } + + /// Returns the type name of the enum. + pub fn name(&self) -> &str { + &self.name + } + + /// Sets the type name of the enum. + pub fn set_name(&mut self, name: String) { + self.name = name; + } + + /// Set the current enum variant represented by this struct. + pub fn set_variant, V: Into>(&mut self, name: I, variant: V) { + self.variant_name = name.into(); + self.variant = variant.into(); + } + + /// Create a [`DynamicEnum`] from an existing one. + /// + /// This is functionally the same as [`DynamicEnum::from_ref`] except it takes an owned value. + pub fn from(value: TEnum) -> Self { + Self::from_ref(&value) + } + + /// Create a [`DynamicEnum`] from an existing one. + /// + /// This is functionally the same as [`DynamicEnum::from`] except it takes a reference. + pub fn from_ref(value: &TEnum) -> Self { + match value.variant_type() { + VariantType::Unit => DynamicEnum::new( + value.type_name(), + value.variant_name(), + DynamicVariant::Unit, + ), + VariantType::Tuple => { + let mut data = DynamicTuple::default(); + for field in value.iter_fields() { + data.insert_boxed(field.value().clone_value()); + } + DynamicEnum::new( + value.type_name(), + value.variant_name(), + DynamicVariant::Tuple(data), + ) + } + VariantType::Struct => { + let mut data = DynamicStruct::default(); + for field in value.iter_fields() { + let name = field.name().unwrap(); + data.insert_boxed(name, field.value().clone_value()); + } + DynamicEnum::new( + value.type_name(), + value.variant_name(), + DynamicVariant::Struct(data), + ) + } + } + } +} + +impl Enum for DynamicEnum { + fn field(&self, name: &str) -> Option<&dyn Reflect> { + if let DynamicVariant::Struct(data) = &self.variant { + data.field(name) + } else { + None + } + } + + fn field_at(&self, index: usize) -> Option<&dyn Reflect> { + if let DynamicVariant::Tuple(data) = &self.variant { + data.field(index) + } else { + None + } + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect> { + if let DynamicVariant::Struct(data) = &mut self.variant { + data.field_mut(name) + } else { + None + } + } + + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> { + if let DynamicVariant::Tuple(data) = &mut self.variant { + data.field_mut(index) + } else { + None + } + } + + fn index_of(&self, name: &str) -> Option { + if let DynamicVariant::Struct(data) = &self.variant { + data.index_of(name) + } else { + None + } + } + + fn name_at(&self, index: usize) -> Option<&str> { + if let DynamicVariant::Struct(data) = &self.variant { + data.name_at(index) + } else { + None + } + } + + fn iter_fields(&self) -> VariantFieldIter { + VariantFieldIter::new(self) + } + + fn field_len(&self) -> usize { + match &self.variant { + DynamicVariant::Unit => 0, + DynamicVariant::Tuple(data) => data.field_len(), + DynamicVariant::Struct(data) => data.field_len(), + } + } + + fn variant_name(&self) -> &str { + &self.variant_name + } + + fn variant_type(&self) -> VariantType { + match &self.variant { + DynamicVariant::Unit => VariantType::Unit, + DynamicVariant::Tuple(..) => VariantType::Tuple, + DynamicVariant::Struct(..) => VariantType::Struct, + } + } + + fn clone_dynamic(&self) -> DynamicEnum { + Self { + name: self.name.clone(), + variant_name: self.variant_name.clone(), + variant: self.variant.clone(), + } + } +} + +impl Reflect for DynamicEnum { + #[inline] + fn type_name(&self) -> &str { + &self.name + } + + #[inline] + fn get_type_info(&self) -> &'static TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + #[inline] + fn as_reflect(&self) -> &dyn Reflect { + self + } + + #[inline] + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } + + #[inline] + fn apply(&mut self, value: &dyn Reflect) { + if let ReflectRef::Enum(value) = value.reflect_ref() { + if Enum::variant_name(self) == value.variant_name() { + // Same variant -> just update fields + match value.variant_type() { + VariantType::Struct => { + for field in value.iter_fields() { + let name = field.name().unwrap(); + if let Some(v) = Enum::field_mut(self, name) { + v.apply(field.value()); + } + } + } + VariantType::Tuple => { + for (index, field) in value.iter_fields().enumerate() { + if let Some(v) = Enum::field_at_mut(self, index) { + v.apply(field.value()); + } + } + } + _ => {} + } + } else { + // New variant -> perform a switch + let dyn_variant = match value.variant_type() { + VariantType::Unit => DynamicVariant::Unit, + VariantType::Tuple => { + let mut dyn_tuple = DynamicTuple::default(); + for field in value.iter_fields() { + dyn_tuple.insert_boxed(field.value().clone_value()); + } + DynamicVariant::Tuple(dyn_tuple) + } + VariantType::Struct => { + let mut dyn_struct = DynamicStruct::default(); + for field in value.iter_fields() { + dyn_struct + .insert_boxed(field.name().unwrap(), field.value().clone_value()); + } + DynamicVariant::Struct(dyn_struct) + } + }; + self.set_variant(value.variant_name(), dyn_variant); + } + } else { + panic!("`{}` is not an enum", value.type_name()); + } + } + + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + #[inline] + fn reflect_ref(&self) -> ReflectRef { + ReflectRef::Enum(self) + } + + #[inline] + fn reflect_mut(&mut self) -> ReflectMut { + ReflectMut::Enum(self) + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(self.clone_dynamic()) + } + + #[inline] + fn reflect_hash(&self) -> Option { + enum_hash(self) + } + + #[inline] + fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option { + enum_partial_eq(self, value) + } + + #[inline] + fn debug(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "DynamicEnum(")?; + enum_debug(self, f)?; + write!(f, ")") + } +} + +impl Typed for DynamicEnum { + fn type_info() -> &'static TypeInfo { + static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); + CELL.get_or_set(|| TypeInfo::Dynamic(DynamicInfo::new::())) + } +} diff --git a/crates/bevy_reflect/src/enums/enum_trait.rs b/crates/bevy_reflect/src/enums/enum_trait.rs new file mode 100644 index 0000000000000..9ee110d8f0a05 --- /dev/null +++ b/crates/bevy_reflect/src/enums/enum_trait.rs @@ -0,0 +1,280 @@ +use crate::{DynamicEnum, Reflect, VariantInfo, VariantType}; +use bevy_utils::HashMap; +use std::any::{Any, TypeId}; +use std::borrow::Cow; +use std::slice::Iter; + +/// A trait representing a [reflected] enum. +/// +/// This allows enums to be processed and modified dynamically at runtime without +/// necessarily knowing the actual type. +/// Enums are much more complex than their struct counterparts. +/// As a result, users will need to be mindful of conventions, considerations, +/// and complications when working with this trait. +/// +/// # Variants +/// +/// An enum is a set of choices called _variants_. +/// An instance of an enum can only exist as one of these choices at any given time. +/// Consider Rust's [`Option`]. It's an enum with two variants: [`None`] and [`Some`]. +/// If you're `None`, you can't be `Some` and vice versa. +/// +/// > ⚠️ __This is very important:__ +/// > The [`Enum`] trait represents an enum _as one of its variants_. +/// > It does not represent the entire enum since that's not true to how enums work. +/// +/// Variants come in a few [flavors](VariantType): +/// +/// | Variant Type | Syntax | +/// | ------------ | ------------------------------ | +/// | Unit | `MyEnum::Foo` | +/// | Tuple | `MyEnum::Foo( i32, i32 )` | +/// | Struct | `MyEnum::Foo{ value: String }` | +/// +/// As you can see, a unit variant contains no fields, while tuple and struct variants +/// can contain one or more fields. +/// The fields in a tuple variant is defined by their _order_ within the variant. +/// Index `0` represents the first field in the variant and so on. +/// Fields in struct variants (excluding tuple structs), on the other hand, are +/// represented by a _name_. +/// +/// # Implementation +/// +/// > đź’ˇ This trait can be automatically implemented using the [`Reflect`] derive macro +/// > on an enum definition. +/// +/// Despite the fact that enums can represent multiple states, traits only exist in one state +/// and must be applied to the entire enum rather than a particular variant. +/// Because of this limitation, the [`Enum`] trait must not only _represent_ any of the +/// three variant types, but also define the _methods_ for all three as well. +/// +/// What does this mean? It means that even though a unit variant contains no fields, a +/// representation of that variant using the [`Enum`] trait will still contain methods for +/// accessing fields! +/// Again, this is to account for _all three_ variant types. +/// +/// We recommend using the built-in [`Reflect`] derive macro to automatically handle all the +/// implementation details for you. +/// However, if you _must_ implement this trait manually, there are a few things to keep in mind... +/// +/// ## Field Order +/// +/// While tuple variants identify their fields by the order in which they are defined, struct +/// variants identify fields by their name. +/// However, both should allow access to fields by their defined order. +/// +/// The reason all fields, regardless of variant type, need to be accessible by their order is +/// due to field iteration. +/// We need a way to iterate through each field in a variant, and the easiest way of achieving +/// that is through the use of field order. +/// +/// The derive macro adds proper struct variant handling for [`Enum::index_of`], [`Enum::name_at`] +/// and [`Enum::field_at[_mut]`](Enum::field_at) methods. +/// The first two methods are __required__ for all struct variant types. +/// By convention, implementors should also handle the last method as well, but this is not +/// a strict requirement. +/// +/// ## Field Names +/// +/// Implementors may choose to handle [`Enum::index_of`], [`Enum::name_at`], and +/// [`Enum::field[_mut]`](Enum::field) for tuple variants by considering stringified `usize`s to be +/// valid names (such as `"3"`). +/// This isn't wrong to do, but the convention set by the derive macro is that it isn't supported. +/// It's preferred that these strings be converted to their proper `usize` representations and +/// the [`Enum::field_at[_mut]`](Enum::field_at) methods be used instead. +/// +/// [reflected]: crate +/// [`None`]: core::option::Option::None +/// [`Some`]: core::option::Option::Some +/// [`Reflect`]: bevy_reflect_derive::Reflect +pub trait Enum: Reflect { + /// Returns a reference to the value of the field (in the current variant) with the given name. + /// + /// For non-[`VariantType::Struct`] variants, this should return `None`. + fn field(&self, name: &str) -> Option<&dyn Reflect>; + /// Returns a reference to the value of the field (in the current variant) at the given index. + fn field_at(&self, index: usize) -> Option<&dyn Reflect>; + /// Returns a mutable reference to the value of the field (in the current variant) with the given name. + /// + /// For non-[`VariantType::Struct`] variants, this should return `None`. + fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect>; + /// Returns a mutable reference to the value of the field (in the current variant) at the given index. + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn Reflect>; + /// Returns the index of the field (in the current variant) with the given name. + /// + /// For non-[`VariantType::Struct`] variants, this should return `None`. + fn index_of(&self, name: &str) -> Option; + /// Returns the name of the field (in the current variant) with the given index. + /// + /// For non-[`VariantType::Struct`] variants, this should return `None`. + fn name_at(&self, index: usize) -> Option<&str>; + /// Returns an iterator over the values of the current variant's fields. + fn iter_fields(&self) -> VariantFieldIter; + /// Returns the number of fields in the current variant. + fn field_len(&self) -> usize; + /// The name of the current variant. + fn variant_name(&self) -> &str; + /// The type of the current variant. + fn variant_type(&self) -> VariantType; + // Clones the enum into a [`DynamicEnum`]. + fn clone_dynamic(&self) -> DynamicEnum; + /// Returns true if the current variant's type matches the given one. + fn is_variant(&self, variant_type: VariantType) -> bool { + self.variant_type() == variant_type + } + /// Returns the full path to the current variant. + fn variant_path(&self) -> String { + format!("{}::{}", self.type_name(), self.variant_name()) + } +} + +/// A container for compile-time enum info, used by [`TypeInfo`](crate::TypeInfo). +#[derive(Clone, Debug)] +pub struct EnumInfo { + type_name: &'static str, + type_id: TypeId, + variants: Box<[VariantInfo]>, + variant_indices: HashMap, usize>, +} + +impl EnumInfo { + /// Create a new [`EnumInfo`]. + /// + /// # Arguments + /// + /// * `variants`: The variants of this enum in the order they are defined + /// + pub fn new(variants: &[VariantInfo]) -> Self { + let variant_indices = variants + .iter() + .enumerate() + .map(|(index, variant)| { + let name = variant.name().clone(); + (name, index) + }) + .collect::>(); + + Self { + type_name: std::any::type_name::(), + type_id: TypeId::of::(), + variants: variants.to_vec().into_boxed_slice(), + variant_indices, + } + } + + /// Get a variant with the given name. + pub fn variant(&self, name: &str) -> Option<&VariantInfo> { + self.variant_indices + .get(name) + .map(|index| &self.variants[*index]) + } + + /// Get a variant at the given index. + pub fn variant_at(&self, index: usize) -> Option<&VariantInfo> { + self.variants.get(index) + } + + /// Get the index of the variant with the given name. + pub fn index_of(&self, name: &str) -> Option { + self.variant_indices.get(name).copied() + } + + /// Returns the full path to the given variant. + /// + /// This does _not_ check if the given variant exists. + pub fn variant_path(&self, name: &str) -> String { + format!("{}::{}", self.type_name(), name) + } + + /// Checks if a variant with the given name exists within this enum. + pub fn contains_variant(&self, name: &str) -> bool { + self.variant_indices.contains_key(name) + } + + /// Iterate over the variants of this enum. + pub fn iter(&self) -> Iter<'_, VariantInfo> { + self.variants.iter() + } + + /// The number of variants in this enum. + pub fn variant_len(&self) -> usize { + self.variants.len() + } + + /// The [type name] of the enum. + /// + /// [type name]: std::any::type_name + pub fn type_name(&self) -> &'static str { + self.type_name + } + + /// The [`TypeId`] of the enum. + pub fn type_id(&self) -> TypeId { + self.type_id + } + + /// Check if the given type matches the enum type. + pub fn is(&self) -> bool { + TypeId::of::() == self.type_id + } +} + +/// An iterator over the fields in the current enum variant. +pub struct VariantFieldIter<'a> { + container: &'a dyn Enum, + index: usize, +} + +impl<'a> VariantFieldIter<'a> { + pub fn new(container: &'a dyn Enum) -> Self { + Self { + container, + index: 0, + } + } +} + +impl<'a> Iterator for VariantFieldIter<'a> { + type Item = VariantField<'a>; + + fn next(&mut self) -> Option { + let value = match self.container.variant_type() { + VariantType::Unit => None, + VariantType::Tuple => Some(VariantField::Tuple(self.container.field_at(self.index)?)), + VariantType::Struct => { + let name = self.container.name_at(self.index)?; + Some(VariantField::Struct(name, self.container.field(name)?)) + } + }; + self.index += 1; + value + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.container.field_len(); + (size, Some(size)) + } +} + +impl<'a> ExactSizeIterator for VariantFieldIter<'a> {} + +pub enum VariantField<'a> { + Struct(&'a str, &'a dyn Reflect), + Tuple(&'a dyn Reflect), +} + +impl<'a> VariantField<'a> { + pub fn name(&self) -> Option<&'a str> { + if let Self::Struct(name, ..) = self { + Some(*name) + } else { + None + } + } + + pub fn value(&self) -> &'a dyn Reflect { + match self { + Self::Struct(.., value) | Self::Tuple(value) => *value, + } + } +} diff --git a/crates/bevy_reflect/src/enums/helpers.rs b/crates/bevy_reflect/src/enums/helpers.rs new file mode 100644 index 0000000000000..c14913d19dede --- /dev/null +++ b/crates/bevy_reflect/src/enums/helpers.rs @@ -0,0 +1,121 @@ +use crate::{Enum, Reflect, ReflectRef, VariantType}; +use std::fmt::Debug; +use std::hash::{Hash, Hasher}; + +/// Returns the `u64` hash of the given [enum](Enum). +#[inline] +pub fn enum_hash(value: &TEnum) -> Option { + let mut hasher = crate::ReflectHasher::default(); + std::any::Any::type_id(value).hash(&mut hasher); + value.variant_name().hash(&mut hasher); + value.variant_type().hash(&mut hasher); + for field in value.iter_fields() { + hasher.write_u64(field.value().reflect_hash()?); + } + Some(hasher.finish()) +} + +/// Compares an [`Enum`] with a [`Reflect`] value. +/// +/// Returns true if and only if all of the following are true: +/// - `b` is an enum; +/// - `b` is the same variant as `a`; +/// - For each field in `a`, `b` contains a field with the same name and +/// [`Reflect::reflect_partial_eq`] returns `Some(true)` for the two field +/// values. +#[inline] +pub fn enum_partial_eq(a: &TEnum, b: &dyn Reflect) -> Option { + // Both enums? + let b = if let ReflectRef::Enum(e) = b.reflect_ref() { + e + } else { + return Some(false); + }; + + // Same variant name? + if a.variant_name() != b.variant_name() { + return Some(false); + } + + // Same variant type? + if !a.is_variant(b.variant_type()) { + return Some(false); + } + + match a.variant_type() { + VariantType::Struct => { + // Same struct fields? + for field in a.iter_fields() { + let field_name = field.name().unwrap(); + if let Some(field_value) = b.field(field_name) { + if let Some(false) | None = field_value.reflect_partial_eq(field.value()) { + // Fields failed comparison + return Some(false); + } + } else { + // Field does not exist + return Some(false); + } + } + Some(true) + } + VariantType::Tuple => { + // Same tuple fields? + for (i, field) in a.iter_fields().enumerate() { + if let Some(field_value) = b.field_at(i) { + if let Some(false) | None = field_value.reflect_partial_eq(field.value()) { + // Fields failed comparison + return Some(false); + } + } else { + // Field does not exist + return Some(false); + } + } + Some(true) + } + _ => Some(true), + } +} + +/// The default debug formatter for [`Enum`] types. +/// +/// # Example +/// ``` +/// use bevy_reflect::Reflect; +/// #[derive(Reflect)] +/// enum MyEnum { +/// A, +/// B (usize), +/// C {value: i32} +/// } +/// +/// let my_enum: &dyn Reflect = &MyEnum::B(123); +/// println!("{:#?}", my_enum); +/// +/// // Output: +/// +/// // B ( +/// // 123, +/// // ) +/// ``` +#[inline] +pub fn enum_debug(dyn_enum: &dyn Enum, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match dyn_enum.variant_type() { + VariantType::Unit => f.write_str(dyn_enum.variant_name()), + VariantType::Tuple => { + let mut debug = f.debug_tuple(dyn_enum.variant_name()); + for field in dyn_enum.iter_fields() { + debug.field(&field.value() as &dyn Debug); + } + debug.finish() + } + VariantType::Struct => { + let mut debug = f.debug_struct(dyn_enum.variant_name()); + for field in dyn_enum.iter_fields() { + debug.field(field.name().unwrap(), &field.value() as &dyn Debug); + } + debug.finish() + } + } +} diff --git a/crates/bevy_reflect/src/enums/mod.rs b/crates/bevy_reflect/src/enums/mod.rs new file mode 100644 index 0000000000000..6a1f80fd38cf3 --- /dev/null +++ b/crates/bevy_reflect/src/enums/mod.rs @@ -0,0 +1,651 @@ +mod dynamic_enum; +mod enum_trait; +mod helpers; +mod variants; + +pub use dynamic_enum::*; +pub use enum_trait::*; +pub use helpers::*; +pub use variants::*; + +#[cfg(test)] +mod tests { + use crate as bevy_reflect; + use crate::*; + + #[derive(Reflect, Debug, PartialEq)] + enum MyEnum { + A, + B(usize, i32), + C { foo: f32, bar: bool }, + } + + #[test] + fn should_get_enum_type_info() { + let info = MyEnum::type_info(); + if let TypeInfo::Enum(info) = info { + assert!(info.is::(), "expected type to be `MyEnum`"); + assert_eq!(std::any::type_name::(), info.type_name()); + + // === MyEnum::A === // + assert_eq!("A", info.variant_at(0).unwrap().name()); + assert_eq!("A", info.variant("A").unwrap().name()); + if let VariantInfo::Unit(variant) = info.variant("A").unwrap() { + assert_eq!("A", variant.name()); + } else { + panic!("Expected `VariantInfo::Unit`"); + } + + // === MyEnum::B === // + assert_eq!("B", info.variant_at(1).unwrap().name()); + assert_eq!("B", info.variant("B").unwrap().name()); + if let VariantInfo::Tuple(variant) = info.variant("B").unwrap() { + assert!(variant.field_at(0).unwrap().is::()); + assert!(variant.field_at(1).unwrap().is::()); + } else { + panic!("Expected `VariantInfo::Tuple`"); + } + + // === MyEnum::C === // + assert_eq!("C", info.variant_at(2).unwrap().name()); + assert_eq!("C", info.variant("C").unwrap().name()); + if let VariantInfo::Struct(variant) = info.variant("C").unwrap() { + assert!(variant.field_at(0).unwrap().is::()); + assert!(variant.field("foo").unwrap().is::()); + } else { + panic!("Expected `VariantInfo::Struct`"); + } + } else { + panic!("Expected `TypeInfo::Enum`"); + } + } + + #[test] + fn dynamic_enum_should_set_variant_fields() { + // === Unit === // + let mut value = MyEnum::A; + let dyn_enum = DynamicEnum::from(MyEnum::A); + value.apply(&dyn_enum); + assert_eq!(MyEnum::A, value); + + // === Tuple === // + let mut value = MyEnum::B(0, 0); + let dyn_enum = DynamicEnum::from(MyEnum::B(123, 321)); + value.apply(&dyn_enum); + assert_eq!(MyEnum::B(123, 321), value); + + // === Struct === // + let mut value = MyEnum::C { + foo: 0.0, + bar: false, + }; + let dyn_enum = DynamicEnum::from(MyEnum::C { + foo: 1.23, + bar: true, + }); + value.apply(&dyn_enum); + assert_eq!( + MyEnum::C { + foo: 1.23, + bar: true, + }, + value + ); + } + + #[test] + fn partial_dynamic_enum_should_set_variant_fields() { + // === Tuple === // + let mut value = MyEnum::B(0, 0); + + let mut data = DynamicTuple::default(); + data.insert(123usize); + + let mut dyn_enum = DynamicEnum::default(); + dyn_enum.set_variant("B", data); + value.apply(&dyn_enum); + assert_eq!(MyEnum::B(123, 0), value); + + // === Struct === // + let mut value = MyEnum::C { + foo: 1.23, + bar: false, + }; + + let mut data = DynamicStruct::default(); + data.insert("bar", true); + + let mut dyn_enum = DynamicEnum::default(); + dyn_enum.set_variant("C", data); + value.apply(&dyn_enum); + assert_eq!( + MyEnum::C { + foo: 1.23, + bar: true, + }, + value + ); + } + + #[test] + fn dynamic_enum_should_apply_dynamic_enum() { + let mut a = DynamicEnum::from(MyEnum::B(123, 321)); + let b = DynamicEnum::from(MyEnum::B(123, 321)); + + // Sanity check that equality check works + assert!( + a.reflect_partial_eq(&b).unwrap_or_default(), + "dynamic enums should be equal" + ); + + a.set_variant("A", ()); + assert!( + !a.reflect_partial_eq(&b).unwrap_or_default(), + "dynamic enums should not be equal" + ); + + a.apply(&b); + assert!(a.reflect_partial_eq(&b).unwrap_or_default()); + } + + #[test] + fn dynamic_enum_should_change_variant() { + let mut value = MyEnum::A; + + // === MyEnum::A -> MyEnum::B === // + let mut dyn_enum = DynamicEnum::from(MyEnum::B(123, 321)); + value.apply(&dyn_enum); + assert_eq!(MyEnum::B(123, 321), value); + + // === MyEnum::B -> MyEnum::C === // + let mut data = DynamicStruct::default(); + data.insert("foo", 1.23_f32); + data.insert("bar", true); + dyn_enum.set_variant("C", data); + value.apply(&dyn_enum); + assert_eq!( + MyEnum::C { + foo: 1.23, + bar: true + }, + value + ); + + // === MyEnum::C -> MyEnum::B === // + let mut data = DynamicTuple::default(); + data.insert(123_usize); + data.insert(321_i32); + dyn_enum.set_variant("B", data); + value.apply(&dyn_enum); + assert_eq!(MyEnum::B(123, 321), value); + + // === MyEnum::B -> MyEnum::A === // + dyn_enum.set_variant("A", ()); + value.apply(&dyn_enum); + assert_eq!(MyEnum::A, value); + } + + #[test] + fn enum_should_iterate_fields() { + // === Unit === // + let value: &dyn Enum = &MyEnum::A; + assert_eq!(0, value.field_len()); + let mut iter = value.iter_fields(); + assert!(iter.next().is_none()); + + // === Tuple === // + let value: &dyn Enum = &MyEnum::B(123, 321); + assert_eq!(2, value.field_len()); + let mut iter = value.iter_fields(); + assert!(iter + .next() + .and_then(|field| field.value().reflect_partial_eq(&123_usize)) + .unwrap_or_default()); + assert!(iter + .next() + .and_then(|field| field.value().reflect_partial_eq(&321_i32)) + .unwrap_or_default()); + + // === Struct === // + let value: &dyn Enum = &MyEnum::C { + foo: 1.23, + bar: true, + }; + assert_eq!(2, value.field_len()); + let mut iter = value.iter_fields(); + assert!(iter + .next() + .and_then(|field| field + .value() + .reflect_partial_eq(&1.23_f32) + .and(field.name().map(|name| name == "foo"))) + .unwrap_or_default()); + assert!(iter + .next() + .and_then(|field| field + .value() + .reflect_partial_eq(&true) + .and(field.name().map(|name| name == "bar"))) + .unwrap_or_default()); + } + + #[test] + fn enum_should_return_correct_variant_type() { + // === Unit === // + let value = MyEnum::A; + assert_eq!(VariantType::Unit, value.variant_type()); + + // === Tuple === // + let value = MyEnum::B(0, 0); + assert_eq!(VariantType::Tuple, value.variant_type()); + + // === Struct === // + let value = MyEnum::C { + foo: 1.23, + bar: true, + }; + assert_eq!(VariantType::Struct, value.variant_type()); + } + + #[test] + fn enum_should_return_correct_variant_path() { + // === Unit === // + let value = MyEnum::A; + assert_eq!( + "bevy_reflect::enums::tests::MyEnum::A", + value.variant_path() + ); + + // === Tuple === // + let value = MyEnum::B(0, 0); + assert_eq!( + "bevy_reflect::enums::tests::MyEnum::B", + value.variant_path() + ); + + // === Struct === // + let value = MyEnum::C { + foo: 1.23, + bar: true, + }; + assert_eq!( + "bevy_reflect::enums::tests::MyEnum::C", + value.variant_path() + ); + } + + #[test] + #[should_panic(expected = "`((usize, i32))` is not an enum")] + fn applying_non_enum_should_panic() { + let mut value = MyEnum::B(0, 0); + let mut dyn_tuple = DynamicTuple::default(); + dyn_tuple.insert((123_usize, 321_i32)); + value.apply(&dyn_tuple); + } + + #[test] + #[allow(dead_code)] + fn should_skip_ignored_variants() { + #[derive(Reflect, Debug, PartialEq)] + enum TestEnum { + A, + #[reflect(ignore)] + B, + C, + } + + if let TypeInfo::Enum(info) = TestEnum::type_info() { + assert_eq!( + 2, + info.variant_len(), + "expected one of the variants to be ignored" + ); + assert_eq!("A", info.variant_at(0).unwrap().name()); + assert_eq!("C", info.variant_at(1).unwrap().name()); + } else { + panic!("expected `TypeInfo::Enum`"); + } + } + + #[test] + fn should_skip_ignored_fields() { + #[derive(Reflect, Debug, PartialEq)] + enum TestEnum { + A, + B, + C { + #[reflect(ignore)] + foo: f32, + bar: bool, + }, + } + + if let TypeInfo::Enum(info) = TestEnum::type_info() { + assert_eq!(3, info.variant_len()); + if let VariantInfo::Struct(variant) = info.variant("C").unwrap() { + assert_eq!( + 1, + variant.field_len(), + "expected one of the fields to be ignored" + ); + assert!(variant.field_at(0).unwrap().is::()); + } else { + panic!("expected `VariantInfo::Struct`"); + } + } else { + panic!("expected `TypeInfo::Enum`"); + } + } + + #[test] + fn enum_should_allow_generics() { + #[derive(Reflect, Debug, PartialEq)] + enum TestEnum { + A, + B(T), + C { value: T }, + } + + if let TypeInfo::Enum(info) = TestEnum::::type_info() { + if let VariantInfo::Tuple(variant) = info.variant("B").unwrap() { + assert!(variant.field_at(0).unwrap().is::()); + } else { + panic!("expected `VariantInfo::Struct`"); + } + if let VariantInfo::Struct(variant) = info.variant("C").unwrap() { + assert!(variant.field("value").unwrap().is::()); + } else { + panic!("expected `VariantInfo::Struct`"); + } + } else { + panic!("expected `TypeInfo::Enum`"); + } + + let mut value = TestEnum::::A; + + // === Tuple === // + let mut data = DynamicTuple::default(); + data.insert(1.23_f32); + let dyn_enum = DynamicEnum::new(std::any::type_name::>(), "B", data); + value.apply(&dyn_enum); + assert_eq!(TestEnum::B(1.23), value); + + // === Struct === // + let mut data = DynamicStruct::default(); + data.insert("value", 1.23_f32); + let dyn_enum = DynamicEnum::new(std::any::type_name::>(), "C", data); + value.apply(&dyn_enum); + assert_eq!(TestEnum::C { value: 1.23 }, value); + } + + #[test] + fn enum_should_allow_struct_fields() { + #[derive(Reflect, Debug, PartialEq)] + enum TestEnum { + A, + B(TestStruct), + C { value: TestStruct }, + } + + #[derive(Reflect, FromReflect, Debug, PartialEq)] + struct TestStruct(usize); + + let mut value = TestEnum::A; + + // === Tuple === // + let mut data = DynamicTuple::default(); + data.insert(TestStruct(123)); + let dyn_enum = DynamicEnum::new(std::any::type_name::(), "B", data); + value.apply(&dyn_enum); + assert_eq!(TestEnum::B(TestStruct(123)), value); + + // === Struct === // + let mut data = DynamicStruct::default(); + data.insert("value", TestStruct(123)); + let dyn_enum = DynamicEnum::new(std::any::type_name::(), "C", data); + value.apply(&dyn_enum); + assert_eq!( + TestEnum::C { + value: TestStruct(123) + }, + value + ); + } + + #[test] + fn enum_should_allow_nesting_enums() { + #[derive(Reflect, Debug, PartialEq)] + enum TestEnum { + A, + B(OtherEnum), + C { value: OtherEnum }, + } + + #[derive(Reflect, FromReflect, Debug, PartialEq)] + enum OtherEnum { + A, + B(usize), + C { value: f32 }, + } + + let mut value = TestEnum::A; + + // === Tuple === // + let mut data = DynamicTuple::default(); + data.insert(OtherEnum::B(123)); + let dyn_enum = DynamicEnum::new(std::any::type_name::(), "B", data); + value.apply(&dyn_enum); + assert_eq!(TestEnum::B(OtherEnum::B(123)), value); + + // === Struct === // + let mut data = DynamicStruct::default(); + data.insert("value", OtherEnum::C { value: 1.23 }); + let dyn_enum = DynamicEnum::new(std::any::type_name::(), "C", data); + value.apply(&dyn_enum); + assert_eq!( + TestEnum::C { + value: OtherEnum::C { value: 1.23 } + }, + value + ); + } + + #[test] + fn enum_should_apply() { + let mut value: Box = Box::new(MyEnum::A); + + // === MyEnum::A -> MyEnum::A === // + value.apply(&MyEnum::A); + assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default()); + + // === MyEnum::A -> MyEnum::B === // + value.apply(&MyEnum::B(123, 321)); + assert!(value + .reflect_partial_eq(&MyEnum::B(123, 321)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::B === // + value.apply(&MyEnum::B(321, 123)); + assert!(value + .reflect_partial_eq(&MyEnum::B(321, 123)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::C === // + value.apply(&MyEnum::C { + foo: 1.23, + bar: true, + }); + assert!(value + .reflect_partial_eq(&MyEnum::C { + foo: 1.23, + bar: true + }) + .unwrap_or_default()); + + // === MyEnum::C -> MyEnum::C === // + value.apply(&MyEnum::C { + foo: 3.21, + bar: false, + }); + assert!(value + .reflect_partial_eq(&MyEnum::C { + foo: 3.21, + bar: false + }) + .unwrap_or_default()); + + // === MyEnum::C -> MyEnum::B === // + value.apply(&MyEnum::B(123, 321)); + assert!(value + .reflect_partial_eq(&MyEnum::B(123, 321)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::A === // + value.apply(&MyEnum::A); + assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default()); + } + + #[test] + fn enum_should_set() { + let mut value: Box = Box::new(MyEnum::A); + + // === MyEnum::A -> MyEnum::A === // + value.set(Box::new(MyEnum::A)).unwrap(); + assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default()); + + // === MyEnum::A -> MyEnum::B === // + value.set(Box::new(MyEnum::B(123, 321))).unwrap(); + assert!(value + .reflect_partial_eq(&MyEnum::B(123, 321)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::B === // + value.set(Box::new(MyEnum::B(321, 123))).unwrap(); + assert!(value + .reflect_partial_eq(&MyEnum::B(321, 123)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::C === // + value + .set(Box::new(MyEnum::C { + foo: 1.23, + bar: true, + })) + .unwrap(); + assert!(value + .reflect_partial_eq(&MyEnum::C { + foo: 1.23, + bar: true + }) + .unwrap_or_default()); + + // === MyEnum::C -> MyEnum::C === // + value + .set(Box::new(MyEnum::C { + foo: 3.21, + bar: false, + })) + .unwrap(); + assert!(value + .reflect_partial_eq(&MyEnum::C { + foo: 3.21, + bar: false + }) + .unwrap_or_default()); + + // === MyEnum::C -> MyEnum::B === // + value.set(Box::new(MyEnum::B(123, 321))).unwrap(); + assert!(value + .reflect_partial_eq(&MyEnum::B(123, 321)) + .unwrap_or_default()); + + // === MyEnum::B -> MyEnum::A === // + value.set(Box::new(MyEnum::A)).unwrap(); + assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default()); + } + + #[test] + fn enum_should_partial_eq() { + #[derive(Reflect)] + enum TestEnum { + A, + A1, + B(usize), + B1(usize), + B2(usize, usize), + C { value: i32 }, + C1 { value: i32 }, + C2 { value: f32 }, + } + + let a: &dyn Reflect = &TestEnum::A; + let b: &dyn Reflect = &TestEnum::A; + assert!( + a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::A == TestEnum::A" + ); + + let a: &dyn Reflect = &TestEnum::A; + let b: &dyn Reflect = &TestEnum::A1; + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::A != TestEnum::A1" + ); + + let a: &dyn Reflect = &TestEnum::B(123); + let b: &dyn Reflect = &TestEnum::B(123); + assert!( + a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::B(123) == TestEnum::B(123)" + ); + + let a: &dyn Reflect = &TestEnum::B(123); + let b: &dyn Reflect = &TestEnum::B(321); + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::B(123) != TestEnum::B(321)" + ); + + let a: &dyn Reflect = &TestEnum::B(123); + let b: &dyn Reflect = &TestEnum::B1(123); + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::B(123) != TestEnum::B1(123)" + ); + + let a: &dyn Reflect = &TestEnum::B(123); + let b: &dyn Reflect = &TestEnum::B2(123, 123); + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::B(123) != TestEnum::B2(123, 123)" + ); + + let a: &dyn Reflect = &TestEnum::C { value: 123 }; + let b: &dyn Reflect = &TestEnum::C { value: 123 }; + assert!( + a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::C{{value: 123}} == TestEnum::C{{value: 123}}" + ); + + let a: &dyn Reflect = &TestEnum::C { value: 123 }; + let b: &dyn Reflect = &TestEnum::C { value: 321 }; + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::C{{value: 123}} != TestEnum::C{{value: 321}}" + ); + + let a: &dyn Reflect = &TestEnum::C { value: 123 }; + let b: &dyn Reflect = &TestEnum::C1 { value: 123 }; + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::C{{value: 123}} != TestEnum::C1{{value: 123}}" + ); + + let a: &dyn Reflect = &TestEnum::C { value: 123 }; + let b: &dyn Reflect = &TestEnum::C2 { value: 1.23 }; + assert!( + !a.reflect_partial_eq(b).unwrap_or_default(), + "expected TestEnum::C{{value: 123}} != TestEnum::C2{{value: 1.23}}" + ); + } +} diff --git a/crates/bevy_reflect/src/enums/variants.rs b/crates/bevy_reflect/src/enums/variants.rs new file mode 100644 index 0000000000000..b1a9740d21c75 --- /dev/null +++ b/crates/bevy_reflect/src/enums/variants.rs @@ -0,0 +1,231 @@ +use crate::{NamedField, UnnamedField}; +use bevy_utils::HashMap; +use std::borrow::Cow; +use std::slice::Iter; + +/// Describes the form of an enum variant. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum VariantType { + /// Struct enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A { + /// foo: usize + /// } + /// } + /// ``` + Struct, + /// Tuple enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A(usize) + /// } + /// ``` + Tuple, + /// Unit enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A + /// } + /// ``` + Unit, +} + +/// A container for compile-time enum variant info. +#[derive(Clone, Debug)] +pub enum VariantInfo { + /// Struct enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A { + /// foo: usize + /// } + /// } + /// ``` + Struct(StructVariantInfo), + /// Tuple enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A(usize) + /// } + /// ``` + Tuple(TupleVariantInfo), + /// Unit enums take the form: + /// + /// ``` + /// enum MyEnum { + /// A + /// } + /// ``` + Unit(UnitVariantInfo), +} + +impl VariantInfo { + pub fn name(&self) -> &Cow<'static, str> { + match self { + Self::Struct(info) => info.name(), + Self::Tuple(info) => info.name(), + Self::Unit(info) => info.name(), + } + } +} + +/// Type info for struct variants. +#[derive(Clone, Debug)] +pub struct StructVariantInfo { + name: Cow<'static, str>, + fields: Box<[NamedField]>, + field_indices: HashMap, usize>, +} + +impl StructVariantInfo { + /// Create a new [`StructVariantInfo`]. + pub fn new(name: &str, fields: &[NamedField]) -> Self { + let field_indices = Self::collect_field_indices(fields); + + Self { + name: Cow::Owned(name.into()), + fields: fields.to_vec().into_boxed_slice(), + field_indices, + } + } + + /// Create a new [`StructVariantInfo`] using a static string. + /// + /// This helps save an allocation when the string has a static lifetime, such + /// as when using defined sa a literal. + pub fn new_static(name: &'static str, fields: &[NamedField]) -> Self { + let field_indices = Self::collect_field_indices(fields); + Self { + name: Cow::Borrowed(name), + fields: fields.to_vec().into_boxed_slice(), + field_indices, + } + } + + /// The name of this variant. + pub fn name(&self) -> &Cow<'static, str> { + &self.name + } + + /// Get the field with the given name. + pub fn field(&self, name: &str) -> Option<&NamedField> { + self.field_indices + .get(name) + .map(|index| &self.fields[*index]) + } + + /// Get the field at the given index. + pub fn field_at(&self, index: usize) -> Option<&NamedField> { + self.fields.get(index) + } + + /// Get the index of the field with the given name. + pub fn index_of(&self, name: &str) -> Option { + self.field_indices.get(name).copied() + } + + /// Iterate over the fields of this variant. + pub fn iter(&self) -> Iter<'_, NamedField> { + self.fields.iter() + } + + /// The total number of fields in this variant. + pub fn field_len(&self) -> usize { + self.fields.len() + } + + fn collect_field_indices(fields: &[NamedField]) -> HashMap, usize> { + fields + .iter() + .enumerate() + .map(|(index, field)| { + let name = field.name().clone(); + (name, index) + }) + .collect() + } +} + +/// Type info for tuple variants. +#[derive(Clone, Debug)] +pub struct TupleVariantInfo { + name: Cow<'static, str>, + fields: Box<[UnnamedField]>, +} + +impl TupleVariantInfo { + /// Create a new [`TupleVariantInfo`]. + pub fn new(name: &str, fields: &[UnnamedField]) -> Self { + Self { + name: Cow::Owned(name.into()), + fields: fields.to_vec().into_boxed_slice(), + } + } + + /// Create a new [`TupleVariantInfo`] using a static string. + /// + /// This helps save an allocation when the string has a static lifetime, such + /// as when using defined sa a literal. + pub fn new_static(name: &'static str, fields: &[UnnamedField]) -> Self { + Self { + name: Cow::Borrowed(name), + fields: fields.to_vec().into_boxed_slice(), + } + } + + /// The name of this variant. + pub fn name(&self) -> &Cow<'static, str> { + &self.name + } + + /// Get the field at the given index. + pub fn field_at(&self, index: usize) -> Option<&UnnamedField> { + self.fields.get(index) + } + + /// Iterate over the fields of this variant. + pub fn iter(&self) -> Iter<'_, UnnamedField> { + self.fields.iter() + } + + /// The total number of fields in this variant. + pub fn field_len(&self) -> usize { + self.fields.len() + } +} + +/// Type info for unit variants. +#[derive(Clone, Debug)] +pub struct UnitVariantInfo { + name: Cow<'static, str>, +} + +impl UnitVariantInfo { + /// Create a new [`UnitVariantInfo`]. + pub fn new(name: &str) -> Self { + Self { + name: Cow::Owned(name.into()), + } + } + + /// Create a new [`UnitVariantInfo`] using a static string. + /// + /// This helps save an allocation when the string has a static lifetime, such + /// as when using defined sa a literal. + pub fn new_static(name: &'static str) -> Self { + Self { + name: Cow::Borrowed(name), + } + } + + /// The name of this variant. + pub fn name(&self) -> &Cow<'static, str> { + &self.name + } +} diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 6ce789f975709..079cb89ab0660 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,8 +1,10 @@ use crate::{self as bevy_reflect, ReflectFromPtr}; use crate::{ - map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, FromReflect, FromType, - GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, ReflectDeserialize, - ReflectMut, ReflectRef, ReflectSerialize, TypeInfo, TypeRegistration, Typed, ValueInfo, + map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, DynamicMap, Enum, + EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, + Reflect, ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, TupleVariantInfo, + TypeInfo, TypeRegistration, Typed, UnitVariantInfo, UnnamedField, ValueInfo, VariantFieldIter, + VariantInfo, VariantType, }; use crate::utility::{GenericTypeInfoCell, NonGenericTypeInfoCell}; @@ -32,7 +34,6 @@ impl_reflect_value!(isize(Debug, Hash, PartialEq, Serialize, Deserialize)); impl_reflect_value!(f32(Debug, PartialEq, Serialize, Deserialize)); impl_reflect_value!(f64(Debug, PartialEq, Serialize, Deserialize)); impl_reflect_value!(String(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(Option()); impl_reflect_value!(Result()); impl_reflect_value!(HashSet()); impl_reflect_value!(Range()); @@ -56,7 +57,6 @@ impl_from_reflect_value!(isize); impl_from_reflect_value!(f32); impl_from_reflect_value!(f64); impl_from_reflect_value!(String); -impl_from_reflect_value!(Option); impl_from_reflect_value!(HashSet); impl_from_reflect_value!(Range); impl_from_reflect_value!(Duration); @@ -557,6 +557,218 @@ impl Reflect for Cow<'static, str> { } } +impl GetTypeRegistration for Option { + fn get_type_registration() -> TypeRegistration { + TypeRegistration::of::>() + } +} + +impl Enum for Option { + fn field(&self, _name: &str) -> Option<&dyn Reflect> { + None + } + + fn field_at(&self, index: usize) -> Option<&dyn Reflect> { + match self { + Some(value) if index == 0 => Some(value), + _ => None, + } + } + + fn field_mut(&mut self, _name: &str) -> Option<&mut dyn Reflect> { + None + } + + fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> { + match self { + Some(value) if index == 0 => Some(value), + _ => None, + } + } + + fn index_of(&self, _name: &str) -> Option { + None + } + + fn name_at(&self, _index: usize) -> Option<&str> { + None + } + + fn iter_fields(&self) -> VariantFieldIter { + VariantFieldIter::new(self) + } + + #[inline] + fn field_len(&self) -> usize { + match self { + Some(..) => 1, + None => 0, + } + } + + #[inline] + fn variant_name(&self) -> &str { + match self { + Some(..) => "Some", + None => "None", + } + } + + #[inline] + fn variant_type(&self) -> VariantType { + match self { + Some(..) => VariantType::Tuple, + None => VariantType::Unit, + } + } + + fn clone_dynamic(&self) -> DynamicEnum { + DynamicEnum::from_ref::(self) + } +} + +impl Reflect for Option { + #[inline] + fn type_name(&self) -> &str { + std::any::type_name::() + } + + #[inline] + fn get_type_info(&self) -> &'static TypeInfo { + ::type_info() + } + + #[inline] + fn into_any(self: Box) -> Box { + self + } + + #[inline] + fn as_any(&self) -> &dyn Any { + self + } + + #[inline] + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn as_reflect(&self) -> &dyn Reflect { + self + } + + fn as_reflect_mut(&mut self) -> &mut dyn Reflect { + self + } + + #[inline] + fn apply(&mut self, value: &dyn Reflect) { + if let ReflectRef::Enum(value) = value.reflect_ref() { + if self.variant_name() == value.variant_name() { + // Same variant -> just update fields + for (index, field) in value.iter_fields().enumerate() { + let name = value.name_at(index).unwrap(); + if let Some(v) = self.field_mut(name) { + v.apply(field.value()); + } + } + } else { + // New variant -> perform a switch + match value.variant_name() { + "Some" => { + let field = value + .field_at(0) + .expect("Field at index 0 should exist") + .clone_value(); + let field = field.take::().unwrap_or_else(|_| { + panic!( + "Field at index 0 should be of type {}", + std::any::type_name::() + ) + }); + *self = Some(field); + } + "None" => { + *self = None; + } + _ => panic!("Enum is not a {}.", std::any::type_name::()), + } + } + } + } + + #[inline] + fn set(&mut self, value: Box) -> Result<(), Box> { + *self = value.take()?; + Ok(()) + } + + fn reflect_ref(&self) -> ReflectRef { + ReflectRef::Enum(self) + } + + fn reflect_mut(&mut self) -> ReflectMut { + ReflectMut::Enum(self) + } + + #[inline] + fn clone_value(&self) -> Box { + Box::new(self.clone()) + } + + fn reflect_hash(&self) -> Option { + crate::enum_hash(self) + } + + fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option { + crate::enum_partial_eq(self, value) + } +} + +impl FromReflect for Option { + fn from_reflect(reflect: &dyn Reflect) -> Option { + if let ReflectRef::Enum(dyn_enum) = reflect.reflect_ref() { + match dyn_enum.variant_name() { + "Some" => { + let field = dyn_enum + .field_at(0) + .expect("Field at index 0 should exist") + .clone_value(); + let field = field.take::().unwrap_or_else(|_| { + panic!( + "Field at index 0 should be of type {}", + std::any::type_name::() + ) + }); + Some(Some(field)) + } + "None" => Some(None), + name => panic!( + "variant with name `{}` does not exist on enum `{}`", + name, + std::any::type_name::() + ), + } + } else { + None + } + } +} + +impl Typed for Option { + fn type_info() -> &'static TypeInfo { + static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); + CELL.get_or_insert::(|| { + let none_variant = VariantInfo::Unit(UnitVariantInfo::new_static("None")); + let some_variant = VariantInfo::Tuple(TupleVariantInfo::new_static( + "Some", + &[UnnamedField::new::(0)], + )); + TypeInfo::Enum(EnumInfo::new::(&[none_variant, some_variant])) + }) + } +} + impl Typed for Cow<'static, str> { fn type_info() -> &'static TypeInfo { static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); @@ -587,7 +799,9 @@ impl FromReflect for Cow<'static, str> { #[cfg(test)] mod tests { - use crate::{Reflect, ReflectSerialize, TypeRegistry}; + use crate::{ + Enum, Reflect, ReflectSerialize, TypeInfo, TypeRegistry, Typed, VariantInfo, VariantType, + }; use bevy_utils::HashMap; use std::f32::consts::{PI, TAU}; @@ -663,10 +877,70 @@ mod tests { } #[test] - fn should_not_partial_eq_option() { - // Option does not contain a `PartialEq` implementation, so it should return `None` + fn should_partial_eq_option() { let a: &dyn Reflect = &Some(123); let b: &dyn Reflect = &Some(123); - assert_eq!(None, a.reflect_partial_eq(b)); + assert_eq!(Some(true), a.reflect_partial_eq(b)); + } + + #[test] + fn option_should_impl_enum() { + let mut value = Some(123usize); + + assert!(value + .reflect_partial_eq(&Some(123usize)) + .unwrap_or_default()); + assert!(!value + .reflect_partial_eq(&Some(321usize)) + .unwrap_or_default()); + + assert_eq!("Some", value.variant_name()); + assert_eq!("core::option::Option::Some", value.variant_path()); + + if value.is_variant(VariantType::Tuple) { + if let Some(field) = value + .field_at_mut(0) + .and_then(|field| field.downcast_mut::()) + { + *field = 321; + } + } else { + panic!("expected `VariantType::Tuple`"); + } + + assert_eq!(Some(321), value); + } + + #[test] + fn option_should_impl_typed() { + type MyOption = Option; + let info = MyOption::type_info(); + if let TypeInfo::Enum(info) = info { + assert_eq!( + "None", + info.variant_at(0).unwrap().name(), + "Expected `None` to be variant at index `0`" + ); + assert_eq!( + "Some", + info.variant_at(1).unwrap().name(), + "Expected `Some` to be variant at index `1`" + ); + assert_eq!("Some", info.variant("Some").unwrap().name()); + if let VariantInfo::Tuple(variant) = info.variant("Some").unwrap() { + assert!( + variant.field_at(0).unwrap().is::(), + "Expected `Some` variant to contain `i32`" + ); + assert!( + variant.field_at(1).is_none(), + "Expected `Some` variant to only contain 1 field" + ); + } else { + panic!("Expected `VariantInfo::Tuple`"); + } + } else { + panic!("Expected `TypeInfo::Enum`"); + } } } diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 7fd60f0f928b3..f6dbc37ce67f3 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -26,6 +26,7 @@ mod impls { pub use self::std::*; } +mod enums; pub mod serde; pub mod std_traits; pub mod utility; @@ -40,6 +41,7 @@ pub mod prelude { } pub use array::*; +pub use enums::*; pub use fields::*; pub use impls::*; pub use list::*; @@ -675,10 +677,6 @@ mod tests { panic!("Expected `TypeInfo::TupleStruct`"); } - let value: &dyn Reflect = &MyTupleStruct(123, 321, MyStruct { foo: 123, bar: 321 }); - let info = value.get_type_info(); - assert!(info.is::()); - // Tuple type MyTuple = (u32, f32, String); @@ -829,8 +827,10 @@ mod tests { map: HashMap, a_struct: SomeStruct, a_tuple_struct: SomeTupleStruct, + enum_unit: SomeEnum, + enum_tuple: SomeEnum, + enum_struct: SomeEnum, custom: CustomDebug, - unknown: Option, #[reflect(ignore)] #[allow(dead_code)] ignored: isize, @@ -841,6 +841,13 @@ mod tests { foo: String, } + #[derive(Reflect)] + enum SomeEnum { + A, + B(usize), + C { value: i32 }, + } + #[derive(Reflect)] struct SomeTupleStruct(String); @@ -865,8 +872,10 @@ mod tests { foo: String::from("A Struct!"), }, a_tuple_struct: SomeTupleStruct(String::from("A Tuple Struct!")), + enum_unit: SomeEnum::A, + enum_tuple: SomeEnum::B(123), + enum_struct: SomeEnum::C { value: 321 }, custom: CustomDebug, - unknown: Some(String::from("Enums aren't supported yet :(")), ignored: 321, }; @@ -893,8 +902,14 @@ bevy_reflect::tests::should_reflect_debug::Test { a_tuple_struct: bevy_reflect::tests::should_reflect_debug::SomeTupleStruct( "A Tuple Struct!", ), + enum_unit: A, + enum_tuple: B( + 123, + ), + enum_struct: C { + value: 321, + }, custom: Cool debug!, - unknown: Reflect(core::option::Option), }"#; assert_eq!(expected, format!("\n{:#?}", reflected)); diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index c1d41710e6f0d..3b3b7c274445a 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -1,6 +1,7 @@ use crate::{ - array_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug, - tuple_struct_debug, Array, List, Map, Struct, Tuple, TupleStruct, TypeInfo, Typed, ValueInfo, + array_debug, enum_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug, + tuple_struct_debug, Array, Enum, List, Map, Struct, Tuple, TupleStruct, TypeInfo, Typed, + ValueInfo, }; use std::{ any::{self, Any, TypeId}, @@ -23,6 +24,7 @@ pub enum ReflectRef<'a> { List(&'a dyn List), Array(&'a dyn Array), Map(&'a dyn Map), + Enum(&'a dyn Enum), Value(&'a dyn Reflect), } @@ -39,6 +41,7 @@ pub enum ReflectMut<'a> { List(&'a mut dyn List), Array(&'a mut dyn Array), Map(&'a mut dyn Map), + Enum(&'a mut dyn Enum), Value(&'a mut dyn Reflect), } @@ -170,6 +173,7 @@ pub trait Reflect: Any + Send + Sync { ReflectRef::List(dyn_list) => list_debug(dyn_list, f), ReflectRef::Array(dyn_array) => array_debug(dyn_array, f), ReflectRef::Map(dyn_map) => map_debug(dyn_map, f), + ReflectRef::Enum(dyn_enum) => enum_debug(dyn_enum, f), _ => write!(f, "Reflect({})", self.type_name()), } } diff --git a/crates/bevy_reflect/src/serde/de.rs b/crates/bevy_reflect/src/serde/de.rs index 950208fbea8e3..ad24185de15f3 100644 --- a/crates/bevy_reflect/src/serde/de.rs +++ b/crates/bevy_reflect/src/serde/de.rs @@ -1,9 +1,9 @@ use crate::{ - serde::type_fields, DynamicArray, DynamicList, DynamicMap, DynamicStruct, DynamicTuple, - DynamicTupleStruct, Map, Reflect, ReflectDeserialize, TypeRegistry, + serde::type_fields, DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, + DynamicTuple, DynamicTupleStruct, Map, Reflect, ReflectDeserialize, TypeRegistry, }; use erased_serde::Deserializer; -use serde::de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor}; +use serde::de::{self, DeserializeSeed, Error, MapAccess, SeqAccess, Visitor}; pub trait DeserializeValue { fn deserialize( @@ -203,6 +203,16 @@ impl<'a, 'de> Visitor<'de> for ReflectVisitor<'a> { })?; return Ok(Box::new(array)); } + type_fields::ENUM => { + let type_name = type_name + .take() + .ok_or_else(|| de::Error::missing_field(type_fields::TYPE))?; + let mut dynamic_enum = map.next_value_seed(EnumDeserializer { + registry: self.registry, + })?; + dynamic_enum.set_name(type_name); + return Ok(Box::new(dynamic_enum)); + } type_fields::VALUE => { let type_name = type_name .take() @@ -507,3 +517,187 @@ impl<'a, 'de> Visitor<'de> for TupleVisitor<'a> { Ok(tuple) } } + +struct EnumDeserializer<'a> { + registry: &'a TypeRegistry, +} + +impl<'a, 'de> DeserializeSeed<'de> for EnumDeserializer<'a> { + type Value = DynamicEnum; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_map(EnumVisitor { + registry: self.registry, + }) + } +} + +struct EnumVisitor<'a> { + registry: &'a TypeRegistry, +} + +impl<'a, 'de> Visitor<'de> for EnumVisitor<'a> { + type Value = DynamicEnum; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("enum value") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let key = map.next_key::()?; + match key.as_deref() { + Some(type_fields::VARIANT) => {} + Some(key) => return Err(V::Error::unknown_field(key, &[type_fields::VARIANT])), + _ => { + return Err(V::Error::missing_field(type_fields::VARIANT)); + } + } + + let variant_name = map.next_value::()?; + + let mut dynamic_enum = DynamicEnum::default(); + + let key = map.next_key::()?; + match key.as_deref() { + Some(type_fields::STRUCT) => { + let dynamic_struct = map.next_value_seed(StructDeserializer { + registry: self.registry, + })?; + dynamic_enum.set_variant(variant_name, dynamic_struct); + } + Some(type_fields::TUPLE) => { + let dynamic_tuple = map.next_value_seed(TupleDeserializer { + registry: self.registry, + })?; + dynamic_enum.set_variant(variant_name, dynamic_tuple); + } + Some(invalid_key) => { + return Err(V::Error::unknown_field( + invalid_key, + &[type_fields::STRUCT, type_fields::TUPLE], + )); + } + None => dynamic_enum.set_variant(variant_name, ()), + } + + Ok(dynamic_enum) + } +} + +#[cfg(test)] +mod tests { + use super::ReflectDeserializer; + use crate as bevy_reflect; + use crate::prelude::*; + use crate::{DynamicEnum, TypeRegistry}; + use ::serde::de::DeserializeSeed; + + fn get_registry() -> TypeRegistry { + let mut registry = TypeRegistry::default(); + registry.register::(); + registry.register::(); + registry.register::(); + registry.register::<(f32, f32)>(); + registry + } + + #[test] + fn enum_should_deserialize() { + #[derive(Reflect)] + enum MyEnum { + Unit, + NewType(usize), + Tuple(f32, f32), + Struct { value: String }, + } + + let mut registry = get_registry(); + registry.register::(); + + // === Unit Variant === // + let input = r#"{ + "type": "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum", + "enum": { + "variant": "Unit", + }, +}"#; + let reflect_deserializer = ReflectDeserializer::new(®istry); + let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); + let output = reflect_deserializer.deserialize(&mut deserializer).unwrap(); + + let expected = DynamicEnum::from(MyEnum::Unit); + assert!(expected.reflect_partial_eq(output.as_ref()).unwrap()); + + // === NewType Variant === // + let input = r#"{ + "type": "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum", + "enum": { + "variant": "NewType", + "tuple": [ + { + "type": "usize", + "value": 123, + }, + ], + }, +}"#; + let reflect_deserializer = ReflectDeserializer::new(®istry); + let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); + let output = reflect_deserializer.deserialize(&mut deserializer).unwrap(); + + let expected = DynamicEnum::from(MyEnum::NewType(123)); + assert!(expected.reflect_partial_eq(output.as_ref()).unwrap()); + + // === Tuple Variant === // + let input = r#"{ + "type": "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum", + "enum": { + "variant": "Tuple", + "tuple": [ + { + "type": "f32", + "value": 1.23, + }, + { + "type": "f32", + "value": 3.21, + }, + ], + }, +}"#; + let reflect_deserializer = ReflectDeserializer::new(®istry); + let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); + let output = reflect_deserializer.deserialize(&mut deserializer).unwrap(); + + let expected = DynamicEnum::from(MyEnum::Tuple(1.23, 3.21)); + assert!(expected.reflect_partial_eq(output.as_ref()).unwrap()); + + // === Struct Variant === // + let input = r#"{ + "type": "bevy_reflect::serde::de::tests::enum_should_deserialize::MyEnum", + "enum": { + "variant": "Struct", + "struct": { + "value": { + "type": "alloc::string::String", + "value": "I <3 Enums", + }, + }, + }, +}"#; + let reflect_deserializer = ReflectDeserializer::new(®istry); + let mut deserializer = ron::de::Deserializer::from_str(input).unwrap(); + let output = reflect_deserializer.deserialize(&mut deserializer).unwrap(); + + let expected = DynamicEnum::from(MyEnum::Struct { + value: String::from("I <3 Enums"), + }); + assert!(expected.reflect_partial_eq(output.as_ref()).unwrap()); + } +} diff --git a/crates/bevy_reflect/src/serde/mod.rs b/crates/bevy_reflect/src/serde/mod.rs index 5e664112a8860..7c7c18f4ea954 100644 --- a/crates/bevy_reflect/src/serde/mod.rs +++ b/crates/bevy_reflect/src/serde/mod.rs @@ -9,6 +9,8 @@ pub(crate) mod type_fields { pub const MAP: &str = "map"; pub const STRUCT: &str = "struct"; pub const TUPLE_STRUCT: &str = "tuple_struct"; + pub const ENUM: &str = "enum"; + pub const VARIANT: &str = "variant"; pub const TUPLE: &str = "tuple"; pub const LIST: &str = "list"; pub const ARRAY: &str = "array"; diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index de0ad7760a6fa..0dd239d6305a7 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -1,10 +1,11 @@ use crate::{ - serde::type_fields, Array, List, Map, Reflect, ReflectRef, ReflectSerialize, Struct, Tuple, - TupleStruct, TypeRegistry, + serde::type_fields, Array, Enum, List, Map, Reflect, ReflectRef, ReflectSerialize, Struct, + Tuple, TupleStruct, TypeRegistry, VariantType, }; +use serde::ser::Error; use serde::{ ser::{SerializeMap, SerializeSeq}, - Serialize, + Serialize, Serializer, }; pub enum Serializable<'a> { @@ -84,6 +85,11 @@ impl<'a> Serialize for ReflectSerializer<'a> { registry: self.registry, } .serialize(serializer), + ReflectRef::Enum(value) => EnumSerializer { + enum_value: value, + registry: self.registry, + } + .serialize(serializer), ReflectRef::Value(value) => ReflectValueSerializer { registry: self.registry, value, @@ -198,6 +204,117 @@ impl<'a> Serialize for TupleStructValueSerializer<'a> { } } +pub struct EnumSerializer<'a> { + pub enum_value: &'a dyn Enum, + pub registry: &'a TypeRegistry, +} + +impl<'a> Serialize for EnumSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_map(Some(2))?; + + state.serialize_entry(type_fields::TYPE, self.enum_value.type_name())?; + state.serialize_entry( + type_fields::ENUM, + &EnumValueSerializer { + enum_value: self.enum_value, + registry: self.registry, + }, + )?; + state.end() + } +} + +pub struct EnumValueSerializer<'a> { + pub enum_value: &'a dyn Enum, + pub registry: &'a TypeRegistry, +} + +impl<'a> Serialize for EnumValueSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let variant_type = self.enum_value.variant_type(); + let variant_name = self.enum_value.variant_name(); + + let mut state = if matches!(variant_type, VariantType::Unit) { + serializer.serialize_map(Some(1))? + } else { + serializer.serialize_map(Some(2))? + }; + + state.serialize_entry(type_fields::VARIANT, variant_name)?; + + match self.enum_value.variant_type() { + VariantType::Struct => { + state.serialize_key(type_fields::STRUCT)?; + state.serialize_value(&StructVariantSerializer { + enum_value: self.enum_value, + registry: self.registry, + })?; + } + VariantType::Tuple => { + state.serialize_key(type_fields::TUPLE)?; + state.serialize_value(&TupleVariantSerializer { + enum_value: self.enum_value, + registry: self.registry, + })?; + } + _ => {} + } + + state.end() + } +} + +pub struct TupleVariantSerializer<'a> { + pub enum_value: &'a dyn Enum, + pub registry: &'a TypeRegistry, +} + +impl<'a> Serialize for TupleVariantSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let field_len = self.enum_value.field_len(); + let mut state = serializer.serialize_seq(Some(field_len))?; + for field in self.enum_value.iter_fields() { + state.serialize_element(&ReflectSerializer::new(field.value(), self.registry))?; + } + state.end() + } +} + +pub struct StructVariantSerializer<'a> { + pub enum_value: &'a dyn Enum, + pub registry: &'a TypeRegistry, +} + +impl<'a> Serialize for StructVariantSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let field_len = self.enum_value.field_len(); + let mut state = serializer.serialize_map(Some(field_len))?; + for (index, field) in self.enum_value.iter_fields().enumerate() { + let name = field.name().ok_or_else(|| { + S::Error::custom(format_args!( + "struct variant missing name for field at index {}", + index + )) + })?; + state.serialize_entry(name, &ReflectSerializer::new(field.value(), self.registry))?; + } + state.end() + } +} + pub struct TupleSerializer<'a> { pub tuple: &'a dyn Tuple, pub registry: &'a TypeRegistry, @@ -366,3 +483,109 @@ impl<'a> Serialize for ArrayValueSerializer<'a> { state.end() } } + +#[cfg(test)] +mod tests { + use super::ReflectSerializer; + use crate as bevy_reflect; + use crate::prelude::*; + use crate::TypeRegistry; + use ron::ser::PrettyConfig; + + fn get_registry() -> TypeRegistry { + let mut registry = TypeRegistry::default(); + registry.register::(); + registry.register::(); + registry.register::(); + registry.register::<(f32, f32)>(); + registry + } + + #[test] + fn enum_should_serialize() { + #[derive(Reflect)] + enum MyEnum { + Unit, + NewType(usize), + Tuple(f32, f32), + Struct { value: String }, + } + + let mut registry = get_registry(); + registry.register::(); + + let config = PrettyConfig::default().new_line(String::from("\n")); + + // === Unit Variant === // + let value = MyEnum::Unit; + let serializer = ReflectSerializer::new(&value, ®istry); + let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); + let expected = r#"{ + "type": "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum", + "enum": { + "variant": "Unit", + }, +}"#; + assert_eq!(expected, output); + + // === NewType Variant === // + let value = MyEnum::NewType(123); + let serializer = ReflectSerializer::new(&value, ®istry); + let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); + let expected = r#"{ + "type": "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum", + "enum": { + "variant": "NewType", + "tuple": [ + { + "type": "usize", + "value": 123, + }, + ], + }, +}"#; + assert_eq!(expected, output); + + // === Tuple Variant === // + let value = MyEnum::Tuple(1.23, 3.21); + let serializer = ReflectSerializer::new(&value, ®istry); + let output = ron::ser::to_string_pretty(&serializer, config.clone()).unwrap(); + let expected = r#"{ + "type": "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum", + "enum": { + "variant": "Tuple", + "tuple": [ + { + "type": "f32", + "value": 1.23, + }, + { + "type": "f32", + "value": 3.21, + }, + ], + }, +}"#; + assert_eq!(expected, output); + + // === Struct Variant === // + let value = MyEnum::Struct { + value: String::from("I <3 Enums"), + }; + let serializer = ReflectSerializer::new(&value, ®istry); + let output = ron::ser::to_string_pretty(&serializer, config).unwrap(); + let expected = r#"{ + "type": "bevy_reflect::serde::ser::tests::enum_should_serialize::MyEnum", + "enum": { + "variant": "Struct", + "struct": { + "value": { + "type": "alloc::string::String", + "value": "I <3 Enums", + }, + }, + }, +}"#; + assert_eq!(expected, output.replace('\r', "")); + } +} diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index 60a9ba9e6125e..4f0b7819f258d 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -276,6 +276,11 @@ impl DynamicStruct { self.insert_boxed(name, Box::new(value)); } } + + /// Gets the index of the field with the given name. + pub fn index_of(&self, name: &str) -> Option { + self.field_indices.get(name).copied() + } } impl Struct for DynamicStruct { diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index 942ba84fdf6e3..afbe037b7243d 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -1,4 +1,6 @@ -use crate::{ArrayInfo, ListInfo, MapInfo, Reflect, StructInfo, TupleInfo, TupleStructInfo}; +use crate::{ + ArrayInfo, EnumInfo, ListInfo, MapInfo, Reflect, StructInfo, TupleInfo, TupleStructInfo, +}; use std::any::{Any, TypeId}; /// A static accessor to compile-time type information. @@ -99,6 +101,7 @@ pub enum TypeInfo { List(ListInfo), Array(ArrayInfo), Map(MapInfo), + Enum(EnumInfo), Value(ValueInfo), /// Type information for "dynamic" types whose metadata can't be known at compile-time. /// @@ -116,6 +119,7 @@ impl TypeInfo { Self::List(info) => info.type_id(), Self::Array(info) => info.type_id(), Self::Map(info) => info.type_id(), + Self::Enum(info) => info.type_id(), Self::Value(info) => info.type_id(), Self::Dynamic(info) => info.type_id(), } @@ -132,6 +136,7 @@ impl TypeInfo { Self::List(info) => info.type_name(), Self::Array(info) => info.type_name(), Self::Map(info) => info.type_name(), + Self::Enum(info) => info.type_name(), Self::Value(info) => info.type_name(), Self::Dynamic(info) => info.type_name(), } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 11d27dee47024..f24e49e9943e5 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -19,6 +19,7 @@ use bevy_ecs::{ }; use bevy_math::{Mat4, UVec2, Vec2, Vec3}; use bevy_reflect::prelude::*; +use bevy_reflect::FromReflect; use bevy_transform::components::GlobalTransform; use bevy_utils::HashSet; use bevy_window::{WindowCreated, WindowId, WindowResized, Windows}; @@ -309,7 +310,7 @@ impl RenderTarget { } } -#[derive(Debug, Clone, Copy, Default, Reflect, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Default, Reflect, FromReflect, Serialize, Deserialize)] #[reflect_value(Serialize, Deserialize)] pub enum DepthCalculation { /// Pythagorean distance; works everywhere, more expensive to compute. diff --git a/crates/bevy_render/src/camera/projection.rs b/crates/bevy_render/src/camera/projection.rs index aaf0e761bba37..a54f7fd5dea58 100644 --- a/crates/bevy_render/src/camera/projection.rs +++ b/crates/bevy_render/src/camera/projection.rs @@ -5,7 +5,8 @@ use bevy_app::{App, CoreStage, Plugin, StartupStage}; use bevy_ecs::{prelude::*, reflect::ReflectComponent}; use bevy_math::Mat4; use bevy_reflect::{ - std_traits::ReflectDefault, GetTypeRegistration, Reflect, ReflectDeserialize, ReflectSerialize, + std_traits::ReflectDefault, FromReflect, GetTypeRegistration, Reflect, ReflectDeserialize, + ReflectSerialize, }; use bevy_window::ModifiesWindows; use serde::{Deserialize, Serialize}; @@ -101,7 +102,7 @@ impl Default for Projection { } } -#[derive(Component, Debug, Clone, Reflect)] +#[derive(Component, Debug, Clone, Reflect, FromReflect)] #[reflect(Component, Default)] pub struct PerspectiveProjection { pub fov: f32, @@ -140,14 +141,14 @@ impl Default for PerspectiveProjection { } // TODO: make this a component instead of a property -#[derive(Debug, Clone, Reflect, Serialize, Deserialize)] +#[derive(Debug, Clone, Reflect, FromReflect, Serialize, Deserialize)] #[reflect_value(Serialize, Deserialize)] pub enum WindowOrigin { Center, BottomLeft, } -#[derive(Debug, Clone, Reflect, Serialize, Deserialize)] +#[derive(Debug, Clone, Reflect, FromReflect, Serialize, Deserialize)] #[reflect_value(Serialize, Deserialize)] pub enum ScalingMode { /// Manually specify left/right/top/bottom values. @@ -166,7 +167,7 @@ pub enum ScalingMode { FixedHorizontal(f32), } -#[derive(Component, Debug, Clone, Reflect)] +#[derive(Component, Debug, Clone, Reflect, FromReflect)] #[reflect(Component, Default)] pub struct OrthographicProjection { pub left: f32, diff --git a/crates/bevy_window/Cargo.toml b/crates/bevy_window/Cargo.toml index 8a93fd404ddb6..4a27f70fca320 100644 --- a/crates/bevy_window/Cargo.toml +++ b/crates/bevy_window/Cargo.toml @@ -13,6 +13,7 @@ keywords = ["bevy"] bevy_app = { path = "../bevy_app", version = "0.8.0" } bevy_ecs = { path = "../bevy_ecs", version = "0.8.0" } bevy_math = { path = "../bevy_math", version = "0.8.0" } +bevy_reflect = { path = "../bevy_reflect", version = "0.8.0" } bevy_utils = { path = "../bevy_utils", version = "0.8.0" } # Used for close_on_esc bevy_input = { path = "../bevy_input", version = "0.8.0" } diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 3972046781d29..a12ca415de64c 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -1,8 +1,10 @@ use bevy_math::{DVec2, IVec2, UVec2, Vec2}; +use bevy_reflect::{FromReflect, Reflect}; use bevy_utils::{tracing::warn, Uuid}; use raw_window_handle::RawWindowHandle; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Reflect, FromReflect)] +#[reflect_value(PartialEq, Hash)] /// A unique ID for a [`Window`]. pub struct WindowId(Uuid); diff --git a/examples/reflection/reflection_types.rs b/examples/reflection/reflection_types.rs index e5600066e35c2..db9dc4b61d995 100644 --- a/examples/reflection/reflection_types.rs +++ b/examples/reflection/reflection_types.rs @@ -24,32 +24,40 @@ pub struct A { z: HashMap, } -/// Deriving reflect on a unit struct will implement `Reflect` and `Struct` traits +/// Deriving reflect on a unit struct will implement the `Reflect` and `Struct` traits #[derive(Reflect)] pub struct B; -/// Deriving reflect on a tuple struct will implement `Reflect` and `TupleStruct` traits +/// Deriving reflect on a tuple struct will implement the `Reflect` and `TupleStruct` traits #[derive(Reflect)] pub struct C(usize); +/// Deriving reflect on an enum will implement the `Reflect` and `Enum` traits +#[derive(Reflect)] +pub enum D { + A, + B(usize), + C { value: f32 }, +} + /// Reflect has "built in" support for some common traits like `PartialEq`, `Hash`, and `Serialize`. -/// These are exposed via methods like `Reflect::hash()`, `Reflect::partial_eq()`, and -/// `Reflect::serialize()`. You can force these implementations to use the actual trait +/// These are exposed via methods like `Reflect::reflect_hash()`, `Reflect::reflect_partial_eq()`, and +/// `Reflect::serializable()`. You can force these implementations to use the actual trait /// implementations (instead of their defaults) like this: #[derive(Reflect, Hash, Serialize, PartialEq, Eq)] #[reflect(Hash, Serialize, PartialEq)] -pub struct D { +pub struct E { x: usize, } -/// By default, deriving with Reflect assumes the type is a "struct". You can tell reflect to treat -/// your type as a "value type" by using the `reflect_value` attribute instead of `reflect`. It is -/// generally a good idea to implement (and reflect) the `PartialEq`, `Serialize`, and `Deserialize` -/// traits on `reflect_value` types to ensure that these values behave as expected when nested -/// underneath Reflect-ed structs. +/// By default, deriving with Reflect assumes the type is either a "struct" or an "enum". +/// You can tell reflect to treat your type instead as a "value type" by using the `reflect_value` +/// attribute in place of `reflect`. It is generally a good idea to implement (and reflect) +/// the `PartialEq`, `Serialize`, and `Deserialize` traits on `reflect_value` types to ensure +/// that these values behave as expected when nested underneath Reflect-ed structs. #[derive(Reflect, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] #[reflect_value(PartialEq, Serialize, Deserialize)] -pub enum E { +pub enum F { X, Y, } @@ -82,6 +90,9 @@ fn setup() { // with fields via their indices. Tuple is automatically implemented for tuples of // arity 12 or less. ReflectRef::Tuple(_) => {} + // `Enum` is a trait automatically implemented for enums that derive Reflect. This trait allows you + // to interact with the current variant and its fields (if it has any) + ReflectRef::Enum(_) => {} // `List` is a special trait that can be manually implemented (instead of deriving Reflect). // This exposes "list" operations on your type, such as insertion. `List` is automatically // implemented for relevant core types like Vec.