Dedicated Reflect implementation for Set-like things (#13014)

# Objective

I just wanted to inspect `HashSet`s in `bevy-inspector-egui` but I
noticed that it didn't work for some reason. A few minutes later I found
myself looking into the bevy reflect impls noticing that `HashSet`s have
been covered only rudimentary up until now.

## Solution

I'm not sure if this is overkill (especially the first bullet), but
here's a list of the changes:

- created a whole new trait and enum variants for `ReflectRef` and the
like called `Set`
- mostly oriented myself at the `Map` trait and made the necessary
changes until RA was happy
- create macro `impl_reflect_for_hashset!` and call it on `std::HashSet`
and `hashbrown::HashSet`

Extra notes:

- no `get_mut` or `get_mut_at` mirroring the `std::HashSet`
- `insert[_boxed]` and `remove` return `bool` mirroring `std::HashSet`,
additionally that bool is reflect as I thought that would be how we
handle things in bevy reflect, but I'm not sure on this
- ser/de are handled via `SeqAccess`
- I'm not sure about the general deduplication property of this impl of
`Set` that is generally expected? I'm also not sure yet if `Map` does
provide this. This mainly refers to the `Dynamic[...]` structs
- I'm not sure if there are other methods missing from the `trait`, I
felt like `contains` or the set-operations (union/diff/...) could've
been helpful, but I wanted to get out the bare minimum for feedback
first

---

## Changelog

### Added
- `Set` trait for `bevy_reflect`

### Changed
- `std::collections::HashSet` and `bevy_utils::hashbrown::HashSet` now
implement a more complete set of reflect functionalities instead of
"just" `reflect_value`
- `TypeInfo` contains a new variant `Set` that contains `SetInfo`
- `ReflectKind` contains a new variant `Set`
- `ReflectRef` contains a new variant `Set`
- `ReflectMut` contains a new variant `Set`
- `ReflectOwned` contains a new variant `Set`

## Migration Guide

- The new `Set` variants on the enums listed in the change section
should probably be considered by people working with this level of the
lib
### Help wanted! 

I'm not sure if this change is able to break code. From my understanding
it shouldn't since we just add functionality but I'm not sure yet if
theres anything missing from my impl that would be normally provided by
`impl_reflect_value!`
This commit is contained in:
Robert Walter 2024-07-24 21:43:26 +02:00 committed by GitHub
parent eabb58aa04
commit 52a2a3b146
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 831 additions and 20 deletions

View File

@ -3,12 +3,12 @@ use crate::utility::{
reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell,
};
use crate::{
self as bevy_reflect, impl_type_path, map_apply, map_partial_eq, map_try_apply, ApplyError,
Array, ArrayInfo, ArrayIter, DynamicMap, DynamicTypePath, FromReflect, FromType,
GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter, MaybeTyped, Reflect,
ReflectDeserialize, ReflectFromPtr, ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned,
ReflectRef, ReflectSerialize, TypeInfo, TypePath, TypeRegistration, TypeRegistry, Typed,
ValueInfo,
self as bevy_reflect, impl_type_path, map_apply, map_partial_eq, map_try_apply, set_apply,
set_partial_eq, set_try_apply, ApplyError, Array, ArrayInfo, ArrayIter, DynamicMap, DynamicSet,
DynamicTypePath, FromReflect, FromType, GetTypeRegistration, List, ListInfo, ListIter, Map,
MapInfo, MapIter, MaybeTyped, Reflect, ReflectDeserialize, ReflectFromPtr, ReflectFromReflect,
ReflectKind, ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize, Set, SetInfo, TypeInfo,
TypePath, TypeRegistration, TypeRegistry, Typed, ValueInfo,
};
use bevy_reflect_derive::{impl_reflect, impl_reflect_value};
use std::fmt;
@ -97,8 +97,6 @@ impl_reflect_value!(::std::path::PathBuf(
));
impl_reflect_value!(::std::any::TypeId(Debug, Hash, PartialEq,));
impl_reflect_value!(::std::collections::BTreeSet<T: Ord + Eq + Clone + Send + Sync>());
impl_reflect_value!(::std::collections::HashSet<T: Hash + Eq + Clone + Send + Sync, S: TypePath + Clone + Send + Sync>());
impl_reflect_value!(::bevy_utils::hashbrown::HashSet<T: Hash + Eq + Clone + Send + Sync, S: TypePath + Clone + Send + Sync>());
impl_reflect_value!(::core::ops::Range<T: Clone + Send + Sync>());
impl_reflect_value!(::core::ops::RangeInclusive<T: Clone + Send + Sync>());
impl_reflect_value!(::core::ops::RangeFrom<T: Clone + Send + Sync>());
@ -216,10 +214,6 @@ impl_reflect_value!(::std::ffi::OsString(
impl_reflect_value!(::std::ffi::OsString(Debug, Hash, PartialEq));
impl_reflect_value!(::alloc::collections::BinaryHeap<T: Clone>);
impl_type_path!(::bevy_utils::NoOpHash);
impl_type_path!(::bevy_utils::EntityHash);
impl_type_path!(::bevy_utils::FixedState);
macro_rules! impl_reflect_for_veclike {
($ty:path, $insert:expr, $remove:expr, $push:expr, $pop:expr, $sub:ty) => {
impl<T: FromReflect + MaybeTyped + TypePath + GetTypeRegistration> List for $ty {
@ -662,6 +656,221 @@ crate::func::macros::impl_function_traits!(::bevy_utils::hashbrown::HashMap<K, V
>
);
macro_rules! impl_reflect_for_hashset {
($ty:path) => {
impl<V, S> Set for $ty
where
V: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
S: TypePath + BuildHasher + Send + Sync,
{
fn get(&self, value: &dyn Reflect) -> Option<&dyn Reflect> {
value
.downcast_ref::<V>()
.and_then(|value| Self::get(self, value))
.map(|value| value as &dyn Reflect)
}
fn len(&self) -> usize {
Self::len(self)
}
fn iter(&self) -> Box<dyn Iterator<Item = &dyn Reflect> + '_> {
let iter = self.iter().map(|v| v as &dyn Reflect);
Box::new(iter)
}
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
self.into_iter()
.map(|value| Box::new(value) as Box<dyn Reflect>)
.collect()
}
fn clone_dynamic(&self) -> DynamicSet {
let mut dynamic_set = DynamicSet::default();
dynamic_set.set_represented_type(self.get_represented_type_info());
for v in self {
dynamic_set.insert_boxed(v.clone_value());
}
dynamic_set
}
fn insert_boxed(&mut self, value: Box<dyn Reflect>) -> bool {
let value = V::take_from_reflect(value).unwrap_or_else(|value| {
panic!(
"Attempted to insert invalid value of type {}.",
value.reflect_type_path()
)
});
self.insert(value)
}
fn remove(&mut self, value: &dyn Reflect) -> bool {
let mut from_reflect = None;
value
.downcast_ref::<V>()
.or_else(|| {
from_reflect = V::from_reflect(value);
from_reflect.as_ref()
})
.map_or(false, |value| self.remove(value))
}
fn contains(&self, value: &dyn Reflect) -> bool {
let mut from_reflect = None;
value
.downcast_ref::<V>()
.or_else(|| {
from_reflect = V::from_reflect(value);
from_reflect.as_ref()
})
.map_or(false, |value| self.contains(value))
}
}
impl<V, S> Reflect for $ty
where
V: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
S: TypePath + BuildHasher + Send + Sync,
{
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
#[inline]
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
fn as_reflect(&self) -> &dyn Reflect {
self
}
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
fn apply(&mut self, value: &dyn Reflect) {
set_apply(self, value);
}
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
set_try_apply(self, value)
}
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
fn reflect_kind(&self) -> ReflectKind {
ReflectKind::Set
}
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Set(self)
}
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Set(self)
}
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::Set(self)
}
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(self.clone_dynamic())
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
set_partial_eq(self, value)
}
}
impl<V, S> Typed for $ty
where
V: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
S: TypePath + BuildHasher + Send + Sync,
{
fn type_info() -> &'static TypeInfo {
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
CELL.get_or_insert::<Self, _>(|| TypeInfo::Set(SetInfo::new::<Self, V>()))
}
}
impl<V, S> GetTypeRegistration for $ty
where
V: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
S: TypePath + BuildHasher + Send + Sync,
{
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Self>();
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
registration
}
fn register_type_dependencies(registry: &mut TypeRegistry) {
registry.register::<V>();
}
}
impl<V, S> FromReflect for $ty
where
V: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
S: TypePath + BuildHasher + Default + Send + Sync,
{
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Set(ref_set) = reflect.reflect_ref() {
let mut new_set = Self::with_capacity_and_hasher(ref_set.len(), S::default());
for value in ref_set.iter() {
let new_value = V::from_reflect(value)?;
new_set.insert(new_value);
}
Some(new_set)
} else {
None
}
}
}
};
}
impl_type_path!(::bevy_utils::NoOpHash);
impl_type_path!(::bevy_utils::EntityHash);
impl_type_path!(::bevy_utils::FixedState);
impl_reflect_for_hashset!(::std::collections::HashSet<V,S>);
impl_type_path!(::std::collections::HashSet<V, S>);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(::std::collections::HashSet<V, S>;
<
V: Hash + Eq + FromReflect + TypePath + GetTypeRegistration,
S: TypePath + BuildHasher + Default + Send + Sync
>
);
impl_reflect_for_hashset!(::bevy_utils::hashbrown::HashSet<V,S>);
impl_type_path!(::bevy_utils::hashbrown::HashSet<V, S>);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(::bevy_utils::hashbrown::HashSet<V, S>;
<
V: Hash + Eq + FromReflect + TypePath + GetTypeRegistration,
S: TypePath + BuildHasher + Default + Send + Sync
>
);
impl<K, V> Map for ::std::collections::BTreeMap<K, V>
where
K: FromReflect + MaybeTyped + TypePath + GetTypeRegistration + Eq + Ord,

View File

@ -482,6 +482,7 @@ mod list;
mod map;
mod path;
mod reflect;
mod set;
mod struct_trait;
mod tuple;
mod tuple_struct;
@ -531,6 +532,7 @@ pub use list::*;
pub use map::*;
pub use path::*;
pub use reflect::*;
pub use set::*;
pub use struct_trait::*;
pub use tuple::*;
pub use tuple_struct::*;

View File

@ -1,6 +1,6 @@
use crate::{
array_debug, enum_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug,
tuple_struct_debug, Array, DynamicTypePath, Enum, List, Map, Struct, Tuple, TupleStruct,
tuple_struct_debug, Array, DynamicTypePath, Enum, List, Map, Set, Struct, Tuple, TupleStruct,
TypeInfo, TypePath, Typed, ValueInfo,
};
use std::{
@ -24,6 +24,7 @@ macro_rules! impl_reflect_enum {
Self::List(_) => ReflectKind::List,
Self::Array(_) => ReflectKind::Array,
Self::Map(_) => ReflectKind::Map,
Self::Set(_) => ReflectKind::Set,
Self::Enum(_) => ReflectKind::Enum,
Self::Value(_) => ReflectKind::Value,
}
@ -39,6 +40,7 @@ macro_rules! impl_reflect_enum {
$name::List(_) => Self::List,
$name::Array(_) => Self::Array,
$name::Map(_) => Self::Map,
$name::Set(_) => Self::Set,
$name::Enum(_) => Self::Enum,
$name::Value(_) => Self::Value,
}
@ -60,6 +62,7 @@ pub enum ReflectRef<'a> {
List(&'a dyn List),
Array(&'a dyn Array),
Map(&'a dyn Map),
Set(&'a dyn Set),
Enum(&'a dyn Enum),
Value(&'a dyn Reflect),
}
@ -78,6 +81,7 @@ pub enum ReflectMut<'a> {
List(&'a mut dyn List),
Array(&'a mut dyn Array),
Map(&'a mut dyn Map),
Set(&'a mut dyn Set),
Enum(&'a mut dyn Enum),
Value(&'a mut dyn Reflect),
}
@ -96,6 +100,7 @@ pub enum ReflectOwned {
List(Box<dyn List>),
Array(Box<dyn Array>),
Map(Box<dyn Map>),
Set(Box<dyn Set>),
Enum(Box<dyn Enum>),
Value(Box<dyn Reflect>),
}
@ -149,6 +154,7 @@ pub enum ReflectKind {
List,
Array,
Map,
Set,
Enum,
Value,
}
@ -162,6 +168,7 @@ impl std::fmt::Display for ReflectKind {
ReflectKind::List => f.pad("list"),
ReflectKind::Array => f.pad("array"),
ReflectKind::Map => f.pad("map"),
ReflectKind::Set => f.pad("set"),
ReflectKind::Enum => f.pad("enum"),
ReflectKind::Value => f.pad("value"),
}

View File

@ -1,9 +1,9 @@
use crate::serde::SerializationData;
use crate::{
ArrayInfo, DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, DynamicTuple,
DynamicTupleStruct, DynamicVariant, EnumInfo, ListInfo, Map, MapInfo, NamedField, Reflect,
ReflectDeserialize, StructInfo, StructVariantInfo, TupleInfo, TupleStructInfo,
TupleVariantInfo, TypeInfo, TypeRegistration, TypeRegistry, VariantInfo,
ArrayInfo, DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicSet, DynamicStruct,
DynamicTuple, DynamicTupleStruct, DynamicVariant, EnumInfo, ListInfo, Map, MapInfo, NamedField,
Reflect, ReflectDeserialize, Set, SetInfo, StructInfo, StructVariantInfo, TupleInfo,
TupleStructInfo, TupleVariantInfo, TypeInfo, TypeRegistration, TypeRegistry, VariantInfo,
};
use erased_serde::Deserializer;
use serde::de::{
@ -582,6 +582,14 @@ impl<'a, 'de> DeserializeSeed<'de> for TypedReflectDeserializer<'a> {
dynamic_map.set_represented_type(Some(self.registration.type_info()));
Ok(Box::new(dynamic_map))
}
TypeInfo::Set(set_info) => {
let mut dynamic_set = deserializer.deserialize_seq(SetVisitor {
set_info,
registry: self.registry,
})?;
dynamic_set.set_represented_type(Some(self.registration.type_info()));
Ok(Box::new(dynamic_set))
}
TypeInfo::Tuple(tuple_info) => {
let mut dynamic_tuple = deserializer.deserialize_tuple(
tuple_info.field_len(),
@ -817,6 +825,39 @@ impl<'a, 'de> Visitor<'de> for MapVisitor<'a> {
}
}
struct SetVisitor<'a> {
set_info: &'static SetInfo,
registry: &'a TypeRegistry,
}
impl<'a, 'de> Visitor<'de> for SetVisitor<'a> {
type Value = DynamicSet;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("reflected set value")
}
fn visit_seq<V>(self, mut set: V) -> Result<Self::Value, V::Error>
where
V: SeqAccess<'de>,
{
let mut dynamic_set = DynamicSet::default();
let value_registration = get_registration(
self.set_info.value_type_id(),
self.set_info.value_type_path_table().path(),
self.registry,
)?;
while let Some(value) = set.next_element_seed(TypedReflectDeserializer {
registration: value_registration,
registry: self.registry,
})? {
dynamic_set.insert_boxed(value);
}
Ok(dynamic_set)
}
}
struct EnumVisitor<'a> {
enum_info: &'static EnumInfo,
registration: &'a TypeRegistration,

View File

@ -1,5 +1,5 @@
use crate::{
Array, Enum, List, Map, Reflect, ReflectRef, ReflectSerialize, Struct, Tuple, TupleStruct,
Array, Enum, List, Map, Reflect, ReflectRef, ReflectSerialize, Set, Struct, Tuple, TupleStruct,
TypeInfo, TypeRegistry, VariantInfo, VariantType,
};
use serde::ser::{
@ -223,6 +223,11 @@ impl<'a> Serialize for TypedReflectSerializer<'a> {
registry: self.registry,
}
.serialize(serializer),
ReflectRef::Set(value) => SetSerializer {
set: value,
registry: self.registry,
}
.serialize(serializer),
ReflectRef::Enum(value) => EnumSerializer {
enum_value: value,
registry: self.registry,
@ -503,6 +508,24 @@ impl<'a> Serialize for MapSerializer<'a> {
}
}
pub struct SetSerializer<'a> {
pub set: &'a dyn Set,
pub registry: &'a TypeRegistry,
}
impl<'a> Serialize for SetSerializer<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_seq(Some(self.set.len()))?;
for value in self.set.iter() {
state.serialize_element(&TypedReflectSerializer::new(value, self.registry))?;
}
state.end()
}
}
pub struct ListSerializer<'a> {
pub list: &'a dyn List,
pub registry: &'a TypeRegistry,

View File

@ -0,0 +1,520 @@
use std::any::{Any, TypeId};
use std::fmt::{Debug, Formatter};
use bevy_reflect_derive::impl_type_path;
use bevy_utils::hashbrown::hash_table::OccupiedEntry as HashTableOccupiedEntry;
use bevy_utils::hashbrown::HashTable;
use crate::{
self as bevy_reflect, hash_error, ApplyError, Reflect, ReflectKind, ReflectMut, ReflectOwned,
ReflectRef, TypeInfo, TypePath, TypePathTable,
};
/// A trait used to power [set-like] operations via [reflection].
///
/// Sets contain zero or more entries of a fixed type, and correspond to types like [`HashSet`](std::collections::HashSet). The
/// order of these entries is not guaranteed by this trait.
///
/// # Hashing
///
/// All values are expected to return a valid hash value from [`Reflect::reflect_hash`].
/// If using the [`#[derive(Reflect)]`](derive@crate::Reflect) macro, this can be done by adding `#[reflect(Hash)]`
/// to the entire struct or enum.
/// This is true even for manual implementors who do not use the hashed value,
/// as it is still relied on by [`DynamicSet`].
///
/// # Example
///
/// ```
/// use bevy_reflect::{Reflect, Set};
/// use bevy_utils::HashSet;
///
///
/// let foo: &mut dyn Set = &mut HashSet::<u32>::new();
/// foo.insert_boxed(Box::new(123_u32));
/// assert_eq!(foo.len(), 1);
///
/// let field: &dyn Reflect = foo.get(&123_u32).unwrap();
/// assert_eq!(field.downcast_ref::<u32>(), Some(&123_u32));
/// ```
///
/// [set-like]: https://doc.rust-lang.org/stable/std/collections/struct.HashSet.html
/// [reflection]: crate
pub trait Set: Reflect {
/// Returns a reference to the value.
///
/// If no value is contained, returns `None`.
fn get(&self, value: &dyn Reflect) -> Option<&dyn Reflect>;
/// Returns the number of elements in the set.
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 values of the set.
fn iter(&self) -> Box<dyn Iterator<Item = &dyn Reflect> + '_>;
/// Drain the values of this set to get a vector of owned values.
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>>;
/// Clones the set, producing a [`DynamicSet`].
fn clone_dynamic(&self) -> DynamicSet;
/// Inserts a value into the set.
///
/// If the set did not have this value present, `true` is returned.
/// If the set did have this value present, `false` is returned.
fn insert_boxed(&mut self, value: Box<dyn Reflect>) -> bool;
/// Removes a value from the set.
///
/// If the set did not have this value present, `true` is returned.
/// If the set did have this value present, `false` is returned.
fn remove(&mut self, value: &dyn Reflect) -> bool;
/// Checks if the given value is contained in the set
fn contains(&self, value: &dyn Reflect) -> bool;
}
/// A container for compile-time set info.
#[derive(Clone, Debug)]
pub struct SetInfo {
type_path: TypePathTable,
type_id: TypeId,
value_type_path: TypePathTable,
value_type_id: TypeId,
#[cfg(feature = "documentation")]
docs: Option<&'static str>,
}
impl SetInfo {
/// Create a new [`SetInfo`].
pub fn new<TSet: Set + TypePath, TValue: Reflect + TypePath>() -> Self {
Self {
type_path: TypePathTable::of::<TSet>(),
type_id: TypeId::of::<TSet>(),
value_type_path: TypePathTable::of::<TValue>(),
value_type_id: TypeId::of::<TValue>(),
#[cfg(feature = "documentation")]
docs: None,
}
}
/// Sets the docstring for this set.
#[cfg(feature = "documentation")]
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
Self { docs, ..self }
}
/// A representation of the type path of the set.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn type_path_table(&self) -> &TypePathTable {
&self.type_path
}
/// The [stable, full type path] of the set.
///
/// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
///
/// [stable, full type path]: TypePath
/// [`type_path_table`]: Self::type_path_table
pub fn type_path(&self) -> &'static str {
self.type_path_table().path()
}
/// The [`TypeId`] of the set.
pub fn type_id(&self) -> TypeId {
self.type_id
}
/// Check if the given type matches the set type.
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id
}
/// A representation of the type path of the value type.
///
/// Provides dynamic access to all methods on [`TypePath`].
pub fn value_type_path_table(&self) -> &TypePathTable {
&self.value_type_path
}
/// The [`TypeId`] of the value.
pub fn value_type_id(&self) -> TypeId {
self.value_type_id
}
/// Check if the given type matches the value type.
pub fn value_is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.value_type_id
}
/// The docstring of this set, if any.
#[cfg(feature = "documentation")]
pub fn docs(&self) -> Option<&'static str> {
self.docs
}
}
/// An ordered set of reflected values.
#[derive(Default)]
pub struct DynamicSet {
represented_type: Option<&'static TypeInfo>,
hash_table: HashTable<Box<dyn Reflect>>,
}
impl DynamicSet {
/// Sets the [type] to be represented by this `DynamicSet`.
///
/// # Panics
///
/// Panics if the given [type] is not a [`TypeInfo::Set`].
///
/// [type]: TypeInfo
pub fn set_represented_type(&mut self, represented_type: Option<&'static TypeInfo>) {
if let Some(represented_type) = represented_type {
assert!(
matches!(represented_type, TypeInfo::Set(_)),
"expected TypeInfo::Set but received: {:?}",
represented_type
);
}
self.represented_type = represented_type;
}
/// Inserts a typed value into the set.
pub fn insert<V: Reflect>(&mut self, value: V) {
self.insert_boxed(Box::new(value));
}
#[allow(clippy::borrowed_box)]
fn internal_hash(value: &Box<dyn Reflect>) -> u64 {
value.reflect_hash().expect(hash_error!(value))
}
#[allow(clippy::borrowed_box)]
fn internal_eq(value: &Box<dyn Reflect>) -> impl FnMut(&Box<dyn Reflect>) -> bool + '_ {
|other| {
value
.reflect_partial_eq(&**other)
.expect("Underlying type does not reflect `PartialEq` and hence doesn't support equality checks")
}
}
}
// I just created this function to have only one point where we ignore the rust warning about the
// unused allocation
fn box_and_clone(val: &dyn Reflect) -> Box<dyn Reflect> {
#[allow(unused_allocation)]
Box::new(val).clone_value()
}
impl Set for DynamicSet {
fn get(&self, value: &dyn Reflect) -> Option<&dyn Reflect> {
let boxed = box_and_clone(value);
self.hash_table
.find(Self::internal_hash(&boxed), Self::internal_eq(&boxed))
.map(|value| &**value)
}
fn len(&self) -> usize {
self.hash_table.len()
}
fn iter(&self) -> Box<dyn Iterator<Item = &dyn Reflect> + '_> {
let iter = self.hash_table.iter().map(|v| &**v);
Box::new(iter)
}
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
self.hash_table.into_iter().collect::<Vec<_>>()
}
fn clone_dynamic(&self) -> DynamicSet {
let mut hash_table = HashTable::new();
self.hash_table
.iter()
.map(|value| value.clone_value())
.for_each(|value| {
hash_table.insert_unique(Self::internal_hash(&value), value, Self::internal_hash);
});
DynamicSet {
represented_type: self.represented_type,
hash_table,
}
}
fn insert_boxed(&mut self, value: Box<dyn Reflect>) -> bool {
assert_eq!(
value.reflect_partial_eq(&*value),
Some(true),
"Values inserted in `Set` like types are expected to reflect `PartialEq`"
);
match self
.hash_table
.find_mut(Self::internal_hash(&value), Self::internal_eq(&value))
{
Some(old) => {
*old = value;
false
}
None => {
self.hash_table.insert_unique(
Self::internal_hash(&value),
value,
Self::internal_hash,
);
true
}
}
}
fn remove(&mut self, value: &dyn Reflect) -> bool {
let boxed = box_and_clone(value);
self.hash_table
.find_entry(Self::internal_hash(&boxed), Self::internal_eq(&boxed))
.map(HashTableOccupiedEntry::remove)
.is_ok()
}
fn contains(&self, value: &dyn Reflect) -> bool {
let boxed = box_and_clone(value);
self.hash_table
.find(Self::internal_hash(&boxed), Self::internal_eq(&boxed))
.is_some()
}
}
impl Reflect for DynamicSet {
#[inline]
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
self.represented_type
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
#[inline]
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
#[inline]
fn as_reflect(&self) -> &dyn Reflect {
self
}
#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
fn apply(&mut self, value: &dyn Reflect) {
set_apply(self, value);
}
fn try_apply(&mut self, value: &dyn Reflect) -> Result<(), ApplyError> {
set_try_apply(self, value)
}
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
fn reflect_kind(&self) -> ReflectKind {
ReflectKind::Set
}
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Set(self)
}
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Set(self)
}
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::Set(self)
}
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(self.clone_dynamic())
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
set_partial_eq(self, value)
}
fn debug(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "DynamicSet(")?;
set_debug(self, f)?;
write!(f, ")")
}
#[inline]
fn is_dynamic(&self) -> bool {
true
}
}
impl_type_path!((in bevy_reflect) DynamicSet);
impl Debug for DynamicSet {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.debug(f)
}
}
impl IntoIterator for DynamicSet {
type Item = Box<dyn Reflect>;
type IntoIter = bevy_utils::hashbrown::hash_table::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.hash_table.into_iter()
}
}
/// Compares a [`Set`] with a [`Reflect`] value.
///
/// Returns true if and only if all of the following are true:
/// - `b` is a set;
/// - `b` is the same length as `a`;
/// - For each value pair in `a`, `b` contains the value too,
/// and [`Reflect::reflect_partial_eq`] returns `Some(true)` for the two values.
///
/// Returns [`None`] if the comparison couldn't even be performed.
#[inline]
pub fn set_partial_eq<M: Set>(a: &M, b: &dyn Reflect) -> Option<bool> {
let ReflectRef::Set(set) = b.reflect_ref() else {
return Some(false);
};
if a.len() != set.len() {
return Some(false);
}
for value in a.iter() {
if let Some(set_value) = set.get(value) {
let eq_result = value.reflect_partial_eq(set_value);
if let failed @ (Some(false) | None) = eq_result {
return failed;
}
} else {
return Some(false);
}
}
Some(true)
}
/// The default debug formatter for [`Set`] types.
///
/// # Example
/// ```
/// # use bevy_utils::HashSet;
/// use bevy_reflect::Reflect;
///
/// let mut my_set = HashSet::new();
/// my_set.insert(String::from("Hello"));
/// println!("{:#?}", &my_set as &dyn Reflect);
///
/// // Output:
///
/// // {
/// // "Hello",
/// // }
/// ```
#[inline]
pub fn set_debug(dyn_set: &dyn Set, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut debug = f.debug_set();
for value in dyn_set.iter() {
debug.entry(&value as &dyn Debug);
}
debug.finish()
}
/// Applies the elements of reflected set `b` to the corresponding elements of set `a`.
///
/// If a value from `b` does not exist in `a`, the value is cloned and inserted.
///
/// # Panics
///
/// This function panics if `b` is not a reflected set.
#[inline]
pub fn set_apply<M: Set>(a: &mut M, b: &dyn Reflect) {
if let ReflectRef::Set(set_value) = b.reflect_ref() {
for b_value in set_value.iter() {
if a.get(b_value).is_none() {
a.insert_boxed(b_value.clone_value());
}
}
} else {
panic!("Attempted to apply a non-set type to a set type.");
}
}
/// Tries to apply the elements of reflected set `b` to the corresponding elements of set `a`
/// and returns a Result.
///
/// If a key from `b` does not exist in `a`, the value is cloned and inserted.
///
/// # Errors
///
/// This function returns an [`ApplyError::MismatchedKinds`] if `b` is not a reflected set or if
/// applying elements to each other fails.
#[inline]
pub fn set_try_apply<S: Set>(a: &mut S, b: &dyn Reflect) -> Result<(), ApplyError> {
if let ReflectRef::Set(set_value) = b.reflect_ref() {
for b_value in set_value.iter() {
if a.get(b_value).is_none() {
a.insert_boxed(b_value.clone_value());
}
}
} else {
return Err(ApplyError::MismatchedKinds {
from_kind: b.reflect_kind(),
to_kind: ReflectKind::Set,
});
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::DynamicSet;
#[test]
fn test_into_iter() {
let expected = ["foo", "bar", "baz"];
let mut set = DynamicSet::default();
set.insert(expected[0].to_string());
set.insert(expected[1].to_string());
set.insert(expected[2].to_string());
for item in set.into_iter() {
let value = item.take::<String>().expect("couldn't downcast to String");
let index = expected
.iter()
.position(|i| *i == value.as_str())
.expect("Element found in expected array");
assert_eq!(expected[index], value);
}
}
}

View File

@ -1,7 +1,7 @@
use crate::{
ArrayInfo, DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, DynamicTuple,
DynamicTupleStruct, EnumInfo, ListInfo, MapInfo, Reflect, ReflectKind, StructInfo, TupleInfo,
TupleStructInfo, TypePath, TypePathTable,
DynamicTupleStruct, EnumInfo, ListInfo, MapInfo, Reflect, ReflectKind, SetInfo, StructInfo,
TupleInfo, TupleStructInfo, TypePath, TypePathTable,
};
use std::any::{Any, TypeId};
use std::fmt::Debug;
@ -164,6 +164,7 @@ pub enum TypeInfo {
List(ListInfo),
Array(ArrayInfo),
Map(MapInfo),
Set(SetInfo),
Enum(EnumInfo),
Value(ValueInfo),
}
@ -178,6 +179,7 @@ impl TypeInfo {
Self::List(info) => info.type_id(),
Self::Array(info) => info.type_id(),
Self::Map(info) => info.type_id(),
Self::Set(info) => info.type_id(),
Self::Enum(info) => info.type_id(),
Self::Value(info) => info.type_id(),
}
@ -194,6 +196,7 @@ impl TypeInfo {
Self::List(info) => info.type_path_table(),
Self::Array(info) => info.type_path_table(),
Self::Map(info) => info.type_path_table(),
Self::Set(info) => info.type_path_table(),
Self::Enum(info) => info.type_path_table(),
Self::Value(info) => info.type_path_table(),
}
@ -224,6 +227,7 @@ impl TypeInfo {
Self::List(info) => info.docs(),
Self::Array(info) => info.docs(),
Self::Map(info) => info.docs(),
Self::Set(info) => info.docs(),
Self::Enum(info) => info.docs(),
Self::Value(info) => info.docs(),
}
@ -240,6 +244,7 @@ impl TypeInfo {
Self::List(_) => ReflectKind::List,
Self::Array(_) => ReflectKind::Array,
Self::Map(_) => ReflectKind::Map,
Self::Set(_) => ReflectKind::Set,
Self::Enum(_) => ReflectKind::Enum,
Self::Value(_) => ReflectKind::Value,
}

View File

@ -105,6 +105,10 @@ fn setup() {
// This exposes "map" operations on your type, such as getting / inserting by key.
// Map is automatically implemented for relevant core types like HashMap<K, V>
ReflectRef::Map(_) => {}
// `Set` is a special trait that can be manually implemented (instead of deriving Reflect).
// This exposes "set" operations on your type, such as getting / inserting by value.
// Set is automatically implemented for relevant core types like HashSet<T>
ReflectRef::Set(_) => {}
// `Value` types do not implement any of the other traits above. They are simply a Reflect
// implementation. Value is implemented for core types like i32, usize, f32, and
// String.