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 kind.
173        expected: ReflectKind,
174        /// Received kind.
175        received: ReflectKind,
176    },
177}
178
179/// Compile-time type information for various reflected types.
180///
181/// Generally, for any given type, this value can be retrieved in one of four ways:
182///
183/// 1. [`Typed::type_info`]
184/// 2. [`DynamicTyped::reflect_type_info`]
185/// 3. [`PartialReflect::get_represented_type_info`]
186/// 4. [`TypeRegistry::get_type_info`]
187///
188/// Each returns a static reference to [`TypeInfo`], but they all have their own use cases.
189/// For example, if you know the type at compile time, [`Typed::type_info`] is probably
190/// the simplest. If you have a `dyn Reflect` you can use [`DynamicTyped::reflect_type_info`].
191/// If all you have is a `dyn PartialReflect`, you'll probably want [`PartialReflect::get_represented_type_info`].
192/// Lastly, if all you have is a [`TypeId`] or [type path], you will need to go through
193/// [`TypeRegistry::get_type_info`].
194///
195/// You may also opt to use [`TypeRegistry::get_type_info`] in place of the other methods simply because
196/// it can be more performant. This is because those other methods may require attaining a lock on
197/// the static [`TypeInfo`], while the registry simply checks a map.
198///
199/// [`TypeRegistry::get_type_info`]: crate::TypeRegistry::get_type_info
200/// [`PartialReflect::get_represented_type_info`]: crate::PartialReflect::get_represented_type_info
201/// [type path]: TypePath::type_path
202#[derive(Debug, Clone)]
203pub enum TypeInfo {
204    /// Type information for a [struct-like] type.
205    ///
206    /// [struct-like]: crate::Struct
207    Struct(StructInfo),
208    /// Type information for a [tuple-struct-like] type.
209    ///
210    /// [tuple-struct-like]: crate::TupleStruct
211    TupleStruct(TupleStructInfo),
212    /// Type information for a [tuple-like] type.
213    ///
214    /// [tuple-like]: crate::Tuple
215    Tuple(TupleInfo),
216    /// Type information for a [list-like] type.
217    ///
218    /// [list-like]: crate::List
219    List(ListInfo),
220    /// Type information for an [array-like] type.
221    ///
222    /// [array-like]: crate::Array
223    Array(ArrayInfo),
224    /// Type information for a [map-like] type.
225    ///
226    /// [map-like]: crate::Map
227    Map(MapInfo),
228    /// Type information for a [set-like] type.
229    ///
230    /// [set-like]: crate::Set
231    Set(SetInfo),
232    /// Type information for an [enum-like] type.
233    ///
234    /// [enum-like]: crate::Enum
235    Enum(EnumInfo),
236    /// Type information for an opaque type - see the [`OpaqueInfo`] docs for
237    /// a discussion of opaque types.
238    Opaque(OpaqueInfo),
239}
240
241impl TypeInfo {
242    /// The underlying Rust [type].
243    ///
244    /// [type]: Type
245    pub fn ty(&self) -> &Type {
246        match self {
247            Self::Struct(info) => info.ty(),
248            Self::TupleStruct(info) => info.ty(),
249            Self::Tuple(info) => info.ty(),
250            Self::List(info) => info.ty(),
251            Self::Array(info) => info.ty(),
252            Self::Map(info) => info.ty(),
253            Self::Set(info) => info.ty(),
254            Self::Enum(info) => info.ty(),
255            Self::Opaque(info) => info.ty(),
256        }
257    }
258
259    /// The [`TypeId`] of the underlying type.
260    #[inline]
261    pub fn type_id(&self) -> TypeId {
262        self.ty().id()
263    }
264
265    /// A representation of the type path of the underlying type.
266    ///
267    /// Provides dynamic access to all methods on [`TypePath`].
268    pub fn type_path_table(&self) -> &TypePathTable {
269        self.ty().type_path_table()
270    }
271
272    /// The [stable, full type path] of the underlying type.
273    ///
274    /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
275    ///
276    /// [stable, full type path]: TypePath
277    /// [`type_path_table`]: Self::type_path_table
278    pub fn type_path(&self) -> &'static str {
279        self.ty().path()
280    }
281
282    /// Check if the given type matches this one.
283    ///
284    /// This only compares the [`TypeId`] of the types
285    /// and does not verify they share the same [`TypePath`]
286    /// (though it implies they do).
287    pub fn is<T: Any>(&self) -> bool {
288        self.ty().is::<T>()
289    }
290
291    /// The docstring of the underlying type, if any.
292    #[cfg(feature = "documentation")]
293    pub fn docs(&self) -> Option<&str> {
294        match self {
295            Self::Struct(info) => info.docs(),
296            Self::TupleStruct(info) => info.docs(),
297            Self::Tuple(info) => info.docs(),
298            Self::List(info) => info.docs(),
299            Self::Array(info) => info.docs(),
300            Self::Map(info) => info.docs(),
301            Self::Set(info) => info.docs(),
302            Self::Enum(info) => info.docs(),
303            Self::Opaque(info) => info.docs(),
304        }
305    }
306
307    /// Returns the [kind] of this `TypeInfo`.
308    ///
309    /// [kind]: ReflectKind
310    pub fn kind(&self) -> ReflectKind {
311        match self {
312            Self::Struct(_) => ReflectKind::Struct,
313            Self::TupleStruct(_) => ReflectKind::TupleStruct,
314            Self::Tuple(_) => ReflectKind::Tuple,
315            Self::List(_) => ReflectKind::List,
316            Self::Array(_) => ReflectKind::Array,
317            Self::Map(_) => ReflectKind::Map,
318            Self::Set(_) => ReflectKind::Set,
319            Self::Enum(_) => ReflectKind::Enum,
320            Self::Opaque(_) => ReflectKind::Opaque,
321        }
322    }
323
324    impl_generic_info_methods!(self => {
325        match self {
326            Self::Struct(info) => info.generics(),
327            Self::TupleStruct(info) => info.generics(),
328            Self::Tuple(info) => info.generics(),
329            Self::List(info) => info.generics(),
330            Self::Array(info) => info.generics(),
331            Self::Map(info) => info.generics(),
332            Self::Set(info) => info.generics(),
333            Self::Enum(info) => info.generics(),
334            Self::Opaque(info) => info.generics(),
335        }
336    });
337}
338
339macro_rules! impl_cast_method {
340    ($name:ident : $kind:ident => $info:ident) => {
341        #[doc = concat!("Attempts a cast to [`", stringify!($info), "`].")]
342        #[doc = concat!("\n\nReturns an error if `self` is not [`TypeInfo::", stringify!($kind), "`].")]
343        pub fn $name(&self) -> Result<&$info, TypeInfoError> {
344            match self {
345                Self::$kind(info) => Ok(info),
346                _ => Err(TypeInfoError::KindMismatch {
347                    expected: ReflectKind::$kind,
348                    received: self.kind(),
349                }),
350            }
351        }
352    };
353}
354
355/// Conversion convenience methods for [`TypeInfo`].
356impl TypeInfo {
357    impl_cast_method!(as_struct: Struct => StructInfo);
358    impl_cast_method!(as_tuple_struct: TupleStruct => TupleStructInfo);
359    impl_cast_method!(as_tuple: Tuple => TupleInfo);
360    impl_cast_method!(as_list: List => ListInfo);
361    impl_cast_method!(as_array: Array => ArrayInfo);
362    impl_cast_method!(as_map: Map => MapInfo);
363    impl_cast_method!(as_enum: Enum => EnumInfo);
364    impl_cast_method!(as_opaque: Opaque => OpaqueInfo);
365}
366
367/// The base representation of a Rust type.
368///
369/// When possible, it is recommended to use [`&'static TypeInfo`] instead of this
370/// as it provides more information as well as being smaller
371/// (since a reference only takes the same number of bytes as a `usize`).
372///
373/// However, where a static reference to [`TypeInfo`] is not possible,
374/// such as with trait objects and other types that can't implement [`Typed`],
375/// this type can be used instead.
376///
377/// It only requires that the type implements [`TypePath`].
378///
379/// And unlike [`TypeInfo`], this type implements [`Copy`], [`Eq`], and [`Hash`],
380/// making it useful as a key type.
381///
382/// It's especially helpful when compared to [`TypeId`] as it can provide the
383/// actual [type path] when debugging, while still having the same performance
384/// as hashing/comparing [`TypeId`] directly—at the cost of a little more memory.
385///
386/// # Examples
387///
388/// ```
389/// use bevy_reflect::{Type, TypePath};
390///
391/// fn assert_char<T: ?Sized + TypePath>(t: &T) -> Result<(), String> {
392///     let ty = Type::of::<T>();
393///     if Type::of::<char>() == ty {
394///         Ok(())
395///     } else {
396///         Err(format!("expected `char`, got `{}`", ty.path()))
397///     }
398/// }
399///
400/// assert_eq!(
401///     assert_char(&'a'),
402///     Ok(())
403/// );
404/// assert_eq!(
405///     assert_char(&String::from("Hello, world!")),
406///     Err(String::from("expected `char`, got `alloc::string::String`"))
407/// );
408/// ```
409///
410/// [`&'static TypeInfo`]: TypeInfo
411#[derive(Copy, Clone)]
412pub struct Type {
413    type_path_table: TypePathTable,
414    type_id: TypeId,
415}
416
417impl Type {
418    /// Create a new [`Type`] from a type that implements [`TypePath`].
419    pub fn of<T: TypePath + ?Sized>() -> Self {
420        Self {
421            type_path_table: TypePathTable::of::<T>(),
422            type_id: TypeId::of::<T>(),
423        }
424    }
425
426    /// Returns the [`TypeId`] of the type.
427    #[inline]
428    pub fn id(&self) -> TypeId {
429        self.type_id
430    }
431
432    /// See [`TypePath::type_path`].
433    pub fn path(&self) -> &'static str {
434        self.type_path_table.path()
435    }
436
437    /// See [`TypePath::short_type_path`].
438    pub fn short_path(&self) -> &'static str {
439        self.type_path_table.short_path()
440    }
441
442    /// See [`TypePath::type_ident`].
443    pub fn ident(&self) -> Option<&'static str> {
444        self.type_path_table.ident()
445    }
446
447    /// See [`TypePath::crate_name`].
448    pub fn crate_name(&self) -> Option<&'static str> {
449        self.type_path_table.crate_name()
450    }
451
452    /// See [`TypePath::module_path`].
453    pub fn module_path(&self) -> Option<&'static str> {
454        self.type_path_table.module_path()
455    }
456
457    /// A representation of the type path of this.
458    ///
459    /// Provides dynamic access to all methods on [`TypePath`].
460    pub fn type_path_table(&self) -> &TypePathTable {
461        &self.type_path_table
462    }
463
464    /// Check if the given type matches this one.
465    ///
466    /// This only compares the [`TypeId`] of the types
467    /// and does not verify they share the same [`TypePath`]
468    /// (though it implies they do).
469    pub fn is<T: Any>(&self) -> bool {
470        TypeId::of::<T>() == self.type_id
471    }
472}
473
474/// This implementation will only output the [type path] of the type.
475///
476/// If you need to include the [`TypeId`] in the output,
477/// you can access it through [`Type::id`].
478///
479/// [type path]: TypePath
480impl Debug for Type {
481    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
482        write!(f, "{}", self.type_path_table.path())
483    }
484}
485
486impl Eq for Type {}
487
488/// This implementation purely relies on the [`TypeId`] of the type,
489/// and not on the [type path].
490///
491/// [type path]: TypePath
492impl PartialEq for Type {
493    #[inline]
494    fn eq(&self, other: &Self) -> bool {
495        self.type_id == other.type_id
496    }
497}
498
499/// This implementation purely relies on the [`TypeId`] of the type,
500/// and not on the [type path].
501///
502/// [type path]: TypePath
503impl Hash for Type {
504    #[inline]
505    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
506        self.type_id.hash(state);
507    }
508}
509
510macro_rules! impl_type_methods {
511    // Generates the type methods based off a single field.
512    ($field:ident) => {
513        $crate::type_info::impl_type_methods!(self => {
514            &self.$field
515        });
516    };
517    // Generates the type methods based off a custom expression.
518    ($self:ident => $expr:expr) => {
519        /// The underlying Rust [type].
520        ///
521        /// [type]: crate::type_info::Type
522        pub fn ty(&$self) -> &$crate::type_info::Type {
523            $expr
524        }
525
526        /// The [`TypeId`] of this type.
527        ///
528        /// [`TypeId`]: core::any::TypeId
529        pub fn type_id(&self) -> ::core::any::TypeId {
530            self.ty().id()
531        }
532
533        /// The [stable, full type path] of this type.
534        ///
535        /// Use [`type_path_table`] if you need access to the other methods on [`TypePath`].
536        ///
537        /// [stable, full type path]: TypePath
538        /// [`type_path_table`]: Self::type_path_table
539        pub fn type_path(&self) -> &'static str {
540            self.ty().path()
541        }
542
543        /// A representation of the type path of this type.
544        ///
545        /// Provides dynamic access to all methods on [`TypePath`].
546        ///
547        /// [`TypePath`]: crate::type_path::TypePath
548        pub fn type_path_table(&self) -> &$crate::type_path::TypePathTable {
549            &self.ty().type_path_table()
550        }
551
552        /// Check if the given type matches this one.
553        ///
554        /// This only compares the [`TypeId`] of the types
555        /// and does not verify they share the same [`TypePath`]
556        /// (though it implies they do).
557        ///
558        /// [`TypeId`]: core::any::TypeId
559        /// [`TypePath`]: crate::type_path::TypePath
560        pub fn is<T: ::core::any::Any>(&self) -> bool {
561            self.ty().is::<T>()
562        }
563    };
564}
565
566use crate::generics::impl_generic_info_methods;
567pub(crate) use impl_type_methods;
568
569/// A container for compile-time info related to reflection-opaque types, including primitives.
570///
571/// This typically represents a type which cannot be broken down any further. This is often
572/// due to technical reasons (or by definition), but it can also be a purposeful choice.
573///
574/// For example, [`i32`] cannot be broken down any further, so it is represented by an [`OpaqueInfo`].
575/// And while [`String`] itself is a struct, its fields are private, so we don't really treat
576/// it _as_ a struct. It therefore makes more sense to represent it as an [`OpaqueInfo`].
577///
578/// [`String`]: alloc::string::String
579#[derive(Debug, Clone)]
580pub struct OpaqueInfo {
581    ty: Type,
582    generics: Generics,
583    #[cfg(feature = "documentation")]
584    docs: Option<&'static str>,
585}
586
587impl OpaqueInfo {
588    /// Creates a new [`OpaqueInfo`].
589    pub fn new<T: Reflect + TypePath + ?Sized>() -> Self {
590        Self {
591            ty: Type::of::<T>(),
592            generics: Generics::new(),
593            #[cfg(feature = "documentation")]
594            docs: None,
595        }
596    }
597
598    /// Sets the docstring for this type.
599    #[cfg(feature = "documentation")]
600    pub fn with_docs(self, doc: Option<&'static str>) -> Self {
601        Self { docs: doc, ..self }
602    }
603
604    impl_type_methods!(ty);
605
606    /// The docstring of this dynamic type, if any.
607    #[cfg(feature = "documentation")]
608    pub fn docs(&self) -> Option<&'static str> {
609        self.docs
610    }
611
612    impl_generic_info_methods!(generics);
613}
614
615#[cfg(test)]
616mod tests {
617    use super::*;
618    use alloc::vec::Vec;
619
620    #[test]
621    fn should_return_error_on_invalid_cast() {
622        let info = <Vec<i32> as Typed>::type_info();
623        assert!(matches!(
624            info.as_struct(),
625            Err(TypeInfoError::KindMismatch {
626                expected: ReflectKind::Struct,
627                received: ReflectKind::List
628            })
629        ));
630    }
631}