Path: blob/main/crates/bevy_reflect/src/enums/enum_trait.rs
6599 views
use crate::generics::impl_generic_info_methods;1use crate::{2attributes::{impl_custom_attribute_methods, CustomAttributes},3type_info::impl_type_methods,4DynamicEnum, Generics, PartialReflect, Type, TypePath, VariantInfo, VariantType,5};6use alloc::{boxed::Box, format, string::String};7use bevy_platform::collections::HashMap;8use bevy_platform::sync::Arc;9use core::slice::Iter;1011/// A trait used to power [enum-like] operations via [reflection].12///13/// This allows enums to be processed and modified dynamically at runtime without14/// necessarily knowing the actual type.15/// Enums are much more complex than their struct counterparts.16/// As a result, users will need to be mindful of conventions, considerations,17/// and complications when working with this trait.18///19/// # Variants20///21/// An enum is a set of choices called _variants_.22/// An instance of an enum can only exist as one of these choices at any given time.23/// Consider Rust's [`Option<T>`]. It's an enum with two variants: [`None`] and [`Some`].24/// If you're `None`, you can't be `Some` and vice versa.25///26/// > ⚠️ __This is very important:__27/// > The [`Enum`] trait represents an enum _as one of its variants_.28/// > It does not represent the entire enum since that's not true to how enums work.29///30/// Variants come in a few [flavors](VariantType):31///32/// | Variant Type | Syntax |33/// | ------------ | ------------------------------ |34/// | Unit | `MyEnum::Foo` |35/// | Tuple | `MyEnum::Foo( i32, i32 )` |36/// | Struct | `MyEnum::Foo{ value: String }` |37///38/// As you can see, a unit variant contains no fields, while tuple and struct variants39/// can contain one or more fields.40/// The fields in a tuple variant is defined by their _order_ within the variant.41/// Index `0` represents the first field in the variant and so on.42/// Fields in struct variants (excluding tuple structs), on the other hand, are43/// represented by a _name_.44///45/// # Implementation46///47/// > 💡 This trait can be automatically implemented using [`#[derive(Reflect)]`](derive@crate::Reflect)48/// > on an enum definition.49///50/// Despite the fact that enums can represent multiple states, traits only exist in one state51/// and must be applied to the entire enum rather than a particular variant.52/// Because of this limitation, the [`Enum`] trait must not only _represent_ any of the53/// three variant types, but also define the _methods_ for all three as well.54///55/// What does this mean? It means that even though a unit variant contains no fields, a56/// representation of that variant using the [`Enum`] trait will still contain methods for57/// accessing fields!58/// Again, this is to account for _all three_ variant types.59///60/// We recommend using the built-in [`#[derive(Reflect)]`](derive@crate::Reflect) macro to automatically handle all the61/// implementation details for you.62/// However, if you _must_ implement this trait manually, there are a few things to keep in mind...63///64/// ## Field Order65///66/// While tuple variants identify their fields by the order in which they are defined, struct67/// variants identify fields by their name.68/// However, both should allow access to fields by their defined order.69///70/// The reason all fields, regardless of variant type, need to be accessible by their order is71/// due to field iteration.72/// We need a way to iterate through each field in a variant, and the easiest way of achieving73/// that is through the use of field order.74///75/// The derive macro adds proper struct variant handling for [`Enum::index_of`], [`Enum::name_at`]76/// and [`Enum::field_at[_mut]`](Enum::field_at) methods.77/// The first two methods are __required__ for all struct variant types.78/// By convention, implementors should also handle the last method as well, but this is not79/// a strict requirement.80///81/// ## Field Names82///83/// Implementors may choose to handle [`Enum::index_of`], [`Enum::name_at`], and84/// [`Enum::field[_mut]`](Enum::field) for tuple variants by considering stringified `usize`s to be85/// valid names (such as `"3"`).86/// This isn't wrong to do, but the convention set by the derive macro is that it isn't supported.87/// It's preferred that these strings be converted to their proper `usize` representations and88/// the [`Enum::field_at[_mut]`](Enum::field_at) methods be used instead.89///90/// [enum-like]: https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html91/// [reflection]: crate92/// [`None`]: Option<T>::None93/// [`Some`]: Option<T>::Some94/// [`Reflect`]: bevy_reflect_derive::Reflect95pub trait Enum: PartialReflect {96/// Returns a reference to the value of the field (in the current variant) with the given name.97///98/// For non-[`VariantType::Struct`] variants, this should return `None`.99fn field(&self, name: &str) -> Option<&dyn PartialReflect>;100/// Returns a reference to the value of the field (in the current variant) at the given index.101fn field_at(&self, index: usize) -> Option<&dyn PartialReflect>;102/// Returns a mutable reference to the value of the field (in the current variant) with the given name.103///104/// For non-[`VariantType::Struct`] variants, this should return `None`.105fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect>;106/// Returns a mutable reference to the value of the field (in the current variant) at the given index.107fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect>;108/// Returns the index of the field (in the current variant) with the given name.109///110/// For non-[`VariantType::Struct`] variants, this should return `None`.111fn index_of(&self, name: &str) -> Option<usize>;112/// Returns the name of the field (in the current variant) with the given index.113///114/// For non-[`VariantType::Struct`] variants, this should return `None`.115fn name_at(&self, index: usize) -> Option<&str>;116/// Returns an iterator over the values of the current variant's fields.117fn iter_fields(&self) -> VariantFieldIter<'_>;118/// Returns the number of fields in the current variant.119fn field_len(&self) -> usize;120/// The name of the current variant.121fn variant_name(&self) -> &str;122/// The index of the current variant.123fn variant_index(&self) -> usize;124/// The type of the current variant.125fn variant_type(&self) -> VariantType;126/// Creates a new [`DynamicEnum`] from this enum.127fn to_dynamic_enum(&self) -> DynamicEnum {128DynamicEnum::from_ref(self)129}130/// Returns true if the current variant's type matches the given one.131fn is_variant(&self, variant_type: VariantType) -> bool {132self.variant_type() == variant_type133}134/// Returns the full path to the current variant.135fn variant_path(&self) -> String {136format!("{}::{}", self.reflect_type_path(), self.variant_name())137}138139/// Will return `None` if [`TypeInfo`] is not available.140///141/// [`TypeInfo`]: crate::TypeInfo142fn get_represented_enum_info(&self) -> Option<&'static EnumInfo> {143self.get_represented_type_info()?.as_enum().ok()144}145}146147/// A container for compile-time enum info, used by [`TypeInfo`](crate::TypeInfo).148#[derive(Clone, Debug)]149pub struct EnumInfo {150ty: Type,151generics: Generics,152variants: Box<[VariantInfo]>,153variant_names: Box<[&'static str]>,154variant_indices: HashMap<&'static str, usize>,155custom_attributes: Arc<CustomAttributes>,156#[cfg(feature = "documentation")]157docs: Option<&'static str>,158}159160impl EnumInfo {161/// Create a new [`EnumInfo`].162///163/// # Arguments164///165/// * `variants`: The variants of this enum in the order they are defined166pub fn new<TEnum: Enum + TypePath>(variants: &[VariantInfo]) -> Self {167let variant_indices = variants168.iter()169.enumerate()170.map(|(index, variant)| (variant.name(), index))171.collect::<HashMap<_, _>>();172173let variant_names = variants.iter().map(VariantInfo::name).collect();174175Self {176ty: Type::of::<TEnum>(),177generics: Generics::new(),178variants: variants.to_vec().into_boxed_slice(),179variant_names,180variant_indices,181custom_attributes: Arc::new(CustomAttributes::default()),182#[cfg(feature = "documentation")]183docs: None,184}185}186187/// Sets the docstring for this enum.188#[cfg(feature = "documentation")]189pub fn with_docs(self, docs: Option<&'static str>) -> Self {190Self { docs, ..self }191}192193/// Sets the custom attributes for this enum.194pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {195Self {196custom_attributes: Arc::new(custom_attributes),197..self198}199}200201/// A slice containing the names of all variants in order.202pub fn variant_names(&self) -> &[&'static str] {203&self.variant_names204}205206/// Get a variant with the given name.207pub fn variant(&self, name: &str) -> Option<&VariantInfo> {208self.variant_indices209.get(name)210.map(|index| &self.variants[*index])211}212213/// Get a variant at the given index.214pub fn variant_at(&self, index: usize) -> Option<&VariantInfo> {215self.variants.get(index)216}217218/// Get the index of the variant with the given name.219pub fn index_of(&self, name: &str) -> Option<usize> {220self.variant_indices.get(name).copied()221}222223/// Returns the full path to the given variant.224///225/// This does _not_ check if the given variant exists.226pub fn variant_path(&self, name: &str) -> String {227format!("{}::{name}", self.type_path())228}229230/// Checks if a variant with the given name exists within this enum.231pub fn contains_variant(&self, name: &str) -> bool {232self.variant_indices.contains_key(name)233}234235/// Iterate over the variants of this enum.236pub fn iter(&self) -> Iter<'_, VariantInfo> {237self.variants.iter()238}239240/// The number of variants in this enum.241pub fn variant_len(&self) -> usize {242self.variants.len()243}244245impl_type_methods!(ty);246247/// The docstring of this enum, if any.248#[cfg(feature = "documentation")]249pub fn docs(&self) -> Option<&'static str> {250self.docs251}252253impl_custom_attribute_methods!(self.custom_attributes, "enum");254255impl_generic_info_methods!(generics);256}257258/// An iterator over the fields in the current enum variant.259pub struct VariantFieldIter<'a> {260container: &'a dyn Enum,261index: usize,262}263264impl<'a> VariantFieldIter<'a> {265/// Creates a new [`VariantFieldIter`].266pub fn new(container: &'a dyn Enum) -> Self {267Self {268container,269index: 0,270}271}272}273274impl<'a> Iterator for VariantFieldIter<'a> {275type Item = VariantField<'a>;276277fn next(&mut self) -> Option<Self::Item> {278let value = match self.container.variant_type() {279VariantType::Unit => None,280VariantType::Tuple => Some(VariantField::Tuple(self.container.field_at(self.index)?)),281VariantType::Struct => {282let name = self.container.name_at(self.index)?;283Some(VariantField::Struct(name, self.container.field(name)?))284}285};286self.index += value.is_some() as usize;287value288}289290fn size_hint(&self) -> (usize, Option<usize>) {291let size = self.container.field_len();292(size, Some(size))293}294}295296impl<'a> ExactSizeIterator for VariantFieldIter<'a> {}297298/// A field in the current enum variant.299pub enum VariantField<'a> {300/// The name and value of a field in a struct variant.301Struct(&'a str, &'a dyn PartialReflect),302/// The value of a field in a tuple variant.303Tuple(&'a dyn PartialReflect),304}305306impl<'a> VariantField<'a> {307/// Returns the name of a struct variant field, or [`None`] for a tuple variant field.308pub fn name(&self) -> Option<&'a str> {309if let Self::Struct(name, ..) = self {310Some(*name)311} else {312None313}314}315316/// Gets a reference to the value of this field.317pub fn value(&self) -> &'a dyn PartialReflect {318match *self {319Self::Struct(_, value) | Self::Tuple(value) => value,320}321}322}323324// Tests that need access to internal fields have to go here rather than in mod.rs325#[cfg(test)]326mod tests {327use crate::*;328329#[derive(Reflect, Debug, PartialEq)]330enum MyEnum {331A,332B(usize, i32),333C { foo: f32, bar: bool },334}335#[test]336fn next_index_increment() {337// unit enums always return none, so index should stay at 0338let unit_enum = MyEnum::A;339let mut iter = unit_enum.iter_fields();340let size = iter.len();341for _ in 0..2 {342assert!(iter.next().is_none());343assert_eq!(size, iter.index);344}345// tuple enums we iter over each value (unnamed fields), stop after that346let tuple_enum = MyEnum::B(0, 1);347let mut iter = tuple_enum.iter_fields();348let size = iter.len();349for _ in 0..2 {350let prev_index = iter.index;351assert!(iter.next().is_some());352assert_eq!(prev_index, iter.index - 1);353}354for _ in 0..2 {355assert!(iter.next().is_none());356assert_eq!(size, iter.index);357}358359// struct enums, we iterate over each field in the struct360let struct_enum = MyEnum::C {361foo: 0.,362bar: false,363};364let mut iter = struct_enum.iter_fields();365let size = iter.len();366for _ in 0..2 {367let prev_index = iter.index;368assert!(iter.next().is_some());369assert_eq!(prev_index, iter.index - 1);370}371for _ in 0..2 {372assert!(iter.next().is_none());373assert_eq!(size, iter.index);374}375}376}377378379