Skip to main content

bevy_reflect/
kind.rs

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