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