bevy_reflect: Improve debug formatting for reflected types (#4218)
# Objective
Debugging reflected types can be somewhat frustrating since all `dyn Reflect` trait objects return something like `Reflect(core::option::Option<alloc::string::String>)`.
It would be much nicer to be able to see the actual value— or even use a custom `Debug` implementation.
## Solution
Added `Reflect::debug` which allows users to customize the debug output. It sets defaults for all `ReflectRef` subtraits and falls back to `Reflect(type_name)` if no `Debug` implementation was registered.
To register a custom `Debug` impl, users can add `#[reflect(Debug)]` like they can with other traits.
### Example
Using the following structs:
```rust
#[derive(Reflect)]
pub struct Foo {
a: usize,
nested: Bar,
#[reflect(ignore)]
_ignored: NonReflectedValue,
}
#[derive(Reflect)]
pub struct Bar {
value: Vec2,
tuple_value: (i32, String),
list_value: Vec<usize>,
// We can't determine debug formatting for Option<T> yet
unknown_value: Option<String>,
custom_debug: CustomDebug
}
#[derive(Reflect)]
#[reflect(Debug)]
struct CustomDebug;
impl Debug for CustomDebug {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "This is a custom debug!")
}
}
pub struct NonReflectedValue {
_a: usize,
}
```
We can do:
```rust
let value = Foo {
a: 1,
_ignored: NonReflectedValue { _a: 10 },
nested: Bar {
value: Vec2::new(1.23, 3.21),
tuple_value: (123, String::from("Hello")),
list_value: vec![1, 2, 3],
unknown_value: Some(String::from("World")),
custom_debug: CustomDebug
},
};
let reflected_value: &dyn Reflect = &value;
println!("{:#?}", reflected_value)
```
Which results in:
```rust
Foo {
a: 2,
nested: Bar {
value: Vec2(
1.23,
3.21,
),
tuple_value: (
123,
"Hello",
),
list_value: [
1,
2,
3,
],
unknown_value: Reflect(core::option::Option<alloc::string::String>),
custom_debug: This is a custom debug!,
},
}
```
Notice that neither `Foo` nor `Bar` implement `Debug`, yet we can still deduce it. This might be a concern if we're worried about leaking internal values. If it is, we might want to consider a way to exclude fields (possibly with a `#[reflect(hide)]` macro) or make it purely opt in (as opposed to the default implementation automatically handled by ReflectRef subtraits).
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2022-05-30 16:41:31 +00:00
|
|
|
use crate::{
|
|
|
|
array_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug,
|
|
|
|
tuple_struct_debug, Array, List, Map, Struct, Tuple, TupleStruct,
|
|
|
|
};
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
use std::{any::Any, fmt::Debug};
|
|
|
|
|
|
|
|
pub use bevy_utils::AHasher as ReflectHasher;
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// An immutable enumeration of "kinds" of reflected type.
|
|
|
|
///
|
|
|
|
/// Each variant contains a trait object with methods specific to a kind of
|
|
|
|
/// type.
|
|
|
|
///
|
|
|
|
/// A `ReflectRef` is obtained via [`Reflect::reflect_ref`].
|
2020-11-28 00:39:59 +00:00
|
|
|
pub enum ReflectRef<'a> {
|
|
|
|
Struct(&'a dyn Struct),
|
|
|
|
TupleStruct(&'a dyn TupleStruct),
|
2021-01-08 03:50:09 +00:00
|
|
|
Tuple(&'a dyn Tuple),
|
2020-11-28 00:39:59 +00:00
|
|
|
List(&'a dyn List),
|
2022-05-13 01:13:30 +00:00
|
|
|
Array(&'a dyn Array),
|
2020-11-28 00:39:59 +00:00
|
|
|
Map(&'a dyn Map),
|
|
|
|
Value(&'a dyn Reflect),
|
|
|
|
}
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// A mutable enumeration of "kinds" of reflected type.
|
|
|
|
///
|
|
|
|
/// Each variant contains a trait object with methods specific to a kind of
|
|
|
|
/// type.
|
|
|
|
///
|
|
|
|
/// A `ReflectMut` is obtained via [`Reflect::reflect_mut`].
|
2020-11-28 00:39:59 +00:00
|
|
|
pub enum ReflectMut<'a> {
|
|
|
|
Struct(&'a mut dyn Struct),
|
|
|
|
TupleStruct(&'a mut dyn TupleStruct),
|
2021-01-09 02:29:03 +00:00
|
|
|
Tuple(&'a mut dyn Tuple),
|
2020-11-28 00:39:59 +00:00
|
|
|
List(&'a mut dyn List),
|
2022-05-13 01:13:30 +00:00
|
|
|
Array(&'a mut dyn Array),
|
2020-11-28 00:39:59 +00:00
|
|
|
Map(&'a mut dyn Map),
|
|
|
|
Value(&'a mut dyn Reflect),
|
|
|
|
}
|
|
|
|
|
2022-01-08 20:45:24 +00:00
|
|
|
/// A reflected Rust type.
|
|
|
|
///
|
|
|
|
/// Methods for working with particular kinds of Rust type are available using the [`List`], [`Map`],
|
|
|
|
/// [`Struct`], [`TupleStruct`], and [`Tuple`] subtraits.
|
|
|
|
///
|
|
|
|
/// When using `#[derive(Reflect)]` with a struct or tuple struct, the suitable subtrait for that
|
|
|
|
/// type (`Struct` or `TupleStruct`) is derived automatically.
|
2021-03-17 22:46:46 +00:00
|
|
|
///
|
|
|
|
/// # Safety
|
2021-04-25 17:24:09 +00:00
|
|
|
/// Implementors _must_ ensure that [`Reflect::any`] and [`Reflect::any_mut`] both return the `self`
|
2022-01-08 20:45:24 +00:00
|
|
|
/// value passed in. If this is not done, [`Reflect::downcast`](trait.Reflect.html#method.downcast)
|
2021-04-25 17:24:09 +00:00
|
|
|
/// will be UB (and also just logically broken).
|
2021-03-17 22:46:46 +00:00
|
|
|
pub unsafe trait Reflect: Any + Send + Sync {
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Returns the [type name] of the underlying type.
|
|
|
|
///
|
|
|
|
/// [type name]: std::any::type_name
|
2020-11-28 00:39:59 +00:00
|
|
|
fn type_name(&self) -> &str;
|
2022-01-14 19:09:44 +00:00
|
|
|
|
|
|
|
/// Returns the value as a [`&dyn Any`][std::any::Any].
|
2020-11-28 00:39:59 +00:00
|
|
|
fn any(&self) -> &dyn Any;
|
2022-01-14 19:09:44 +00:00
|
|
|
|
|
|
|
/// Returns the value as a [`&mut dyn Any`][std::any::Any].
|
2020-11-28 00:39:59 +00:00
|
|
|
fn any_mut(&mut self) -> &mut dyn Any;
|
2022-01-14 19:09:44 +00:00
|
|
|
|
2022-04-25 13:54:48 +00:00
|
|
|
/// Casts this type to a reflected value
|
|
|
|
fn as_reflect(&self) -> &dyn Reflect;
|
|
|
|
|
|
|
|
/// Casts this type to a mutable reflected value
|
|
|
|
fn as_reflect_mut(&mut self) -> &mut dyn Reflect;
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Applies a reflected value to this value.
|
|
|
|
///
|
|
|
|
/// If a type implements a subtrait of `Reflect`, then the semantics of this
|
|
|
|
/// method are as follows:
|
|
|
|
/// - If `T` is a [`Struct`], then the value of each named field of `value` is
|
|
|
|
/// applied to the corresponding named field of `self`. Fields which are
|
|
|
|
/// not present in both structs are ignored.
|
|
|
|
/// - If `T` is a [`TupleStruct`] or [`Tuple`], then the value of each
|
|
|
|
/// numbered field is applied to the corresponding numbered field of
|
|
|
|
/// `self.` Fields which are not present in both values are ignored.
|
|
|
|
/// - If `T` is a [`List`], then each element of `value` is applied to the
|
|
|
|
/// corresponding element of `self`. Up to `self.len()` items are applied,
|
|
|
|
/// and excess elements in `value` are appended to `self`.
|
|
|
|
/// - If `T` is a [`Map`], then for each key in `value`, the associated
|
|
|
|
/// value is applied to the value associated with the same key in `self`.
|
|
|
|
/// Keys which are not present in both maps are ignored.
|
|
|
|
/// - If `T` is none of these, then `value` is downcast to `T`, cloned, and
|
|
|
|
/// assigned to `self`.
|
|
|
|
///
|
|
|
|
/// Note that `Reflect` must be implemented manually for [`List`]s and
|
|
|
|
/// [`Map`]s in order to achieve the correct semantics, as derived
|
|
|
|
/// implementations will have the semantics for [`Struct`], [`TupleStruct`]
|
|
|
|
/// or none of the above depending on the kind of type. For lists, use the
|
|
|
|
/// [`list_apply`] helper function when implementing this method.
|
|
|
|
///
|
|
|
|
/// [`list_apply`]: crate::list_apply
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Derived implementations of this method will panic:
|
|
|
|
/// - If the type of `value` is not of the same kind as `T` (e.g. if `T` is
|
|
|
|
/// a `List`, while `value` is a `Struct`).
|
|
|
|
/// - If `T` is any complex type and the corresponding fields or elements of
|
|
|
|
/// `self` and `value` are not of the same type.
|
|
|
|
/// - If `T` is a value type and `self` cannot be downcast to `T`
|
2020-11-28 00:39:59 +00:00
|
|
|
fn apply(&mut self, value: &dyn Reflect);
|
2022-01-14 19:09:44 +00:00
|
|
|
|
|
|
|
/// Performs a type-checked assignment of a reflected value to this value.
|
|
|
|
///
|
|
|
|
/// If `value` does not contain a value of type `T`, returns an `Err`
|
|
|
|
/// containing the trait object.
|
2020-11-28 00:39:59 +00:00
|
|
|
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>>;
|
2022-01-14 19:09:44 +00:00
|
|
|
|
|
|
|
/// Returns an enumeration of "kinds" of type.
|
|
|
|
///
|
|
|
|
/// See [`ReflectRef`].
|
2020-11-28 00:39:59 +00:00
|
|
|
fn reflect_ref(&self) -> ReflectRef;
|
2022-01-14 19:09:44 +00:00
|
|
|
|
|
|
|
/// Returns a mutable enumeration of "kinds" of type.
|
|
|
|
///
|
|
|
|
/// See [`ReflectMut`].
|
2020-11-28 00:39:59 +00:00
|
|
|
fn reflect_mut(&mut self) -> ReflectMut;
|
2022-01-14 19:09:44 +00:00
|
|
|
|
|
|
|
/// Clones the value as a `Reflect` trait object.
|
|
|
|
///
|
|
|
|
/// When deriving `Reflect` for a struct or struct tuple, the value is
|
|
|
|
/// cloned via [`Struct::clone_dynamic`] (resp.
|
|
|
|
/// [`TupleStruct::clone_dynamic`]). Implementors of other `Reflect`
|
|
|
|
/// subtraits (e.g. [`List`], [`Map`]) should use those subtraits'
|
|
|
|
/// respective `clone_dynamic` methods.
|
2020-11-28 00:39:59 +00:00
|
|
|
fn clone_value(&self) -> Box<dyn Reflect>;
|
2022-01-14 19:09:44 +00:00
|
|
|
|
|
|
|
/// Returns a hash of the value (which includes the type).
|
|
|
|
///
|
|
|
|
/// If the underlying type does not support hashing, returns `None`.
|
bevy_reflect: Small refactor and default `Reflect` methods (#4739)
# Objective
Quick followup to #4712.
While updating some [other PRs](https://github.com/bevyengine/bevy/pull/4218), I realized the `ReflectTraits` struct could be improved. The issue with the current implementation is that `ReflectTraits::get_xxx_impl(...)` returns just the _logic_ to the corresponding `Reflect` trait method, rather than the entire function.
This makes it slightly more annoying to manage since the variable names need to be consistent across files. For example, `get_partial_eq_impl` uses a `value` variable. But the name "value" isn't defined in the `get_partial_eq_impl` method, it's defined in three other methods in a completely separate file.
It's not likely to cause any bugs if we keep it as it is since differing variable names will probably just result in a compile error (except in very particular cases). But it would be useful to someone who wanted to edit/add/remove a method.
## Solution
Made `get_hash_impl`, `get_partial_eq_impl` and `get_serialize_impl` return the entire method implementation for `reflect_hash`, `reflect_partial_eq`, and `serializable`, respectively.
As a result of this, those three `Reflect` methods were also given default implementations. This was fairly simple to do since all three could just be made to return `None`.
---
## Changelog
* Small cleanup/refactor to `ReflectTraits` in `bevy_reflect_derive`
* Gave `Reflect::reflect_hash`, `Reflect::reflect_partial_eq`, and `Reflect::serializable` default implementations
2022-05-18 12:26:11 +00:00
|
|
|
fn reflect_hash(&self) -> Option<u64> {
|
|
|
|
None
|
|
|
|
}
|
2022-01-14 19:09:44 +00:00
|
|
|
|
|
|
|
/// Returns a "partial equality" comparison result.
|
|
|
|
///
|
|
|
|
/// If the underlying type does not support equality testing, returns `None`.
|
bevy_reflect: Small refactor and default `Reflect` methods (#4739)
# Objective
Quick followup to #4712.
While updating some [other PRs](https://github.com/bevyengine/bevy/pull/4218), I realized the `ReflectTraits` struct could be improved. The issue with the current implementation is that `ReflectTraits::get_xxx_impl(...)` returns just the _logic_ to the corresponding `Reflect` trait method, rather than the entire function.
This makes it slightly more annoying to manage since the variable names need to be consistent across files. For example, `get_partial_eq_impl` uses a `value` variable. But the name "value" isn't defined in the `get_partial_eq_impl` method, it's defined in three other methods in a completely separate file.
It's not likely to cause any bugs if we keep it as it is since differing variable names will probably just result in a compile error (except in very particular cases). But it would be useful to someone who wanted to edit/add/remove a method.
## Solution
Made `get_hash_impl`, `get_partial_eq_impl` and `get_serialize_impl` return the entire method implementation for `reflect_hash`, `reflect_partial_eq`, and `serializable`, respectively.
As a result of this, those three `Reflect` methods were also given default implementations. This was fairly simple to do since all three could just be made to return `None`.
---
## Changelog
* Small cleanup/refactor to `ReflectTraits` in `bevy_reflect_derive`
* Gave `Reflect::reflect_hash`, `Reflect::reflect_partial_eq`, and `Reflect::serializable` default implementations
2022-05-18 12:26:11 +00:00
|
|
|
fn reflect_partial_eq(&self, _value: &dyn Reflect) -> Option<bool> {
|
|
|
|
None
|
|
|
|
}
|
2022-01-14 19:09:44 +00:00
|
|
|
|
bevy_reflect: Improve debug formatting for reflected types (#4218)
# Objective
Debugging reflected types can be somewhat frustrating since all `dyn Reflect` trait objects return something like `Reflect(core::option::Option<alloc::string::String>)`.
It would be much nicer to be able to see the actual value— or even use a custom `Debug` implementation.
## Solution
Added `Reflect::debug` which allows users to customize the debug output. It sets defaults for all `ReflectRef` subtraits and falls back to `Reflect(type_name)` if no `Debug` implementation was registered.
To register a custom `Debug` impl, users can add `#[reflect(Debug)]` like they can with other traits.
### Example
Using the following structs:
```rust
#[derive(Reflect)]
pub struct Foo {
a: usize,
nested: Bar,
#[reflect(ignore)]
_ignored: NonReflectedValue,
}
#[derive(Reflect)]
pub struct Bar {
value: Vec2,
tuple_value: (i32, String),
list_value: Vec<usize>,
// We can't determine debug formatting for Option<T> yet
unknown_value: Option<String>,
custom_debug: CustomDebug
}
#[derive(Reflect)]
#[reflect(Debug)]
struct CustomDebug;
impl Debug for CustomDebug {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "This is a custom debug!")
}
}
pub struct NonReflectedValue {
_a: usize,
}
```
We can do:
```rust
let value = Foo {
a: 1,
_ignored: NonReflectedValue { _a: 10 },
nested: Bar {
value: Vec2::new(1.23, 3.21),
tuple_value: (123, String::from("Hello")),
list_value: vec![1, 2, 3],
unknown_value: Some(String::from("World")),
custom_debug: CustomDebug
},
};
let reflected_value: &dyn Reflect = &value;
println!("{:#?}", reflected_value)
```
Which results in:
```rust
Foo {
a: 2,
nested: Bar {
value: Vec2(
1.23,
3.21,
),
tuple_value: (
123,
"Hello",
),
list_value: [
1,
2,
3,
],
unknown_value: Reflect(core::option::Option<alloc::string::String>),
custom_debug: This is a custom debug!,
},
}
```
Notice that neither `Foo` nor `Bar` implement `Debug`, yet we can still deduce it. This might be a concern if we're worried about leaking internal values. If it is, we might want to consider a way to exclude fields (possibly with a `#[reflect(hide)]` macro) or make it purely opt in (as opposed to the default implementation automatically handled by ReflectRef subtraits).
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2022-05-30 16:41:31 +00:00
|
|
|
/// Debug formatter for the value.
|
|
|
|
///
|
|
|
|
/// Any value that is not an implementor of other `Reflect` subtraits
|
|
|
|
/// (e.g. [`List`], [`Map`]), will default to the format: `"Reflect(type_name)"`,
|
|
|
|
/// where `type_name` is the [type name] of the underlying type.
|
|
|
|
///
|
|
|
|
/// [type name]: Self::type_name
|
|
|
|
fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match self.reflect_ref() {
|
|
|
|
ReflectRef::Struct(dyn_struct) => struct_debug(dyn_struct, f),
|
|
|
|
ReflectRef::TupleStruct(dyn_tuple_struct) => tuple_struct_debug(dyn_tuple_struct, f),
|
|
|
|
ReflectRef::Tuple(dyn_tuple) => tuple_debug(dyn_tuple, f),
|
|
|
|
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),
|
|
|
|
_ => write!(f, "Reflect({})", self.type_name()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Returns a serializable version of the value.
|
|
|
|
///
|
|
|
|
/// If the underlying type does not support serialization, returns `None`.
|
bevy_reflect: Small refactor and default `Reflect` methods (#4739)
# Objective
Quick followup to #4712.
While updating some [other PRs](https://github.com/bevyengine/bevy/pull/4218), I realized the `ReflectTraits` struct could be improved. The issue with the current implementation is that `ReflectTraits::get_xxx_impl(...)` returns just the _logic_ to the corresponding `Reflect` trait method, rather than the entire function.
This makes it slightly more annoying to manage since the variable names need to be consistent across files. For example, `get_partial_eq_impl` uses a `value` variable. But the name "value" isn't defined in the `get_partial_eq_impl` method, it's defined in three other methods in a completely separate file.
It's not likely to cause any bugs if we keep it as it is since differing variable names will probably just result in a compile error (except in very particular cases). But it would be useful to someone who wanted to edit/add/remove a method.
## Solution
Made `get_hash_impl`, `get_partial_eq_impl` and `get_serialize_impl` return the entire method implementation for `reflect_hash`, `reflect_partial_eq`, and `serializable`, respectively.
As a result of this, those three `Reflect` methods were also given default implementations. This was fairly simple to do since all three could just be made to return `None`.
---
## Changelog
* Small cleanup/refactor to `ReflectTraits` in `bevy_reflect_derive`
* Gave `Reflect::reflect_hash`, `Reflect::reflect_partial_eq`, and `Reflect::serializable` default implementations
2022-05-18 12:26:11 +00:00
|
|
|
fn serializable(&self) -> Option<Serializable> {
|
|
|
|
None
|
|
|
|
}
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// A trait for types which can be constructed from a reflected type.
|
|
|
|
///
|
|
|
|
/// This trait can be derived on types which implement [`Reflect`]. Some complex
|
|
|
|
/// types (such as `Vec<T>`) may only be reflected if their element types
|
|
|
|
/// implement this trait.
|
|
|
|
///
|
|
|
|
/// For structs and tuple structs, fields marked with the `#[reflect(ignore)]`
|
|
|
|
/// attribute will be constructed using the `Default` implementation of the
|
|
|
|
/// field type, rather than the corresponding field value (if any) of the
|
|
|
|
/// reflected value.
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
pub trait FromReflect: Reflect + Sized {
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Constructs a concrete instance of `Self` from a reflected value.
|
Add FromReflect trait to convert dynamic types to concrete types (#1395)
Dynamic types (`DynamicStruct`, `DynamicTupleStruct`, `DynamicTuple`, `DynamicList` and `DynamicMap`) are used when deserializing scenes, but currently they can only be applied to existing concrete types. This leads to issues when trying to spawn non trivial deserialized scene.
For components, the issue is avoided by requiring that reflected components implement ~~`FromResources`~~ `FromWorld` (or `Default`). When spawning, a new concrete type is created that way, and the dynamic type is applied to it. Unfortunately, some components don't have any valid implementation of these traits.
In addition, any `Vec` or `HashMap` inside a component will panic when a dynamic type is pushed into it (for instance, `Text` panics when adding a text section).
To solve this issue, this PR adds the `FromReflect` trait that creates a concrete type from a dynamic type that represent it, derives the trait alongside the `Reflect` trait, drops the ~~`FromResources`~~ `FromWorld` requirement on reflected components, ~~and enables reflection for UI and Text bundles~~. It also adds the requirement that fields ignored with `#[reflect(ignore)]` implement `Default`, since we need to initialize them somehow.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-12-26 18:49:01 +00:00
|
|
|
fn from_reflect(reflect: &dyn Reflect) -> Option<Self>;
|
|
|
|
}
|
|
|
|
|
2020-11-28 00:39:59 +00:00
|
|
|
impl Debug for dyn Reflect {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
bevy_reflect: Improve debug formatting for reflected types (#4218)
# Objective
Debugging reflected types can be somewhat frustrating since all `dyn Reflect` trait objects return something like `Reflect(core::option::Option<alloc::string::String>)`.
It would be much nicer to be able to see the actual value— or even use a custom `Debug` implementation.
## Solution
Added `Reflect::debug` which allows users to customize the debug output. It sets defaults for all `ReflectRef` subtraits and falls back to `Reflect(type_name)` if no `Debug` implementation was registered.
To register a custom `Debug` impl, users can add `#[reflect(Debug)]` like they can with other traits.
### Example
Using the following structs:
```rust
#[derive(Reflect)]
pub struct Foo {
a: usize,
nested: Bar,
#[reflect(ignore)]
_ignored: NonReflectedValue,
}
#[derive(Reflect)]
pub struct Bar {
value: Vec2,
tuple_value: (i32, String),
list_value: Vec<usize>,
// We can't determine debug formatting for Option<T> yet
unknown_value: Option<String>,
custom_debug: CustomDebug
}
#[derive(Reflect)]
#[reflect(Debug)]
struct CustomDebug;
impl Debug for CustomDebug {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "This is a custom debug!")
}
}
pub struct NonReflectedValue {
_a: usize,
}
```
We can do:
```rust
let value = Foo {
a: 1,
_ignored: NonReflectedValue { _a: 10 },
nested: Bar {
value: Vec2::new(1.23, 3.21),
tuple_value: (123, String::from("Hello")),
list_value: vec![1, 2, 3],
unknown_value: Some(String::from("World")),
custom_debug: CustomDebug
},
};
let reflected_value: &dyn Reflect = &value;
println!("{:#?}", reflected_value)
```
Which results in:
```rust
Foo {
a: 2,
nested: Bar {
value: Vec2(
1.23,
3.21,
),
tuple_value: (
123,
"Hello",
),
list_value: [
1,
2,
3,
],
unknown_value: Reflect(core::option::Option<alloc::string::String>),
custom_debug: This is a custom debug!,
},
}
```
Notice that neither `Foo` nor `Bar` implement `Debug`, yet we can still deduce it. This might be a concern if we're worried about leaking internal values. If it is, we might want to consider a way to exclude fields (possibly with a `#[reflect(hide)]` macro) or make it purely opt in (as opposed to the default implementation automatically handled by ReflectRef subtraits).
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2022-05-30 16:41:31 +00:00
|
|
|
self.debug(f)
|
2020-11-28 00:39:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl dyn Reflect {
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Downcasts the value to type `T`, consuming the trait object.
|
|
|
|
///
|
|
|
|
/// If the underlying value is not of type `T`, returns `Err(self)`.
|
2020-11-28 00:39:59 +00:00
|
|
|
pub fn downcast<T: Reflect>(self: Box<dyn Reflect>) -> Result<Box<T>, Box<dyn Reflect>> {
|
2021-03-11 00:27:30 +00:00
|
|
|
// SAFE?: Same approach used by std::any::Box::downcast. ReflectValue is always Any and type
|
|
|
|
// has been checked.
|
2020-11-28 00:39:59 +00:00
|
|
|
if self.is::<T>() {
|
|
|
|
unsafe {
|
|
|
|
let raw: *mut dyn Reflect = Box::into_raw(self);
|
|
|
|
Ok(Box::from_raw(raw as *mut T))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Downcasts the value to type `T`, unboxing and consuming the trait object.
|
|
|
|
///
|
|
|
|
/// If the underlying value is not of type `T`, returns `Err(self)`.
|
2020-11-28 00:39:59 +00:00
|
|
|
pub fn take<T: Reflect>(self: Box<dyn Reflect>) -> Result<T, Box<dyn Reflect>> {
|
|
|
|
self.downcast::<T>().map(|value| *value)
|
|
|
|
}
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Returns `true` if the underlying value is of type `T`, or `false`
|
|
|
|
/// otherwise.
|
2020-11-28 00:39:59 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn is<T: Reflect>(&self) -> bool {
|
|
|
|
self.any().is::<T>()
|
|
|
|
}
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Downcasts the value to type `T` by reference.
|
|
|
|
///
|
|
|
|
/// If the underlying value is not of type `T`, returns `None`.
|
2020-11-28 00:39:59 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn downcast_ref<T: Reflect>(&self) -> Option<&T> {
|
|
|
|
self.any().downcast_ref::<T>()
|
|
|
|
}
|
|
|
|
|
2022-01-14 19:09:44 +00:00
|
|
|
/// Downcasts the value to type `T` by mutable reference.
|
|
|
|
///
|
|
|
|
/// If the underlying value is not of type `T`, returns `None`.
|
2020-11-28 00:39:59 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn downcast_mut<T: Reflect>(&mut self) -> Option<&mut T> {
|
|
|
|
self.any_mut().downcast_mut::<T>()
|
|
|
|
}
|
|
|
|
}
|