Path: blob/main/crates/bevy_reflect/src/enums/enum_trait.rs
9374 views
use crate::generics::impl_generic_info_methods;1use crate::{2attributes::{impl_custom_attribute_methods, CustomAttributes},3enums::{DynamicEnum, VariantInfo, VariantType},4type_info::impl_type_methods,5Generics, PartialReflect, Type, TypePath,6};7use alloc::{boxed::Box, format, string::String};8use bevy_platform::collections::HashMap;9use bevy_platform::sync::Arc;10use core::slice::Iter;1112/// A trait used to power [enum-like] operations via [reflection].13///14/// This allows enums to be processed and modified dynamically at runtime without15/// necessarily knowing the actual type.16/// Enums are much more complex than their struct counterparts.17/// As a result, users will need to be mindful of conventions, considerations,18/// and complications when working with this trait.19///20/// # Variants21///22/// An enum is a set of choices called _variants_.23/// An instance of an enum can only exist as one of these choices at any given time.24/// Consider Rust's [`Option<T>`]. It's an enum with two variants: [`None`] and [`Some`].25/// If you're `None`, you can't be `Some` and vice versa.26///27/// > ⚠️ __This is very important:__28/// > The [`Enum`] trait represents an enum _as one of its variants_.29/// > It does not represent the entire enum since that's not true to how enums work.30///31/// Variants come in a few [flavors](VariantType):32///33/// | Variant Type | Syntax |34/// | ------------ | ------------------------------ |35/// | Unit | `MyEnum::Foo` |36/// | Tuple | `MyEnum::Foo( i32, i32 )` |37/// | Struct | `MyEnum::Foo{ value: String }` |38///39/// As you can see, a unit variant contains no fields, while tuple and struct variants40/// can contain one or more fields.41/// The fields in a tuple variant is defined by their _order_ within the variant.42/// Index `0` represents the first field in the variant and so on.43/// Fields in struct variants (excluding tuple structs), on the other hand, are44/// represented by a _name_.45///46/// # Implementation47///48/// > 💡 This trait can be automatically implemented using [`#[derive(Reflect)]`](derive@crate::Reflect)49/// > on an enum definition.50///51/// Despite the fact that enums can represent multiple states, traits only exist in one state52/// and must be applied to the entire enum rather than a particular variant.53/// Because of this limitation, the [`Enum`] trait must not only _represent_ any of the54/// three variant types, but also define the _methods_ for all three as well.55///56/// What does this mean? It means that even though a unit variant contains no fields, a57/// representation of that variant using the [`Enum`] trait will still contain methods for58/// accessing fields!59/// Again, this is to account for _all three_ variant types.60///61/// We recommend using the built-in [`#[derive(Reflect)]`](derive@crate::Reflect) macro to automatically handle all the62/// implementation details for you.63/// However, if you _must_ implement this trait manually, there are a few things to keep in mind...64///65/// ## Field Order66///67/// While tuple variants identify their fields by the order in which they are defined, struct68/// variants identify fields by their name.69/// However, both should allow access to fields by their defined order.70///71/// The reason all fields, regardless of variant type, need to be accessible by their order is72/// due to field iteration.73/// We need a way to iterate through each field in a variant, and the easiest way of achieving74/// that is through the use of field order.75///76/// The derive macro adds proper struct variant handling for [`Enum::index_of`], [`Enum::name_at`]77/// and [`Enum::field_at[_mut]`](Enum::field_at) methods.78/// The first two methods are __required__ for all struct variant types.79/// By convention, implementors should also handle the last method as well, but this is not80/// a strict requirement.81///82/// ## Field Names83///84/// Implementors may choose to handle [`Enum::index_of`], [`Enum::name_at`], and85/// [`Enum::field[_mut]`](Enum::field) for tuple variants by considering stringified `usize`s to be86/// valid names (such as `"3"`).87/// This isn't wrong to do, but the convention set by the derive macro is that it isn't supported.88/// It's preferred that these strings be converted to their proper `usize` representations and89/// the [`Enum::field_at[_mut]`](Enum::field_at) methods be used instead.90///91/// [enum-like]: https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html92/// [reflection]: crate93/// [`None`]: Option<T>::None94/// [`Some`]: Option<T>::Some95/// [`Reflect`]: bevy_reflect_derive::Reflect96pub trait Enum: PartialReflect {97/// Returns a reference to the value of the field (in the current variant) with the given name.98///99/// For non-[`VariantType::Struct`] variants, this should return `None`.100fn field(&self, name: &str) -> Option<&dyn PartialReflect>;101/// Returns a reference to the value of the field (in the current variant) at the given index.102fn field_at(&self, index: usize) -> Option<&dyn PartialReflect>;103/// Returns a mutable reference to the value of the field (in the current variant) with the given name.104///105/// For non-[`VariantType::Struct`] variants, this should return `None`.106fn field_mut(&mut self, name: &str) -> Option<&mut dyn PartialReflect>;107/// Returns a mutable reference to the value of the field (in the current variant) at the given index.108fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn PartialReflect>;109/// Returns the index of the field (in the current variant) with the given name.110///111/// For non-[`VariantType::Struct`] variants, this should return `None`.112fn index_of(&self, name: &str) -> Option<usize>;113/// Returns the name of the field (in the current variant) with the given index.114///115/// For non-[`VariantType::Struct`] variants, this should return `None`.116fn name_at(&self, index: usize) -> Option<&str>;117/// Returns an iterator over the values of the current variant's fields.118fn iter_fields(&self) -> VariantFieldIter<'_>;119/// Returns the number of fields in the current variant.120fn field_len(&self) -> usize;121/// The name of the current variant.122fn variant_name(&self) -> &str;123/// The index of the current variant.124fn variant_index(&self) -> usize;125/// The type of the current variant.126fn variant_type(&self) -> VariantType;127/// Creates a new [`DynamicEnum`] from this enum.128fn to_dynamic_enum(&self) -> DynamicEnum {129DynamicEnum::from_ref(self)130}131/// Returns true if the current variant's type matches the given one.132fn is_variant(&self, variant_type: VariantType) -> bool {133self.variant_type() == variant_type134}135/// Returns the full path to the current variant.136fn variant_path(&self) -> String {137format!("{}::{}", self.reflect_type_path(), self.variant_name())138}139140/// Will return `None` if [`TypeInfo`] is not available.141///142/// [`TypeInfo`]: crate::TypeInfo143fn get_represented_enum_info(&self) -> Option<&'static EnumInfo> {144self.get_represented_type_info()?.as_enum().ok()145}146}147148/// A container for compile-time enum info, used by [`TypeInfo`](crate::TypeInfo).149#[derive(Clone, Debug)]150pub struct EnumInfo {151ty: Type,152generics: Generics,153variants: Box<[VariantInfo]>,154variant_names: Box<[&'static str]>,155variant_indices: HashMap<&'static str, usize>,156custom_attributes: Arc<CustomAttributes>,157#[cfg(feature = "reflect_documentation")]158docs: Option<&'static str>,159}160161impl EnumInfo {162/// Create a new [`EnumInfo`].163///164/// # Arguments165///166/// * `variants`: The variants of this enum in the order they are defined167pub fn new<TEnum: Enum + TypePath>(variants: &[VariantInfo]) -> Self {168let variant_indices = variants169.iter()170.enumerate()171.map(|(index, variant)| (variant.name(), index))172.collect::<HashMap<_, _>>();173174let variant_names = variants.iter().map(VariantInfo::name).collect();175176Self {177ty: Type::of::<TEnum>(),178generics: Generics::new(),179variants: variants.to_vec().into_boxed_slice(),180variant_names,181variant_indices,182custom_attributes: Arc::new(CustomAttributes::default()),183#[cfg(feature = "reflect_documentation")]184docs: None,185}186}187188/// Sets the docstring for this enum.189#[cfg(feature = "reflect_documentation")]190pub fn with_docs(self, docs: Option<&'static str>) -> Self {191Self { docs, ..self }192}193194/// Sets the custom attributes for this enum.195pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {196Self {197custom_attributes: Arc::new(custom_attributes),198..self199}200}201202/// A slice containing the names of all variants in order.203pub fn variant_names(&self) -> &[&'static str] {204&self.variant_names205}206207/// Get a variant with the given name.208pub fn variant(&self, name: &str) -> Option<&VariantInfo> {209self.variant_indices210.get(name)211.map(|index| &self.variants[*index])212}213214/// Get a variant at the given index.215pub fn variant_at(&self, index: usize) -> Option<&VariantInfo> {216self.variants.get(index)217}218219/// Get the index of the variant with the given name.220pub fn index_of(&self, name: &str) -> Option<usize> {221self.variant_indices.get(name).copied()222}223224/// Returns the full path to the given variant.225///226/// This does _not_ check if the given variant exists.227pub fn variant_path(&self, name: &str) -> String {228format!("{}::{name}", self.type_path())229}230231/// Checks if a variant with the given name exists within this enum.232pub fn contains_variant(&self, name: &str) -> bool {233self.variant_indices.contains_key(name)234}235236/// Iterate over the variants of this enum.237pub fn iter(&self) -> Iter<'_, VariantInfo> {238self.variants.iter()239}240241/// The number of variants in this enum.242pub fn variant_len(&self) -> usize {243self.variants.len()244}245246impl_type_methods!(ty);247248/// The docstring of this enum, if any.249#[cfg(feature = "reflect_documentation")]250pub fn docs(&self) -> Option<&'static str> {251self.docs252}253254impl_custom_attribute_methods!(self.custom_attributes, "enum");255256impl_generic_info_methods!(generics);257}258259/// An iterator over the fields in the current enum variant.260pub struct VariantFieldIter<'a> {261container: &'a dyn Enum,262index: usize,263}264265impl<'a> VariantFieldIter<'a> {266/// Creates a new [`VariantFieldIter`].267pub fn new(container: &'a dyn Enum) -> Self {268Self {269container,270index: 0,271}272}273}274275impl<'a> Iterator for VariantFieldIter<'a> {276type Item = VariantField<'a>;277278fn next(&mut self) -> Option<Self::Item> {279let value = match self.container.variant_type() {280VariantType::Unit => None,281VariantType::Tuple => Some(VariantField::Tuple(self.container.field_at(self.index)?)),282VariantType::Struct => {283let name = self.container.name_at(self.index)?;284Some(VariantField::Struct(name, self.container.field(name)?))285}286};287self.index += value.is_some() as usize;288value289}290291fn size_hint(&self) -> (usize, Option<usize>) {292let size = self.container.field_len();293(size, Some(size))294}295}296297impl<'a> ExactSizeIterator for VariantFieldIter<'a> {}298299/// A field in the current enum variant.300pub enum VariantField<'a> {301/// The name and value of a field in a struct variant.302Struct(&'a str, &'a dyn PartialReflect),303/// The value of a field in a tuple variant.304Tuple(&'a dyn PartialReflect),305}306307impl<'a> VariantField<'a> {308/// Returns the name of a struct variant field, or [`None`] for a tuple variant field.309pub fn name(&self) -> Option<&'a str> {310if let Self::Struct(name, ..) = self {311Some(*name)312} else {313None314}315}316317/// Gets a reference to the value of this field.318pub fn value(&self) -> &'a dyn PartialReflect {319match *self {320Self::Struct(_, value) | Self::Tuple(value) => value,321}322}323}324325// Tests that need access to internal fields have to go here rather than in mod.rs326#[cfg(test)]327mod tests {328use crate::{enums::*, Reflect};329330#[derive(Reflect, Debug, PartialEq)]331enum MyEnum {332A,333B(usize, i32),334C { foo: f32, bar: bool },335}336#[test]337fn next_index_increment() {338// unit enums always return none, so index should stay at 0339let unit_enum = MyEnum::A;340let mut iter = unit_enum.iter_fields();341let size = iter.len();342for _ in 0..2 {343assert!(iter.next().is_none());344assert_eq!(size, iter.index);345}346// tuple enums we iter over each value (unnamed fields), stop after that347let tuple_enum = MyEnum::B(0, 1);348let mut iter = tuple_enum.iter_fields();349let size = iter.len();350for _ in 0..2 {351let prev_index = iter.index;352assert!(iter.next().is_some());353assert_eq!(prev_index, iter.index - 1);354}355for _ in 0..2 {356assert!(iter.next().is_none());357assert_eq!(size, iter.index);358}359360// struct enums, we iterate over each field in the struct361let struct_enum = MyEnum::C {362foo: 0.,363bar: false,364};365let mut iter = struct_enum.iter_fields();366let size = iter.len();367for _ in 0..2 {368let prev_index = iter.index;369assert!(iter.next().is_some());370assert_eq!(prev_index, iter.index - 1);371}372for _ in 0..2 {373assert!(iter.next().is_none());374assert_eq!(size, iter.index);375}376}377}378379380