bevy_reflect/
kind.rs

1use alloc::boxed::Box;
2use thiserror::Error;
3
4#[cfg(feature = "functions")]
5use crate::func::Function;
6use crate::{Array, Enum, List, Map, PartialReflect, Set, Struct, Tuple, TupleStruct};
7
8/// An enumeration of the "kinds" of a reflected type.
9///
10/// Each kind corresponds to a specific reflection trait,
11/// such as [`Struct`] or [`List`],
12/// which itself corresponds to the kind or structure of a type.
13///
14/// A [`ReflectKind`] is obtained via [`PartialReflect::reflect_kind`],
15/// or via [`ReflectRef::kind`],[`ReflectMut::kind`] or [`ReflectOwned::kind`].
16#[derive(Debug, PartialEq, Eq, Clone, Copy)]
17pub enum ReflectKind {
18    /// A [struct-like] type.
19    ///
20    /// [struct-like]: Struct
21    Struct,
22    /// A [tuple-struct-like] type.
23    ///
24    /// [tuple-struct-like]: TupleStruct
25    TupleStruct,
26    /// A [tuple-like] type.
27    ///
28    /// [tuple-like]: Tuple
29    Tuple,
30    /// A [list-like] type.
31    ///
32    /// [list-like]: List
33    List,
34    /// An [array-like] type.
35    ///
36    /// [array-like]: Array
37    Array,
38    /// A [map-like] type.
39    ///
40    /// [map-like]: Map
41    Map,
42    /// A [set-like] type.
43    ///
44    /// [set-like]: Set
45    Set,
46    /// An [enum-like] type.
47    ///
48    /// [enum-like]: Enum
49    Enum,
50    /// A [function-like] type.
51    ///
52    /// [function-like]: Function
53    #[cfg(feature = "functions")]
54    Function,
55    /// An opaque type.
56    ///
57    /// This most often represents a type where it is either impossible, difficult,
58    /// or unuseful to reflect the type further.
59    ///
60    /// This includes types like `String` and `Instant`.
61    ///
62    /// Despite not technically being opaque types,
63    /// primitives like `u32` `i32` are considered opaque for the purposes of reflection.
64    ///
65    /// Additionally, any type that [derives `Reflect`] with the `#[reflect(opaque)]` attribute
66    /// will be considered an opaque type.
67    ///
68    /// [derives `Reflect`]: bevy_reflect_derive::Reflect
69    Opaque,
70}
71
72impl core::fmt::Display for ReflectKind {
73    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74        match self {
75            ReflectKind::Struct => f.pad("struct"),
76            ReflectKind::TupleStruct => f.pad("tuple struct"),
77            ReflectKind::Tuple => f.pad("tuple"),
78            ReflectKind::List => f.pad("list"),
79            ReflectKind::Array => f.pad("array"),
80            ReflectKind::Map => f.pad("map"),
81            ReflectKind::Set => f.pad("set"),
82            ReflectKind::Enum => f.pad("enum"),
83            #[cfg(feature = "functions")]
84            ReflectKind::Function => f.pad("function"),
85            ReflectKind::Opaque => f.pad("opaque"),
86        }
87    }
88}
89
90macro_rules! impl_reflect_kind_conversions {
91    ($name:ident$(<$lifetime:lifetime>)?) => {
92        impl $name$(<$lifetime>)? {
93            /// Returns the "kind" of this reflected type without any information.
94            pub fn kind(&self) -> ReflectKind {
95                match self {
96                    Self::Struct(_) => ReflectKind::Struct,
97                    Self::TupleStruct(_) => ReflectKind::TupleStruct,
98                    Self::Tuple(_) => ReflectKind::Tuple,
99                    Self::List(_) => ReflectKind::List,
100                    Self::Array(_) => ReflectKind::Array,
101                    Self::Map(_) => ReflectKind::Map,
102                    Self::Set(_) => ReflectKind::Set,
103                    Self::Enum(_) => ReflectKind::Enum,
104                    #[cfg(feature = "functions")]
105                    Self::Function(_) => ReflectKind::Function,
106                    Self::Opaque(_) => ReflectKind::Opaque,
107                }
108            }
109        }
110
111        impl From<$name$(<$lifetime>)?> for ReflectKind {
112            fn from(value: $name) -> Self {
113                match value {
114                    $name::Struct(_) => Self::Struct,
115                    $name::TupleStruct(_) => Self::TupleStruct,
116                    $name::Tuple(_) => Self::Tuple,
117                    $name::List(_) => Self::List,
118                    $name::Array(_) => Self::Array,
119                    $name::Map(_) => Self::Map,
120                    $name::Set(_) => Self::Set,
121                    $name::Enum(_) => Self::Enum,
122                    #[cfg(feature = "functions")]
123                    $name::Function(_) => Self::Function,
124                    $name::Opaque(_) => Self::Opaque,
125                }
126            }
127        }
128    };
129}
130
131/// Caused when a type was expected to be of a certain [kind], but was not.
132///
133/// [kind]: ReflectKind
134#[derive(Debug, Error)]
135#[error("kind mismatch: expected {expected:?}, received {received:?}")]
136pub struct ReflectKindMismatchError {
137    pub expected: ReflectKind,
138    pub received: ReflectKind,
139}
140
141macro_rules! impl_cast_method {
142    ($name:ident : Opaque => $retval:ty) => {
143        #[doc = "Attempts a cast to a [`PartialReflect`] trait object."]
144        #[doc = "\n\nReturns an error if `self` is not the [`Self::Opaque`] variant."]
145        pub fn $name(self) -> Result<$retval, ReflectKindMismatchError> {
146            match self {
147                Self::Opaque(value) => Ok(value),
148                _ => Err(ReflectKindMismatchError {
149                    expected: ReflectKind::Opaque,
150                    received: self.kind(),
151                }),
152            }
153        }
154    };
155    ($name:ident : $kind:ident => $retval:ty) => {
156        #[doc = concat!("Attempts a cast to a [`", stringify!($kind), "`] trait object.")]
157        #[doc = concat!("\n\nReturns an error if `self` is not the [`Self::", stringify!($kind), "`] variant.")]
158        pub fn $name(self) -> Result<$retval, ReflectKindMismatchError> {
159            match self {
160                Self::$kind(value) => Ok(value),
161                _ => Err(ReflectKindMismatchError {
162                    expected: ReflectKind::$kind,
163                    received: self.kind(),
164                }),
165            }
166        }
167    };
168}
169
170/// An immutable enumeration of ["kinds"] of a reflected type.
171///
172/// Each variant contains a trait object with methods specific to a kind of
173/// type.
174///
175/// A [`ReflectRef`] is obtained via [`PartialReflect::reflect_ref`].
176///
177/// ["kinds"]: ReflectKind
178pub enum ReflectRef<'a> {
179    Struct(&'a dyn Struct),
180    TupleStruct(&'a dyn TupleStruct),
181    Tuple(&'a dyn Tuple),
182    List(&'a dyn List),
183    Array(&'a dyn Array),
184    Map(&'a dyn Map),
185    Set(&'a dyn Set),
186    Enum(&'a dyn Enum),
187    #[cfg(feature = "functions")]
188    Function(&'a dyn Function),
189    Opaque(&'a dyn PartialReflect),
190}
191impl_reflect_kind_conversions!(ReflectRef<'_>);
192
193impl<'a> ReflectRef<'a> {
194    impl_cast_method!(as_struct: Struct => &'a dyn Struct);
195    impl_cast_method!(as_tuple_struct: TupleStruct => &'a dyn TupleStruct);
196    impl_cast_method!(as_tuple: Tuple => &'a dyn Tuple);
197    impl_cast_method!(as_list: List => &'a dyn List);
198    impl_cast_method!(as_array: Array => &'a dyn Array);
199    impl_cast_method!(as_map: Map => &'a dyn Map);
200    impl_cast_method!(as_set: Set => &'a dyn Set);
201    impl_cast_method!(as_enum: Enum => &'a dyn Enum);
202    impl_cast_method!(as_opaque: Opaque => &'a dyn PartialReflect);
203}
204
205/// A mutable enumeration of ["kinds"] of a reflected type.
206///
207/// Each variant contains a trait object with methods specific to a kind of
208/// type.
209///
210/// A [`ReflectMut`] is obtained via [`PartialReflect::reflect_mut`].
211///
212/// ["kinds"]: ReflectKind
213pub enum ReflectMut<'a> {
214    Struct(&'a mut dyn Struct),
215    TupleStruct(&'a mut dyn TupleStruct),
216    Tuple(&'a mut dyn Tuple),
217    List(&'a mut dyn List),
218    Array(&'a mut dyn Array),
219    Map(&'a mut dyn Map),
220    Set(&'a mut dyn Set),
221    Enum(&'a mut dyn Enum),
222    #[cfg(feature = "functions")]
223    Function(&'a mut dyn Function),
224    Opaque(&'a mut dyn PartialReflect),
225}
226impl_reflect_kind_conversions!(ReflectMut<'_>);
227
228impl<'a> ReflectMut<'a> {
229    impl_cast_method!(as_struct: Struct => &'a mut dyn Struct);
230    impl_cast_method!(as_tuple_struct: TupleStruct => &'a mut dyn TupleStruct);
231    impl_cast_method!(as_tuple: Tuple => &'a mut dyn Tuple);
232    impl_cast_method!(as_list: List => &'a mut dyn List);
233    impl_cast_method!(as_array: Array => &'a mut dyn Array);
234    impl_cast_method!(as_map: Map => &'a mut dyn Map);
235    impl_cast_method!(as_set: Set => &'a mut dyn Set);
236    impl_cast_method!(as_enum: Enum => &'a mut dyn Enum);
237    impl_cast_method!(as_opaque: Opaque => &'a mut dyn PartialReflect);
238}
239
240/// An owned enumeration of ["kinds"] of a reflected type.
241///
242/// Each variant contains a trait object with methods specific to a kind of
243/// type.
244///
245/// A [`ReflectOwned`] is obtained via [`PartialReflect::reflect_owned`].
246///
247/// ["kinds"]: ReflectKind
248pub enum ReflectOwned {
249    Struct(Box<dyn Struct>),
250    TupleStruct(Box<dyn TupleStruct>),
251    Tuple(Box<dyn Tuple>),
252    List(Box<dyn List>),
253    Array(Box<dyn Array>),
254    Map(Box<dyn Map>),
255    Set(Box<dyn Set>),
256    Enum(Box<dyn Enum>),
257    #[cfg(feature = "functions")]
258    Function(Box<dyn Function>),
259    Opaque(Box<dyn PartialReflect>),
260}
261impl_reflect_kind_conversions!(ReflectOwned);
262
263impl ReflectOwned {
264    impl_cast_method!(into_struct: Struct => Box<dyn Struct>);
265    impl_cast_method!(into_tuple_struct: TupleStruct => Box<dyn TupleStruct>);
266    impl_cast_method!(into_tuple: Tuple => Box<dyn Tuple>);
267    impl_cast_method!(into_list: List => Box<dyn List>);
268    impl_cast_method!(into_array: Array => Box<dyn Array>);
269    impl_cast_method!(into_map: Map => Box<dyn Map>);
270    impl_cast_method!(into_set: Set => Box<dyn Set>);
271    impl_cast_method!(into_enum: Enum => Box<dyn Enum>);
272    impl_cast_method!(into_value: Opaque => Box<dyn PartialReflect>);
273}
274
275#[cfg(test)]
276mod tests {
277    use alloc::vec;
278    use std::collections::HashSet;
279
280    use super::*;
281
282    #[test]
283    fn should_cast_ref() {
284        let value = vec![1, 2, 3];
285
286        let result = value.reflect_ref().as_list();
287        assert!(result.is_ok());
288
289        let result = value.reflect_ref().as_array();
290        assert!(matches!(
291            result,
292            Err(ReflectKindMismatchError {
293                expected: ReflectKind::Array,
294                received: ReflectKind::List
295            })
296        ));
297    }
298
299    #[test]
300    fn should_cast_mut() {
301        let mut value: HashSet<i32> = HashSet::default();
302
303        let result = value.reflect_mut().as_set();
304        assert!(result.is_ok());
305
306        let result = value.reflect_mut().as_map();
307        assert!(matches!(
308            result,
309            Err(ReflectKindMismatchError {
310                expected: ReflectKind::Map,
311                received: ReflectKind::Set
312            })
313        ));
314    }
315
316    #[test]
317    fn should_cast_owned() {
318        let value = Box::new(Some(123));
319
320        let result = value.reflect_owned().into_enum();
321        assert!(result.is_ok());
322
323        let value = Box::new(Some(123));
324
325        let result = value.reflect_owned().into_struct();
326        assert!(matches!(
327            result,
328            Err(ReflectKindMismatchError {
329                expected: ReflectKind::Struct,
330                received: ReflectKind::Enum
331            })
332        ));
333    }
334}