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