Skip to content

Commit

Permalink
document more of bevy_reflect (#3655)
Browse files Browse the repository at this point in the history
This adds documentation for:

- The trait methods of `Reflect` and its subtraits
- The `partial_eq` and `apply` functions for `Map` et al.
- `DynamicList` and `DynamicMap`
- `TypeRegistry` and related types & traits
- `GetPath`, including an explanation of path string syntax

among other things.

Still to be documented are the various macros and `bevy_reflect::serde`.
  • Loading branch information
dataphract committed Jan 14, 2022
1 parent 3e8e6c5 commit f073b2d
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 18 deletions.
39 changes: 39 additions & 0 deletions crates/bevy_reflect/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,27 @@ use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef};

/// An ordered, mutable list of [Reflect] items. This corresponds to types like [`std::vec::Vec`].
pub trait List: Reflect {
/// Returns a reference to the element at `index`, or `None` if out of bounds.
fn get(&self, index: usize) -> Option<&dyn Reflect>;

/// Returns a mutable reference to the element at `index`, or `None` if out of bounds.
fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect>;

/// Appends an element to the list.
fn push(&mut self, value: Box<dyn Reflect>);

/// Returns the number of elements in the list.
fn len(&self) -> usize;

/// Returns `true` if the list contains no elements.
fn is_empty(&self) -> bool {
self.len() == 0
}

/// Returns an iterator over the list.
fn iter(&self) -> ListIter;

/// Clones the list, producing a [`DynamicList`].
fn clone_dynamic(&self) -> DynamicList {
DynamicList {
name: self.type_name().to_string(),
Expand All @@ -20,25 +33,36 @@ pub trait List: Reflect {
}
}

/// A list of reflected values.
#[derive(Default)]
pub struct DynamicList {
name: String,
values: Vec<Box<dyn Reflect>>,
}

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.
pub fn push<T: Reflect>(&mut self, value: T) {
self.values.push(Box::new(value));
}

/// Appends a [`Reflect`] trait object to the list.
pub fn push_box(&mut self, value: Box<dyn Reflect>) {
self.values.push(value);
}
Expand Down Expand Up @@ -136,6 +160,7 @@ unsafe impl Reflect for DynamicList {
}
}

/// An iterator over the elements of a [`List`].
pub struct ListIter<'a> {
pub(crate) list: &'a dyn List,
pub(crate) index: usize,
Expand All @@ -158,6 +183,14 @@ impl<'a> Iterator for ListIter<'a> {

impl<'a> ExactSizeIterator for ListIter<'a> {}

/// 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.
#[inline]
pub fn list_apply<L: List>(a: &mut L, b: &dyn Reflect) {
if let ReflectRef::List(list_value) = b.reflect_ref() {
Expand All @@ -175,6 +208,12 @@ pub fn list_apply<L: List>(a: &mut L, b: &dyn Reflect) {
}
}

/// 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`.
#[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() {
Expand Down
49 changes: 46 additions & 3 deletions crates/bevy_reflect/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,48 @@ use bevy_utils::HashMap;

use crate::{serde::Serializable, Reflect, ReflectMut, ReflectRef};

/// An ordered `ReflectValue->ReflectValue` mapping. `ReflectValue` `Keys` are assumed to return a
/// non-`None` hash. Ideally the ordering is stable across runs, but this is not required.
/// This corresponds to types like [`std::collections::HashMap`].
/// An ordered mapping between [`Reflect`] values.
///
/// Because the values are reflected, the underlying types of keys and values
/// may differ between entries.
///
///`ReflectValue` `Keys` are assumed to return a non-`None` hash. The ordering
/// of `Map` entries is not guaranteed to be stable across runs or between
/// instances.
///
/// This trait corresponds to types like [`std::collections::HashMap`].
pub trait Map: Reflect {
/// Returns a reference to the value associated with the given key.
///
/// If no value is associated with `key`, returns `None`.
fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect>;

/// Returns a mutable reference to the value associated with the given key.
///
/// If no value is associated with `key`, returns `None`.
fn get_mut(&mut self, key: &dyn Reflect) -> Option<&mut dyn Reflect>;

/// Returns the key-value pair at `index` by reference, or `None` if out of bounds.
fn get_at(&self, index: usize) -> Option<(&dyn Reflect, &dyn Reflect)>;

/// Returns the number of elements in the map.
fn len(&self) -> usize;

/// Returns `true` if the list contains no elements.
fn is_empty(&self) -> bool {
self.len() == 0
}

/// Returns an iterator over the key-value pairs of the map.
fn iter(&self) -> MapIter;

/// Clones the map, producing a [`DynamicMap`].
fn clone_dynamic(&self) -> DynamicMap;
}

const HASH_ERROR: &str = "the given key does not support hashing";

/// An ordered mapping between reflected values.
#[derive(Default)]
pub struct DynamicMap {
name: String,
Expand All @@ -29,18 +54,28 @@ pub struct DynamicMap {
}

impl DynamicMap {
/// Returns the type name of the map.
///
/// 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 map.
///
/// The value set by this method is the same value returned by
/// [`Reflect::type_name`].
pub fn set_name(&mut self, name: String) {
self.name = name;
}

/// Inserts a typed key-value pair into the map.
pub fn insert<K: Reflect, V: Reflect>(&mut self, key: K, value: V) {
self.insert_boxed(Box::new(key), Box::new(value));
}

/// Inserts a key-value pair of [`Reflect`] values into the map.
pub fn insert_boxed(&mut self, key: Box<dyn Reflect>, value: Box<dyn Reflect>) {
match self.indices.entry(key.reflect_hash().expect(HASH_ERROR)) {
Entry::Occupied(entry) => {
Expand Down Expand Up @@ -154,6 +189,7 @@ unsafe impl Reflect for DynamicMap {
}
}

/// An iterator over the key-value pairs of a [`Map`].
pub struct MapIter<'a> {
pub(crate) map: &'a dyn Map,
pub(crate) index: usize,
Expand All @@ -176,6 +212,13 @@ impl<'a> Iterator for MapIter<'a> {

impl<'a> ExactSizeIterator for MapIter<'a> {}

/// Compares a [`Map`] with a [`Reflect`] value.
///
/// Returns true if and only if all of the following are true:
/// - `b` is a map;
/// - `b` is the same length as `a`;
/// - For each key-value pair in `a`, `b` contains a value for the given key,
/// and [`Reflect::reflect_partial_eq`] returns `Some(true)` for the two values.
#[inline]
pub fn map_partial_eq<M: Map>(a: &M, b: &dyn Reflect) -> Option<bool> {
let map = if let ReflectRef::Map(map) = b.reflect_ref() {
Expand Down
31 changes: 31 additions & 0 deletions crates/bevy_reflect/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::num::ParseIntError;
use crate::{Reflect, ReflectMut, ReflectRef};
use thiserror::Error;

/// An error returned from a failed path string query.
#[derive(Debug, PartialEq, Eq, Error)]
pub enum ReflectPathError<'a> {
#[error("expected an identifier at the given index")]
Expand Down Expand Up @@ -30,13 +31,41 @@ pub enum ReflectPathError<'a> {
InvalidDowncast,
}

/// A trait which allows nested values to be retrieved with path strings.
///
/// Path strings use Rust syntax:
/// - [`Struct`] items are accessed with a dot and a field name: `.field_name`
/// - [`TupleStruct`] and [`Tuple`] items are accessed with a dot and a number: `.0`
/// - [`List`] items are accessed with brackets: `[0]`
///
/// If the initial path element is a field of a struct, tuple struct, or tuple,
/// the initial '.' may be omitted.
///
/// For example, given a struct with a field `foo` which is a reflected list of
/// 2-tuples (like a `Vec<(T, U)>`), the path string `foo[3].0` would access tuple
/// element 0 of element 3 of `foo`.
///
/// [`Struct`]: crate::Struct
/// [`TupleStruct`]: crate::TupleStruct
/// [`Tuple`]: crate::Tuple
/// [`List`]: crate::List
pub trait GetPath {
/// Returns a reference to the value specified by `path`.
///
/// To retrieve a statically typed reference, use
/// [`get_path`][GetPath::get_path].
fn path<'r, 'p>(&'r self, path: &'p str) -> Result<&'r dyn Reflect, ReflectPathError<'p>>;

/// Returns a mutable reference to the value specified by `path`.
///
/// To retrieve a statically typed mutable reference, use
/// [`get_path_mut`][GetPath::get_path_mut].
fn path_mut<'r, 'p>(
&'r mut self,
path: &'p str,
) -> Result<&'r mut dyn Reflect, ReflectPathError<'p>>;

/// Returns a statically typed reference to the value specified by `path`.
fn get_path<'r, 'p, T: Reflect>(
&'r self,
path: &'p str,
Expand All @@ -47,6 +76,8 @@ pub trait GetPath {
})
}

/// Returns a statically typed mutable reference to the value specified by
/// `path`.
fn get_path_mut<'r, 'p, T: Reflect>(
&'r mut self,
path: &'p str,
Expand Down
Loading

0 comments on commit f073b2d

Please sign in to comment.