document Struct, TupleStruct and Tuple (#3081)

# Objective

These traits are undocumented on `main`.

## Solution

Now they have docs! Included are examples for each trait and their corresponding `GetTypeField` trait. The docs also mention that `#[derive(Reflect)]` will automatically derive the correct subtrait on structs and tuple structs.
This commit is contained in:
dataphract 2022-01-08 20:45:24 +00:00
parent 458cb7a9e9
commit 4b4dbb021f
4 changed files with 212 additions and 5 deletions

View file

@ -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;

View file

@ -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::<String>(), 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::<String>("bar").unwrap().truncate(5);
/// assert_eq!(foo.get_field::<String>("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<T: Reflect>(&self, name: &str) -> Option<&T>;
/// Returns a mutable reference to the value of the field named `name`,
/// downcast to `T`.
fn get_field_mut<T: Reflect>(&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<dyn Reflect>) {
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<T: Reflect>(&mut self, name: &str, value: T) {
if let Some(index) = self.field_indices.get(name) {
self.fields[*index] = Box::new(value);

View file

@ -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::<String>(), 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::<String>(0), Some(&"blue".to_string()));
/// assert_eq!(foo.get_field::<i32>(1), Some(&42));
/// # }
/// ```
pub trait GetTupleField {
/// Returns a reference to the value of the field with index `index`,
/// downcast to `T`.
fn get_field<T: Reflect>(&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<T: Reflect>(&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<dyn Reflect>) {
self.fields.push(value);
self.generate_name();
}
/// Appends a typed element with value `value` to the tuple.
pub fn insert<T: Reflect>(&mut self, value: T) {
self.insert_boxed(Box::new(value));
self.generate_name();

View file

@ -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::<String>(), 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::<String>(0).unwrap().truncate(5);
/// assert_eq!(foo.get_field::<String>(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<T: Reflect>(&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<T: Reflect>(&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<dyn Reflect>) {
self.fields.push(value);
}
/// Appends a typed element with value `value` to the tuple struct.
pub fn insert<T: Reflect>(&mut self, value: T) {
self.insert_boxed(Box::new(value));
}