diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index aff3f0d130..16520dd496 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -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()); -impl_reflect_value!(::std::collections::HashSet()); -impl_reflect_value!(::bevy_utils::hashbrown::HashSet()); impl_reflect_value!(::core::ops::Range()); impl_reflect_value!(::core::ops::RangeInclusive()); impl_reflect_value!(::core::ops::RangeFrom()); @@ -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); -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 List for $ty { @@ -662,6 +656,221 @@ crate::func::macros::impl_function_traits!(::bevy_utils::hashbrown::HashMap ); +macro_rules! impl_reflect_for_hashset { + ($ty:path) => { + impl 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::() + .and_then(|value| Self::get(self, value)) + .map(|value| value as &dyn Reflect) + } + + fn len(&self) -> usize { + Self::len(self) + } + + fn iter(&self) -> Box + '_> { + let iter = self.iter().map(|v| v as &dyn Reflect); + Box::new(iter) + } + + fn drain(self: Box) -> Vec> { + self.into_iter() + .map(|value| Box::new(value) as Box) + .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) -> 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::() + .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::() + .or_else(|| { + from_reflect = V::from_reflect(value); + from_reflect.as_ref() + }) + .map_or(false, |value| self.contains(value)) + } + } + + impl 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(::type_info()) + } + + fn into_any(self: Box) -> Box { + self + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + #[inline] + fn into_reflect(self: Box) -> Box { + 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) -> Result<(), Box> { + *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) -> ReflectOwned { + ReflectOwned::Set(self) + } + + fn clone_value(&self) -> Box { + Box::new(self.clone_dynamic()) + } + + fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option { + set_partial_eq(self, value) + } + } + + impl 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::(|| TypeInfo::Set(SetInfo::new::())) + } + } + + impl 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::(); + registration.insert::(FromType::::from_type()); + registration + } + + fn register_type_dependencies(registry: &mut TypeRegistry) { + registry.register::(); + } + } + + impl FromReflect for $ty + where + V: FromReflect + TypePath + GetTypeRegistration + Eq + Hash, + S: TypePath + BuildHasher + Default + Send + Sync, + { + fn from_reflect(reflect: &dyn Reflect) -> Option { + 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); +impl_type_path!(::std::collections::HashSet); +#[cfg(feature = "functions")] +crate::func::macros::impl_function_traits!(::std::collections::HashSet; + < + V: Hash + Eq + FromReflect + TypePath + GetTypeRegistration, + S: TypePath + BuildHasher + Default + Send + Sync + > +); + +impl_reflect_for_hashset!(::bevy_utils::hashbrown::HashSet); +impl_type_path!(::bevy_utils::hashbrown::HashSet); +#[cfg(feature = "functions")] +crate::func::macros::impl_function_traits!(::bevy_utils::hashbrown::HashSet; + < + V: Hash + Eq + FromReflect + TypePath + GetTypeRegistration, + S: TypePath + BuildHasher + Default + Send + Sync + > +); + impl Map for ::std::collections::BTreeMap where K: FromReflect + MaybeTyped + TypePath + GetTypeRegistration + Eq + Ord, diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 868694f5d3..37957c098a 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -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::*; diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index 8238048d0a..2063cf3170 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -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), Array(Box), Map(Box), + Set(Box), Enum(Box), Value(Box), } @@ -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"), } diff --git a/crates/bevy_reflect/src/serde/de.rs b/crates/bevy_reflect/src/serde/de.rs index 38dd63cd25..3670d06623 100644 --- a/crates/bevy_reflect/src/serde/de.rs +++ b/crates/bevy_reflect/src/serde/de.rs @@ -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(self, mut set: V) -> Result + 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, diff --git a/crates/bevy_reflect/src/serde/ser.rs b/crates/bevy_reflect/src/serde/ser.rs index f862d0139e..ebc1bcf185 100644 --- a/crates/bevy_reflect/src/serde/ser.rs +++ b/crates/bevy_reflect/src/serde/ser.rs @@ -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(&self, serializer: S) -> Result + 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, diff --git a/crates/bevy_reflect/src/set.rs b/crates/bevy_reflect/src/set.rs new file mode 100644 index 0000000000..cde29d7ded --- /dev/null +++ b/crates/bevy_reflect/src/set.rs @@ -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::::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::(), 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 + '_>; + + /// Drain the values of this set to get a vector of owned values. + fn drain(self: Box) -> Vec>; + + /// 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) -> 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() -> Self { + Self { + type_path: TypePathTable::of::(), + type_id: TypeId::of::(), + value_type_path: TypePathTable::of::(), + value_type_id: TypeId::of::(), + #[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(&self) -> bool { + TypeId::of::() == 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(&self) -> bool { + TypeId::of::() == 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>, +} + +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(&mut self, value: V) { + self.insert_boxed(Box::new(value)); + } + + #[allow(clippy::borrowed_box)] + fn internal_hash(value: &Box) -> u64 { + value.reflect_hash().expect(hash_error!(value)) + } + + #[allow(clippy::borrowed_box)] + fn internal_eq(value: &Box) -> impl FnMut(&Box) -> 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 { + #[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 + '_> { + let iter = self.hash_table.iter().map(|v| &**v); + Box::new(iter) + } + + fn drain(self: Box) -> Vec> { + self.hash_table.into_iter().collect::>() + } + + 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) -> 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) -> Box { + self + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + #[inline] + fn into_reflect(self: Box) -> Box { + 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) -> Result<(), Box> { + *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) -> ReflectOwned { + ReflectOwned::Set(self) + } + + fn clone_value(&self) -> Box { + Box::new(self.clone_dynamic()) + } + + fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option { + 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; + type IntoIter = bevy_utils::hashbrown::hash_table::IntoIter; + + 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(a: &M, b: &dyn Reflect) -> Option { + 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(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(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::().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); + } + } +} diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index 1de6e754a1..bbd9b22464 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -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, } diff --git a/examples/reflection/reflection_types.rs b/examples/reflection/reflection_types.rs index 3271b5a5d3..1ecb88ca8b 100644 --- a/examples/reflection/reflection_types.rs +++ b/examples/reflection/reflection_types.rs @@ -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 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 + 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.