Skip to main content

bevy_reflect/
map.rs

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