bevy/crates/bevy_reflect/src/list.rs

290 lines
6.9 KiB
Rust
Raw Normal View History

2020-11-28 00:39:59 +00:00
use std::any::Any;
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 std::fmt::{Debug, Formatter};
2020-11-28 00:39:59 +00:00
use crate::{serde::Serializable, Array, ArrayIter, DynamicArray, Reflect, ReflectMut, ReflectRef};
2020-11-28 00:39:59 +00:00
/// An ordered, mutable list of [Reflect] items. This corresponds to types like [`std::vec::Vec`].
///
/// This is a sub-trait of [`Array`] as it implements a [`push`](List::push) function, allowing
/// it's internal size to grow.
pub trait List: Reflect + Array {
/// Appends an element to the list.
2020-11-28 00:39:59 +00:00
fn push(&mut self, value: Box<dyn Reflect>);
/// Clones the list, producing a [`DynamicList`].
2020-11-28 00:39:59 +00:00
fn clone_dynamic(&self) -> DynamicList {
DynamicList {
name: self.type_name().to_string(),
2020-11-28 00:39:59 +00:00
values: self.iter().map(|value| value.clone_value()).collect(),
}
}
}
/// A list of reflected values.
2020-11-28 00:39:59 +00:00
#[derive(Default)]
pub struct DynamicList {
name: String,
values: Vec<Box<dyn Reflect>>,
2020-11-28 00:39:59 +00:00
}
impl DynamicList {
/// Returns the type name of the list.
///
/// The value returned by this method is the same value returned by
/// [`Reflect::type_name`].
pub fn name(&self) -> &str {
&self.name
}
/// Sets the type name of the list.
///
/// The value set by this method is the value returned by
/// [`Reflect::type_name`].
pub fn set_name(&mut self, name: String) {
self.name = name;
}
/// Appends a typed value to the list.
2020-11-28 00:39:59 +00:00
pub fn push<T: Reflect>(&mut self, value: T) {
self.values.push(Box::new(value));
}
/// Appends a [`Reflect`] trait object to the list.
2020-11-28 00:39:59 +00:00
pub fn push_box(&mut self, value: Box<dyn Reflect>) {
self.values.push(value);
}
}
impl Array for DynamicList {
2020-11-28 00:39:59 +00:00
fn get(&self, index: usize) -> Option<&dyn Reflect> {
self.values.get(index).map(|value| &**value)
}
fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {
self.values.get_mut(index).map(|value| &mut **value)
}
fn len(&self) -> usize {
self.values.len()
}
fn iter(&self) -> ArrayIter {
ArrayIter {
array: self,
index: 0,
}
}
fn clone_dynamic(&self) -> DynamicArray {
DynamicArray {
name: self.name.clone(),
2020-11-28 00:39:59 +00:00
values: self
.values
.iter()
.map(|value| value.clone_value())
.collect(),
}
}
}
2020-11-28 00:39:59 +00:00
impl List for DynamicList {
2020-11-28 00:39:59 +00:00
fn push(&mut self, value: Box<dyn Reflect>) {
DynamicList::push_box(self, value);
}
fn clone_dynamic(&self) -> DynamicList {
DynamicList {
name: self.name.clone(),
values: self
.values
.iter()
.map(|value| value.clone_value())
.collect(),
}
}
2020-11-28 00:39:59 +00:00
}
// SAFE: any and any_mut both return self
unsafe impl Reflect for DynamicList {
2020-11-28 00:39:59 +00:00
#[inline]
fn type_name(&self) -> &str {
self.name.as_str()
2020-11-28 00:39:59 +00:00
}
#[inline]
fn any(&self) -> &dyn Any {
self
}
#[inline]
fn any_mut(&mut self) -> &mut dyn Any {
self
}
bevy_reflect: Add `as_reflect` and `as_reflect_mut` (#4350) # Objective Trait objects that have `Reflect` as a supertrait cannot be upcast to a `dyn Reflect`. Attempting something like: ```rust trait MyTrait: Reflect { // ... } fn foo(value: &dyn MyTrait) { let reflected = value as &dyn Reflect; // Error! // ... } ``` Results in `error[E0658]: trait upcasting coercion is experimental`. The reason this is important is that a lot of `bevy_reflect` methods require a `&dyn Reflect`. This is trivial with concrete types, but if we don't know the concrete type (we only have the trait object), we can't use these methods. For example, we couldn't create a `ReflectSerializer` for the type since it expects a `&dyn Reflect` value— even though we should be able to. ## Solution Add `as_reflect` and `as_reflect_mut` to `Reflect` to allow upcasting to a `dyn Reflect`: ```rust trait MyTrait: Reflect { // ... } fn foo(value: &dyn MyTrait) { let reflected = value.as_reflect(); // ... } ``` ## Alternatives We could defer this type of logic to the crate/user. They can add these methods to their trait in the same exact way we do here. The main benefit of doing it ourselves is it makes things convenient for them (especially when using the derive macro). We could also create an `AsReflect` trait with a blanket impl over all reflected types, however, I could not get that to work for trait objects since they aren't sized. --- ## Changelog - Added trait method `Reflect::as_reflect(&self)` - Added trait method `Reflect::as_reflect_mut(&mut self)` ## Migration Guide - Manual implementors of `Reflect` will need to add implementations for the methods above (this should be pretty easy as most cases just need to return `self`)
2022-04-25 13:54:48 +00:00
#[inline]
fn as_reflect(&self) -> &dyn Reflect {
self
}
#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
2020-11-28 00:39:59 +00:00
fn apply(&mut self, value: &dyn Reflect) {
list_apply(self, value);
}
#[inline]
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
#[inline]
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::List(self)
}
#[inline]
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::List(self)
}
#[inline]
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(List::clone_dynamic(self))
2020-11-28 00:39:59 +00:00
}
#[inline]
fn reflect_hash(&self) -> Option<u64> {
crate::array_hash(self)
2020-11-28 00:39:59 +00:00
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
2020-11-28 00:39:59 +00:00
list_partial_eq(self, value)
}
fn serializable(&self) -> Option<Serializable> {
None
2020-11-28 00:39:59 +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
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)
}
2020-11-28 00:39:59 +00:00
}
impl IntoIterator for DynamicList {
type Item = Box<dyn Reflect>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.values.into_iter()
}
}
/// Applies the elements of `b` to the corresponding elements of `a`.
///
/// If the length of `b` is greater than that of `a`, the excess elements of `b`
/// are cloned and appended to `a`.
///
/// # Panics
///
/// This function panics if `b` is not a list.
2020-11-28 00:39:59 +00:00
#[inline]
pub fn list_apply<L: List>(a: &mut L, b: &dyn Reflect) {
if let ReflectRef::List(list_value) = b.reflect_ref() {
for (i, value) in list_value.iter().enumerate() {
if i < a.len() {
if let Some(v) = a.get_mut(i) {
v.apply(value);
}
} else {
List::push(a, value.clone_value());
}
}
} else {
panic!("Attempted to apply a non-list type to a list type.");
2020-11-28 00:39:59 +00:00
}
}
/// Compares a [`List`] with a [`Reflect`] value.
///
/// Returns true if and only if all of the following are true:
/// - `b` is a list;
/// - `b` is the same length as `a`;
/// - [`Reflect::reflect_partial_eq`] returns `Some(true)` for pairwise elements of `a` and `b`.
2020-11-28 00:39:59 +00:00
#[inline]
pub fn list_partial_eq<L: List>(a: &L, b: &dyn Reflect) -> Option<bool> {
let list = if let ReflectRef::List(list) = b.reflect_ref() {
list
} else {
return Some(false);
};
if a.len() != list.len() {
return Some(false);
}
for (a_value, b_value) in a.iter().zip(list.iter()) {
if let Some(false) | None = a_value.reflect_partial_eq(b_value) {
2020-11-28 00:39:59 +00:00
return Some(false);
}
}
Some(true)
}
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
/// 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 std::assert_eq;
#[test]
fn test_into_iter() {
let mut list = DynamicList::default();
list.push(0usize);
list.push(1usize);
list.push(2usize);
let items = list.into_iter();
for (index, item) in items.into_iter().enumerate() {
let value = item.take::<usize>().expect("couldn't downcast to usize");
assert_eq!(index, value);
}
}
}