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    /// This method tests for `self` and `other` values to be equal.
16    ///
17    /// Implementers should avoid returning `true` when the underlying types are
18    /// not the same.
19    fn dyn_eq(&self, other: &dyn DynEq) -> bool;
20}
21
22// Tests that this trait is dyn-compatible
23const _: Option<Box<dyn DynEq>> = None;
24
25impl<T> DynEq for T
26where
27    T: Any + Eq,
28{
29    fn dyn_eq(&self, other: &dyn DynEq) -> bool {
30        if let Some(other) = (other as &dyn 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    /// Feeds this value into the given [`Hasher`].
41    fn dyn_hash(&self, state: &mut dyn Hasher);
42}
43
44// Tests that this trait is dyn-compatible
45const _: Option<Box<dyn DynHash>> = None;
46
47impl<T> DynHash for T
48where
49    T: DynEq + Hash,
50{
51    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
52        T::hash(self, &mut state);
53        self.type_id().hash(&mut state);
54    }
55}
56
57/// Macro to define a new label trait
58///
59/// # Example
60///
61/// ```
62/// # use bevy_ecs::define_label;
63/// define_label!(
64///     /// Documentation of label trait
65///     MyNewLabelTrait,
66///     MY_NEW_LABEL_TRAIT_INTERNER
67/// );
68///
69/// define_label!(
70///     /// Documentation of another label trait
71///     MyNewExtendedLabelTrait,
72///     MY_NEW_EXTENDED_LABEL_TRAIT_INTERNER,
73///     extra_methods: {
74///         // Extra methods for the trait can be defined here
75///         fn additional_method(&self) -> i32;
76///     },
77///     extra_methods_impl: {
78///         // Implementation of the extra methods for Interned<dyn MyNewExtendedLabelTrait>
79///         fn additional_method(&self) -> i32 {
80///             0
81///         }
82///     }
83/// );
84/// ```
85#[macro_export]
86macro_rules! define_label {
87    (
88        $(#[$label_attr:meta])*
89        $label_trait_name:ident,
90        $interner_name:ident
91    ) => {
92        $crate::define_label!(
93            $(#[$label_attr])*
94            $label_trait_name,
95            $interner_name,
96            extra_methods: {},
97            extra_methods_impl: {}
98        );
99    };
100    (
101        $(#[$label_attr:meta])*
102        $label_trait_name:ident,
103        $interner_name:ident,
104        extra_methods: { $($trait_extra_methods:tt)* },
105        extra_methods_impl: { $($interned_extra_methods_impl:tt)* }
106    ) => {
107
108        $(#[$label_attr])*
109        pub trait $label_trait_name: Send + Sync + ::core::fmt::Debug + $crate::label::DynEq + $crate::label::DynHash {
110
111            $($trait_extra_methods)*
112
113            /// Clones this `
114            #[doc = stringify!($label_trait_name)]
115            ///`.
116            fn dyn_clone(&self) -> $crate::label::Box<dyn $label_trait_name>;
117
118            /// Returns an [`Interned`] value corresponding to `self`.
119            fn intern(&self) -> $crate::intern::Interned<dyn $label_trait_name>
120            where Self: Sized {
121                $interner_name.intern(self)
122            }
123        }
124
125        #[diagnostic::do_not_recommend]
126        impl $label_trait_name for $crate::intern::Interned<dyn $label_trait_name> {
127
128            $($interned_extra_methods_impl)*
129
130            fn dyn_clone(&self) -> $crate::label::Box<dyn $label_trait_name> {
131                (**self).dyn_clone()
132            }
133
134            fn intern(&self) -> Self {
135                *self
136            }
137        }
138
139        impl PartialEq for dyn $label_trait_name {
140            fn eq(&self, other: &Self) -> bool {
141                self.dyn_eq(other)
142            }
143        }
144
145        impl Eq for dyn $label_trait_name {}
146
147        impl ::core::hash::Hash for dyn $label_trait_name {
148            fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
149                self.dyn_hash(state);
150            }
151        }
152
153        impl $crate::intern::Internable for dyn $label_trait_name {
154            fn leak(&self) -> &'static Self {
155                $crate::label::Box::leak(self.dyn_clone())
156            }
157
158            fn ref_eq(&self, other: &Self) -> bool {
159                use ::core::ptr;
160
161                // Test that both the type id and pointer address are equivalent.
162                self.type_id() == other.type_id()
163                    && ptr::addr_eq(ptr::from_ref::<Self>(self), ptr::from_ref::<Self>(other))
164            }
165
166            fn ref_hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
167                use ::core::{hash::Hash, ptr};
168
169                // Hash the type id...
170                self.type_id().hash(state);
171
172                // ...and the pointer address.
173                // Cast to a unit `()` first to discard any pointer metadata.
174                ptr::from_ref::<Self>(self).cast::<()>().hash(state);
175            }
176        }
177
178        static $interner_name: $crate::intern::Interner<dyn $label_trait_name> =
179            $crate::intern::Interner::new();
180    };
181}