bevy_ecs/
label.rs

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