bevy_reflect/type_info.rs
1use crate::{
2 ArrayInfo, DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, DynamicTuple,
3 DynamicTupleStruct, EnumInfo, Generics, ListInfo, MapInfo, PartialReflect, Reflect,
4 ReflectKind, SetInfo, StructInfo, TupleInfo, TupleStructInfo, TypePath, TypePathTable,
5};
6use core::{
7 any::{Any, TypeId},
8 fmt::{Debug, Formatter},
9 hash::Hash,
10};
11use thiserror::Error;
12
13/// A static accessor to compile-time type information.
14///
15/// This trait is automatically implemented by the [`#[derive(Reflect)]`](derive@crate::Reflect) macro
16/// and allows type information to be processed without an instance of that type.
17///
18/// If you need to use this trait as a generic bound along with other reflection traits,
19/// for your convenience, consider using [`Reflectable`] instead.
20///
21/// # Implementing
22///
23/// While it is recommended to leave implementing this trait to the `#[derive(Reflect)]` macro,
24/// it is possible to implement this trait manually. If a manual implementation is needed,
25/// you _must_ ensure that the information you provide is correct, otherwise various systems that
26/// rely on this trait may fail in unexpected ways.
27///
28/// Implementors may have difficulty in generating a reference to [`TypeInfo`] with a static
29/// lifetime. Luckily, this crate comes with some [utility] structs, to make generating these
30/// statics much simpler.
31///
32/// # Example
33///
34/// ```
35/// # use core::any::Any;
36/// # use bevy_reflect::{DynamicTypePath, NamedField, PartialReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, TypeInfo, TypePath, OpaqueInfo, ApplyError};
37/// # use bevy_reflect::utility::NonGenericTypeInfoCell;
38/// use bevy_reflect::Typed;
39///
40/// struct MyStruct {
41/// foo: usize,
42/// bar: (f32, f32)
43/// }
44///
45/// impl Typed for MyStruct {
46/// fn type_info() -> &'static TypeInfo {
47/// static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
48/// CELL.get_or_set(|| {
49/// let fields = [
50/// NamedField::new::<usize >("foo"),
51/// NamedField::new::<(f32, f32) >("bar"),
52/// ];
53/// let info = StructInfo::new::<Self>(&fields);
54/// TypeInfo::Struct(info)
55/// })
56/// }
57/// }
58///
59/// # impl TypePath for MyStruct {
60/// # fn type_path() -> &'static str { todo!() }
61/// # fn short_type_path() -> &'static str { todo!() }
62/// # }
63/// # impl PartialReflect for MyStruct {
64/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() }
65/// # fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> { todo!() }
66/// # fn as_partial_reflect(&self) -> &dyn PartialReflect { todo!() }
67/// # fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect { todo!() }
68/// # fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> { todo!() }
69/// # fn try_as_reflect(&self) -> Option<&dyn Reflect> { todo!() }
70/// # fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> { todo!() }
71/// # fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> { todo!() }
72/// # fn reflect_ref(&self) -> ReflectRef { todo!() }
73/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() }
74/// # fn reflect_owned(self: Box<Self>) -> ReflectOwned { todo!() }
75/// # }
76/// # impl Reflect for MyStruct {
77/// # fn into_any(self: Box<Self>) -> Box<dyn Any> { todo!() }
78/// # fn as_any(&self) -> &dyn Any { todo!() }
79/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() }
80/// # fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> { todo!() }
81/// # fn as_reflect(&self) -> &dyn Reflect { todo!() }
82/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() }
83/// # fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> { todo!() }
84/// # }
85/// ```
86///
87/// [`Reflectable`]: crate::Reflectable
88/// [utility]: crate::utility
89#[diagnostic::on_unimplemented(
90 message = "`{Self}` does not implement `Typed` so cannot provide static type information",
91 note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
92)]
93pub trait Typed: Reflect + TypePath {
94 /// Returns the compile-time [info] for the underlying type.
95 ///
96 /// [info]: TypeInfo
97 fn type_info() -> &'static TypeInfo;
98}
99
100/// A wrapper trait around [`Typed`].
101///
102/// This trait is used to provide a way to get compile-time type information for types that
103/// do implement `Typed` while also allowing for types that do not implement `Typed` to be used.
104/// It's used instead of `Typed` directly to avoid making dynamic types also
105/// implement `Typed` in order to be used as active fields.
106///
107/// This trait has a blanket implementation for all types that implement `Typed`
108/// and manual implementations for all dynamic types (which simply return `None`).
109#[doc(hidden)]
110#[diagnostic::on_unimplemented(
111 message = "`{Self}` does not implement `Typed` so cannot provide static type information",
112 note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
113)]
114pub trait MaybeTyped: PartialReflect {
115 /// Returns the compile-time [info] for the underlying type, if it exists.
116 ///
117 /// [info]: TypeInfo
118 fn maybe_type_info() -> Option<&'static TypeInfo> {
119 None
120 }
121}
122
123impl<T: Typed> MaybeTyped for T {
124 fn maybe_type_info() -> Option<&'static TypeInfo> {
125 Some(T::type_info())
126 }
127}
128
129impl MaybeTyped for DynamicEnum {}
130
131impl MaybeTyped for DynamicTupleStruct {}
132
133impl MaybeTyped for DynamicStruct {}
134
135impl MaybeTyped for DynamicMap {}
136
137impl MaybeTyped for DynamicList {}
138
139impl MaybeTyped for DynamicArray {}
140
141impl MaybeTyped for DynamicTuple {}
142
143/// Dynamic dispatch for [`Typed`].
144///
145/// Since this is a supertrait of [`Reflect`] its methods can be called on a `dyn Reflect`.
146///
147/// [`Reflect`]: crate::Reflect
148#[diagnostic::on_unimplemented(
149 message = "`{Self}` can not provide dynamic type information through reflection",
150 note = "consider annotating `{Self}` with `#[derive(Reflect)]`"
151)]
152pub trait DynamicTyped {
153 /// See [`Typed::type_info`].
154 fn reflect_type_info(&self) -> &'static TypeInfo;
155}
156
157impl<T: Typed> DynamicTyped for T {
158 #[inline]
159 fn reflect_type_info(&self) -> &'static TypeInfo {
160 Self::type_info()
161 }
162}
163
164/// A [`TypeInfo`]-specific error.
165#[derive(Debug, Error)]
166pub enum TypeInfoError {
167 /// Caused when a type was expected to be of a certain [kind], but was not.
168 ///
169 /// [kind]: ReflectKind
170 #[error("kind mismatch: expected {expected:?}, received {received:?}")]
171 KindMismatch {
172 expected: ReflectKind,
173 received: ReflectKind,
174 },
175}
176
177/// Compile-time type information for various reflected types.
178///
179/// Generally, for any given type, this value can be retrieved in one of four ways:
180///
181/// 1. [`Typed::type_info`]
182/// 2. [`DynamicTyped::reflect_type_info`]
183/// 3. [`PartialReflect::get_represented_type_info`]
184/// 4. [`TypeRegistry::get_type_info`]
185///
186/// Each return a static reference to [`TypeInfo`], but they all have their own use cases.
187/// For example, if you know the type at compile time, [`Typed::type_info`] is probably
188/// the simplest. If you have a `dyn Reflect` you can use [`DynamicTyped::reflect_type_info`].
189/// If all you have is a `dyn PartialReflect`, you'll probably want [`PartialReflect::get_represented_type_info`].
190/// Lastly, if all you have is a [`TypeId`] or [type path], you will need to go through
191/// [`TypeRegistry::get_type_info`].
192///
193/// You may also opt to use [`TypeRegistry::get_type_info`] in place of the other methods simply because
194/// it can be more performant. This is because those other methods may require attaining a lock on
195/// the static [`TypeInfo`], while the registry simply checks a map.
196///
197/// [`TypeRegistry::get_type_info`]: crate::TypeRegistry::get_type_info
198/// [`PartialReflect::get_represented_type_info`]: crate::PartialReflect::get_represented_type_info
199/// [type path]: TypePath::type_path
200#[derive(Debug, Clone)]
201pub enum TypeInfo {
202 Struct(StructInfo),
203 TupleStruct(TupleStructInfo),
204 Tuple(TupleInfo),
205 List(ListInfo),
206 Array(ArrayInfo),
207 Map(MapInfo),
208 Set(SetInfo),
209 Enum(EnumInfo),
210 Opaque(OpaqueInfo),
211}
212
213impl TypeInfo {
214 /// The underlying Rust [type].
215 ///
216 /// [type]: Type
217 pub fn ty(&self) -> &Type {
218 match self {
219 Self::Struct(info) => info.ty(),
220 Self::TupleStruct(info) => info.ty(),
221 Self::Tuple(info) => info.ty(),
222 Self::List(info) => info.ty(),
223 Self::Array(info) => info.ty(),
224 Self::Map(info) => info.ty(),
225 Self::Set(info) => info.ty(),
226 Self::Enum(info) => info.ty(),
227 Self::Opaque(info) => info.ty(),
228 }
229 }
230
231 /// The [`TypeId`] of the underlying type.
232 #[inline]
233 pub fn type_id(&self) -> TypeId {
234 self.ty().id()
235 }
236
237 /// A representation of the type path of the underlying type.
238 ///
239 /// Provides dynamic access to all methods on [`TypePath`].
240 pub fn type_path_table(&self) -> &TypePathTable {
241 self.ty().type_path_table()
242 }
243
244 /// The [stable, full type path] of the underlying type.
245 ///
246 /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
247 ///
248 /// [stable, full type path]: TypePath
249 /// [`type_path_table`]: Self::type_path_table
250 pub fn type_path(&self) -> &'static str {
251 self.ty().path()
252 }
253
254 /// Check if the given type matches this one.
255 ///
256 /// This only compares the [`TypeId`] of the types
257 /// and does not verify they share the same [`TypePath`]
258 /// (though it implies they do).
259 pub fn is<T: Any>(&self) -> bool {
260 self.ty().is::<T>()
261 }
262
263 /// The docstring of the underlying type, if any.
264 #[cfg(feature = "documentation")]
265 pub fn docs(&self) -> Option<&str> {
266 match self {
267 Self::Struct(info) => info.docs(),
268 Self::TupleStruct(info) => info.docs(),
269 Self::Tuple(info) => info.docs(),
270 Self::List(info) => info.docs(),
271 Self::Array(info) => info.docs(),
272 Self::Map(info) => info.docs(),
273 Self::Set(info) => info.docs(),
274 Self::Enum(info) => info.docs(),
275 Self::Opaque(info) => info.docs(),
276 }
277 }
278
279 /// Returns the [kind] of this `TypeInfo`.
280 ///
281 /// [kind]: ReflectKind
282 pub fn kind(&self) -> ReflectKind {
283 match self {
284 Self::Struct(_) => ReflectKind::Struct,
285 Self::TupleStruct(_) => ReflectKind::TupleStruct,
286 Self::Tuple(_) => ReflectKind::Tuple,
287 Self::List(_) => ReflectKind::List,
288 Self::Array(_) => ReflectKind::Array,
289 Self::Map(_) => ReflectKind::Map,
290 Self::Set(_) => ReflectKind::Set,
291 Self::Enum(_) => ReflectKind::Enum,
292 Self::Opaque(_) => ReflectKind::Opaque,
293 }
294 }
295
296 impl_generic_info_methods!(self => {
297 match self {
298 Self::Struct(info) => info.generics(),
299 Self::TupleStruct(info) => info.generics(),
300 Self::Tuple(info) => info.generics(),
301 Self::List(info) => info.generics(),
302 Self::Array(info) => info.generics(),
303 Self::Map(info) => info.generics(),
304 Self::Set(info) => info.generics(),
305 Self::Enum(info) => info.generics(),
306 Self::Opaque(info) => info.generics(),
307 }
308 });
309}
310
311macro_rules! impl_cast_method {
312 ($name:ident : $kind:ident => $info:ident) => {
313 #[doc = concat!("Attempts a cast to [`", stringify!($info), "`].")]
314 #[doc = concat!("\n\nReturns an error if `self` is not [`TypeInfo::", stringify!($kind), "`].")]
315 pub fn $name(&self) -> Result<&$info, TypeInfoError> {
316 match self {
317 Self::$kind(info) => Ok(info),
318 _ => Err(TypeInfoError::KindMismatch {
319 expected: ReflectKind::$kind,
320 received: self.kind(),
321 }),
322 }
323 }
324 };
325}
326
327/// Conversion convenience methods for [`TypeInfo`].
328impl TypeInfo {
329 impl_cast_method!(as_struct: Struct => StructInfo);
330 impl_cast_method!(as_tuple_struct: TupleStruct => TupleStructInfo);
331 impl_cast_method!(as_tuple: Tuple => TupleInfo);
332 impl_cast_method!(as_list: List => ListInfo);
333 impl_cast_method!(as_array: Array => ArrayInfo);
334 impl_cast_method!(as_map: Map => MapInfo);
335 impl_cast_method!(as_enum: Enum => EnumInfo);
336 impl_cast_method!(as_opaque: Opaque => OpaqueInfo);
337}
338
339/// The base representation of a Rust type.
340///
341/// When possible, it is recommended to use [`&'static TypeInfo`] instead of this
342/// as it provides more information as well as being smaller
343/// (since a reference only takes the same number of bytes as a `usize`).
344///
345/// However, where a static reference to [`TypeInfo`] is not possible,
346/// such as with trait objects and other types that can't implement [`Typed`],
347/// this type can be used instead.
348///
349/// It only requires that the type implements [`TypePath`].
350///
351/// And unlike [`TypeInfo`], this type implements [`Copy`], [`Eq`], and [`Hash`],
352/// making it useful as a key type.
353///
354/// It's especially helpful when compared to [`TypeId`] as it can provide the
355/// actual [type path] when debugging, while still having the same performance
356/// as hashing/comparing [`TypeId`] directly—at the cost of a little more memory.
357///
358/// # Examples
359///
360/// ```
361/// use bevy_reflect::{Type, TypePath};
362///
363/// fn assert_char<T: ?Sized + TypePath>(t: &T) -> Result<(), String> {
364/// let ty = Type::of::<T>();
365/// if Type::of::<char>() == ty {
366/// Ok(())
367/// } else {
368/// Err(format!("expected `char`, got `{}`", ty.path()))
369/// }
370/// }
371///
372/// assert_eq!(
373/// assert_char(&'a'),
374/// Ok(())
375/// );
376/// assert_eq!(
377/// assert_char(&String::from("Hello, world!")),
378/// Err(String::from("expected `char`, got `alloc::string::String`"))
379/// );
380/// ```
381///
382/// [`&'static TypeInfo`]: TypeInfo
383#[derive(Copy, Clone)]
384pub struct Type {
385 type_path_table: TypePathTable,
386 type_id: TypeId,
387}
388
389impl Type {
390 /// Create a new [`Type`] from a type that implements [`TypePath`].
391 pub fn of<T: TypePath + ?Sized>() -> Self {
392 Self {
393 type_path_table: TypePathTable::of::<T>(),
394 type_id: TypeId::of::<T>(),
395 }
396 }
397
398 /// Returns the [`TypeId`] of the type.
399 #[inline]
400 pub fn id(&self) -> TypeId {
401 self.type_id
402 }
403
404 /// See [`TypePath::type_path`].
405 pub fn path(&self) -> &'static str {
406 self.type_path_table.path()
407 }
408
409 /// See [`TypePath::short_type_path`].
410 pub fn short_path(&self) -> &'static str {
411 self.type_path_table.short_path()
412 }
413
414 /// See [`TypePath::type_ident`].
415 pub fn ident(&self) -> Option<&'static str> {
416 self.type_path_table.ident()
417 }
418
419 /// See [`TypePath::crate_name`].
420 pub fn crate_name(&self) -> Option<&'static str> {
421 self.type_path_table.crate_name()
422 }
423
424 /// See [`TypePath::module_path`].
425 pub fn module_path(&self) -> Option<&'static str> {
426 self.type_path_table.module_path()
427 }
428
429 /// A representation of the type path of this.
430 ///
431 /// Provides dynamic access to all methods on [`TypePath`].
432 pub fn type_path_table(&self) -> &TypePathTable {
433 &self.type_path_table
434 }
435
436 /// Check if the given type matches this one.
437 ///
438 /// This only compares the [`TypeId`] of the types
439 /// and does not verify they share the same [`TypePath`]
440 /// (though it implies they do).
441 pub fn is<T: Any>(&self) -> bool {
442 TypeId::of::<T>() == self.type_id
443 }
444}
445
446/// This implementation will only output the [type path] of the type.
447///
448/// If you need to include the [`TypeId`] in the output,
449/// you can access it through [`Type::id`].
450///
451/// [type path]: TypePath
452impl Debug for Type {
453 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
454 write!(f, "{}", self.type_path_table.path())
455 }
456}
457
458impl Eq for Type {}
459
460/// This implementation purely relies on the [`TypeId`] of the type,
461/// and not on the [type path].
462///
463/// [type path]: TypePath
464impl PartialEq for Type {
465 #[inline]
466 fn eq(&self, other: &Self) -> bool {
467 self.type_id == other.type_id
468 }
469}
470
471/// This implementation purely relies on the [`TypeId`] of the type,
472/// and not on the [type path].
473///
474/// [type path]: TypePath
475impl Hash for Type {
476 #[inline]
477 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
478 self.type_id.hash(state);
479 }
480}
481
482macro_rules! impl_type_methods {
483 // Generates the type methods based off a single field.
484 ($field:ident) => {
485 $crate::type_info::impl_type_methods!(self => {
486 &self.$field
487 });
488 };
489 // Generates the type methods based off a custom expression.
490 ($self:ident => $expr:expr) => {
491 /// The underlying Rust [type].
492 ///
493 /// [type]: crate::type_info::Type
494 pub fn ty(&$self) -> &$crate::type_info::Type {
495 $expr
496 }
497
498 /// The [`TypeId`] of this type.
499 ///
500 /// [`TypeId`]: core::any::TypeId
501 pub fn type_id(&self) -> ::core::any::TypeId {
502 self.ty().id()
503 }
504
505 /// The [stable, full type path] of this type.
506 ///
507 /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
508 ///
509 /// [stable, full type path]: TypePath
510 /// [`type_path_table`]: Self::type_path_table
511 pub fn type_path(&self) -> &'static str {
512 self.ty().path()
513 }
514
515 /// A representation of the type path of this type.
516 ///
517 /// Provides dynamic access to all methods on [`TypePath`].
518 ///
519 /// [`TypePath`]: crate::type_path::TypePath
520 pub fn type_path_table(&self) -> &$crate::type_path::TypePathTable {
521 &self.ty().type_path_table()
522 }
523
524 /// Check if the given type matches this one.
525 ///
526 /// This only compares the [`TypeId`] of the types
527 /// and does not verify they share the same [`TypePath`]
528 /// (though it implies they do).
529 ///
530 /// [`TypeId`]: core::any::TypeId
531 /// [`TypePath`]: crate::type_path::TypePath
532 pub fn is<T: ::core::any::Any>(&self) -> bool {
533 self.ty().is::<T>()
534 }
535 };
536}
537
538use crate::generics::impl_generic_info_methods;
539pub(crate) use impl_type_methods;
540
541/// A container for compile-time info related to reflection-opaque types, including primitives.
542///
543/// This typically represents a type which cannot be broken down any further. This is often
544/// due to technical reasons (or by definition), but it can also be a purposeful choice.
545///
546/// For example, [`i32`] cannot be broken down any further, so it is represented by an [`OpaqueInfo`].
547/// And while [`String`] itself is a struct, its fields are private, so we don't really treat
548/// it _as_ a struct. It therefore makes more sense to represent it as an [`OpaqueInfo`].
549///
550/// [`String`]: alloc::string::String
551#[derive(Debug, Clone)]
552pub struct OpaqueInfo {
553 ty: Type,
554 generics: Generics,
555 #[cfg(feature = "documentation")]
556 docs: Option<&'static str>,
557}
558
559impl OpaqueInfo {
560 pub fn new<T: Reflect + TypePath + ?Sized>() -> Self {
561 Self {
562 ty: Type::of::<T>(),
563 generics: Generics::new(),
564 #[cfg(feature = "documentation")]
565 docs: None,
566 }
567 }
568
569 /// Sets the docstring for this type.
570 #[cfg(feature = "documentation")]
571 pub fn with_docs(self, doc: Option<&'static str>) -> Self {
572 Self { docs: doc, ..self }
573 }
574
575 impl_type_methods!(ty);
576
577 /// The docstring of this dynamic type, if any.
578 #[cfg(feature = "documentation")]
579 pub fn docs(&self) -> Option<&'static str> {
580 self.docs
581 }
582
583 impl_generic_info_methods!(generics);
584}
585
586#[cfg(test)]
587mod tests {
588 use super::*;
589 use alloc::vec::Vec;
590
591 #[test]
592 fn should_return_error_on_invalid_cast() {
593 let info = <Vec<i32> as Typed>::type_info();
594 assert!(matches!(
595 info.as_struct(),
596 Err(TypeInfoError::KindMismatch {
597 expected: ReflectKind::Struct,
598 received: ReflectKind::List
599 })
600 ));
601 }
602}