[Merged by Bors] - bevy_reflect: Improve debug formatting for reflected types#4218
[Merged by Bors] - bevy_reflect: Improve debug formatting for reflected types#4218MrGVSV wants to merge 9 commits intobevyengine:mainfrom
Conversation
|
Should we wrap the debug output with This might be helpful to differentiate between concrete types and reflected ones. |
PROMETHIA-27
left a comment
There was a problem hiding this comment.
Looks good! I think this'll be really convenient for debugging scripts.
Yeah it was really annoying not being able to debug reflected types well 😖 Also, @PROMETHIA-27 any thoughts on wrapping output with |
|
I think that's a good idea. Seems like it could very easily cause a very confusing situation trying to compare two seemingly equivalent things and not realizing that one is reflected, or something like that. |
Hm 🤔 Reflect(bevy_reflect::tests::should_reflect_debug::Test {
value: 123,
list: Reflect([
"A",
"B",
"C",
]),
array: Reflect([
1.0,
2.0,
3.0,
]),
map: Reflect({
123: 1.23,
}),
a_struct: Reflect(bevy_reflect::tests::should_reflect_debug::SomeStruct {
foo: "A Struct!",
}),
a_tuple_struct: Reflect(bevy_reflect::tests::should_reflect_debug::SomeTupleStruct(
"A Tuple Struct!",
)),
custom: Cool debug!,
unknown: Reflect(Reflect(core::option::Option<alloc::string::String>)),
})It doesn't seem too bad. One awkward part is the Not too sure what to do about that. We could leave the PR as is, allow the |
|
We could maybe make If anyone else has any other thoughts on this whole |
# Objective Quick followup to #4712. While updating some [other PRs](#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
dyc3
left a comment
There was a problem hiding this comment.
Definitely seems nice to have. Looks like there are some merge conflicts, though.
Conflicts resolved! |
…e#4739) # Objective Quick followup to bevyengine#4712. While updating some [other PRs](bevyengine#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
|
This is very nice; simple useful functionality implemented in the obvious way. |
|
Canceled. |
|
bors r+ |
# 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>
…e#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>
…e#4739) # Objective Quick followup to bevyengine#4712. While updating some [other PRs](bevyengine#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
…e#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>
Objective
Debugging reflected types can be somewhat frustrating since all
dyn Reflecttrait objects return something likeReflect(core::option::Option<alloc::string::String>).It would be much nicer to be able to see the actual value— or even use a custom
Debugimplementation.Solution
Added
Reflect::debugwhich allows users to customize the debug output. It sets defaults for allReflectRefsubtraits and falls back toReflect(type_name)if noDebugimplementation was registered.To register a custom
Debugimpl, users can add#[reflect(Debug)]like they can with other traits.Example
Using the following structs:
We can do:
Which results in:
Notice that neither
FoonorBarimplementDebug, 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).