Skip to main content

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}