Skip to main content

bevy_reflect/enums/
helpers.rs

1use crate::{
2    enums::{Enum, VariantType},
3    utility::reflect_hasher,
4    PartialReflect, ReflectRef,
5};
6use core::{
7    fmt::Debug,
8    hash::{Hash, Hasher},
9};
10
11/// Returns the `u64` hash of the given [enum](Enum).
12#[inline(never)]
13pub fn enum_hash(value: &dyn Enum) -> Option<u64> {
14    let mut hasher = reflect_hasher();
15    core::any::Any::type_id(value).hash(&mut hasher);
16    value.variant_name().hash(&mut hasher);
17    value.variant_type().hash(&mut hasher);
18    for field in value.iter_fields() {
19        hasher.write_u64(field.value().reflect_hash()?);
20    }
21    Some(hasher.finish())
22}
23
24/// Compares an [`Enum`] with a [`PartialReflect`] value.
25///
26/// Returns true if and only if all of the following are true:
27/// - `b` is an enum;
28/// - `b` is the same variant as `a`;
29/// - For each field in `a`, `b` contains a field with the same name and
30///   [`PartialReflect::reflect_partial_eq`] returns `Some(true)` for the two field
31///   values.
32#[inline(never)]
33pub fn enum_partial_eq(a: &dyn Enum, b: &dyn PartialReflect) -> Option<bool> {
34    // Both enums?
35    let ReflectRef::Enum(b) = b.reflect_ref() else {
36        return Some(false);
37    };
38
39    // Same variant name?
40    if a.variant_name() != b.variant_name() {
41        return Some(false);
42    }
43
44    // Same variant type?
45    if !a.is_variant(b.variant_type()) {
46        return Some(false);
47    }
48
49    match a.variant_type() {
50        VariantType::Struct => {
51            if a.field_len() != b.field_len() {
52                return Some(false);
53            }
54            // Same struct fields?
55            for field in a.iter_fields() {
56                let field_name = field.name().unwrap();
57                if let Some(field_value) = b.field(field_name) {
58                    if let Some(false) | None = field_value.reflect_partial_eq(field.value()) {
59                        // Fields failed comparison
60                        return Some(false);
61                    }
62                } else {
63                    // Field does not exist
64                    return Some(false);
65                }
66            }
67            Some(true)
68        }
69        VariantType::Tuple => {
70            if a.field_len() != b.field_len() {
71                return Some(false);
72            }
73            // Same tuple fields?
74            for (i, field) in a.iter_fields().enumerate() {
75                if let Some(field_value) = b.field_at(i) {
76                    if let Some(false) | None = field_value.reflect_partial_eq(field.value()) {
77                        // Fields failed comparison
78                        return Some(false);
79                    }
80                } else {
81                    // Field does not exist
82                    return Some(false);
83                }
84            }
85            Some(true)
86        }
87        _ => Some(true),
88    }
89}
90
91/// Compares two [`Enum`] values (by variant) and returns their ordering.
92///
93/// Returns [`None`] if the comparison couldn't be performed (e.g., kinds mismatch
94/// or an element comparison returns `None`).
95///
96/// The ordering is same with `derive` macro. First order by variant index, then by fields.
97#[inline(never)]
98pub fn enum_partial_cmp(a: &dyn Enum, b: &dyn PartialReflect) -> Option<::core::cmp::Ordering> {
99    // Both enums?
100    let ReflectRef::Enum(b) = b.reflect_ref() else {
101        return None;
102    };
103
104    // Same variant name?
105    if a.variant_name() != b.variant_name() {
106        // Different variant names.
107        // Ordering by variant index here can result in inconsistencies with
108        // partial_eq when comparing between two different concrete enums,
109        // so we simply return None here
110        return None;
111    }
112
113    // Same variant type?
114    if !a.is_variant(b.variant_type()) {
115        return None;
116    }
117
118    match a.variant_type() {
119        VariantType::Struct => {
120            if a.field_len() != b.field_len() {
121                return None;
122            }
123            crate::structs::partial_cmp_by_field_names(
124                a.field_len(),
125                |i| a.name_at(i),
126                |i| a.field_at(i),
127                |i| b.name_at(i),
128                |i| b.field_at(i),
129                |name| b.field(name),
130            )
131        }
132        VariantType::Tuple => {
133            if a.field_len() != b.field_len() {
134                return None;
135            }
136            for (i, field) in a.iter_fields().enumerate() {
137                if let Some(field_value) = b.field_at(i) {
138                    match field.value().reflect_partial_cmp(field_value) {
139                        None => return None,
140                        Some(core::cmp::Ordering::Equal) => continue,
141                        Some(ord) => return Some(ord),
142                    }
143                }
144                return None;
145            }
146            Some(core::cmp::Ordering::Equal)
147        }
148        _ => Some(core::cmp::Ordering::Equal),
149    }
150}
151
152/// The default debug formatter for [`Enum`] types.
153///
154/// # Example
155/// ```
156/// use bevy_reflect::Reflect;
157/// #[derive(Reflect)]
158/// enum MyEnum {
159///   A,
160///   B (usize),
161///   C {value: i32}
162/// }
163///
164/// let my_enum: &dyn Reflect = &MyEnum::B(123);
165/// println!("{:#?}", my_enum);
166///
167/// // Output:
168///
169/// // B (
170/// //   123,
171/// // )
172/// ```
173#[inline]
174pub fn enum_debug(dyn_enum: &dyn Enum, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
175    match dyn_enum.variant_type() {
176        VariantType::Unit => f.write_str(dyn_enum.variant_name()),
177        VariantType::Tuple => {
178            let mut debug = f.debug_tuple(dyn_enum.variant_name());
179            for field in dyn_enum.iter_fields() {
180                debug.field(&field.value() as &dyn Debug);
181            }
182            debug.finish()
183        }
184        VariantType::Struct => {
185            let mut debug = f.debug_struct(dyn_enum.variant_name());
186            for field in dyn_enum.iter_fields() {
187                debug.field(field.name().unwrap(), &field.value() as &dyn Debug);
188            }
189            debug.finish()
190        }
191    }
192}