Skip to content

Commit e0c304d

Browse files
Davierjames7132
authored andcommitted
bevy_reflect: support map insertion (bevyengine#5173)
# Objective This is a rebase of bevyengine#3701 which is currently scheduled for 0.8 but is marked for adoption. > Fixes bevyengine#3609 ## Solution > - add an `insert_boxed()` method on the `Map` trait > - implement it for `HashMap` using a new `FromReflect` generic bound > - add a `map_apply()` helper method to implement `Map::apply()`, that inserts new values instead of ignoring them --- ## Changelog TODO Co-authored-by: james7132 <[email protected]>
1 parent f7052ac commit e0c304d

File tree

5 files changed

+92
-41
lines changed

5 files changed

+92
-41
lines changed

crates/bevy_reflect/src/impls/std.rs

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate as bevy_reflect;
22
use crate::{
3-
map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, FromReflect, FromType,
3+
map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap, FromReflect, FromType,
44
GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, Reflect, ReflectDeserialize,
55
ReflectMut, ReflectRef, ReflectSerialize, TypeInfo, TypeRegistration, Typed, ValueInfo,
66
};
@@ -193,7 +193,7 @@ impl<T: FromReflect> FromReflect for Vec<T> {
193193
}
194194
}
195195

196-
impl<K: Reflect + Eq + Hash, V: Reflect> Map for HashMap<K, V> {
196+
impl<K: FromReflect + Eq + Hash, V: FromReflect> Map for HashMap<K, V> {
197197
fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> {
198198
key.downcast_ref::<K>()
199199
.and_then(|key| HashMap::get(self, key))
@@ -231,9 +231,34 @@ impl<K: Reflect + Eq + Hash, V: Reflect> Map for HashMap<K, V> {
231231
}
232232
dynamic_map
233233
}
234+
235+
fn insert_boxed(
236+
&mut self,
237+
key: Box<dyn Reflect>,
238+
value: Box<dyn Reflect>,
239+
) -> Option<Box<dyn Reflect>> {
240+
let key = key.take::<K>().unwrap_or_else(|key| {
241+
K::from_reflect(&*key).unwrap_or_else(|| {
242+
panic!(
243+
"Attempted to insert invalid key of type {}.",
244+
key.type_name()
245+
)
246+
})
247+
});
248+
let value = value.take::<V>().unwrap_or_else(|value| {
249+
V::from_reflect(&*value).unwrap_or_else(|| {
250+
panic!(
251+
"Attempted to insert invalid value of type {}.",
252+
value.type_name()
253+
)
254+
})
255+
});
256+
self.insert(key, value)
257+
.map(|old_value| Box::new(old_value) as Box<dyn Reflect>)
258+
}
234259
}
235260

236-
impl<K: Reflect + Eq + Hash, V: Reflect> Reflect for HashMap<K, V> {
261+
impl<K: FromReflect + Eq + Hash, V: FromReflect> Reflect for HashMap<K, V> {
237262
fn type_name(&self) -> &str {
238263
std::any::type_name::<Self>()
239264
}
@@ -263,15 +288,7 @@ impl<K: Reflect + Eq + Hash, V: Reflect> Reflect for HashMap<K, V> {
263288
}
264289

265290
fn apply(&mut self, value: &dyn Reflect) {
266-
if let ReflectRef::Map(map_value) = value.reflect_ref() {
267-
for (key, value) in map_value.iter() {
268-
if let Some(v) = Map::get_mut(self, key) {
269-
v.apply(value);
270-
}
271-
}
272-
} else {
273-
panic!("Attempted to apply a non-map type to a map type.");
274-
}
291+
map_apply(self, value);
275292
}
276293

277294
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
@@ -296,7 +313,7 @@ impl<K: Reflect + Eq + Hash, V: Reflect> Reflect for HashMap<K, V> {
296313
}
297314
}
298315

299-
impl<K: Reflect + Eq + Hash, V: Reflect> Typed for HashMap<K, V> {
316+
impl<K: FromReflect + Eq + Hash, V: FromReflect> Typed for HashMap<K, V> {
300317
fn type_info() -> &'static TypeInfo {
301318
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
302319
CELL.get_or_insert::<Self, _>(|| TypeInfo::Map(MapInfo::new::<Self, K, V>()))
@@ -305,8 +322,8 @@ impl<K: Reflect + Eq + Hash, V: Reflect> Typed for HashMap<K, V> {
305322

306323
impl<K, V> GetTypeRegistration for HashMap<K, V>
307324
where
308-
K: Reflect + Clone + Eq + Hash + for<'de> Deserialize<'de>,
309-
V: Reflect + Clone + for<'de> Deserialize<'de>,
325+
K: FromReflect + Clone + Eq + Hash + for<'de> Deserialize<'de>,
326+
V: FromReflect + Clone + for<'de> Deserialize<'de>,
310327
{
311328
fn get_type_registration() -> TypeRegistration {
312329
let mut registration = TypeRegistration::of::<Self>();

crates/bevy_reflect/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ mod tests {
353353

354354
let mut map = DynamicMap::default();
355355
map.insert(2usize, 3i8);
356+
map.insert(3usize, 4i8);
356357
foo_patch.insert("d", map);
357358

358359
let mut bar_patch = DynamicStruct::default();
@@ -394,6 +395,7 @@ mod tests {
394395
let mut hash_map = HashMap::default();
395396
hash_map.insert(1, 1);
396397
hash_map.insert(2, 3);
398+
hash_map.insert(3, 4);
397399

398400
let mut hash_map_baz = HashMap::default();
399401
hash_map_baz.insert(1, Bar { x: 7 });
@@ -416,6 +418,7 @@ mod tests {
416418

417419
let mut hash_map = HashMap::default();
418420
hash_map.insert(2, 3);
421+
hash_map.insert(3, 4);
419422

420423
let expected_new_foo = Foo {
421424
a: 2,

crates/bevy_reflect/src/map.rs

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ pub trait Map: Reflect {
4444

4545
/// Clones the map, producing a [`DynamicMap`].
4646
fn clone_dynamic(&self) -> DynamicMap;
47+
48+
/// Inserts a key-value pair into the map.
49+
///
50+
/// If the map did not have this key present, `None` is returned.
51+
/// If the map did have this key present, the value is updated, and the old value is returned.
52+
fn insert_boxed(
53+
&mut self,
54+
key: Box<dyn Reflect>,
55+
value: Box<dyn Reflect>,
56+
) -> Option<Box<dyn Reflect>>;
4757
}
4858

4959
/// A container for compile-time map info.
@@ -153,19 +163,6 @@ impl DynamicMap {
153163
pub fn insert<K: Reflect, V: Reflect>(&mut self, key: K, value: V) {
154164
self.insert_boxed(Box::new(key), Box::new(value));
155165
}
156-
157-
/// Inserts a key-value pair of [`Reflect`] values into the map.
158-
pub fn insert_boxed(&mut self, key: Box<dyn Reflect>, value: Box<dyn Reflect>) {
159-
match self.indices.entry(key.reflect_hash().expect(HASH_ERROR)) {
160-
Entry::Occupied(entry) => {
161-
self.values[*entry.get()] = (key, value);
162-
}
163-
Entry::Vacant(entry) => {
164-
entry.insert(self.values.len());
165-
self.values.push((key, value));
166-
}
167-
}
168-
}
169166
}
170167

171168
impl Map for DynamicMap {
@@ -210,6 +207,25 @@ impl Map for DynamicMap {
210207
.get(index)
211208
.map(|(key, value)| (&**key, &**value))
212209
}
210+
211+
fn insert_boxed(
212+
&mut self,
213+
key: Box<dyn Reflect>,
214+
mut value: Box<dyn Reflect>,
215+
) -> Option<Box<dyn Reflect>> {
216+
match self.indices.entry(key.reflect_hash().expect(HASH_ERROR)) {
217+
Entry::Occupied(entry) => {
218+
let (_old_key, old_value) = self.values.get_mut(*entry.get()).unwrap();
219+
std::mem::swap(old_value, &mut value);
220+
Some(value)
221+
}
222+
Entry::Vacant(entry) => {
223+
entry.insert(self.values.len());
224+
self.values.push((key, value));
225+
None
226+
}
227+
}
228+
}
213229
}
214230

215231
impl Reflect for DynamicMap {
@@ -245,15 +261,7 @@ impl Reflect for DynamicMap {
245261
}
246262

247263
fn apply(&mut self, value: &dyn Reflect) {
248-
if let ReflectRef::Map(map_value) = value.reflect_ref() {
249-
for (key, value) in map_value.iter() {
250-
if let Some(v) = self.get_mut(key) {
251-
v.apply(value);
252-
}
253-
}
254-
} else {
255-
panic!("Attempted to apply a non-map type to a map type.");
256-
}
264+
map_apply(self, value);
257265
}
258266

259267
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
@@ -387,6 +395,28 @@ pub fn map_debug(dyn_map: &dyn Map, f: &mut std::fmt::Formatter<'_>) -> std::fmt
387395
debug.finish()
388396
}
389397

398+
/// Applies the elements of reflected map `b` to the corresponding elements of map `a`.
399+
///
400+
/// If a key from `b` does not exist in `a`, the value is cloned and inserted.
401+
///
402+
/// # Panics
403+
///
404+
/// This function panics if `b` is not a reflected map.
405+
#[inline]
406+
pub fn map_apply<M: Map>(a: &mut M, b: &dyn Reflect) {
407+
if let ReflectRef::Map(map_value) = b.reflect_ref() {
408+
for (key, b_value) in map_value.iter() {
409+
if let Some(a_value) = a.get_mut(key) {
410+
a_value.apply(b_value);
411+
} else {
412+
a.insert_boxed(key.clone_value(), b_value.clone_value());
413+
}
414+
}
415+
} else {
416+
panic!("Attempted to apply a non-map type to a map type.");
417+
}
418+
}
419+
390420
#[cfg(test)]
391421
mod tests {
392422
use super::DynamicMap;

crates/bevy_reflect/src/reflect.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,18 @@ pub trait Reflect: Any + Send + Sync {
9393
/// and excess elements in `value` are appended to `self`.
9494
/// - If `T` is a [`Map`], then for each key in `value`, the associated
9595
/// value is applied to the value associated with the same key in `self`.
96-
/// Keys which are not present in both maps are ignored.
96+
/// Keys which are not present in `self` are inserted.
9797
/// - If `T` is none of these, then `value` is downcast to `T`, cloned, and
9898
/// assigned to `self`.
9999
///
100100
/// Note that `Reflect` must be implemented manually for [`List`]s and
101101
/// [`Map`]s in order to achieve the correct semantics, as derived
102102
/// implementations will have the semantics for [`Struct`], [`TupleStruct`]
103-
/// or none of the above depending on the kind of type. For lists, use the
104-
/// [`list_apply`] helper function when implementing this method.
103+
/// or none of the above depending on the kind of type. For lists and maps, use the
104+
/// [`list_apply`] and [`map_apply`] helper functions when implementing this method.
105105
///
106106
/// [`list_apply`]: crate::list_apply
107+
/// [`map_apply`]: crate::map_apply
107108
///
108109
/// # Panics
109110
///

crates/bevy_reflect/src/serde/de.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
serde::type_fields, DynamicArray, DynamicList, DynamicMap, DynamicStruct, DynamicTuple,
3-
DynamicTupleStruct, Reflect, ReflectDeserialize, TypeRegistry,
3+
DynamicTupleStruct, Map, Reflect, ReflectDeserialize, TypeRegistry,
44
};
55
use erased_serde::Deserializer;
66
use serde::de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor};

0 commit comments

Comments
 (0)