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