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