diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index 7ad42390bf..ae9dbb168d 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -21,11 +21,17 @@ pub enum ReflectMut<'a> { Value(&'a mut dyn Reflect), } -/// A reflected rust type. +/// 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. /// /// # Safety /// Implementors _must_ ensure that [`Reflect::any`] and [`Reflect::any_mut`] both return the `self` -/// value passed in If this is not done, [`Reflect::downcast`](trait.Reflect.html#method.downcast) +/// value passed in. If this is not done, [`Reflect::downcast`](trait.Reflect.html#method.downcast) /// will be UB (and also just logically broken). pub unsafe trait Reflect: Any + Send + Sync { fn type_name(&self) -> &str; diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index c644c902ee..bbad3ea81d 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -2,19 +2,65 @@ use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef}; use bevy_utils::HashMap; use std::{any::Any, borrow::Cow, collections::hash_map::Entry}; -/// An ordered &str->ReflectValue mapping where &str is a "field". -/// This corresponds to rust struct types. +/// A reflected Rust regular struct type. +/// +/// Implementors of this trait allow their fields to be addressed by name as +/// well as by index. +/// +/// This trait is automatically implemented for `struct` types with named fields +/// when using `#[derive(Reflect)]`. +/// +/// # Example +/// +/// ``` +/// use bevy_reflect::{Reflect, Struct}; +/// +/// #[derive(Reflect)] +/// struct Foo { +/// bar: String, +/// } +/// +/// # fn main() { +/// let foo = Foo { bar: "Hello, world!".to_string() }; +/// +/// assert_eq!(foo.field_len(), 1); +/// assert_eq!(foo.name_at(0), Some("bar")); +/// +/// let bar = foo.field("bar").unwrap(); +/// assert_eq!(bar.downcast_ref::(), Some(&"Hello, world!".to_string())); +/// # } +/// ``` pub trait Struct: Reflect { + /// Returns a reference to the value of the field named `name` as a `&dyn + /// Reflect`. fn field(&self, name: &str) -> Option<&dyn Reflect>; + + /// Returns a mutable reference to the value of the field named `name` as a + /// `&mut dyn Reflect`. fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect>; + + /// Returns a reference to the value of the field with index `index` as a + /// `&dyn Reflect`. fn field_at(&self, index: usize) -> Option<&dyn Reflect>; + + /// Returns a mutable reference to the value of the field with index `index` + /// as a `&mut dyn Reflect`. fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn Reflect>; + + /// Returns the name of the field with index `index`. fn name_at(&self, index: usize) -> Option<&str>; + + /// Returns the number of fields in the struct. fn field_len(&self) -> usize; + + /// Returns an iterator over the values of the struct's fields. fn iter_fields(&self) -> FieldIter; + + /// Clones the struct into a [`DynamicStruct`]. fn clone_dynamic(&self) -> DynamicStruct; } +/// An iterator over the field values of a struct. pub struct FieldIter<'a> { pub(crate) struct_val: &'a dyn Struct, pub(crate) index: usize, @@ -46,8 +92,33 @@ impl<'a> Iterator for FieldIter<'a> { impl<'a> ExactSizeIterator for FieldIter<'a> {} +/// A convenience trait which combines fetching and downcasting of struct +/// fields. +/// +/// # Example +/// +/// ``` +/// use bevy_reflect::{GetField, Reflect}; +/// +/// #[derive(Reflect)] +/// struct Foo { +/// bar: String, +/// } +/// +/// # fn main() { +/// let mut foo = Foo { bar: "Hello, world!".to_string() }; +/// +/// foo.get_field_mut::("bar").unwrap().truncate(5); +/// assert_eq!(foo.get_field::("bar"), Some(&"Hello".to_string())); +/// # } +/// ``` pub trait GetField { + /// Returns a reference to the value of the field named `name`, downcast to + /// `T`. fn get_field(&self, name: &str) -> Option<&T>; + + /// Returns a mutable reference to the value of the field named `name`, + /// downcast to `T`. fn get_field_mut(&mut self, name: &str) -> Option<&mut T>; } @@ -73,6 +144,7 @@ impl GetField for dyn Struct { } } +/// A struct type which allows fields to be added at runtime. #[derive(Default)] pub struct DynamicStruct { name: String, @@ -82,14 +154,19 @@ pub struct DynamicStruct { } impl DynamicStruct { + /// Returns the name of the struct. pub fn name(&self) -> &str { &self.name } + /// Sets the name of the struct. pub fn set_name(&mut self, name: String) { self.name = name; } + /// Inserts a field named `name` with value `value` into the struct. + /// + /// If the field already exists, it is overwritten. pub fn insert_boxed(&mut self, name: &str, value: Box) { let name = Cow::Owned(name.to_string()); match self.field_indices.entry(name) { @@ -104,6 +181,9 @@ impl DynamicStruct { } } + /// Inserts a field named `name` with the typed value `value` into the struct. + /// + /// If the field already exists, it is overwritten. pub fn insert(&mut self, name: &str, value: T) { if let Some(index) = self.field_indices.get(name) { self.fields[*index] = Box::new(value); diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index ed921aaf9e..ba0d614de4 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -2,14 +2,44 @@ use std::any::Any; use crate::{serde::Serializable, FromReflect, Reflect, ReflectMut, ReflectRef}; +/// A reflected Rust tuple. +/// +/// This trait is automatically implemented for arbitrary tuples of up to 12 +/// elements, provided that each element implements [`Reflect`]. +/// +/// # Example +/// +/// ``` +/// use bevy_reflect::Tuple; +/// +/// # fn main() { +/// let foo = ("blue".to_string(), 42_i32); +/// assert_eq!(foo.field_len(), 2); +/// +/// let first = foo.field(0).unwrap(); +/// assert_eq!(first.downcast_ref::(), Some(&"blue".to_string())); +/// # } +/// ``` pub trait Tuple: Reflect { + /// Returns a reference to the value of the field with index `index` as a + /// `&dyn Reflect`. fn field(&self, index: usize) -> Option<&dyn Reflect>; + + /// Returns a mutable reference to the value of the field with index `index` + /// as a `&mut dyn Reflect`. fn field_mut(&mut self, index: usize) -> Option<&mut dyn Reflect>; + + /// Returns the number of fields in the tuple. fn field_len(&self) -> usize; + + /// Returns an iterator over the values of the tuple's fields. fn iter_fields(&self) -> TupleFieldIter; + + /// Clones the struct into a [`DynamicTuple`]. fn clone_dynamic(&self) -> DynamicTuple; } +/// An iterator over the field values of a tuple. pub struct TupleFieldIter<'a> { pub(crate) tuple: &'a dyn Tuple, pub(crate) index: usize, @@ -41,8 +71,28 @@ impl<'a> Iterator for TupleFieldIter<'a> { impl<'a> ExactSizeIterator for TupleFieldIter<'a> {} +/// A convenience trait which combines fetching and downcasting of tuple +/// fields. +/// +/// # Example +/// +/// ``` +/// use bevy_reflect::GetTupleField; +/// +/// # fn main() { +/// let foo = ("blue".to_string(), 42_i32); +/// +/// assert_eq!(foo.get_field::(0), Some(&"blue".to_string())); +/// assert_eq!(foo.get_field::(1), Some(&42)); +/// # } +/// ``` pub trait GetTupleField { + /// Returns a reference to the value of the field with index `index`, + /// downcast to `T`. fn get_field(&self, index: usize) -> Option<&T>; + + /// Returns a mutable reference to the value of the field with index + /// `index`, downcast to `T`. fn get_field_mut(&mut self, index: usize) -> Option<&mut T>; } @@ -70,6 +120,7 @@ impl GetTupleField for dyn Tuple { } } +/// A tuple which allows fields to be added at runtime. #[derive(Default)] pub struct DynamicTuple { name: String, @@ -77,19 +128,27 @@ pub struct DynamicTuple { } impl DynamicTuple { + /// Returns the name of the tuple. + /// + /// The tuple's name is automatically generated from its element types. pub fn name(&self) -> &str { &self.name } + /// Manually sets the name of the tuple. + /// + /// Note that the tuple name will be overwritten when elements are added. pub fn set_name(&mut self, name: String) { self.name = name; } + /// Appends an element with value `value` to the tuple. pub fn insert_boxed(&mut self, value: Box) { self.fields.push(value); self.generate_name(); } + /// Appends a typed element with value `value` to the tuple. pub fn insert(&mut self, value: T) { self.insert_boxed(Box::new(value)); self.generate_name(); diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index 63385df6bb..f16752ddad 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -1,15 +1,49 @@ use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef}; use std::any::Any; -/// A rust "tuple struct" reflection +/// A reflected Rust tuple struct. +/// +/// Implementors of this trait allow their tuple fields to be addressed by +/// index. +/// +/// This trait is automatically implemented for tuple struct types when using +/// `#[derive(Reflect)]`. +/// +/// ``` +/// use bevy_reflect::{Reflect, TupleStruct}; +/// +/// #[derive(Reflect)] +/// struct Foo(String); +/// +/// # fn main() { +/// let foo = Foo("Hello, world!".to_string()); +/// +/// assert_eq!(foo.field_len(), 1); +/// +/// let first = foo.field(0).unwrap(); +/// assert_eq!(first.downcast_ref::(), Some(&"Hello, world!".to_string())); +/// # } +/// ``` pub trait TupleStruct: Reflect { + /// Returns a reference to the value of the field with index `index` as a + /// `&dyn Reflect`. fn field(&self, index: usize) -> Option<&dyn Reflect>; + + /// Returns a mutable reference to the value of the field with index `index` + /// as a `&mut dyn Reflect`. fn field_mut(&mut self, index: usize) -> Option<&mut dyn Reflect>; + + /// Returns the number of fields in the tuple struct. fn field_len(&self) -> usize; + + /// Returns an iterator over the values of the tuple struct's fields. fn iter_fields(&self) -> TupleStructFieldIter; + + /// Clones the struct into a [`DynamicTupleStruct`]. fn clone_dynamic(&self) -> DynamicTupleStruct; } +/// An iterator over the field values of a tuple struct. pub struct TupleStructFieldIter<'a> { pub(crate) tuple_struct: &'a dyn TupleStruct, pub(crate) index: usize, @@ -41,8 +75,31 @@ impl<'a> Iterator for TupleStructFieldIter<'a> { impl<'a> ExactSizeIterator for TupleStructFieldIter<'a> {} +/// A convenience trait which combines fetching and downcasting of tuple +/// struct fields. +/// +/// # Example +/// +/// ``` +/// use bevy_reflect::{GetTupleStructField, Reflect}; +/// +/// #[derive(Reflect)] +/// struct Foo(String); +/// +/// # fn main() { +/// let mut foo = Foo("Hello, world!".to_string()); +/// +/// foo.get_field_mut::(0).unwrap().truncate(5); +/// assert_eq!(foo.get_field::(0), Some(&"Hello".to_string())); +/// # } +/// ``` pub trait GetTupleStructField { + /// Returns a reference to the value of the field with index `index`, + /// downcast to `T`. fn get_field(&self, index: usize) -> Option<&T>; + + /// Returns a mutable reference to the value of the field with index + /// `index`, downcast to `T`. fn get_field_mut(&mut self, index: usize) -> Option<&mut T>; } @@ -70,6 +127,7 @@ impl GetTupleStructField for dyn TupleStruct { } } +/// A tuple struct which allows fields to be added at runtime. #[derive(Default)] pub struct DynamicTupleStruct { name: String, @@ -77,18 +135,22 @@ pub struct DynamicTupleStruct { } impl DynamicTupleStruct { + /// Returns the name of the tuple struct. pub fn name(&self) -> &str { &self.name } + /// Sets the name of the tuple struct. pub fn set_name(&mut self, name: String) { self.name = name; } + /// Appends an element with value `value` to the tuple struct. pub fn insert_boxed(&mut self, value: Box) { self.fields.push(value); } + /// Appends a typed element with value `value` to the tuple struct. pub fn insert(&mut self, value: T) { self.insert_boxed(Box::new(value)); }