bevy_reflect/
map.rs

1use core::fmt::{Debug, Formatter};
2
3use bevy_platform::collections::HashTable;
4use bevy_reflect_derive::impl_type_path;
5
6use 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};
11use 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
48pub 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)]
114pub 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
125impl 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]
194macro_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)]
219pub struct DynamicMap {
220    represented_type: Option<&'static TypeInfo>,
221    hash_table: HashTable<(Box<dyn PartialReflect>, Box<dyn PartialReflect>)>,
222}
223
224impl 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
263impl 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
333impl 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
406impl_type_path!((in bevy_reflect) DynamicMap);
407
408impl Debug for DynamicMap {
409    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
410        self.debug(f)
411    }
412}
413
414impl 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
426impl<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
436impl 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
445impl<'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]
472pub 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]
513pub 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]
530pub 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]
547pub 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)]
563mod 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}