Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/src/map.rs
9334 views
1
//! Traits and types used to power [map-like] operations via reflection.
2
//!
3
//! [map-like]: https://doc.rust-lang.org/book/ch08-03-hash-maps.html
4
use core::fmt::{Debug, Formatter};
5
6
use bevy_platform::collections::HashTable;
7
use bevy_reflect_derive::impl_type_path;
8
9
use crate::{
10
generics::impl_generic_info_methods, type_info::impl_type_methods, ApplyError, Generics,
11
MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type,
12
TypeInfo, TypePath,
13
};
14
use alloc::{boxed::Box, format, vec::Vec};
15
16
/// A trait used to power [map-like] operations via [reflection].
17
///
18
/// Maps contain zero or more entries of a key and its associated value,
19
/// and correspond to types like [`HashMap`] and [`BTreeMap`].
20
/// The order of these entries is not guaranteed by this trait.
21
///
22
/// # Hashing and equality
23
///
24
/// All keys are expected to return a valid hash value from [`PartialReflect::reflect_hash`] and be
25
/// comparable using [`PartialReflect::reflect_partial_eq`].
26
/// If using the [`#[derive(Reflect)]`](derive@crate::Reflect) macro, this can be done by adding
27
/// `#[reflect(Hash, PartialEq)]` to the entire struct or enum.
28
/// The ordering is expected to be total, that is as if the reflected type implements the [`Eq`] trait.
29
/// This is true even for manual implementors who do not hash or compare values,
30
/// as it is still relied on by [`DynamicMap`].
31
///
32
/// # Example
33
///
34
/// ```
35
/// use bevy_reflect::{PartialReflect, Reflect, map::Map};
36
/// use std::collections::HashMap;
37
///
38
///
39
/// let foo: &mut dyn Map = &mut HashMap::<u32, bool>::new();
40
/// foo.insert_boxed(Box::new(123_u32), Box::new(true));
41
/// assert_eq!(foo.len(), 1);
42
///
43
/// let field: &dyn PartialReflect = foo.get(&123_u32).unwrap();
44
/// assert_eq!(field.try_downcast_ref::<bool>(), Some(&true));
45
/// ```
46
///
47
/// [`HashMap`]: std::collections::HashMap
48
/// [`BTreeMap`]: alloc::collections::BTreeMap
49
/// [map-like]: https://doc.rust-lang.org/book/ch08-03-hash-maps.html
50
/// [reflection]: crate
51
pub trait Map: PartialReflect {
52
/// Returns a reference to the value associated with the given key.
53
///
54
/// If no value is associated with `key`, returns `None`.
55
fn get(&self, key: &dyn PartialReflect) -> Option<&dyn PartialReflect>;
56
57
/// Returns a mutable reference to the value associated with the given key.
58
///
59
/// If no value is associated with `key`, returns `None`.
60
fn get_mut(&mut self, key: &dyn PartialReflect) -> Option<&mut dyn PartialReflect>;
61
62
/// Returns the number of elements in the map.
63
fn len(&self) -> usize;
64
65
/// Returns `true` if the list contains no elements.
66
fn is_empty(&self) -> bool {
67
self.len() == 0
68
}
69
70
/// Returns an iterator over the key-value pairs of the map.
71
fn iter(&self) -> Box<dyn Iterator<Item = (&dyn PartialReflect, &dyn PartialReflect)> + '_>;
72
73
/// Drain the key-value pairs of this map to get a vector of owned values.
74
///
75
/// After calling this function, `self` will be empty.
76
fn drain(&mut self) -> Vec<(Box<dyn PartialReflect>, Box<dyn PartialReflect>)>;
77
78
/// Retain only the elements specified by the predicate.
79
///
80
/// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`.
81
fn retain(&mut self, f: &mut dyn FnMut(&dyn PartialReflect, &mut dyn PartialReflect) -> bool);
82
83
/// Creates a new [`DynamicMap`] from this map.
84
fn to_dynamic_map(&self) -> DynamicMap {
85
let mut map = DynamicMap::default();
86
map.set_represented_type(self.get_represented_type_info());
87
for (key, value) in self.iter() {
88
map.insert_boxed(key.to_dynamic(), value.to_dynamic());
89
}
90
map
91
}
92
93
/// Inserts a key-value pair into the map.
94
///
95
/// If the map did not have this key present, `None` is returned.
96
/// If the map did have this key present, the value is updated, and the old value is returned.
97
fn insert_boxed(
98
&mut self,
99
key: Box<dyn PartialReflect>,
100
value: Box<dyn PartialReflect>,
101
) -> Option<Box<dyn PartialReflect>>;
102
103
/// Removes an entry from the map.
104
///
105
/// If the map did not have this key present, `None` is returned.
106
/// If the map did have this key present, the removed value is returned.
107
fn remove(&mut self, key: &dyn PartialReflect) -> Option<Box<dyn PartialReflect>>;
108
109
/// Will return `None` if [`TypeInfo`] is not available.
110
fn get_represented_map_info(&self) -> Option<&'static MapInfo> {
111
self.get_represented_type_info()?.as_map().ok()
112
}
113
}
114
115
/// A container for compile-time map info.
116
#[derive(Clone, Debug)]
117
pub struct MapInfo {
118
ty: Type,
119
generics: Generics,
120
key_info: fn() -> Option<&'static TypeInfo>,
121
key_ty: Type,
122
value_info: fn() -> Option<&'static TypeInfo>,
123
value_ty: Type,
124
#[cfg(feature = "reflect_documentation")]
125
docs: Option<&'static str>,
126
}
127
128
impl MapInfo {
129
/// Create a new [`MapInfo`].
130
pub fn new<
131
TMap: Map + TypePath,
132
TKey: Reflect + MaybeTyped + TypePath,
133
TValue: Reflect + MaybeTyped + TypePath,
134
>() -> Self {
135
Self {
136
ty: Type::of::<TMap>(),
137
generics: Generics::new(),
138
key_info: TKey::maybe_type_info,
139
key_ty: Type::of::<TKey>(),
140
value_info: TValue::maybe_type_info,
141
value_ty: Type::of::<TValue>(),
142
#[cfg(feature = "reflect_documentation")]
143
docs: None,
144
}
145
}
146
147
/// Sets the docstring for this map.
148
#[cfg(feature = "reflect_documentation")]
149
pub fn with_docs(self, docs: Option<&'static str>) -> Self {
150
Self { docs, ..self }
151
}
152
153
impl_type_methods!(ty);
154
155
/// The [`TypeInfo`] of the key type.
156
///
157
/// Returns `None` if the key type does not contain static type information,
158
/// such as for dynamic types.
159
pub fn key_info(&self) -> Option<&'static TypeInfo> {
160
(self.key_info)()
161
}
162
163
/// The [type] of the key type.
164
///
165
/// [type]: Type
166
pub fn key_ty(&self) -> Type {
167
self.key_ty
168
}
169
170
/// The [`TypeInfo`] of the value type.
171
///
172
/// Returns `None` if the value type does not contain static type information,
173
/// such as for dynamic types.
174
pub fn value_info(&self) -> Option<&'static TypeInfo> {
175
(self.value_info)()
176
}
177
178
/// The [type] of the value type.
179
///
180
/// [type]: Type
181
pub fn value_ty(&self) -> Type {
182
self.value_ty
183
}
184
185
/// The docstring of this map, if any.
186
#[cfg(feature = "reflect_documentation")]
187
pub fn docs(&self) -> Option<&'static str> {
188
self.docs
189
}
190
191
impl_generic_info_methods!(generics);
192
}
193
194
/// Used to produce an error message when an attempt is made to hash
195
/// a [`PartialReflect`] value that does not support hashing.
196
#[macro_export]
197
macro_rules! hash_error {
198
( $key:expr ) => {{
199
let type_path = (*$key).reflect_type_path();
200
if !$key.is_dynamic() {
201
format!(
202
"the given key of type `{}` does not support hashing",
203
type_path
204
)
205
} else {
206
match (*$key).get_represented_type_info() {
207
// Handle dynamic types that do not represent a type (i.e a plain `DynamicStruct`):
208
None => format!("the dynamic type `{}` does not support hashing", type_path),
209
// Handle dynamic types that do represent a type (i.e. a `DynamicStruct` proxying `Foo`):
210
Some(s) => format!(
211
"the dynamic type `{}` (representing `{}`) does not support hashing",
212
type_path,
213
s.type_path()
214
),
215
}
216
}
217
}}
218
}
219
220
/// An unordered mapping between reflected values.
221
#[derive(Default)]
222
pub struct DynamicMap {
223
represented_type: Option<&'static TypeInfo>,
224
hash_table: HashTable<(Box<dyn PartialReflect>, Box<dyn PartialReflect>)>,
225
}
226
227
impl DynamicMap {
228
/// Sets the [type] to be represented by this `DynamicMap`.
229
///
230
/// # Panics
231
///
232
/// Panics if the given [type] is not a [`TypeInfo::Map`].
233
///
234
/// [type]: TypeInfo
235
pub fn set_represented_type(&mut self, represented_type: Option<&'static TypeInfo>) {
236
if let Some(represented_type) = represented_type {
237
assert!(
238
matches!(represented_type, TypeInfo::Map(_)),
239
"expected TypeInfo::Map but received: {represented_type:?}"
240
);
241
}
242
243
self.represented_type = represented_type;
244
}
245
246
/// Inserts a typed key-value pair into the map.
247
pub fn insert<K: PartialReflect, V: PartialReflect>(&mut self, key: K, value: V) {
248
self.insert_boxed(Box::new(key), Box::new(value));
249
}
250
251
fn internal_hash(value: &dyn PartialReflect) -> u64 {
252
value.reflect_hash().expect(&hash_error!(value))
253
}
254
255
fn internal_eq(
256
key: &dyn PartialReflect,
257
) -> impl FnMut(&(Box<dyn PartialReflect>, Box<dyn PartialReflect>)) -> bool + '_ {
258
|(other, _)| {
259
key
260
.reflect_partial_eq(&**other)
261
.expect("underlying type does not reflect `PartialEq` and hence doesn't support equality checks")
262
}
263
}
264
}
265
266
impl Map for DynamicMap {
267
fn get(&self, key: &dyn PartialReflect) -> Option<&dyn PartialReflect> {
268
self.hash_table
269
.find(Self::internal_hash(key), Self::internal_eq(key))
270
.map(|(_, value)| &**value)
271
}
272
273
fn get_mut(&mut self, key: &dyn PartialReflect) -> Option<&mut dyn PartialReflect> {
274
self.hash_table
275
.find_mut(Self::internal_hash(key), Self::internal_eq(key))
276
.map(|(_, value)| &mut **value)
277
}
278
279
fn len(&self) -> usize {
280
self.hash_table.len()
281
}
282
283
fn iter(&self) -> Box<dyn Iterator<Item = (&dyn PartialReflect, &dyn PartialReflect)> + '_> {
284
let iter = self.hash_table.iter().map(|(k, v)| (&**k, &**v));
285
Box::new(iter)
286
}
287
288
fn drain(&mut self) -> Vec<(Box<dyn PartialReflect>, Box<dyn PartialReflect>)> {
289
self.hash_table.drain().collect()
290
}
291
292
fn retain(&mut self, f: &mut dyn FnMut(&dyn PartialReflect, &mut dyn PartialReflect) -> bool) {
293
self.hash_table
294
.retain(move |(key, value)| f(&**key, &mut **value));
295
}
296
297
fn insert_boxed(
298
&mut self,
299
key: Box<dyn PartialReflect>,
300
value: Box<dyn PartialReflect>,
301
) -> Option<Box<dyn PartialReflect>> {
302
assert_eq!(
303
key.reflect_partial_eq(&*key),
304
Some(true),
305
"keys inserted in `Map`-like types are expected to reflect `PartialEq`"
306
);
307
308
let hash = Self::internal_hash(&*key);
309
let eq = Self::internal_eq(&*key);
310
match self.hash_table.find_mut(hash, eq) {
311
Some((_, old)) => Some(core::mem::replace(old, value)),
312
None => {
313
self.hash_table.insert_unique(
314
Self::internal_hash(key.as_ref()),
315
(key, value),
316
|(key, _)| Self::internal_hash(&**key),
317
);
318
None
319
}
320
}
321
}
322
323
fn remove(&mut self, key: &dyn PartialReflect) -> Option<Box<dyn PartialReflect>> {
324
let hash = Self::internal_hash(key);
325
let eq = Self::internal_eq(key);
326
match self.hash_table.find_entry(hash, eq) {
327
Ok(entry) => {
328
let ((_, old_value), _) = entry.remove();
329
Some(old_value)
330
}
331
Err(_) => None,
332
}
333
}
334
}
335
336
impl PartialReflect for DynamicMap {
337
#[inline]
338
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
339
self.represented_type
340
}
341
342
#[inline]
343
fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> {
344
self
345
}
346
347
#[inline]
348
fn as_partial_reflect(&self) -> &dyn PartialReflect {
349
self
350
}
351
352
#[inline]
353
fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect {
354
self
355
}
356
357
fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> {
358
Err(self)
359
}
360
361
fn try_as_reflect(&self) -> Option<&dyn Reflect> {
362
None
363
}
364
365
fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> {
366
None
367
}
368
369
fn apply(&mut self, value: &dyn PartialReflect) {
370
map_apply(self, value);
371
}
372
373
fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> {
374
map_try_apply(self, value)
375
}
376
377
fn reflect_kind(&self) -> ReflectKind {
378
ReflectKind::Map
379
}
380
381
fn reflect_ref(&self) -> ReflectRef<'_> {
382
ReflectRef::Map(self)
383
}
384
385
fn reflect_mut(&mut self) -> ReflectMut<'_> {
386
ReflectMut::Map(self)
387
}
388
389
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
390
ReflectOwned::Map(self)
391
}
392
393
fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
394
map_partial_eq(self, value)
395
}
396
397
fn debug(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
398
write!(f, "DynamicMap(")?;
399
map_debug(self, f)?;
400
write!(f, ")")
401
}
402
403
#[inline]
404
fn is_dynamic(&self) -> bool {
405
true
406
}
407
}
408
409
impl_type_path!((in bevy_reflect) DynamicMap);
410
411
impl Debug for DynamicMap {
412
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
413
self.debug(f)
414
}
415
}
416
417
impl FromIterator<(Box<dyn PartialReflect>, Box<dyn PartialReflect>)> for DynamicMap {
418
fn from_iter<I: IntoIterator<Item = (Box<dyn PartialReflect>, Box<dyn PartialReflect>)>>(
419
items: I,
420
) -> Self {
421
let mut map = Self::default();
422
for (key, value) in items.into_iter() {
423
map.insert_boxed(key, value);
424
}
425
map
426
}
427
}
428
429
impl<K: Reflect, V: Reflect> FromIterator<(K, V)> for DynamicMap {
430
fn from_iter<I: IntoIterator<Item = (K, V)>>(items: I) -> Self {
431
let mut map = Self::default();
432
for (key, value) in items.into_iter() {
433
map.insert(key, value);
434
}
435
map
436
}
437
}
438
439
impl IntoIterator for DynamicMap {
440
type Item = (Box<dyn PartialReflect>, Box<dyn PartialReflect>);
441
type IntoIter = bevy_platform::collections::hash_table::IntoIter<Self::Item>;
442
443
fn into_iter(self) -> Self::IntoIter {
444
self.hash_table.into_iter()
445
}
446
}
447
448
impl<'a> IntoIterator for &'a DynamicMap {
449
type Item = (&'a dyn PartialReflect, &'a dyn PartialReflect);
450
type IntoIter = core::iter::Map<
451
bevy_platform::collections::hash_table::Iter<
452
'a,
453
(Box<dyn PartialReflect>, Box<dyn PartialReflect>),
454
>,
455
fn(&'a (Box<dyn PartialReflect>, Box<dyn PartialReflect>)) -> Self::Item,
456
>;
457
458
fn into_iter(self) -> Self::IntoIter {
459
self.hash_table
460
.iter()
461
.map(|(k, v)| (k.as_ref(), v.as_ref()))
462
}
463
}
464
465
/// Compares a [`Map`] with a [`PartialReflect`] value.
466
///
467
/// Returns true if and only if all of the following are true:
468
/// - `b` is a map;
469
/// - `b` is the same length as `a`;
470
/// - For each key-value pair in `a`, `b` contains a value for the given key,
471
/// and [`PartialReflect::reflect_partial_eq`] returns `Some(true)` for the two values.
472
///
473
/// Returns [`None`] if the comparison couldn't even be performed.
474
#[inline]
475
pub fn map_partial_eq<M: Map + ?Sized>(a: &M, b: &dyn PartialReflect) -> Option<bool> {
476
let ReflectRef::Map(map) = b.reflect_ref() else {
477
return Some(false);
478
};
479
480
if a.len() != map.len() {
481
return Some(false);
482
}
483
484
for (key, value) in a.iter() {
485
if let Some(map_value) = map.get(key) {
486
let eq_result = value.reflect_partial_eq(map_value);
487
if let failed @ (Some(false) | None) = eq_result {
488
return failed;
489
}
490
} else {
491
return Some(false);
492
}
493
}
494
495
Some(true)
496
}
497
498
/// Lexicographically compares two [`Map`] values according to their iteration order
499
/// (suitable for ordered maps like `BTreeMap`).
500
///
501
/// For each entry pair `(a_k, a_v)` and `(b_k, b_v)` in the iteration order,
502
/// compare `a_k` to `b_k` using `reflect_partial_cmp`, returning the first
503
/// non-equal ordering. If keys are equal, compare values `a_v` and `b_v `similarly. If all
504
/// compared entries are equal, the shorter map is `Less` and longer is `Greater`.
505
///
506
/// Returns [`None`] if the comparison couldn't be performed (kinds mismatch or
507
/// an element comparison returns `None`).
508
#[inline]
509
pub fn map_partial_cmp<M: Map + ?Sized>(
510
a: &M,
511
b: &dyn PartialReflect,
512
) -> Option<::core::cmp::Ordering> {
513
let ReflectRef::Map(map) = b.reflect_ref() else {
514
return None;
515
};
516
517
let mut a_iter = a.iter();
518
let mut b_iter = map.iter();
519
520
loop {
521
match (a_iter.next(), b_iter.next()) {
522
(Some((a_k, a_v)), Some((b_k, b_v))) => {
523
match a_k.reflect_partial_cmp(b_k) {
524
None => return None,
525
Some(core::cmp::Ordering::Equal) => {}
526
Some(ord) => return Some(ord),
527
}
528
529
match a_v.reflect_partial_cmp(b_v) {
530
None => return None,
531
Some(core::cmp::Ordering::Equal) => {}
532
Some(ord) => return Some(ord),
533
}
534
}
535
(None, None) => return Some(::core::cmp::Ordering::Equal),
536
(None, Some(_)) => return Some(::core::cmp::Ordering::Less),
537
(Some(_), None) => return Some(::core::cmp::Ordering::Greater),
538
}
539
}
540
}
541
542
/// The default debug formatter for [`Map`] types.
543
///
544
/// # Example
545
/// ```
546
/// # use std::collections::HashMap;
547
/// use bevy_reflect::Reflect;
548
///
549
/// let mut my_map = HashMap::new();
550
/// my_map.insert(123, String::from("Hello"));
551
/// println!("{:#?}", &my_map as &dyn Reflect);
552
///
553
/// // Output:
554
///
555
/// // {
556
/// // 123: "Hello",
557
/// // }
558
/// ```
559
#[inline]
560
pub fn map_debug(dyn_map: &dyn Map, f: &mut Formatter<'_>) -> core::fmt::Result {
561
let mut debug = f.debug_map();
562
for (key, value) in dyn_map.iter() {
563
debug.entry(&key as &dyn Debug, &value as &dyn Debug);
564
}
565
debug.finish()
566
}
567
568
/// Applies the elements of reflected map `b` to the corresponding elements of map `a`.
569
///
570
/// If a key from `b` does not exist in `a`, the value is cloned and inserted.
571
/// If a key from `a` does not exist in `b`, the value is removed.
572
///
573
/// # Panics
574
///
575
/// This function panics if `b` is not a reflected map.
576
#[inline]
577
pub fn map_apply<M: Map>(a: &mut M, b: &dyn PartialReflect) {
578
if let Err(err) = map_try_apply(a, b) {
579
panic!("{err}");
580
}
581
}
582
583
/// Tries to apply the elements of reflected map `b` to the corresponding elements of map `a`
584
/// and returns a Result.
585
///
586
/// If a key from `b` does not exist in `a`, the value is cloned and inserted.
587
/// If a key from `a` does not exist in `b`, the value is removed.
588
///
589
/// # Errors
590
///
591
/// This function returns an [`ApplyError::MismatchedKinds`] if `b` is not a reflected map or if
592
/// applying elements to each other fails.
593
#[inline]
594
pub fn map_try_apply<M: Map>(a: &mut M, b: &dyn PartialReflect) -> Result<(), ApplyError> {
595
let map_value = b.reflect_ref().as_map()?;
596
597
for (key, b_value) in map_value.iter() {
598
if let Some(a_value) = a.get_mut(key) {
599
a_value.try_apply(b_value)?;
600
} else {
601
a.insert_boxed(key.to_dynamic(), b_value.to_dynamic());
602
}
603
}
604
a.retain(&mut |key, _| map_value.get(key).is_some());
605
606
Ok(())
607
}
608
609
#[cfg(test)]
610
mod tests {
611
612
use crate::PartialReflect;
613
614
use super::{DynamicMap, Map};
615
616
#[test]
617
fn remove() {
618
let mut map = DynamicMap::default();
619
map.insert(0, 0);
620
map.insert(1, 1);
621
622
assert_eq!(map.remove(&0).unwrap().try_downcast_ref(), Some(&0));
623
assert!(map.get(&0).is_none());
624
assert_eq!(map.get(&1).unwrap().try_downcast_ref(), Some(&1));
625
626
assert_eq!(map.remove(&1).unwrap().try_downcast_ref(), Some(&1));
627
assert!(map.get(&1).is_none());
628
629
assert!(map.remove(&1).is_none());
630
assert!(map.get(&1).is_none());
631
}
632
633
#[test]
634
fn apply() {
635
let mut map_a = DynamicMap::default();
636
map_a.insert(0, 0);
637
map_a.insert(1, 1);
638
639
let mut map_b = DynamicMap::default();
640
map_b.insert(10, 10);
641
map_b.insert(1, 5);
642
643
map_a.apply(&map_b);
644
645
assert!(map_a.get(&0).is_none());
646
assert_eq!(map_a.get(&1).unwrap().try_downcast_ref(), Some(&5));
647
assert_eq!(map_a.get(&10).unwrap().try_downcast_ref(), Some(&10));
648
}
649
}
650
651