-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
bevy_reflect: support map insertion #3701
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,8 +1,8 @@ | ||||||
use crate as bevy_reflect; | ||||||
use crate::{ | ||||||
map_partial_eq, serde::Serializable, DynamicMap, FromReflect, FromType, GetTypeRegistration, | ||||||
List, ListIter, Map, MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef, | ||||||
TypeRegistration, | ||||||
map_apply, map_partial_eq, serde::Serializable, DynamicMap, FromReflect, FromType, | ||||||
GetTypeRegistration, List, ListIter, Map, MapIter, Reflect, ReflectDeserialize, ReflectMut, | ||||||
ReflectRef, TypeRegistration, | ||||||
}; | ||||||
|
||||||
use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value}; | ||||||
|
@@ -166,7 +166,7 @@ impl<T: FromReflect> FromReflect for Vec<T> { | |||||
} | ||||||
} | ||||||
|
||||||
impl<K: Reflect + Eq + Hash, V: Reflect> Map for HashMap<K, V> { | ||||||
impl<K: FromReflect + Eq + Hash, V: FromReflect> Map for HashMap<K, V> { | ||||||
fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> { | ||||||
key.downcast_ref::<K>() | ||||||
.and_then(|key| HashMap::get(self, key)) | ||||||
|
@@ -204,10 +204,35 @@ impl<K: Reflect + Eq + Hash, V: Reflect> Map for HashMap<K, V> { | |||||
} | ||||||
dynamic_map | ||||||
} | ||||||
|
||||||
fn insert_boxed( | ||||||
&mut self, | ||||||
key: Box<dyn Reflect>, | ||||||
value: Box<dyn Reflect>, | ||||||
) -> Option<Box<dyn Reflect>> { | ||||||
let key = key.take::<K>().unwrap_or_else(|key| { | ||||||
K::from_reflect(&*key).unwrap_or_else(|| { | ||||||
panic!( | ||||||
"Attempted to insert invalid key of type {}.", | ||||||
key.type_name() | ||||||
) | ||||||
}) | ||||||
}); | ||||||
let value = value.take::<V>().unwrap_or_else(|value| { | ||||||
V::from_reflect(&*value).unwrap_or_else(|| { | ||||||
panic!( | ||||||
"Attempted to push invalid value of type {}.", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Why is this
Suggested change
|
||||||
value.type_name() | ||||||
) | ||||||
}) | ||||||
}); | ||||||
self.insert(key, value) | ||||||
.map(|old_value| Box::new(old_value) as Box<dyn Reflect>) | ||||||
} | ||||||
} | ||||||
|
||||||
// SAFE: any and any_mut both return self | ||||||
unsafe impl<K: Reflect + Eq + Hash, V: Reflect> Reflect for HashMap<K, V> { | ||||||
unsafe impl<K: FromReflect + Eq + Hash, V: FromReflect> Reflect for HashMap<K, V> { | ||||||
fn type_name(&self) -> &str { | ||||||
std::any::type_name::<Self>() | ||||||
} | ||||||
|
@@ -221,15 +246,7 @@ unsafe impl<K: Reflect + Eq + Hash, V: Reflect> Reflect for HashMap<K, V> { | |||||
} | ||||||
|
||||||
fn apply(&mut self, value: &dyn Reflect) { | ||||||
if let ReflectRef::Map(map_value) = value.reflect_ref() { | ||||||
for (key, value) in map_value.iter() { | ||||||
if let Some(v) = Map::get_mut(self, key) { | ||||||
v.apply(value) | ||||||
} | ||||||
} | ||||||
} else { | ||||||
panic!("Attempted to apply a non-map type to a map type."); | ||||||
} | ||||||
map_apply(self, value) | ||||||
} | ||||||
|
||||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { | ||||||
|
@@ -264,8 +281,8 @@ unsafe impl<K: Reflect + Eq + Hash, V: Reflect> Reflect for HashMap<K, V> { | |||||
|
||||||
impl<K, V> GetTypeRegistration for HashMap<K, V> | ||||||
where | ||||||
K: Reflect + Clone + Eq + Hash + for<'de> Deserialize<'de>, | ||||||
V: Reflect + Clone + for<'de> Deserialize<'de>, | ||||||
K: FromReflect + Clone + Eq + Hash + for<'de> Deserialize<'de>, | ||||||
V: FromReflect + Clone + for<'de> Deserialize<'de>, | ||||||
{ | ||||||
fn get_type_registration() -> TypeRegistration { | ||||||
let mut registration = TypeRegistration::of::<HashMap<K, V>>(); | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -41,6 +41,16 @@ pub trait Map: Reflect { | |||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
/// Clones the map, producing a [`DynamicMap`]. | ||||||||||||||||||||||||||||||
fn clone_dynamic(&self) -> DynamicMap; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
/// Inserts a key-value pair into the map. | ||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||
/// If the map did not have this key present, `None` is returned. | ||||||||||||||||||||||||||||||
/// If the map did have this key present, the value is updated, and the old value is returned. | ||||||||||||||||||||||||||||||
fn insert_boxed( | ||||||||||||||||||||||||||||||
&mut self, | ||||||||||||||||||||||||||||||
key: Box<dyn Reflect>, | ||||||||||||||||||||||||||||||
value: Box<dyn Reflect>, | ||||||||||||||||||||||||||||||
) -> Option<Box<dyn Reflect>>; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
const HASH_ERROR: &str = "the given key does not support hashing"; | ||||||||||||||||||||||||||||||
|
@@ -74,19 +84,6 @@ impl DynamicMap { | |||||||||||||||||||||||||||||
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) => { | ||||||||||||||||||||||||||||||
self.values[*entry.get()] = (key, value); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
Entry::Vacant(entry) => { | ||||||||||||||||||||||||||||||
entry.insert(self.values.len()); | ||||||||||||||||||||||||||||||
self.values.push((key, value)); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
impl Map for DynamicMap { | ||||||||||||||||||||||||||||||
|
@@ -131,6 +128,25 @@ impl Map for DynamicMap { | |||||||||||||||||||||||||||||
.get(index) | ||||||||||||||||||||||||||||||
.map(|(key, value)| (&**key, &**value)) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
fn insert_boxed( | ||||||||||||||||||||||||||||||
&mut self, | ||||||||||||||||||||||||||||||
key: Box<dyn Reflect>, | ||||||||||||||||||||||||||||||
mut value: Box<dyn Reflect>, | ||||||||||||||||||||||||||||||
) -> Option<Box<dyn Reflect>> { | ||||||||||||||||||||||||||||||
match self.indices.entry(key.reflect_hash().expect(HASH_ERROR)) { | ||||||||||||||||||||||||||||||
Entry::Occupied(entry) => { | ||||||||||||||||||||||||||||||
let (_old_key, old_value) = self.values.get_mut(*entry.get()).unwrap(); | ||||||||||||||||||||||||||||||
std::mem::swap(old_value, &mut value); | ||||||||||||||||||||||||||||||
Some(value) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
Entry::Vacant(entry) => { | ||||||||||||||||||||||||||||||
entry.insert(self.values.len()); | ||||||||||||||||||||||||||||||
self.values.push((key, value)); | ||||||||||||||||||||||||||||||
None | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
// SAFE: any and any_mut both return self | ||||||||||||||||||||||||||||||
|
@@ -148,15 +164,7 @@ unsafe impl Reflect for DynamicMap { | |||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
fn apply(&mut self, value: &dyn Reflect) { | ||||||||||||||||||||||||||||||
if let ReflectRef::Map(map_value) = value.reflect_ref() { | ||||||||||||||||||||||||||||||
for (key, value) in map_value.iter() { | ||||||||||||||||||||||||||||||
if let Some(v) = self.get_mut(key) { | ||||||||||||||||||||||||||||||
v.apply(value) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||
panic!("Attempted to apply a non-map type to a map type."); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
map_apply(self, value); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { | ||||||||||||||||||||||||||||||
|
@@ -243,3 +251,25 @@ pub fn map_partial_eq<M: Map>(a: &M, b: &dyn Reflect) -> Option<bool> { | |||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
Some(true) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
/// Applies the elements of `b` to the corresponding elements of `a`. | ||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||
/// If a key from `b` does not exist in `a`, the value is cloned and inserted. | ||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||
/// # Panics | ||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||
/// This function panics if `b` is not a map. | ||||||||||||||||||||||||||||||
Comment on lines
+255
to
+261
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Maybe reiterate that these two parameters are maps (as opposed to an iterator of tuples or something else).
Suggested change
|
||||||||||||||||||||||||||||||
#[inline] | ||||||||||||||||||||||||||||||
pub fn map_apply<M: Map>(a: &mut M, b: &dyn Reflect) { | ||||||||||||||||||||||||||||||
if let ReflectRef::Map(map_value) = b.reflect_ref() { | ||||||||||||||||||||||||||||||
for (key, b_value) in map_value.iter() { | ||||||||||||||||||||||||||||||
if let Some(a_value) = a.get_mut(key) { | ||||||||||||||||||||||||||||||
a_value.apply(b_value); | ||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||
a.insert_boxed(key.clone_value(), b_value.clone_value()); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||
panic!("Attempted to apply a non-map type to a map type."); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagine this could be a breaking change. Maybe add a
Migration Guide
section on the PR comment explaining that key and value types now have aFromReflect
bound instead ofReflect
?