Path: blob/main/crates/bevy_reflect/src/enums/variants.rs
6599 views
use crate::{1attributes::{impl_custom_attribute_methods, CustomAttributes},2NamedField, UnnamedField,3};4use alloc::boxed::Box;5use bevy_platform::collections::HashMap;6use bevy_platform::sync::Arc;7use core::slice::Iter;8use thiserror::Error;910/// Describes the form of an enum variant.11#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]12pub enum VariantType {13/// Struct enums take the form:14///15/// ```16/// enum MyEnum {17/// A {18/// foo: usize19/// }20/// }21/// ```22Struct,23/// Tuple enums take the form:24///25/// ```26/// enum MyEnum {27/// A(usize)28/// }29/// ```30Tuple,31/// Unit enums take the form:32///33/// ```34/// enum MyEnum {35/// A36/// }37/// ```38Unit,39}4041/// A [`VariantInfo`]-specific error.42#[derive(Debug, Error)]43pub enum VariantInfoError {44/// Caused when a variant was expected to be of a certain [type], but was not.45///46/// [type]: VariantType47#[error("variant type mismatch: expected {expected:?}, received {received:?}")]48TypeMismatch {49/// Expected variant type.50expected: VariantType,51/// Received variant type.52received: VariantType,53},54}5556/// A container for compile-time enum variant info.57#[derive(Clone, Debug)]58pub enum VariantInfo {59/// Struct enums take the form:60///61/// ```62/// enum MyEnum {63/// A {64/// foo: usize65/// }66/// }67/// ```68Struct(StructVariantInfo),69/// Tuple enums take the form:70///71/// ```72/// enum MyEnum {73/// A(usize)74/// }75/// ```76Tuple(TupleVariantInfo),77/// Unit enums take the form:78///79/// ```80/// enum MyEnum {81/// A82/// }83/// ```84Unit(UnitVariantInfo),85}8687impl VariantInfo {88/// The name of the enum variant.89pub fn name(&self) -> &'static str {90match self {91Self::Struct(info) => info.name(),92Self::Tuple(info) => info.name(),93Self::Unit(info) => info.name(),94}95}9697/// The docstring of the underlying variant, if any.98#[cfg(feature = "documentation")]99pub fn docs(&self) -> Option<&str> {100match self {101Self::Struct(info) => info.docs(),102Self::Tuple(info) => info.docs(),103Self::Unit(info) => info.docs(),104}105}106107/// Returns the [type] of this variant.108///109/// [type]: VariantType110pub fn variant_type(&self) -> VariantType {111match self {112Self::Struct(_) => VariantType::Struct,113Self::Tuple(_) => VariantType::Tuple,114Self::Unit(_) => VariantType::Unit,115}116}117118impl_custom_attribute_methods!(119self,120match self {121Self::Struct(info) => info.custom_attributes(),122Self::Tuple(info) => info.custom_attributes(),123Self::Unit(info) => info.custom_attributes(),124},125"variant"126);127}128129macro_rules! impl_cast_method {130($name:ident : $kind:ident => $info:ident) => {131#[doc = concat!("Attempts a cast to [`", stringify!($info), "`].")]132#[doc = concat!("\n\nReturns an error if `self` is not [`VariantInfo::", stringify!($kind), "`].")]133pub fn $name(&self) -> Result<&$info, VariantInfoError> {134match self {135Self::$kind(info) => Ok(info),136_ => Err(VariantInfoError::TypeMismatch {137expected: VariantType::$kind,138received: self.variant_type(),139}),140}141}142};143}144145/// Conversion convenience methods for [`VariantInfo`].146impl VariantInfo {147impl_cast_method!(as_struct_variant: Struct => StructVariantInfo);148impl_cast_method!(as_tuple_variant: Tuple => TupleVariantInfo);149impl_cast_method!(as_unit_variant: Unit => UnitVariantInfo);150}151152/// Type info for struct variants.153#[derive(Clone, Debug)]154pub struct StructVariantInfo {155name: &'static str,156fields: Box<[NamedField]>,157field_names: Box<[&'static str]>,158field_indices: HashMap<&'static str, usize>,159custom_attributes: Arc<CustomAttributes>,160#[cfg(feature = "documentation")]161docs: Option<&'static str>,162}163164impl StructVariantInfo {165/// Create a new [`StructVariantInfo`].166pub fn new(name: &'static str, fields: &[NamedField]) -> Self {167let field_indices = Self::collect_field_indices(fields);168let field_names = fields.iter().map(NamedField::name).collect();169Self {170name,171fields: fields.to_vec().into_boxed_slice(),172field_names,173field_indices,174custom_attributes: Arc::new(CustomAttributes::default()),175#[cfg(feature = "documentation")]176docs: None,177}178}179180/// Sets the docstring for this variant.181#[cfg(feature = "documentation")]182pub fn with_docs(self, docs: Option<&'static str>) -> Self {183Self { docs, ..self }184}185186/// Sets the custom attributes for this variant.187pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {188Self {189custom_attributes: Arc::new(custom_attributes),190..self191}192}193194/// The name of this variant.195pub fn name(&self) -> &'static str {196self.name197}198199/// A slice containing the names of all fields in order.200pub fn field_names(&self) -> &[&'static str] {201&self.field_names202}203204/// Get the field with the given name.205pub fn field(&self, name: &str) -> Option<&NamedField> {206self.field_indices207.get(name)208.map(|index| &self.fields[*index])209}210211/// Get the field at the given index.212pub fn field_at(&self, index: usize) -> Option<&NamedField> {213self.fields.get(index)214}215216/// Get the index of the field with the given name.217pub fn index_of(&self, name: &str) -> Option<usize> {218self.field_indices.get(name).copied()219}220221/// Iterate over the fields of this variant.222pub fn iter(&self) -> Iter<'_, NamedField> {223self.fields.iter()224}225226/// The total number of fields in this variant.227pub fn field_len(&self) -> usize {228self.fields.len()229}230231fn collect_field_indices(fields: &[NamedField]) -> HashMap<&'static str, usize> {232fields233.iter()234.enumerate()235.map(|(index, field)| (field.name(), index))236.collect()237}238239/// The docstring of this variant, if any.240#[cfg(feature = "documentation")]241pub fn docs(&self) -> Option<&'static str> {242self.docs243}244245impl_custom_attribute_methods!(self.custom_attributes, "variant");246}247248/// Type info for tuple variants.249#[derive(Clone, Debug)]250pub struct TupleVariantInfo {251name: &'static str,252fields: Box<[UnnamedField]>,253custom_attributes: Arc<CustomAttributes>,254#[cfg(feature = "documentation")]255docs: Option<&'static str>,256}257258impl TupleVariantInfo {259/// Create a new [`TupleVariantInfo`].260pub fn new(name: &'static str, fields: &[UnnamedField]) -> Self {261Self {262name,263fields: fields.to_vec().into_boxed_slice(),264custom_attributes: Arc::new(CustomAttributes::default()),265#[cfg(feature = "documentation")]266docs: None,267}268}269270/// Sets the docstring for this variant.271#[cfg(feature = "documentation")]272pub fn with_docs(self, docs: Option<&'static str>) -> Self {273Self { docs, ..self }274}275276/// Sets the custom attributes for this variant.277pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {278Self {279custom_attributes: Arc::new(custom_attributes),280..self281}282}283284/// The name of this variant.285pub fn name(&self) -> &'static str {286self.name287}288289/// Get the field at the given index.290pub fn field_at(&self, index: usize) -> Option<&UnnamedField> {291self.fields.get(index)292}293294/// Iterate over the fields of this variant.295pub fn iter(&self) -> Iter<'_, UnnamedField> {296self.fields.iter()297}298299/// The total number of fields in this variant.300pub fn field_len(&self) -> usize {301self.fields.len()302}303304/// The docstring of this variant, if any.305#[cfg(feature = "documentation")]306pub fn docs(&self) -> Option<&'static str> {307self.docs308}309310impl_custom_attribute_methods!(self.custom_attributes, "variant");311}312313/// Type info for unit variants.314#[derive(Clone, Debug)]315pub struct UnitVariantInfo {316name: &'static str,317custom_attributes: Arc<CustomAttributes>,318#[cfg(feature = "documentation")]319docs: Option<&'static str>,320}321322impl UnitVariantInfo {323/// Create a new [`UnitVariantInfo`].324pub fn new(name: &'static str) -> Self {325Self {326name,327custom_attributes: Arc::new(CustomAttributes::default()),328#[cfg(feature = "documentation")]329docs: None,330}331}332333/// Sets the docstring for this variant.334#[cfg(feature = "documentation")]335pub fn with_docs(self, docs: Option<&'static str>) -> Self {336Self { docs, ..self }337}338339/// Sets the custom attributes for this variant.340pub fn with_custom_attributes(self, custom_attributes: CustomAttributes) -> Self {341Self {342custom_attributes: Arc::new(custom_attributes),343..self344}345}346347/// The name of this variant.348pub fn name(&self) -> &'static str {349self.name350}351352/// The docstring of this variant, if any.353#[cfg(feature = "documentation")]354pub fn docs(&self) -> Option<&'static str> {355self.docs356}357358impl_custom_attribute_methods!(self.custom_attributes, "variant");359}360361#[cfg(test)]362mod tests {363use super::*;364use crate::{Reflect, Typed};365366#[test]367fn should_return_error_on_invalid_cast() {368#[derive(Reflect)]369enum Foo {370Bar,371}372373let info = Foo::type_info().as_enum().unwrap();374let variant = info.variant_at(0).unwrap();375assert!(matches!(376variant.as_tuple_variant(),377Err(VariantInfoError::TypeMismatch {378expected: VariantType::Tuple,379received: VariantType::Unit380})381));382}383}384385386