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}