bevy_ecs/
label.rs

1//! Traits used by label implementations
2
3use core::{
4    any::Any,
5    hash::{Hash, Hasher},
6};
7
8// Re-exported for use within `define_label!`
9#[doc(hidden)]
10pub use alloc::boxed::Box;
11
12/// An object safe version of [`Eq`]. This trait is automatically implemented
13/// for any `'static` type that implements `Eq`.
14pub trait DynEq: Any {
15    /// Casts the type to `dyn Any`.
16    fn as_any(&self) -> &dyn Any;
17
18    /// This method tests for `self` and `other` values to be equal.
19    ///
20    /// Implementers should avoid returning `true` when the underlying types are
21    /// not the same.
22    fn dyn_eq(&self, other: &dyn DynEq) -> bool;
23}
24
25impl<T> DynEq for T
26where
27    T: Any + Eq,
28{
29    fn as_any(&self) -> &dyn Any {
30        self
31    }
32
33    fn dyn_eq(&self, other: &dyn DynEq) -> bool {
34        if let Some(other) = other.as_any().downcast_ref::<T>() {
35            return self == other;
36        }
37        false
38    }
39}
40
41/// An object safe version of [`Hash`]. This trait is automatically implemented
42/// for any `'static` type that implements `Hash`.
43pub trait DynHash: DynEq {
44    /// Casts the type to `dyn Any`.
45    fn as_dyn_eq(&self) -> &dyn DynEq;
46
47    /// Feeds this value into the given [`Hasher`].
48    fn dyn_hash(&self, state: &mut dyn Hasher);
49}
50
51impl<T> DynHash for T
52where
53    T: DynEq + Hash,
54{
55    fn as_dyn_eq(&self) -> &dyn DynEq {
56        self
57    }
58
59    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
60        T::hash(self, &mut state);
61        self.type_id().hash(&mut state);
62    }
63}
64
65/// Macro to define a new label trait
66///
67/// # Example
68///
69/// ```
70/// # use bevy_ecs::define_label;
71/// define_label!(
72///     /// Documentation of label trait
73///     MyNewLabelTrait,
74///     MY_NEW_LABEL_TRAIT_INTERNER
75/// );
76///
77/// define_label!(
78///     /// Documentation of another label trait
79///     MyNewExtendedLabelTrait,
80///     MY_NEW_EXTENDED_LABEL_TRAIT_INTERNER,
81///     extra_methods: {
82///         // Extra methods for the trait can be defined here
83///         fn additional_method(&self) -> i32;
84///     },
85///     extra_methods_impl: {
86///         // Implementation of the extra methods for Interned<dyn MyNewExtendedLabelTrait>
87///         fn additional_method(&self) -> i32 {
88///             0
89///         }
90///     }
91/// );
92/// ```
93#[macro_export]
94macro_rules! define_label {
95    (
96        $(#[$label_attr:meta])*
97        $label_trait_name:ident,
98        $interner_name:ident
99    ) => {
100        $crate::define_label!(
101            $(#[$label_attr])*
102            $label_trait_name,
103            $interner_name,
104            extra_methods: {},
105            extra_methods_impl: {}
106        );
107    };
108    (
109        $(#[$label_attr:meta])*
110        $label_trait_name:ident,
111        $interner_name:ident,
112        extra_methods: { $($trait_extra_methods:tt)* },
113        extra_methods_impl: { $($interned_extra_methods_impl:tt)* }
114    ) => {
115
116        $(#[$label_attr])*
117        pub trait $label_trait_name: 'static + Send + Sync + ::core::fmt::Debug {
118
119            $($trait_extra_methods)*
120
121            /// Clones this `
122            #[doc = stringify!($label_trait_name)]
123            ///`.
124            fn dyn_clone(&self) -> $crate::label::Box<dyn $label_trait_name>;
125
126            /// Casts this value to a form where it can be compared with other type-erased values.
127            fn as_dyn_eq(&self) -> &dyn $crate::label::DynEq;
128
129            /// Feeds this value into the given [`Hasher`].
130            fn dyn_hash(&self, state: &mut dyn ::core::hash::Hasher);
131
132            /// Returns an [`Interned`] value corresponding to `self`.
133            fn intern(&self) -> $crate::intern::Interned<dyn $label_trait_name>
134            where Self: Sized {
135                $interner_name.intern(self)
136            }
137        }
138
139        impl $label_trait_name for $crate::intern::Interned<dyn $label_trait_name> {
140
141            $($interned_extra_methods_impl)*
142
143            fn dyn_clone(&self) -> $crate::label::Box<dyn $label_trait_name> {
144                (**self).dyn_clone()
145            }
146
147            /// Casts this value to a form where it can be compared with other type-erased values.
148            fn as_dyn_eq(&self) -> &dyn $crate::label::DynEq {
149                (**self).as_dyn_eq()
150            }
151
152            fn dyn_hash(&self, state: &mut dyn ::core::hash::Hasher) {
153                (**self).dyn_hash(state);
154            }
155
156            fn intern(&self) -> Self {
157                *self
158            }
159        }
160
161        impl PartialEq for dyn $label_trait_name {
162            fn eq(&self, other: &Self) -> bool {
163                self.as_dyn_eq().dyn_eq(other.as_dyn_eq())
164            }
165        }
166
167        impl Eq for dyn $label_trait_name {}
168
169        impl ::core::hash::Hash for dyn $label_trait_name {
170            fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
171                self.dyn_hash(state);
172            }
173        }
174
175        impl $crate::intern::Internable for dyn $label_trait_name {
176            fn leak(&self) -> &'static Self {
177                Box::leak(self.dyn_clone())
178            }
179
180            fn ref_eq(&self, other: &Self) -> bool {
181                use ::core::ptr;
182
183                // Test that both the type id and pointer address are equivalent.
184                self.as_dyn_eq().type_id() == other.as_dyn_eq().type_id()
185                    && ptr::addr_eq(ptr::from_ref::<Self>(self), ptr::from_ref::<Self>(other))
186            }
187
188            fn ref_hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
189                use ::core::{hash::Hash, ptr};
190
191                // Hash the type id...
192                self.as_dyn_eq().type_id().hash(state);
193
194                // ...and the pointer address.
195                // Cast to a unit `()` first to discard any pointer metadata.
196                ptr::from_ref::<Self>(self).cast::<()>().hash(state);
197            }
198        }
199
200        static $interner_name: $crate::intern::Interner<dyn $label_trait_name> =
201            $crate::intern::Interner::new();
202    };
203}
204
205#[cfg(test)]
206mod tests {
207    use super::{DynEq, DynHash};
208    use bevy_utils::assert_object_safe;
209
210    #[test]
211    fn dyn_eq_object_safe() {
212        assert_object_safe::<dyn DynEq>();
213    }
214
215    #[test]
216    fn dyn_hash_object_safe() {
217        assert_object_safe::<dyn DynHash>();
218    }
219}