From a30e403ba53e5c84ec46317992910a9408d78308 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 15 Mar 2022 14:51:22 -0700 Subject: [PATCH 1/8] Improved debug for reflected types --- .../src/container_attributes.rs | 24 ++++++++++ .../bevy_reflect_derive/src/impls.rs | 9 ++++ crates/bevy_reflect/src/array.rs | 27 +++++++++++ crates/bevy_reflect/src/impls/glam.rs | 38 ++++++++-------- crates/bevy_reflect/src/impls/std.rs | 32 ++++++------- crates/bevy_reflect/src/list.rs | 41 +++++++++++++++++ crates/bevy_reflect/src/map.rs | 41 +++++++++++++++++ crates/bevy_reflect/src/reflect.rs | 27 ++++++++++- crates/bevy_reflect/src/struct_trait.rs | 45 +++++++++++++++++++ crates/bevy_reflect/src/tuple.rs | 33 ++++++++++++++ crates/bevy_reflect/src/tuple_struct.rs | 42 +++++++++++++++++ 11 files changed, 322 insertions(+), 37 deletions(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 8e16e77626c5a..390f4f314368f 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -15,6 +15,7 @@ use syn::{Meta, NestedMeta, Path}; // The "special" trait idents that are used internally for reflection. // Received via attributes like `#[reflect(PartialEq, Hash, ...)]` +const DEBUG_ATTR: &str = "Debug"; const PARTIAL_EQ_ATTR: &str = "PartialEq"; const HASH_ATTR: &str = "Hash"; const SERIALIZE_ATTR: &str = "Serialize"; @@ -46,6 +47,7 @@ impl Default for TraitImpl { /// `Reflect` derive macro using the helper attribute: `#[reflect(...)]`. /// /// The list of special traits are as follows: +/// * `Debug` /// * `Hash` /// * `PartialEq` /// * `Serialize` @@ -101,6 +103,7 @@ impl Default for TraitImpl { /// #[derive(Default)] pub(crate) struct ReflectTraits { + debug: TraitImpl, hash: TraitImpl, partial_eq: TraitImpl, serialize: TraitImpl, @@ -123,6 +126,7 @@ impl ReflectTraits { }; match ident.as_str() { + DEBUG_ATTR => traits.debug = TraitImpl::Implemented, PARTIAL_EQ_ATTR => traits.partial_eq = TraitImpl::Implemented, HASH_ATTR => traits.hash = TraitImpl::Implemented, SERIALIZE_ATTR => traits.serialize = TraitImpl::Implemented, @@ -145,6 +149,7 @@ impl ReflectTraits { // This should be the ident of the custom function let trait_func_ident = TraitImpl::Custom(segment.ident.clone()); match ident.as_str() { + DEBUG_ATTR => traits.debug = trait_func_ident, PARTIAL_EQ_ATTR => traits.partial_eq = trait_func_ident, HASH_ATTR => traits.hash = trait_func_ident, SERIALIZE_ATTR => traits.serialize = trait_func_ident, @@ -224,6 +229,25 @@ impl ReflectTraits { TraitImpl::NotImplemented => None, } } + + /// Returns the logic for `Reflect::debug` as a `TokenStream`. + /// + /// If `Debug` was not registered, returns `None`. + pub fn get_debug_impl(&self) -> Option { + match &self.debug { + TraitImpl::Implemented => Some(quote! { + fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } + }), + TraitImpl::Custom(impl_fn) => Some(quote! { + fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + #impl_fn(self, f) + } + }), + TraitImpl::NotImplemented => None, + } + } } impl Parse for ReflectTraits { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs index 18b4c0ab96e4b..968166a036142 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls.rs @@ -51,6 +51,7 @@ pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { #bevy_reflect_path::struct_partial_eq(self, value) } }); + let debug_fn = derive_data.traits().get_debug_impl(); let get_type_registration_impl = derive_data.get_type_registration(); let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); @@ -177,6 +178,8 @@ pub(crate) fn impl_struct(derive_data: &ReflectDeriveData) -> TokenStream { fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { #partial_eq_fn } + + #debug_fn } }) } @@ -210,6 +213,7 @@ pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream #bevy_reflect_path::tuple_struct_partial_eq(self, value) } }); + let debug_fn = derive_data.traits().get_debug_impl(); let (impl_generics, ty_generics, where_clause) = derive_data.generics().split_for_impl(); TokenStream::from(quote! { @@ -312,6 +316,8 @@ pub(crate) fn impl_tuple_struct(derive_data: &ReflectDeriveData) -> TokenStream fn reflect_partial_eq(&self, value: &dyn #bevy_reflect_path::Reflect) -> Option { #partial_eq_fn } + + #debug_fn } }) } @@ -333,6 +339,7 @@ pub(crate) fn impl_value( let serialize_fn = reflect_attrs .get_serialize_impl(bevy_reflect_path) .unwrap_or_else(|| quote!(None)); + let debug_fn = reflect_attrs.get_debug_impl(); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); TokenStream::from(quote! { @@ -405,6 +412,8 @@ pub(crate) fn impl_value( fn serializable(&self) -> Option<#bevy_reflect_path::serde::Serializable> { #serialize_fn } + + #debug_fn } }) } diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index b4b3e29851ad7..1765effedbbdf 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -4,6 +4,7 @@ use std::{ any::Any, hash::{Hash, Hasher}, }; +use std::fmt::Debug; /// A static-sized array of [`Reflect`] items. /// @@ -298,3 +299,29 @@ pub fn array_partial_eq(array: &A, reflect: &dyn Reflect) -> Option) -> std::fmt::Result { + let mut debug = f.debug_list(); + for item in dyn_array.iter() { + debug.entry(&item as &dyn Debug); + } + debug.finish() +} \ No newline at end of file diff --git a/crates/bevy_reflect/src/impls/glam.rs b/crates/bevy_reflect/src/impls/glam.rs index 93f803cc0cf94..101f5e6597d07 100644 --- a/crates/bevy_reflect/src/impls/glam.rs +++ b/crates/bevy_reflect/src/impls/glam.rs @@ -6,14 +6,14 @@ use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_struct, impl_ref use glam::*; impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct IVec2 { x: i32, y: i32, } ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct IVec3 { x: i32, y: i32, @@ -21,7 +21,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct IVec4 { x: i32, y: i32, @@ -31,14 +31,14 @@ impl_reflect_struct!( ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct UVec2 { x: u32, y: u32, } ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct UVec3 { x: u32, y: u32, @@ -46,7 +46,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct UVec4 { x: u32, y: u32, @@ -56,14 +56,14 @@ impl_reflect_struct!( ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct Vec2 { x: f32, y: f32, } ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct Vec3 { x: f32, y: f32, @@ -71,7 +71,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct Vec3A { x: f32, y: f32, @@ -79,7 +79,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct Vec4 { x: f32, y: f32, @@ -89,14 +89,14 @@ impl_reflect_struct!( ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct DVec2 { x: f64, y: f64, } ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct DVec3 { x: f64, y: f64, @@ -104,7 +104,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct DVec4 { x: f64, y: f64, @@ -114,7 +114,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct Mat3 { x_axis: Vec3, y_axis: Vec3, @@ -122,7 +122,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct Mat4 { x_axis: Vec4, y_axis: Vec4, @@ -132,7 +132,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct DMat3 { x_axis: DVec3, y_axis: DVec3, @@ -140,7 +140,7 @@ impl_reflect_struct!( } ); impl_reflect_struct!( - #[reflect(PartialEq, Serialize, Deserialize, Default)] + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] struct DMat4 { x_axis: DVec4, y_axis: DVec4, @@ -153,8 +153,8 @@ impl_reflect_struct!( // mechanisms for read-only fields. I doubt those mechanisms would be added, // so for now quaternions will remain as values. They are represented identically // to Vec4 and DVec4, so you may use those instead and convert between. -impl_reflect_value!(Quat(PartialEq, Serialize, Deserialize, Default)); -impl_reflect_value!(DQuat(PartialEq, Serialize, Deserialize, Default)); +impl_reflect_value!(Quat(Debug, PartialEq, Serialize, Deserialize, Default)); +impl_reflect_value!(DQuat(Debug, PartialEq, Serialize, Deserialize, Default)); impl_from_reflect_value!(Quat); impl_from_reflect_value!(DQuat); diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index f678627c4a410..4c09538e2217d 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -15,22 +15,22 @@ use std::{ ops::Range, }; -impl_reflect_value!(bool(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(u8(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(u16(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(u32(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(u64(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(u128(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(usize(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(i8(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(i16(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(i32(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(i64(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(i128(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(isize(Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(f32(PartialEq, Serialize, Deserialize)); -impl_reflect_value!(f64(PartialEq, Serialize, Deserialize)); -impl_reflect_value!(String(Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(bool(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(u8(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(u16(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(u32(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(u64(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(u128(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(usize(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(i8(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(i16(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(i32(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(i64(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(i128(Debug, Hash, PartialEq, Serialize, Deserialize)); +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 Deserialize<'de> + Reflect + 'static>(Serialize, Deserialize)); impl_reflect_value!(HashSet Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize)); impl_reflect_value!(Range Deserialize<'de> + Send + Sync + 'static>(Serialize, Deserialize)); diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index f2927556eadd3..c76bf0301478a 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -1,4 +1,5 @@ use std::any::Any; +use std::fmt::{Debug, Formatter}; use crate::{serde::Serializable, Array, ArrayIter, DynamicArray, Reflect, ReflectMut, ReflectRef}; @@ -167,6 +168,18 @@ unsafe impl Reflect for DynamicList { fn serializable(&self) -> Option { Some(Serializable::Borrowed(self)) } + + fn debug(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "DynamicList(")?; + list_debug(self, f)?; + write!(f, ")") + } +} + +impl Debug for DynamicList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.debug(f) + } } impl serde::Serialize for DynamicList { @@ -239,10 +252,38 @@ pub fn list_partial_eq(a: &L, b: &dyn Reflect) -> Option { Some(true) } +/// The default debug formatter for [`List`] types. +/// +/// # Example +/// ``` +/// use bevy_reflect::Reflect; +/// +/// let my_list: &dyn Reflect = &vec![1, 2, 3]; +/// println!("{:#?}", my_list); +/// +/// // Output: +/// +/// // [ +/// // 1, +/// // 2, +/// // 3, +/// // ] +/// ``` +#[inline] +pub fn list_debug(dyn_list: &dyn List, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_list(); + for item in dyn_list.iter() { + debug.entry(&item as &dyn Debug); + } + debug.finish() +} + #[cfg(test)] mod tests { use super::DynamicList; + use crate::List; use std::assert_eq; + use std::fmt::Debug; #[test] fn test_into_iter() { diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index f67683c1b661a..47f362030008c 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -1,4 +1,5 @@ use std::any::Any; +use std::fmt::{Debug, Formatter}; use bevy_utils::{Entry, HashMap}; @@ -197,6 +198,18 @@ unsafe impl Reflect for DynamicMap { fn serializable(&self) -> Option { None } + + fn debug(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "DynamicMap(")?; + map_debug(self, f)?; + write!(f, ")") + } +} + +impl Debug for DynamicMap { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.debug(f) + } } /// An iterator over the key-value pairs of a [`Map`]. @@ -263,9 +276,37 @@ pub fn map_partial_eq(a: &M, b: &dyn Reflect) -> Option { Some(true) } +/// The default debug formatter for [`Map`] types. +/// +/// # Example +/// ``` +/// # use bevy_utils::HashMap; +/// use bevy_reflect::Reflect; +/// +/// let mut my_map = HashMap::new(); +/// my_map.insert(123, String::from("Hello")); +/// println!("{:#?}", &my_map as &dyn Reflect); +/// +/// // Output: +/// +/// // { +/// // 123: "Hello", +/// // } +/// ``` +#[inline] +pub fn map_debug(dyn_map: &dyn Map, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_map(); + for (key, value) in dyn_map.iter() { + debug.entry(&key as &dyn Debug, &value as &dyn Debug); + } + debug.finish() +} + #[cfg(test)] mod tests { use super::DynamicMap; + use crate::Map; + use std::fmt::Debug; #[test] fn test_into_iter() { diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index aeb72669faab9..004dbbccecb57 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -1,4 +1,8 @@ -use crate::{serde::Serializable, Array, List, Map, Struct, Tuple, TupleStruct}; +use crate::{ + array_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug, + tuple_struct_debug, Array, List, Map, Struct, Tuple, TupleStruct, +}; + use std::{any::Any, fmt::Debug}; pub use bevy_utils::AHasher as ReflectHasher; @@ -141,6 +145,25 @@ pub unsafe trait Reflect: Any + Send + Sync { /// /// If the underlying type does not support serialization, returns `None`. fn serializable(&self) -> Option; + + /// Debug formatter for the value. + /// + /// any value that is not an implementor of a 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()), + } + } } /// A trait for types which can be constructed from a reflected type. @@ -160,7 +183,7 @@ pub trait FromReflect: Reflect + Sized { impl Debug for dyn Reflect { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Reflect({})", self.type_name()) + self.debug(f) } } diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index f1941ce1bbac2..99e390970dd0b 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -1,5 +1,6 @@ use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef}; use bevy_utils::{Entry, HashMap}; +use std::fmt::{Debug, Formatter}; use std::{any::Any, borrow::Cow}; /// A reflected Rust regular struct type. @@ -323,6 +324,18 @@ unsafe impl Reflect for DynamicStruct { fn serializable(&self) -> Option { None } + + fn debug(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "DynamicStruct(")?; + struct_debug(self, f)?; + write!(f, ")") + } +} + +impl Debug for DynamicStruct { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.debug(f) + } } /// Compares a [`Struct`] with a [`Reflect`] value. @@ -357,3 +370,35 @@ pub fn struct_partial_eq(a: &S, b: &dyn Reflect) -> Option { Some(true) } + +/// The default debug formatter for [`Struct`] types. +/// +/// # Example +/// ``` +/// use bevy_reflect::Reflect; +/// #[derive(Reflect)] +/// struct MyStruct { +/// foo: usize +/// } +/// +/// let my_struct: &dyn Reflect = &MyStruct { foo: 123 }; +/// println!("{:#?}", my_struct); +/// +/// // Output: +/// +/// // MyStruct { +/// // foo: 123, +/// // } +/// ``` +#[inline] +pub fn struct_debug(dyn_struct: &dyn Struct, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct(dyn_struct.type_name()); + for field_index in 0..dyn_struct.field_len() { + let field = dyn_struct.field_at(field_index).unwrap(); + debug.field( + dyn_struct.name_at(field_index).unwrap(), + &field as &dyn Debug, + ); + } + debug.finish() +} diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index e62fc2bc8c8ac..928a841404a1a 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -4,6 +4,7 @@ use crate::{ }; use serde::Deserialize; use std::any::Any; +use std::fmt::{Debug, Formatter}; /// A reflected Rust tuple. /// @@ -270,6 +271,12 @@ unsafe impl Reflect for DynamicTuple { fn serializable(&self) -> Option { None } + + fn debug(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "DynamicTuple(")?; + tuple_debug(self, f)?; + write!(f, ")") + } } /// Applies the elements of `b` to the corresponding elements of `a`. @@ -318,6 +325,32 @@ pub fn tuple_partial_eq(a: &T, b: &dyn Reflect) -> Option { Some(true) } +/// The default debug formatter for [`Tuple`] types. +/// +/// # Example +/// ``` +/// use bevy_reflect::Reflect; +/// +/// let my_tuple: &dyn Reflect = &(1, 2, 3); +/// println!("{:#?}", my_tuple); +/// +/// // Output: +/// +/// // ( +/// // 1, +/// // 2, +/// // 3, +/// // ) +/// ``` +#[inline] +pub fn tuple_debug(dyn_tuple: &dyn Tuple, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_tuple(""); + for field in dyn_tuple.iter_fields() { + debug.field(&field as &dyn Debug); + } + debug.finish() +} + macro_rules! impl_reflect_tuple { {$($index:tt : $name:tt),*} => { impl<$($name: Reflect),*> Tuple for ($($name,)*) { diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index 09f484ca15644..59b3524663782 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -1,5 +1,6 @@ use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef}; use std::any::Any; +use std::fmt::{Debug, Formatter}; /// A reflected Rust tuple struct. /// @@ -262,6 +263,18 @@ unsafe impl Reflect for DynamicTupleStruct { fn serializable(&self) -> Option { None } + + fn debug(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "DynamicTupleStruct(")?; + tuple_struct_debug(self, f)?; + write!(f, ")") + } +} + +impl Debug for DynamicTupleStruct { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.debug(f) + } } /// Compares a [`TupleStruct`] with a [`Reflect`] value. @@ -294,3 +307,32 @@ pub fn tuple_struct_partial_eq(a: &S, b: &dyn Reflect) -> Option Some(true) } + +/// The default debug formatter for [`TupleStruct`] types. +/// +/// # Example +/// ``` +/// use bevy_reflect::Reflect; +/// #[derive(Reflect)] +/// struct MyTupleStruct(usize); +/// +/// let my_tuple_struct: &dyn Reflect = &MyTupleStruct(123); +/// println!("{:#?}", my_tuple_struct); +/// +/// // Output: +/// +/// // MyTupleStruct ( +/// // 123, +/// // ) +/// ``` +#[inline] +pub fn tuple_struct_debug( + dyn_tuple_struct: &dyn TupleStruct, + f: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + let mut debug = f.debug_tuple(dyn_tuple_struct.type_name()); + for field in dyn_tuple_struct.iter_fields() { + debug.field(&field as &dyn Debug); + } + debug.finish() +} From 24af17f057eeea632fd2e631178d285bb8efb559 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 15 Mar 2022 15:56:29 -0700 Subject: [PATCH 2/8] Fixed doc example --- crates/bevy_reflect/src/map.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index 47f362030008c..d0719775e866d 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -282,6 +282,7 @@ pub fn map_partial_eq(a: &M, b: &dyn Reflect) -> Option { /// ``` /// # use bevy_utils::HashMap; /// use bevy_reflect::Reflect; +/// use bevy_utils::HashMap; /// /// let mut my_map = HashMap::new(); /// my_map.insert(123, String::from("Hello")); From 4998d856aa63733b01d53ac2f8cec09359a513fa Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Thu, 12 May 2022 21:51:56 -0700 Subject: [PATCH 3/8] Add reflected debug test --- crates/bevy_reflect/src/lib.rs | 81 ++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index b484e47738cfd..f39f11a7ae9a9 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -84,6 +84,7 @@ pub mod __macro_exports { #[cfg(test)] #[allow(clippy::blacklisted_name, clippy::approx_constant)] mod tests { + use std::fmt::{Debug, Formatter}; #[cfg(feature = "glam")] use ::glam::{vec3, Vec3}; use ::serde::de::DeserializeSeed; @@ -489,6 +490,86 @@ mod tests { let _ = trait_object.as_reflect(); } + #[test] + fn should_reflect_debug() { + #[derive(Reflect)] + struct Test { + value: usize, + list: Vec, + array: [f32; 3], + map: HashMap, + a_struct: SomeStruct, + a_tuple_struct: SomeTupleStruct, + custom: CustomDebug, + unknown: Option, + #[reflect(ignore)] + ignored: isize + } + + #[derive(Reflect)] + struct SomeStruct { + foo: String, + } + + #[derive(Reflect)] + struct SomeTupleStruct(String); + + #[derive(Reflect)] + #[reflect(Debug)] + struct CustomDebug; + impl Debug for CustomDebug { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("Cool debug!") + } + } + + let mut map = HashMap::new(); + map.insert(123, 1.23); + + let test = Test { + value: 123, + list: vec![String::from("A"), String::from("B"), String::from("C")], + array: [1.0, 2.0, 3.0], + map, + a_struct: SomeStruct { + foo: String::from("A Struct!"), + }, + a_tuple_struct: SomeTupleStruct(String::from("A Tuple Struct!")), + custom: CustomDebug, + unknown: Some(String::from("Enums aren't supported yet :(")), + ignored: 321 + }; + + let reflected: &dyn Reflect = &test; + let expected = r#" +bevy_reflect::tests::should_reflect_debug::Test { + value: 123, + list: [ + "A", + "B", + "C", + ], + array: [ + 1.0, + 2.0, + 3.0, + ], + map: { + 123: 1.23, + }, + a_struct: bevy_reflect::tests::should_reflect_debug::SomeStruct { + foo: "A Struct!", + }, + a_tuple_struct: bevy_reflect::tests::should_reflect_debug::SomeTupleStruct( + "A Tuple Struct!", + ), + custom: Cool debug!, + unknown: Reflect(core::option::Option), +}"#; + + assert_eq!(expected, format!("\n{:#?}", reflected)); + } + #[cfg(feature = "glam")] mod glam { use super::*; From 3526fed1ce397050b3e3ebe8ac0cebd04828ce83 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Thu, 12 May 2022 21:52:55 -0700 Subject: [PATCH 4/8] Fix build warnings --- crates/bevy_reflect/src/array.rs | 4 ++-- crates/bevy_reflect/src/lib.rs | 6 +++--- crates/bevy_reflect/src/list.rs | 2 -- crates/bevy_reflect/src/map.rs | 2 -- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index 1765effedbbdf..4bae8fdbb9d56 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -1,10 +1,10 @@ use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef}; use serde::ser::SerializeSeq; +use std::fmt::Debug; use std::{ any::Any, hash::{Hash, Hasher}, }; -use std::fmt::Debug; /// A static-sized array of [`Reflect`] items. /// @@ -324,4 +324,4 @@ pub fn array_debug(dyn_array: &dyn Array, f: &mut std::fmt::Formatter<'_>) -> st debug.entry(&item as &dyn Debug); } debug.finish() -} \ No newline at end of file +} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index f39f11a7ae9a9..a4e1e88565494 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -84,7 +84,6 @@ pub mod __macro_exports { #[cfg(test)] #[allow(clippy::blacklisted_name, clippy::approx_constant)] mod tests { - use std::fmt::{Debug, Formatter}; #[cfg(feature = "glam")] use ::glam::{vec3, Vec3}; use ::serde::de::DeserializeSeed; @@ -93,6 +92,7 @@ mod tests { ser::{to_string_pretty, PrettyConfig}, Deserializer, }; + use std::fmt::{Debug, Formatter}; use super::*; use crate as bevy_reflect; @@ -503,7 +503,7 @@ mod tests { custom: CustomDebug, unknown: Option, #[reflect(ignore)] - ignored: isize + ignored: isize, } #[derive(Reflect)] @@ -537,7 +537,7 @@ mod tests { a_tuple_struct: SomeTupleStruct(String::from("A Tuple Struct!")), custom: CustomDebug, unknown: Some(String::from("Enums aren't supported yet :(")), - ignored: 321 + ignored: 321, }; let reflected: &dyn Reflect = &test; diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index c76bf0301478a..a3fabcc5edf30 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -281,9 +281,7 @@ pub fn list_debug(dyn_list: &dyn List, f: &mut std::fmt::Formatter<'_>) -> std:: #[cfg(test)] mod tests { use super::DynamicList; - use crate::List; use std::assert_eq; - use std::fmt::Debug; #[test] fn test_into_iter() { diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index d0719775e866d..59519a03b9eaa 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -306,8 +306,6 @@ pub fn map_debug(dyn_map: &dyn Map, f: &mut std::fmt::Formatter<'_>) -> std::fmt #[cfg(test)] mod tests { use super::DynamicMap; - use crate::Map; - use std::fmt::Debug; #[test] fn test_into_iter() { From 6b288640c84efa4d3bdb56916f420e107b8e6b09 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Thu, 12 May 2022 21:54:37 -0700 Subject: [PATCH 5/8] Fix doc wording --- .../bevy_reflect_derive/src/container_attributes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs index 390f4f314368f..3e4fbb6ada049 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/container_attributes.rs @@ -230,7 +230,7 @@ impl ReflectTraits { } } - /// Returns the logic for `Reflect::debug` as a `TokenStream`. + /// Returns the implementation of `Reflect::debug` as a `TokenStream`. /// /// If `Debug` was not registered, returns `None`. pub fn get_debug_impl(&self) -> Option { From eb27aea5c805777355a6dc39c5096df2b1a0a84f Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Thu, 12 May 2022 22:31:03 -0700 Subject: [PATCH 6/8] Fix doc test --- crates/bevy_reflect/src/array.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index 4bae8fdbb9d56..ea850b18e6637 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -306,7 +306,7 @@ pub fn array_partial_eq(array: &A, reflect: &dyn Reflect) -> Option Date: Thu, 12 May 2022 23:09:32 -0700 Subject: [PATCH 7/8] Fix more CI errors --- crates/bevy_reflect/src/lib.rs | 1 + crates/bevy_reflect/src/map.rs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index a4e1e88565494..0319b144a2461 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -503,6 +503,7 @@ mod tests { custom: CustomDebug, unknown: Option, #[reflect(ignore)] + #[allow(dead_code)] ignored: isize, } diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index 59519a03b9eaa..fcaddbdcb067b 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -282,7 +282,6 @@ pub fn map_partial_eq(a: &M, b: &dyn Reflect) -> Option { /// ``` /// # use bevy_utils::HashMap; /// use bevy_reflect::Reflect; -/// use bevy_utils::HashMap; /// /// let mut my_map = HashMap::new(); /// my_map.insert(123, String::from("Hello")); From 4a260e3aa119173f574ab12da08f3a72012d19c1 Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Mon, 30 May 2022 09:08:42 -0700 Subject: [PATCH 8/8] Fix doc comment --- crates/bevy_reflect/src/reflect.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index c8d637c352c44..1a7bb63973fe2 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -147,7 +147,7 @@ pub unsafe trait Reflect: Any + Send + Sync { /// Debug formatter for the value. /// - /// any value that is not an implementor of a other `Reflect` subtraits + /// 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. ///