mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
bevy_reflect: Reflect enums (#4761)
# 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<dyn Enum> = 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
}
]
}
}
```
<details>
<summary>Effects on Option</summary>
This ends up making `Option` look a little ugly:
```json
{
"type": "core::option::Option<usize>",
"enum": {
"variant": "Some",
"tuple": [
{
"type": "usize",
"value": 123
}
]
}
}
```
</details>
##### Struct
```json
{
"type": "my_crate::TestEnum",
"enum": {
"variant": "C",
"struct": {
"foo": {
"type": "f32",
"value": 1.23
},
"bar": {
"type": "bool",
"value": false
}
}
}
}
```
## Design Decisions
<details>
<summary><strong>View Section</strong></summary>
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<i32> = None;
let my_enum = Box::new(foo) as Box<dyn TupleVariant>;
```
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](71d27ab3c6
) 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<Cow<'static, str>, 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<T>` 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.
</details>
---
## 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<T>` 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<T>`) 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 <gino.valente.code@gmail.com>
This commit is contained in:
parent
a9cb18eefc
commit
15826d6019
31 changed files with 3828 additions and 736 deletions
|
@ -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,
|
||||
|
|
|
@ -1,14 +1,78 @@
|
|||
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};
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) enum DeriveType {
|
||||
Struct,
|
||||
TupleStruct,
|
||||
UnitStruct,
|
||||
Value,
|
||||
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>),
|
||||
}
|
||||
|
||||
/// Metadata present on all reflected types, including name, generics, and attributes.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[derive(Reflect)]
|
||||
/// // traits
|
||||
/// // |----------------------------------------|
|
||||
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
/// // type_name generics
|
||||
/// // |-------------------||----------|
|
||||
/// struct ThingThatImReflecting<T1, T2, T3> {/* ... */}
|
||||
/// ```
|
||||
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<T1, T2, T3> {
|
||||
/// x: T1, // |
|
||||
/// y: T2, // |- fields
|
||||
/// z: T3 // |
|
||||
/// }
|
||||
/// ```
|
||||
pub(crate) struct ReflectStruct<'a> {
|
||||
meta: ReflectMeta<'a>,
|
||||
fields: Vec<StructField<'a>>,
|
||||
}
|
||||
|
||||
/// Enum data used by derive macros for `Reflect` and `FromReflect`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[derive(Reflect)]
|
||||
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
/// enum ThingThatImReflecting<T1, T2, T3> {
|
||||
/// A(T1), // |
|
||||
/// B, // |- variants
|
||||
/// C { foo: T2, bar: T3 } // |
|
||||
/// }
|
||||
/// ```
|
||||
pub(crate) struct ReflectEnum<'a> {
|
||||
meta: ReflectMeta<'a>,
|
||||
variants: Vec<EnumVariant<'a>>,
|
||||
}
|
||||
|
||||
/// Represents a field on a struct or tuple struct.
|
||||
|
@ -21,41 +85,28 @@ pub(crate) struct StructField<'a> {
|
|||
pub index: usize,
|
||||
}
|
||||
|
||||
/// Data used by derive macros for `Reflect` and `FromReflect`
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// // attrs
|
||||
/// // |----------------------------------------|
|
||||
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
|
||||
/// // type_name generics
|
||||
/// // |-------------------||----------|
|
||||
/// struct ThingThatImReflecting<T1, T2, T3> {
|
||||
/// x: T1, // |
|
||||
/// y: T2, // |- fields
|
||||
/// z: T3 // |
|
||||
/// }
|
||||
/// ```
|
||||
pub(crate) struct ReflectDeriveData<'a> {
|
||||
derive_type: DeriveType,
|
||||
traits: ReflectTraits,
|
||||
type_name: &'a Ident,
|
||||
generics: &'a Generics,
|
||||
fields: Vec<StructField<'a>>,
|
||||
bevy_reflect_path: Path,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
impl<'a> ReflectDeriveData<'a> {
|
||||
pub fn from_input(input: &'a DeriveInput) -> Result<Self, syn::Error> {
|
||||
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(),
|
||||
};
|
||||
pub(crate) enum EnumVariantFields<'a> {
|
||||
Named(Vec<StructField<'a>>),
|
||||
Unnamed(Vec<StructField<'a>>),
|
||||
Unit,
|
||||
}
|
||||
|
||||
impl<'a> ReflectDerive<'a> {
|
||||
pub fn from_input(input: &'a DeriveInput) -> Result<Self, syn::Error> {
|
||||
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;
|
||||
}
|
||||
return Ok(output);
|
||||
}
|
||||
_ => {
|
||||
return Ok(output);
|
||||
}
|
||||
};
|
||||
let meta = ReflectMeta::new(&input.ident, &input.generics, traits);
|
||||
|
||||
let mut errors: Option<syn::Error> = None;
|
||||
output.fields = fields
|
||||
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)),
|
||||
}
|
||||
}
|
||||
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",
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
fn collect_struct_fields(fields: &'a Fields) -> Result<Vec<StructField<'a>>, syn::Error> {
|
||||
let sifter: utility::ResultSifter<StructField<'a>> = 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<StructField, syn::Error> {
|
||||
let attrs = parse_field_attrs(&field.attrs)?;
|
||||
Ok(StructField {
|
||||
index,
|
||||
attrs,
|
||||
data: field,
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Vec<StructField>>();
|
||||
if let Some(errs) = errors {
|
||||
return Err(errs);
|
||||
.fold(
|
||||
utility::ResultSifter::default(),
|
||||
utility::ResultSifter::fold,
|
||||
);
|
||||
|
||||
sifter.finish()
|
||||
}
|
||||
|
||||
fn collect_enum_variants(
|
||||
variants: &'a Punctuated<Variant, Token![,]>,
|
||||
) -> Result<Vec<EnumVariant<'a>>, syn::Error> {
|
||||
let sifter: utility::ResultSifter<EnumVariant<'a>> = variants
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, variant)| -> Result<EnumVariant, syn::Error> {
|
||||
let fields = Self::collect_struct_fields(&variant.fields)?;
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
sifter.finish()
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Get an iterator over the active fields
|
||||
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
|
||||
self.fields.iter().filter(|field| !field.attrs.ignore)
|
||||
}
|
||||
|
||||
/// Get an iterator over the ignored fields
|
||||
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
|
||||
self.fields.iter().filter(|field| field.attrs.ignore)
|
||||
}
|
||||
|
||||
/// Get a collection of all active types
|
||||
pub fn active_types(&self) -> Vec<syn::Type> {
|
||||
self.active_fields()
|
||||
.map(|field| field.data.ty.clone())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// The [`DeriveType`] of this struct.
|
||||
pub fn derive_type(&self) -> &DeriveType {
|
||||
&self.derive_type
|
||||
}
|
||||
|
||||
/// 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<Item = &StructField<'a>> {
|
||||
self.fields.iter().filter(|field| !field.attrs.ignore)
|
||||
}
|
||||
|
||||
/// Get an iterator over the ignored fields.
|
||||
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
|
||||
self.fields.iter().filter(|field| field.attrs.ignore)
|
||||
}
|
||||
|
||||
/// Get a collection of all active types.
|
||||
pub fn active_types(&self) -> impl Iterator<Item = &Type> {
|
||||
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<Item = &EnumVariant<'a>> {
|
||||
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<Item = &EnumVariant<'a>> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
84
crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs
Normal file
84
crates/bevy_reflect/bevy_reflect_derive/src/enum_utility.rs
Normal file
|
@ -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<String>,
|
||||
/// The stream of tokens that will construct each variant.
|
||||
pub variant_constructors: Vec<proc_macro2::TokenStream>,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
|
@ -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<Self> {
|
||||
|
@ -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<Self> {
|
||||
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::<Self>()),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Container for a struct's members (field name or index) and their
|
||||
/// corresponding values.
|
||||
struct MemberValuePair(Vec<Member>, Vec<proc_macro2::TokenStream>);
|
||||
|
@ -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<T>` 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);
|
||||
|
|
|
@ -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::<Vec<String>>();
|
||||
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::<Vec<_>>();
|
||||
let field_types = derive_data
|
||||
.active_fields()
|
||||
.map(|field| field.data.ty.clone())
|
||||
.collect::<Vec<_>>();
|
||||
let field_count = field_idents.len();
|
||||
let field_indices = (0..field_count).collect::<Vec<usize>>();
|
||||
|
||||
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<bool> {
|
||||
#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::<Self>(&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::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo {
|
||||
<Self as #bevy_reflect_path::Typed>::type_info()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
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<dyn #bevy_reflect_path::Reflect> {
|
||||
Box::new(#bevy_reflect_path::Struct::clone_dynamic(self))
|
||||
}
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
||||
*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::<Vec<_>>();
|
||||
let field_types = derive_data
|
||||
.active_fields()
|
||||
.map(|field| field.data.ty.clone())
|
||||
.collect::<Vec<_>>();
|
||||
let field_count = field_idents.len();
|
||||
let field_indices = (0..field_count).collect::<Vec<usize>>();
|
||||
|
||||
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<bool> {
|
||||
#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::<Self>(&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::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo {
|
||||
<Self as #bevy_reflect_path::Typed>::type_info()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
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<dyn #bevy_reflect_path::Reflect> {
|
||||
Box::new(#bevy_reflect_path::TupleStruct::clone_dynamic(self))
|
||||
}
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
||||
*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::<Self>();
|
||||
#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::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo {
|
||||
<Self as #bevy_reflect_path::Typed>::type_info()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
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<dyn #bevy_reflect_path::Reflect> {
|
||||
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>() {
|
||||
*self = value.clone();
|
||||
} else {
|
||||
panic!("Value is not {}.", std::any::type_name::<Self>());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
||||
*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::<Self, _>(|| {
|
||||
#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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
363
crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs
Normal file
363
crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs
Normal file
|
@ -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<u64> {
|
||||
#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<bool> {
|
||||
#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::<Self>(&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<usize> {
|
||||
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>(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::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo {
|
||||
<Self as #bevy_reflect_path::Typed>::type_info()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
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<dyn #bevy_reflect_path::Reflect> {
|
||||
Box::new(#bevy_reflect_path::Enum::clone_dynamic(self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&mut self, #ref_value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
||||
*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::<Self>()),
|
||||
}
|
||||
}
|
||||
} 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<proc_macro2::TokenStream>,
|
||||
enum_field: Vec<proc_macro2::TokenStream>,
|
||||
enum_field_at: Vec<proc_macro2::TokenStream>,
|
||||
enum_index_of: Vec<proc_macro2::TokenStream>,
|
||||
enum_name_at: Vec<proc_macro2::TokenStream>,
|
||||
enum_field_len: Vec<proc_macro2::TokenStream>,
|
||||
enum_variant_name: Vec<proc_macro2::TokenStream>,
|
||||
enum_variant_type: Vec<proc_macro2::TokenStream>,
|
||||
}
|
||||
|
||||
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<proc_macro2::TokenStream>) {
|
||||
let mut constructor_argument = Vec::new();
|
||||
let mut reflect_idx = 0;
|
||||
for field in fields.iter() {
|
||||
if field.attrs.ignore {
|
||||
// Ignored field
|
||||
continue;
|
||||
}
|
||||
constructor_argument.push(generate_for_field(reflect_idx, field.index, field));
|
||||
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,
|
||||
}
|
||||
}
|
11
crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs
Normal file
11
crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs
Normal file
|
@ -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;
|
201
crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs
Normal file
201
crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs
Normal file
|
@ -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::<Vec<String>>();
|
||||
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::<Vec<_>>();
|
||||
let field_types = reflect_struct.active_types();
|
||||
let field_count = field_idents.len();
|
||||
let field_indices = (0..field_count).collect::<Vec<usize>>();
|
||||
|
||||
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<bool> {
|
||||
#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::<Self>(&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::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo {
|
||||
<Self as #bevy_reflect_path::Typed>::type_info()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
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<dyn #bevy_reflect_path::Reflect> {
|
||||
Box::new(#bevy_reflect_path::Struct::clone_dynamic(self))
|
||||
}
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
||||
*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
|
||||
}
|
||||
})
|
||||
}
|
|
@ -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::<Vec<_>>();
|
||||
let field_types = reflect_struct.active_types();
|
||||
let field_count = field_idents.len();
|
||||
let field_indices = (0..field_count).collect::<Vec<usize>>();
|
||||
|
||||
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<bool> {
|
||||
#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::<Self>(&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::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo {
|
||||
<Self as #bevy_reflect_path::Typed>::type_info()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
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<dyn #bevy_reflect_path::Reflect> {
|
||||
Box::new(#bevy_reflect_path::TupleStruct::clone_dynamic(self))
|
||||
}
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
||||
*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
|
||||
}
|
||||
})
|
||||
}
|
38
crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs
Normal file
38
crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs
Normal file
|
@ -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::<Self, _>(|| {
|
||||
#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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
105
crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs
Normal file
105
crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs
Normal file
|
@ -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::<Self>();
|
||||
#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::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_type_info(&self) -> &'static #bevy_reflect_path::TypeInfo {
|
||||
<Self as #bevy_reflect_path::Typed>::type_info()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
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<dyn #bevy_reflect_path::Reflect> {
|
||||
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>() {
|
||||
*self = value.clone();
|
||||
} else {
|
||||
panic!("Value is not {}.", std::any::type_name::<Self>());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn #bevy_reflect_path::Reflect>) -> Result<(), Box<dyn #bevy_reflect_path::Reflect>> {
|
||||
*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
|
||||
}
|
||||
})
|
||||
}
|
|
@ -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();
|
||||
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
|
||||
TokenStream::from(quote! {
|
||||
#impl_struct
|
||||
|
||||
#impl_from_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(),
|
||||
))
|
||||
}
|
||||
|
|
|
@ -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<Vec<T>, syn::Error>`,
|
||||
/// combining errors into one along the way.
|
||||
pub(crate) struct ResultSifter<T> {
|
||||
items: Vec<T>,
|
||||
errors: Option<syn::Error>,
|
||||
}
|
||||
|
||||
/// 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<T> Default for ResultSifter<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
items: Vec::new(),
|
||||
errors: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ResultSifter<T> {
|
||||
/// Sift the given result, combining errors if necessary.
|
||||
pub fn sift(&mut self, result: Result<T, syn::Error>) {
|
||||
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<T, syn::Error>) -> Self {
|
||||
sifter.sift(result);
|
||||
sifter
|
||||
}
|
||||
|
||||
/// Complete the sifting process and return the final result.
|
||||
pub fn finish(self) -> Result<Vec<T>, syn::Error> {
|
||||
if let Some(errors) = self.errors {
|
||||
Err(errors)
|
||||
} else {
|
||||
Ok(self.items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
375
crates/bevy_reflect/src/enums/dynamic_enum.rs
Normal file
375
crates/bevy_reflect/src/enums/dynamic_enum.rs
Normal file
|
@ -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<DynamicTuple> for DynamicVariant {
|
||||
fn from(dyn_tuple: DynamicTuple) -> Self {
|
||||
Self::Tuple(dyn_tuple)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DynamicStruct> 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<usize> = 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<I: Into<String>, V: Into<DynamicVariant>>(
|
||||
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<I: Into<String>, V: Into<DynamicVariant>>(&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<TEnum: Enum>(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<TEnum: Enum>(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<usize> {
|
||||
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 {
|
||||
<Self as Typed>::type_info()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_any(self: Box<Self>) -> Box<dyn Any> {
|
||||
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<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*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<dyn Reflect> {
|
||||
Box::new(self.clone_dynamic())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_hash(&self) -> Option<u64> {
|
||||
enum_hash(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
|
||||
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::<Self>()))
|
||||
}
|
||||
}
|
280
crates/bevy_reflect/src/enums/enum_trait.rs
Normal file
280
crates/bevy_reflect/src/enums/enum_trait.rs
Normal file
|
@ -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<T>`]. 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<T>::None
|
||||
/// [`Some`]: core::option::Option<T>::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<usize>;
|
||||
/// 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<Cow<'static, str>, usize>,
|
||||
}
|
||||
|
||||
impl EnumInfo {
|
||||
/// Create a new [`EnumInfo`].
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `variants`: The variants of this enum in the order they are defined
|
||||
///
|
||||
pub fn new<TEnum: Enum>(variants: &[VariantInfo]) -> Self {
|
||||
let variant_indices = variants
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, variant)| {
|
||||
let name = variant.name().clone();
|
||||
(name, index)
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
Self {
|
||||
type_name: std::any::type_name::<TEnum>(),
|
||||
type_id: TypeId::of::<TEnum>(),
|
||||
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<usize> {
|
||||
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<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == 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<Self::Item> {
|
||||
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<usize>) {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
121
crates/bevy_reflect/src/enums/helpers.rs
Normal file
121
crates/bevy_reflect/src/enums/helpers.rs
Normal file
|
@ -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<TEnum: Enum>(value: &TEnum) -> Option<u64> {
|
||||
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<TEnum: Enum>(a: &TEnum, b: &dyn Reflect) -> Option<bool> {
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
}
|
651
crates/bevy_reflect/src/enums/mod.rs
Normal file
651
crates/bevy_reflect/src/enums/mod.rs
Normal file
|
@ -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::<MyEnum>(), "expected type to be `MyEnum`");
|
||||
assert_eq!(std::any::type_name::<MyEnum>(), 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::<usize>());
|
||||
assert!(variant.field_at(1).unwrap().is::<i32>());
|
||||
} 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::<f32>());
|
||||
assert!(variant.field("foo").unwrap().is::<f32>());
|
||||
} 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::<bool>());
|
||||
} else {
|
||||
panic!("expected `VariantInfo::Struct`");
|
||||
}
|
||||
} else {
|
||||
panic!("expected `TypeInfo::Enum`");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_should_allow_generics() {
|
||||
#[derive(Reflect, Debug, PartialEq)]
|
||||
enum TestEnum<T: FromReflect> {
|
||||
A,
|
||||
B(T),
|
||||
C { value: T },
|
||||
}
|
||||
|
||||
if let TypeInfo::Enum(info) = TestEnum::<f32>::type_info() {
|
||||
if let VariantInfo::Tuple(variant) = info.variant("B").unwrap() {
|
||||
assert!(variant.field_at(0).unwrap().is::<f32>());
|
||||
} else {
|
||||
panic!("expected `VariantInfo::Struct`");
|
||||
}
|
||||
if let VariantInfo::Struct(variant) = info.variant("C").unwrap() {
|
||||
assert!(variant.field("value").unwrap().is::<f32>());
|
||||
} else {
|
||||
panic!("expected `VariantInfo::Struct`");
|
||||
}
|
||||
} else {
|
||||
panic!("expected `TypeInfo::Enum`");
|
||||
}
|
||||
|
||||
let mut value = TestEnum::<f32>::A;
|
||||
|
||||
// === Tuple === //
|
||||
let mut data = DynamicTuple::default();
|
||||
data.insert(1.23_f32);
|
||||
let dyn_enum = DynamicEnum::new(std::any::type_name::<TestEnum<f32>>(), "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::<TestEnum<f32>>(), "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::<TestEnum>(), "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::<TestEnum>(), "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::<TestEnum>(), "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::<TestEnum>(), "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<dyn Reflect> = 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<dyn Reflect> = 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}}"
|
||||
);
|
||||
}
|
||||
}
|
231
crates/bevy_reflect/src/enums/variants.rs
Normal file
231
crates/bevy_reflect/src/enums/variants.rs
Normal file
|
@ -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<Cow<'static, str>, 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<usize> {
|
||||
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<Cow<'static, str>, 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
|
||||
}
|
||||
}
|
|
@ -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<T: Clone + Reflect + 'static>());
|
||||
impl_reflect_value!(Result<T: Clone + Reflect + 'static, E: Clone + Reflect + 'static>());
|
||||
impl_reflect_value!(HashSet<T: Hash + Eq + Clone + Send + Sync + 'static>());
|
||||
impl_reflect_value!(Range<T: Clone + Send + Sync + 'static>());
|
||||
|
@ -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<T: Clone + Reflect + 'static>);
|
||||
impl_from_reflect_value!(HashSet<T: Hash + Eq + Clone + Send + Sync + 'static>);
|
||||
impl_from_reflect_value!(Range<T: Clone + Send + Sync + 'static>);
|
||||
impl_from_reflect_value!(Duration);
|
||||
|
@ -557,6 +557,218 @@ impl Reflect for Cow<'static, str> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Reflect + Clone> GetTypeRegistration for Option<T> {
|
||||
fn get_type_registration() -> TypeRegistration {
|
||||
TypeRegistration::of::<Option<T>>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflect + Clone> Enum for Option<T> {
|
||||
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<usize> {
|
||||
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>(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflect + Clone> Reflect for Option<T> {
|
||||
#[inline]
|
||||
fn type_name(&self) -> &str {
|
||||
std::any::type_name::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_type_info(&self) -> &'static TypeInfo {
|
||||
<Self as Typed>::type_info()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_any(self: Box<Self>) -> Box<dyn Any> {
|
||||
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::<T>().unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"Field at index 0 should be of type {}",
|
||||
std::any::type_name::<T>()
|
||||
)
|
||||
});
|
||||
*self = Some(field);
|
||||
}
|
||||
"None" => {
|
||||
*self = None;
|
||||
}
|
||||
_ => panic!("Enum is not a {}.", std::any::type_name::<Self>()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
|
||||
*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<dyn Reflect> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn reflect_hash(&self) -> Option<u64> {
|
||||
crate::enum_hash(self)
|
||||
}
|
||||
|
||||
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
|
||||
crate::enum_partial_eq(self, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflect + Clone> FromReflect for Option<T> {
|
||||
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
|
||||
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::<T>().unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"Field at index 0 should be of type {}",
|
||||
std::any::type_name::<T>()
|
||||
)
|
||||
});
|
||||
Some(Some(field))
|
||||
}
|
||||
"None" => Some(None),
|
||||
name => panic!(
|
||||
"variant with name `{}` does not exist on enum `{}`",
|
||||
name,
|
||||
std::any::type_name::<Self>()
|
||||
),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflect + Clone> Typed for Option<T> {
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
CELL.get_or_insert::<Self, _>(|| {
|
||||
let none_variant = VariantInfo::Unit(UnitVariantInfo::new_static("None"));
|
||||
let some_variant = VariantInfo::Tuple(TupleVariantInfo::new_static(
|
||||
"Some",
|
||||
&[UnnamedField::new::<T>(0)],
|
||||
));
|
||||
TypeInfo::Enum(EnumInfo::new::<Self>(&[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<T> 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<usize>::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::<usize>())
|
||||
{
|
||||
*field = 321;
|
||||
}
|
||||
} else {
|
||||
panic!("expected `VariantType::Tuple`");
|
||||
}
|
||||
|
||||
assert_eq!(Some(321), value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_should_impl_typed() {
|
||||
type MyOption = Option<i32>;
|
||||
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::<i32>(),
|
||||
"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`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::<MyTupleStruct>());
|
||||
|
||||
// Tuple
|
||||
type MyTuple = (u32, f32, String);
|
||||
|
||||
|
@ -829,8 +827,10 @@ mod tests {
|
|||
map: HashMap<i32, f32>,
|
||||
a_struct: SomeStruct,
|
||||
a_tuple_struct: SomeTupleStruct,
|
||||
enum_unit: SomeEnum,
|
||||
enum_tuple: SomeEnum,
|
||||
enum_struct: SomeEnum,
|
||||
custom: CustomDebug,
|
||||
unknown: Option<String>,
|
||||
#[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<alloc::string::String>),
|
||||
}"#;
|
||||
|
||||
assert_eq!(expected, format!("\n{:#?}", reflected));
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
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<V>(self, mut map: V) -> Result<Self::Value, V::Error>
|
||||
where
|
||||
V: MapAccess<'de>,
|
||||
{
|
||||
let key = map.next_key::<String>()?;
|
||||
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::<String>()?;
|
||||
|
||||
let mut dynamic_enum = DynamicEnum::default();
|
||||
|
||||
let key = map.next_key::<String>()?;
|
||||
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::<usize>();
|
||||
registry.register::<f32>();
|
||||
registry.register::<String>();
|
||||
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::<MyEnum>();
|
||||
|
||||
// === 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
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::<usize>();
|
||||
registry.register::<f32>();
|
||||
registry.register::<String>();
|
||||
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::<MyEnum>();
|
||||
|
||||
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', ""));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<usize> {
|
||||
self.field_indices.get(name).copied()
|
||||
}
|
||||
}
|
||||
|
||||
impl Struct for DynamicStruct {
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -24,32 +24,40 @@ pub struct A {
|
|||
z: HashMap<String, f32>,
|
||||
}
|
||||
|
||||
/// 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<T>.
|
||||
|
|
Loading…
Reference in a new issue