Skip to main content

bevy_reflect/enums/
mod.rs

1//! Traits and types used to power [enum-like] operations via reflection.
2//!
3//! [enum-like]: https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html
4mod dynamic_enum;
5mod enum_trait;
6mod helpers;
7mod variants;
8
9pub use dynamic_enum::*;
10pub use enum_trait::*;
11pub use helpers::*;
12pub use variants::*;
13
14#[cfg(test)]
15mod tests {
16    use crate::{enums::*, structs::*, tuple::*, *};
17    use alloc::boxed::Box;
18
19    #[derive(Reflect, Debug, PartialEq)]
20    enum MyEnum {
21        A,
22        B(usize, i32),
23        C { foo: f32, bar: bool },
24    }
25
26    #[test]
27    fn should_get_enum_type_info() {
28        let info = MyEnum::type_info();
29        if let TypeInfo::Enum(info) = info {
30            assert!(info.is::<MyEnum>(), "expected type to be `MyEnum`");
31            assert_eq!(MyEnum::type_path(), info.type_path());
32            assert_eq!(MyEnum::type_path(), info.type_path_table().path());
33            assert_eq!(MyEnum::type_ident(), info.type_path_table().ident());
34            assert_eq!(MyEnum::module_path(), info.type_path_table().module_path());
35            assert_eq!(MyEnum::crate_name(), info.type_path_table().crate_name());
36            assert_eq!(
37                MyEnum::short_type_path(),
38                info.type_path_table().short_path()
39            );
40
41            // === MyEnum::A === //
42            assert_eq!("A", info.variant_at(0).unwrap().name());
43            assert_eq!("A", info.variant("A").unwrap().name());
44            if let VariantInfo::Unit(variant) = info.variant("A").unwrap() {
45                assert_eq!("A", variant.name());
46            } else {
47                panic!("Expected `VariantInfo::Unit`");
48            }
49
50            // === MyEnum::B === //
51            assert_eq!("B", info.variant_at(1).unwrap().name());
52            assert_eq!("B", info.variant("B").unwrap().name());
53            if let VariantInfo::Tuple(variant) = info.variant("B").unwrap() {
54                assert!(variant.field_at(0).unwrap().is::<usize>());
55                assert!(variant.field_at(1).unwrap().is::<i32>());
56                assert!(variant
57                    .field_at(0)
58                    .unwrap()
59                    .type_info()
60                    .unwrap()
61                    .is::<usize>());
62                assert!(variant
63                    .field_at(1)
64                    .unwrap()
65                    .type_info()
66                    .unwrap()
67                    .is::<i32>());
68            } else {
69                panic!("Expected `VariantInfo::Tuple`");
70            }
71
72            // === MyEnum::C === //
73            assert_eq!("C", info.variant_at(2).unwrap().name());
74            assert_eq!("C", info.variant("C").unwrap().name());
75            if let VariantInfo::Struct(variant) = info.variant("C").unwrap() {
76                assert!(variant.field_at(0).unwrap().is::<f32>());
77                assert!(variant.field("foo").unwrap().is::<f32>());
78                assert!(variant
79                    .field("foo")
80                    .unwrap()
81                    .type_info()
82                    .unwrap()
83                    .is::<f32>());
84            } else {
85                panic!("Expected `VariantInfo::Struct`");
86            }
87        } else {
88            panic!("Expected `TypeInfo::Enum`");
89        }
90    }
91
92    #[test]
93    fn dynamic_enum_should_set_variant_fields() {
94        // === Unit === //
95        let mut value = MyEnum::A;
96        let dyn_enum = DynamicEnum::try_from(MyEnum::A).unwrap();
97        value.apply(&dyn_enum);
98        assert_eq!(MyEnum::A, value);
99
100        // === Tuple === //
101        let mut value = MyEnum::B(0, 0);
102        let dyn_enum = DynamicEnum::try_from(MyEnum::B(123, 321)).unwrap();
103        value.apply(&dyn_enum);
104        assert_eq!(MyEnum::B(123, 321), value);
105
106        // === Struct === //
107        let mut value = MyEnum::C {
108            foo: 0.0,
109            bar: false,
110        };
111        let dyn_enum = DynamicEnum::try_from(MyEnum::C {
112            foo: 1.23,
113            bar: true,
114        })
115        .unwrap();
116        value.apply(&dyn_enum);
117        assert_eq!(
118            MyEnum::C {
119                foo: 1.23,
120                bar: true,
121            },
122            value
123        );
124    }
125
126    #[test]
127    fn partial_dynamic_enum_should_set_variant_fields() {
128        // === Tuple === //
129        let mut value = MyEnum::B(0, 0);
130
131        let mut data = DynamicTuple::default();
132        data.insert(123usize);
133
134        let mut dyn_enum = DynamicEnum::default();
135        dyn_enum.set_variant("B", data);
136        value.apply(&dyn_enum);
137        assert_eq!(MyEnum::B(123, 0), value);
138
139        // === Struct === //
140        let mut value = MyEnum::C {
141            foo: 1.23,
142            bar: false,
143        };
144
145        let mut data = DynamicStruct::default();
146        data.insert("bar", true);
147
148        let mut dyn_enum = DynamicEnum::default();
149        dyn_enum.set_variant("C", data);
150        value.apply(&dyn_enum);
151        assert_eq!(
152            MyEnum::C {
153                foo: 1.23,
154                bar: true,
155            },
156            value
157        );
158    }
159
160    #[test]
161    fn dynamic_enum_should_apply_dynamic_enum() {
162        let mut a = DynamicEnum::try_from(MyEnum::B(123, 321)).unwrap();
163        let b = DynamicEnum::try_from(MyEnum::B(123, 321)).unwrap();
164
165        // Sanity check that equality check works
166        assert!(
167            a.reflect_partial_eq(&b).unwrap_or_default(),
168            "dynamic enums should be equal"
169        );
170
171        a.set_variant("A", ());
172        assert!(
173            !a.reflect_partial_eq(&b).unwrap_or_default(),
174            "dynamic enums should not be equal"
175        );
176
177        a.apply(&b);
178        assert!(a.reflect_partial_eq(&b).unwrap_or_default());
179    }
180
181    #[test]
182    fn dynamic_enum_should_change_variant() {
183        let mut value = MyEnum::A;
184
185        // === MyEnum::A -> MyEnum::B === //
186        let mut dyn_enum = DynamicEnum::try_from(MyEnum::B(123, 321)).unwrap();
187        value.apply(&dyn_enum);
188        assert_eq!(MyEnum::B(123, 321), value);
189
190        // === MyEnum::B -> MyEnum::C === //
191        let mut data = DynamicStruct::default();
192        data.insert("foo", 1.23_f32);
193        data.insert("bar", true);
194        dyn_enum.set_variant("C", data);
195        value.apply(&dyn_enum);
196        assert_eq!(
197            MyEnum::C {
198                foo: 1.23,
199                bar: true
200            },
201            value
202        );
203
204        // === MyEnum::C -> MyEnum::B === //
205        let mut data = DynamicTuple::default();
206        data.insert(123_usize);
207        data.insert(321_i32);
208        dyn_enum.set_variant("B", data);
209        value.apply(&dyn_enum);
210        assert_eq!(MyEnum::B(123, 321), value);
211
212        // === MyEnum::B -> MyEnum::A === //
213        dyn_enum.set_variant("A", ());
214        value.apply(&dyn_enum);
215        assert_eq!(MyEnum::A, value);
216    }
217
218    #[test]
219    fn dynamic_enum_should_return_is_dynamic() {
220        let dyn_enum = DynamicEnum::try_from(MyEnum::B(123, 321)).unwrap();
221        assert!(dyn_enum.is_dynamic());
222    }
223
224    #[test]
225    fn enum_should_iterate_fields() {
226        // === Unit === //
227        let value: &dyn Enum = &MyEnum::A;
228        assert_eq!(0, value.field_len());
229        let mut iter = value.iter_fields();
230        assert!(iter.next().is_none());
231
232        // === Tuple === //
233        let value: &dyn Enum = &MyEnum::B(123, 321);
234        assert_eq!(2, value.field_len());
235        let mut iter = value.iter_fields();
236        assert!(iter
237            .next()
238            .and_then(|field| field.value().reflect_partial_eq(&123_usize))
239            .unwrap_or_default());
240        assert!(iter
241            .next()
242            .and_then(|field| field.value().reflect_partial_eq(&321_i32))
243            .unwrap_or_default());
244
245        // === Struct === //
246        let value: &dyn Enum = &MyEnum::C {
247            foo: 1.23,
248            bar: true,
249        };
250        assert_eq!(2, value.field_len());
251        let mut iter = value.iter_fields();
252        assert!(iter
253            .next()
254            .and_then(|field| field
255                .value()
256                .reflect_partial_eq(&1.23_f32)
257                .and(field.name().map(|name| name == "foo")))
258            .unwrap_or_default());
259        assert!(iter
260            .next()
261            .and_then(|field| field
262                .value()
263                .reflect_partial_eq(&true)
264                .and(field.name().map(|name| name == "bar")))
265            .unwrap_or_default());
266    }
267
268    #[test]
269    fn enum_should_return_correct_variant_type() {
270        // === Unit === //
271        let value = MyEnum::A;
272        assert_eq!(VariantType::Unit, value.variant_type());
273
274        // === Tuple === //
275        let value = MyEnum::B(0, 0);
276        assert_eq!(VariantType::Tuple, value.variant_type());
277
278        // === Struct === //
279        let value = MyEnum::C {
280            foo: 1.23,
281            bar: true,
282        };
283        assert_eq!(VariantType::Struct, value.variant_type());
284    }
285
286    #[test]
287    fn enum_should_return_correct_variant_path() {
288        // === Unit === //
289        let value = MyEnum::A;
290        assert_eq!(
291            "bevy_reflect::enums::tests::MyEnum::A",
292            value.variant_path()
293        );
294
295        // === Tuple === //
296        let value = MyEnum::B(0, 0);
297        assert_eq!(
298            "bevy_reflect::enums::tests::MyEnum::B",
299            value.variant_path()
300        );
301
302        // === Struct === //
303        let value = MyEnum::C {
304            foo: 1.23,
305            bar: true,
306        };
307        assert_eq!(
308            "bevy_reflect::enums::tests::MyEnum::C",
309            value.variant_path()
310        );
311    }
312
313    #[test]
314    #[should_panic(
315        expected = "called `Result::unwrap()` on an `Err` value: MismatchedKinds { from_kind: Tuple, to_kind: Enum }"
316    )]
317    fn applying_non_enum_should_panic() {
318        let mut value = MyEnum::B(0, 0);
319        let mut dyn_tuple = DynamicTuple::default();
320        dyn_tuple.insert((123_usize, 321_i32));
321        value.apply(&dyn_tuple);
322    }
323
324    #[test]
325    fn enum_try_apply_should_detect_type_mismatch() {
326        #[derive(Reflect, Debug, PartialEq)]
327        enum MyEnumAnalogue {
328            A(u32),
329            B(usize, usize),
330            C { foo: f32, bar: u8 },
331        }
332
333        let mut target = MyEnumAnalogue::A(0);
334
335        // === Tuple === //
336        let result = target.try_apply(&MyEnum::B(0, 1));
337        assert!(
338            matches!(result, Err(ApplyError::MismatchedTypes { .. })),
339            "`result` was {result:?}"
340        );
341
342        // === Struct === //
343        target = MyEnumAnalogue::C { foo: 0.0, bar: 1 };
344        let result = target.try_apply(&MyEnum::C {
345            foo: 1.0,
346            bar: true,
347        });
348        assert!(
349            matches!(result, Err(ApplyError::MismatchedTypes { .. })),
350            "`result` was {result:?}"
351        );
352        // Type mismatch should occur after partial application.
353        assert_eq!(target, MyEnumAnalogue::C { foo: 1.0, bar: 1 });
354    }
355
356    #[test]
357    fn should_skip_ignored_fields() {
358        #[derive(Reflect, Debug, PartialEq)]
359        enum TestEnum {
360            A,
361            B,
362            C {
363                #[reflect(ignore)]
364                foo: f32,
365                bar: bool,
366            },
367        }
368
369        if let TypeInfo::Enum(info) = TestEnum::type_info() {
370            assert_eq!(3, info.variant_len());
371            if let VariantInfo::Struct(variant) = info.variant("C").unwrap() {
372                assert_eq!(
373                    1,
374                    variant.field_len(),
375                    "expected one of the fields to be ignored"
376                );
377                assert!(variant.field_at(0).unwrap().is::<bool>());
378            } else {
379                panic!("expected `VariantInfo::Struct`");
380            }
381        } else {
382            panic!("expected `TypeInfo::Enum`");
383        }
384    }
385
386    #[test]
387    fn enum_should_allow_generics() {
388        #[derive(Reflect, Debug, PartialEq)]
389        enum TestEnum<T: FromReflect> {
390            A,
391            B(T),
392            C { value: T },
393        }
394
395        if let TypeInfo::Enum(info) = TestEnum::<f32>::type_info() {
396            if let VariantInfo::Tuple(variant) = info.variant("B").unwrap() {
397                assert!(variant.field_at(0).unwrap().is::<f32>());
398            } else {
399                panic!("expected `VariantInfo::Struct`");
400            }
401            if let VariantInfo::Struct(variant) = info.variant("C").unwrap() {
402                assert!(variant.field("value").unwrap().is::<f32>());
403            } else {
404                panic!("expected `VariantInfo::Struct`");
405            }
406        } else {
407            panic!("expected `TypeInfo::Enum`");
408        }
409
410        let mut value = TestEnum::<f32>::A;
411
412        // === Tuple === //
413        let mut data = DynamicTuple::default();
414        data.insert(1.23_f32);
415        let dyn_enum = DynamicEnum::new("B", data);
416        value.apply(&dyn_enum);
417        assert_eq!(TestEnum::B(1.23), value);
418
419        // === Struct === //
420        let mut data = DynamicStruct::default();
421        data.insert("value", 1.23_f32);
422        let dyn_enum = DynamicEnum::new("C", data);
423        value.apply(&dyn_enum);
424        assert_eq!(TestEnum::C { value: 1.23 }, value);
425    }
426
427    #[test]
428    fn enum_should_allow_struct_fields() {
429        #[derive(Reflect, Debug, PartialEq)]
430        enum TestEnum {
431            A,
432            B(TestStruct),
433            C { value: TestStruct },
434        }
435
436        #[derive(Reflect, Debug, PartialEq)]
437        struct TestStruct(usize);
438
439        let mut value = TestEnum::A;
440
441        // === Tuple === //
442        let mut data = DynamicTuple::default();
443        data.insert(TestStruct(123));
444        let dyn_enum = DynamicEnum::new("B", data);
445        value.apply(&dyn_enum);
446        assert_eq!(TestEnum::B(TestStruct(123)), value);
447
448        // === Struct === //
449        let mut data = DynamicStruct::default();
450        data.insert("value", TestStruct(123));
451        let dyn_enum = DynamicEnum::new("C", data);
452        value.apply(&dyn_enum);
453        assert_eq!(
454            TestEnum::C {
455                value: TestStruct(123)
456            },
457            value
458        );
459    }
460
461    #[test]
462    fn enum_should_allow_nesting_enums() {
463        #[derive(Reflect, Debug, PartialEq)]
464        enum TestEnum {
465            A,
466            B(OtherEnum),
467            C { value: OtherEnum },
468        }
469
470        #[derive(Reflect, Debug, PartialEq)]
471        enum OtherEnum {
472            A,
473            B(usize),
474            C { value: f32 },
475        }
476
477        let mut value = TestEnum::A;
478
479        // === Tuple === //
480        let mut data = DynamicTuple::default();
481        data.insert(OtherEnum::B(123));
482        let dyn_enum = DynamicEnum::new("B", data);
483        value.apply(&dyn_enum);
484        assert_eq!(TestEnum::B(OtherEnum::B(123)), value);
485
486        // === Struct === //
487        let mut data = DynamicStruct::default();
488        data.insert("value", OtherEnum::C { value: 1.23 });
489        let dyn_enum = DynamicEnum::new("C", data);
490        value.apply(&dyn_enum);
491        assert_eq!(
492            TestEnum::C {
493                value: OtherEnum::C { value: 1.23 }
494            },
495            value
496        );
497    }
498
499    #[test]
500    fn enum_should_apply() {
501        let mut value: Box<dyn Reflect> = Box::new(MyEnum::A);
502
503        // === MyEnum::A -> MyEnum::A === //
504        value.apply(&MyEnum::A);
505        assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default());
506
507        // === MyEnum::A -> MyEnum::B === //
508        value.apply(&MyEnum::B(123, 321));
509        assert!(value
510            .reflect_partial_eq(&MyEnum::B(123, 321))
511            .unwrap_or_default());
512
513        // === MyEnum::B -> MyEnum::B === //
514        value.apply(&MyEnum::B(321, 123));
515        assert!(value
516            .reflect_partial_eq(&MyEnum::B(321, 123))
517            .unwrap_or_default());
518
519        // === MyEnum::B -> MyEnum::C === //
520        value.apply(&MyEnum::C {
521            foo: 1.23,
522            bar: true,
523        });
524        assert!(value
525            .reflect_partial_eq(&MyEnum::C {
526                foo: 1.23,
527                bar: true
528            })
529            .unwrap_or_default());
530
531        // === MyEnum::C -> MyEnum::C === //
532        value.apply(&MyEnum::C {
533            foo: 3.21,
534            bar: false,
535        });
536        assert!(value
537            .reflect_partial_eq(&MyEnum::C {
538                foo: 3.21,
539                bar: false
540            })
541            .unwrap_or_default());
542
543        // === MyEnum::C -> MyEnum::B === //
544        value.apply(&MyEnum::B(123, 321));
545        assert!(value
546            .reflect_partial_eq(&MyEnum::B(123, 321))
547            .unwrap_or_default());
548
549        // === MyEnum::B -> MyEnum::A === //
550        value.apply(&MyEnum::A);
551        assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default());
552    }
553
554    #[test]
555    fn enum_should_set() {
556        let mut value: Box<dyn Reflect> = Box::new(MyEnum::A);
557
558        // === MyEnum::A -> MyEnum::A === //
559        value.set(Box::new(MyEnum::A)).unwrap();
560        assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default());
561
562        // === MyEnum::A -> MyEnum::B === //
563        value.set(Box::new(MyEnum::B(123, 321))).unwrap();
564        assert!(value
565            .reflect_partial_eq(&MyEnum::B(123, 321))
566            .unwrap_or_default());
567
568        // === MyEnum::B -> MyEnum::B === //
569        value.set(Box::new(MyEnum::B(321, 123))).unwrap();
570        assert!(value
571            .reflect_partial_eq(&MyEnum::B(321, 123))
572            .unwrap_or_default());
573
574        // === MyEnum::B -> MyEnum::C === //
575        value
576            .set(Box::new(MyEnum::C {
577                foo: 1.23,
578                bar: true,
579            }))
580            .unwrap();
581        assert!(value
582            .reflect_partial_eq(&MyEnum::C {
583                foo: 1.23,
584                bar: true
585            })
586            .unwrap_or_default());
587
588        // === MyEnum::C -> MyEnum::C === //
589        value
590            .set(Box::new(MyEnum::C {
591                foo: 3.21,
592                bar: false,
593            }))
594            .unwrap();
595        assert!(value
596            .reflect_partial_eq(&MyEnum::C {
597                foo: 3.21,
598                bar: false
599            })
600            .unwrap_or_default());
601
602        // === MyEnum::C -> MyEnum::B === //
603        value.set(Box::new(MyEnum::B(123, 321))).unwrap();
604        assert!(value
605            .reflect_partial_eq(&MyEnum::B(123, 321))
606            .unwrap_or_default());
607
608        // === MyEnum::B -> MyEnum::A === //
609        value.set(Box::new(MyEnum::A)).unwrap();
610        assert!(value.reflect_partial_eq(&MyEnum::A).unwrap_or_default());
611    }
612
613    #[test]
614    fn enum_should_partial_eq() {
615        #[derive(Reflect)]
616        enum TestEnum {
617            A,
618            A1,
619            B(usize),
620            B1(usize),
621            B2(usize, usize),
622            C { value: i32 },
623            C1 { value: i32 },
624            C2 { value: f32 },
625        }
626
627        let a: &dyn PartialReflect = &TestEnum::A;
628        let b: &dyn PartialReflect = &TestEnum::A;
629        assert!(
630            a.reflect_partial_eq(b).unwrap_or_default(),
631            "expected TestEnum::A == TestEnum::A"
632        );
633
634        let a: &dyn PartialReflect = &TestEnum::A;
635        let b: &dyn PartialReflect = &TestEnum::A1;
636        assert!(
637            !a.reflect_partial_eq(b).unwrap_or_default(),
638            "expected TestEnum::A != TestEnum::A1"
639        );
640
641        let a: &dyn PartialReflect = &TestEnum::B(123);
642        let b: &dyn PartialReflect = &TestEnum::B(123);
643        assert!(
644            a.reflect_partial_eq(b).unwrap_or_default(),
645            "expected TestEnum::B(123) == TestEnum::B(123)"
646        );
647
648        let a: &dyn PartialReflect = &TestEnum::B(123);
649        let b: &dyn PartialReflect = &TestEnum::B(321);
650        assert!(
651            !a.reflect_partial_eq(b).unwrap_or_default(),
652            "expected TestEnum::B(123) != TestEnum::B(321)"
653        );
654
655        let a: &dyn PartialReflect = &TestEnum::B(123);
656        let b: &dyn PartialReflect = &TestEnum::B1(123);
657        assert!(
658            !a.reflect_partial_eq(b).unwrap_or_default(),
659            "expected TestEnum::B(123) != TestEnum::B1(123)"
660        );
661
662        let a: &dyn PartialReflect = &TestEnum::B(123);
663        let b: &dyn PartialReflect = &TestEnum::B2(123, 123);
664        assert!(
665            !a.reflect_partial_eq(b).unwrap_or_default(),
666            "expected TestEnum::B(123) != TestEnum::B2(123, 123)"
667        );
668
669        let a: &dyn PartialReflect = &TestEnum::C { value: 123 };
670        let b: &dyn PartialReflect = &TestEnum::C { value: 123 };
671        assert!(
672            a.reflect_partial_eq(b).unwrap_or_default(),
673            "expected TestEnum::C{{value: 123}} == TestEnum::C{{value: 123}}"
674        );
675
676        let a: &dyn PartialReflect = &TestEnum::C { value: 123 };
677        let b: &dyn PartialReflect = &TestEnum::C { value: 321 };
678        assert!(
679            !a.reflect_partial_eq(b).unwrap_or_default(),
680            "expected TestEnum::C{{value: 123}} != TestEnum::C{{value: 321}}"
681        );
682
683        let a: &dyn PartialReflect = &TestEnum::C { value: 123 };
684        let b: &dyn PartialReflect = &TestEnum::C1 { value: 123 };
685        assert!(
686            !a.reflect_partial_eq(b).unwrap_or_default(),
687            "expected TestEnum::C{{value: 123}} != TestEnum::C1{{value: 123}}"
688        );
689
690        let a: &dyn PartialReflect = &TestEnum::C { value: 123 };
691        let b: &dyn PartialReflect = &TestEnum::C2 { value: 1.23 };
692        assert!(
693            !a.reflect_partial_eq(b).unwrap_or_default(),
694            "expected TestEnum::C{{value: 123}} != TestEnum::C2{{value: 1.23}}"
695        );
696
697        #[derive(Reflect)]
698        enum TestEnum2 {
699            A,
700            A1,
701            B(usize, usize),
702            C { value: i32, value2: f32 },
703        }
704        let a: &dyn PartialReflect = &TestEnum::C { value: 123 };
705        let a2: &dyn PartialReflect = &TestEnum2::C {
706            value: 123,
707            value2: 1.23,
708        };
709        assert!(
710            !a.reflect_partial_eq(a2).unwrap_or_default(),
711            "expected TestEnum::C{{value: 123}} != TestEnum2::C{{value: 123, value2: 1.23}}"
712        );
713        let b: &dyn PartialReflect = &TestEnum::B(123);
714        let b2 = &TestEnum2::B(123, 321);
715        assert!(
716            !b.reflect_partial_eq(b2).unwrap_or_default(),
717            "expected TestEnum::C{{value: 123}} != TestEnum2::B(123, 321)"
718        );
719    }
720}