bevy_reflect/
utility.rs

1//! Helpers for working with Bevy reflection.
2
3use crate::TypeInfo;
4use alloc::boxed::Box;
5use bevy_platform::{
6    hash::{DefaultHasher, FixedHasher, NoOpHash},
7    sync::{OnceLock, PoisonError, RwLock},
8};
9use bevy_utils::TypeIdMap;
10use core::{
11    any::{Any, TypeId},
12    hash::BuildHasher,
13};
14
15/// A type that can be stored in a ([`Non`])[`GenericTypeCell`].
16///
17/// [`Non`]: NonGenericTypeCell
18pub trait TypedProperty: sealed::Sealed {
19    /// The type of the value stored in [`GenericTypeCell`].
20    type Stored: 'static;
21}
22
23/// Used to store a [`String`] in a [`GenericTypePathCell`] as part of a [`TypePath`] implementation.
24///
25/// [`TypePath`]: crate::TypePath
26/// [`String`]: alloc::string::String
27pub struct TypePathComponent;
28
29mod sealed {
30    use super::{TypeInfo, TypePathComponent, TypedProperty};
31    use alloc::string::String;
32
33    pub trait Sealed {}
34
35    impl Sealed for TypeInfo {}
36    impl Sealed for TypePathComponent {}
37
38    impl TypedProperty for TypeInfo {
39        type Stored = Self;
40    }
41
42    impl TypedProperty for TypePathComponent {
43        type Stored = String;
44    }
45}
46
47/// A container for [`TypeInfo`] over non-generic types, allowing instances to be stored statically.
48///
49/// This is specifically meant for use with _non_-generic types. If your type _is_ generic,
50/// then use [`GenericTypeCell`] instead. Otherwise, it will not take into account all
51/// monomorphizations of your type.
52///
53/// Non-generic [`TypePath`]s should be trivially generated with string literals and [`concat!`].
54///
55/// ## Example
56///
57/// ```
58/// # use core::any::Any;
59/// # use bevy_reflect::{DynamicTypePath, NamedField, PartialReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, Typed, TypeInfo, TypePath, ApplyError};
60/// use bevy_reflect::utility::NonGenericTypeInfoCell;
61///
62/// struct Foo {
63///     bar: i32
64/// }
65///
66/// impl Typed for Foo {
67///     fn type_info() -> &'static TypeInfo {
68///         static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
69///         CELL.get_or_set(|| {
70///             let fields = [NamedField::new::<i32>("bar")];
71///             let info = StructInfo::new::<Self>(&fields);
72///             TypeInfo::Struct(info)
73///         })
74///     }
75/// }
76/// # impl TypePath for Foo {
77/// #     fn type_path() -> &'static str { todo!() }
78/// #     fn short_type_path() -> &'static str { todo!() }
79/// # }
80/// # impl PartialReflect for Foo {
81/// #     fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() }
82/// #     fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> { todo!() }
83/// #     fn as_partial_reflect(&self) -> &dyn PartialReflect { todo!() }
84/// #     fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect { todo!() }
85/// #     fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> { todo!() }
86/// #     fn try_as_reflect(&self) -> Option<&dyn Reflect> { todo!() }
87/// #     fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> { todo!() }
88/// #     fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> { todo!() }
89/// #     fn reflect_ref(&self) -> ReflectRef<'_> { todo!() }
90/// #     fn reflect_mut(&mut self) -> ReflectMut<'_> { todo!() }
91/// #     fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
92/// # }
93/// # impl Reflect for Foo {
94/// #     fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
95/// #     fn as_any(&self) -> &dyn Any { todo!() }
96/// #     fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }
97/// #     fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> { todo!() }
98/// #     fn as_reflect(&self) -> &dyn Reflect { todo!() }
99/// #     fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() }
100/// #     fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { todo!() }
101/// # }
102/// ```
103///
104/// [`TypePath`]: crate::TypePath
105pub struct NonGenericTypeCell<T: TypedProperty>(OnceLock<T::Stored>);
106
107/// See [`NonGenericTypeCell`].
108pub type NonGenericTypeInfoCell = NonGenericTypeCell<TypeInfo>;
109
110impl<T: TypedProperty> NonGenericTypeCell<T> {
111    /// Initialize a [`NonGenericTypeCell`] for non-generic types.
112    pub const fn new() -> Self {
113        Self(OnceLock::new())
114    }
115
116    /// Returns a reference to the [`TypedProperty`] stored in the cell.
117    ///
118    /// If there is no entry found, a new one will be generated from the given function.
119    pub fn get_or_set<F>(&self, f: F) -> &T::Stored
120    where
121        F: FnOnce() -> T::Stored,
122    {
123        self.0.get_or_init(f)
124    }
125}
126
127impl<T: TypedProperty> Default for NonGenericTypeCell<T> {
128    fn default() -> Self {
129        Self::new()
130    }
131}
132
133/// A container for [`TypedProperty`] over generic types, allowing instances to be stored statically.
134///
135/// This is specifically meant for use with generic types. If your type isn't generic,
136/// then use [`NonGenericTypeCell`] instead as it should be much more performant.
137///
138/// `#[derive(TypePath)]` and [`impl_type_path`] should always be used over [`GenericTypePathCell`]
139/// where possible.
140///
141/// ## Examples
142///
143/// Implementing [`TypeInfo`] with generics.
144///
145/// ```
146/// # use core::any::Any;
147/// # use bevy_reflect::{DynamicTypePath, PartialReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, TypePath, UnnamedField, ApplyError, Generics, TypeParamInfo};
148/// use bevy_reflect::utility::GenericTypeInfoCell;
149///
150/// struct Foo<T>(T);
151///
152/// impl<T: Reflect + Typed + TypePath> Typed for Foo<T> {
153///     fn type_info() -> &'static TypeInfo {
154///         static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
155///         CELL.get_or_insert::<Self, _>(|| {
156///             let fields = [UnnamedField::new::<T>(0)];
157///             let info = TupleStructInfo::new::<Self>(&fields)
158///                 .with_generics(Generics::from_iter([TypeParamInfo::new::<T>("T")]));
159///             TypeInfo::TupleStruct(info)
160///         })
161///     }
162/// }
163/// # impl<T: TypePath> TypePath for Foo<T> {
164/// #     fn type_path() -> &'static str { todo!() }
165/// #     fn short_type_path() -> &'static str { todo!() }
166/// # }
167/// # impl<T: PartialReflect + TypePath> PartialReflect for Foo<T> {
168/// #     fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() }
169/// #     fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> { todo!() }
170/// #     fn as_partial_reflect(&self) -> &dyn PartialReflect { todo!() }
171/// #     fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect { todo!() }
172/// #     fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> { todo!() }
173/// #     fn try_as_reflect(&self) -> Option<&dyn Reflect> { todo!() }
174/// #     fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> { todo!() }
175/// #     fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> { todo!() }
176/// #     fn reflect_ref(&self) -> ReflectRef<'_> { todo!() }
177/// #     fn reflect_mut(&mut self) -> ReflectMut<'_> { todo!() }
178/// #     fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
179/// # }
180/// # impl<T: Reflect + Typed + TypePath> Reflect for Foo<T> {
181/// #     fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
182/// #     fn as_any(&self) -> &dyn Any { todo!() }
183/// #     fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }
184/// #     fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> { todo!() }
185/// #     fn as_reflect(&self) -> &dyn Reflect { todo!() }
186/// #     fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() }
187/// #     fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { todo!() }
188/// # }
189/// ```
190///
191///  Implementing [`TypePath`] with generics.
192///
193/// ```
194/// # use core::any::Any;
195/// # use bevy_reflect::TypePath;
196/// use bevy_reflect::utility::GenericTypePathCell;
197///
198/// struct Foo<T>(T);
199///
200/// impl<T: TypePath> TypePath for Foo<T> {
201///     fn type_path() -> &'static str {
202///         static CELL: GenericTypePathCell = GenericTypePathCell::new();
203///         CELL.get_or_insert::<Self, _>(|| format!("my_crate::foo::Foo<{}>", T::type_path()))
204///     }
205///
206///     fn short_type_path() -> &'static str {
207///         static CELL: GenericTypePathCell = GenericTypePathCell::new();
208///         CELL.get_or_insert::<Self, _>(|| format!("Foo<{}>", T::short_type_path()))
209///     }
210///
211///     fn type_ident() -> Option<&'static str> {
212///         Some("Foo")
213///     }
214///
215///     fn module_path() -> Option<&'static str> {
216///         Some("my_crate::foo")
217///     }
218///
219///     fn crate_name() -> Option<&'static str> {
220///         Some("my_crate")
221///     }
222/// }
223/// ```
224/// [`impl_type_path`]: crate::impl_type_path
225/// [`TypePath`]: crate::TypePath
226pub struct GenericTypeCell<T: TypedProperty>(RwLock<TypeIdMap<&'static T::Stored>>);
227
228/// See [`GenericTypeCell`].
229pub type GenericTypeInfoCell = GenericTypeCell<TypeInfo>;
230/// See [`GenericTypeCell`].
231pub type GenericTypePathCell = GenericTypeCell<TypePathComponent>;
232
233impl<T: TypedProperty> GenericTypeCell<T> {
234    /// Initialize a [`GenericTypeCell`] for generic types.
235    pub const fn new() -> Self {
236        Self(RwLock::new(TypeIdMap::with_hasher(NoOpHash)))
237    }
238
239    /// Returns a reference to the [`TypedProperty`] stored in the cell.
240    ///
241    /// This method will then return the correct [`TypedProperty`] reference for the given type `T`.
242    /// If there is no entry found, a new one will be generated from the given function.
243    pub fn get_or_insert<G, F>(&self, f: F) -> &T::Stored
244    where
245        G: Any + ?Sized,
246        F: FnOnce() -> T::Stored,
247    {
248        self.get_or_insert_by_type_id(TypeId::of::<G>(), f)
249    }
250
251    /// Returns a reference to the [`TypedProperty`] stored in the cell, if any.
252    ///
253    /// This method will then return the correct [`TypedProperty`] reference for the given type `T`.
254    fn get_by_type_id(&self, type_id: TypeId) -> Option<&T::Stored> {
255        self.0
256            .read()
257            .unwrap_or_else(PoisonError::into_inner)
258            .get(&type_id)
259            .copied()
260    }
261
262    /// Returns a reference to the [`TypedProperty`] stored in the cell.
263    ///
264    /// This method will then return the correct [`TypedProperty`] reference for the given type `T`.
265    /// If there is no entry found, a new one will be generated from the given function.
266    fn get_or_insert_by_type_id<F>(&self, type_id: TypeId, f: F) -> &T::Stored
267    where
268        F: FnOnce() -> T::Stored,
269    {
270        match self.get_by_type_id(type_id) {
271            Some(info) => info,
272            None => self.insert_by_type_id(type_id, f()),
273        }
274    }
275
276    fn insert_by_type_id(&self, type_id: TypeId, value: T::Stored) -> &T::Stored {
277        let mut write_lock = self.0.write().unwrap_or_else(PoisonError::into_inner);
278
279        write_lock
280            .entry(type_id)
281            .insert({
282                // We leak here in order to obtain a `&'static` reference.
283                // Otherwise, we won't be able to return a reference due to the `RwLock`.
284                // This should be okay, though, since we expect it to remain statically
285                // available over the course of the application.
286                Box::leak(Box::new(value))
287            })
288            .get()
289    }
290}
291
292impl<T: TypedProperty> Default for GenericTypeCell<T> {
293    fn default() -> Self {
294        Self::new()
295    }
296}
297
298/// Deterministic fixed state hasher to be used by implementors of [`Reflect::reflect_hash`].
299///
300/// Hashes should be deterministic across processes so hashes can be used as
301/// checksums for saved scenes, rollback snapshots etc. This function returns
302/// such a hasher.
303///
304/// [`Reflect::reflect_hash`]: crate::Reflect
305#[inline]
306pub fn reflect_hasher() -> DefaultHasher<'static> {
307    FixedHasher.build_hasher()
308}