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