Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - document more of bevy_reflect #3655

Closed
wants to merge 11 commits into from
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