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