Path: blob/main/crates/bevy_reflect/derive/src/impls/enums.rs
6600 views
use crate::{1derive_data::{EnumVariantFields, ReflectEnum, StructField},2enum_utility::{EnumVariantOutputData, TryApplyVariantBuilder, VariantBuilder},3impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed},4};5use bevy_macro_utils::fq_std::{FQOption, FQResult};6use proc_macro2::{Ident, Span};7use quote::quote;8use syn::{Fields, Path};910pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream {11let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();12let enum_path = reflect_enum.meta().type_path();13let is_remote = reflect_enum.meta().is_remote_wrapper();1415// For `match self` expressions where self is a reference16let match_this = if is_remote {17quote!(&self.0)18} else {19quote!(self)20};21// For `match self` expressions where self is a mutable reference22let match_this_mut = if is_remote {23quote!(&mut self.0)24} else {25quote!(self)26};27// For `*self` assignments28let deref_this = if is_remote {29quote!(self.0)30} else {31quote!(*self)32};3334let ref_name = Ident::new("__name_param", Span::call_site());35let ref_index = Ident::new("__index_param", Span::call_site());36let ref_value = Ident::new("__value_param", Span::call_site());3738let EnumImpls {39enum_field,40enum_field_mut,41enum_field_at,42enum_field_at_mut,43enum_index_of,44enum_name_at,45enum_field_len,46enum_variant_name,47enum_variant_index,48enum_variant_type,49} = generate_impls(reflect_enum, &ref_index, &ref_name);5051let EnumVariantOutputData {52variant_names,53variant_constructors,54..55} = TryApplyVariantBuilder::new(reflect_enum).build(&ref_value);5657let where_clause_options = reflect_enum.where_clause_options();58let typed_impl = impl_typed(&where_clause_options, reflect_enum.to_info_tokens());5960let type_path_impl = impl_type_path(reflect_enum.meta());61let full_reflect_impl = impl_full_reflect(&where_clause_options);62let common_methods = common_partial_reflect_methods(63reflect_enum.meta(),64|| Some(quote!(#bevy_reflect_path::enum_partial_eq)),65|| Some(quote!(#bevy_reflect_path::enum_hash)),66);67let clone_fn = reflect_enum.get_clone_impl();6869#[cfg(not(feature = "functions"))]70let function_impls = None::<proc_macro2::TokenStream>;71#[cfg(feature = "functions")]72let function_impls = crate::impls::impl_function_traits(&where_clause_options);7374let get_type_registration_impl = reflect_enum.get_type_registration(&where_clause_options);7576let (impl_generics, ty_generics, where_clause) =77reflect_enum.meta().type_path().generics().split_for_impl();7879#[cfg(not(feature = "auto_register"))]80let auto_register = None::<proc_macro2::TokenStream>;81#[cfg(feature = "auto_register")]82let auto_register = crate::impls::reflect_auto_registration(reflect_enum.meta());8384let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);8586quote! {87#get_type_registration_impl8889#typed_impl9091#type_path_impl9293#full_reflect_impl9495#function_impls9697#auto_register9899impl #impl_generics #bevy_reflect_path::Enum for #enum_path #ty_generics #where_reflect_clause {100fn field(&self, #ref_name: &str) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {101match #match_this {102#(#enum_field,)*103_ => #FQOption::None,104}105}106107fn field_at(&self, #ref_index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {108match #match_this {109#(#enum_field_at,)*110_ => #FQOption::None,111}112}113114fn field_mut(&mut self, #ref_name: &str) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {115match #match_this_mut {116#(#enum_field_mut,)*117_ => #FQOption::None,118}119}120121fn field_at_mut(&mut self, #ref_index: usize) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {122match #match_this_mut {123#(#enum_field_at_mut,)*124_ => #FQOption::None,125}126}127128fn index_of(&self, #ref_name: &str) -> #FQOption<usize> {129match #match_this {130#(#enum_index_of,)*131_ => #FQOption::None,132}133}134135fn name_at(&self, #ref_index: usize) -> #FQOption<&str> {136match #match_this {137#(#enum_name_at,)*138_ => #FQOption::None,139}140}141142fn iter_fields(&self) -> #bevy_reflect_path::VariantFieldIter {143#bevy_reflect_path::VariantFieldIter::new(self)144}145146#[inline]147fn field_len(&self) -> usize {148match #match_this {149#(#enum_field_len,)*150_ => 0,151}152}153154#[inline]155fn variant_name(&self) -> &str {156match #match_this {157#(#enum_variant_name,)*158_ => unreachable!(),159}160}161162#[inline]163fn variant_index(&self) -> usize {164match #match_this {165#(#enum_variant_index,)*166_ => unreachable!(),167}168}169170#[inline]171fn variant_type(&self) -> #bevy_reflect_path::VariantType {172match #match_this {173#(#enum_variant_type,)*174_ => unreachable!(),175}176}177178fn to_dynamic_enum(&self) -> #bevy_reflect_path::DynamicEnum {179#bevy_reflect_path::DynamicEnum::from_ref::<Self>(self)180}181}182183impl #impl_generics #bevy_reflect_path::PartialReflect for #enum_path #ty_generics #where_reflect_clause {184#[inline]185fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {186#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())187}188189#[inline]190fn try_apply(191&mut self,192#ref_value: &dyn #bevy_reflect_path::PartialReflect193) -> #FQResult<(), #bevy_reflect_path::ApplyError> {194if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) =195#bevy_reflect_path::PartialReflect::reflect_ref(#ref_value) {196if #bevy_reflect_path::Enum::variant_name(self) == #bevy_reflect_path::Enum::variant_name(#ref_value) {197// Same variant -> just update fields198match #bevy_reflect_path::Enum::variant_type(#ref_value) {199#bevy_reflect_path::VariantType::Struct => {200for field in #bevy_reflect_path::Enum::iter_fields(#ref_value) {201let name = field.name().unwrap();202if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_mut(self, name) {203#bevy_reflect_path::PartialReflect::try_apply(v, field.value())?;204}205}206}207#bevy_reflect_path::VariantType::Tuple => {208for (index, field) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::Enum::iter_fields(#ref_value)) {209if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_at_mut(self, index) {210#bevy_reflect_path::PartialReflect::try_apply(v, field.value())?;211}212}213}214_ => {}215}216} else {217// New variant -> perform a switch218match #bevy_reflect_path::Enum::variant_name(#ref_value) {219#(#variant_names => {220#deref_this = #variant_constructors221})*222name => {223return #FQResult::Err(224#bevy_reflect_path::ApplyError::UnknownVariant {225enum_name: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(self)),226variant_name: ::core::convert::Into::into(name),227}228);229}230}231}232} else {233return #FQResult::Err(234#bevy_reflect_path::ApplyError::MismatchedKinds {235from_kind: #bevy_reflect_path::PartialReflect::reflect_kind(#ref_value),236to_kind: #bevy_reflect_path::ReflectKind::Enum,237}238);239}240#FQResult::Ok(())241}242243fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {244#bevy_reflect_path::ReflectKind::Enum245}246247fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {248#bevy_reflect_path::ReflectRef::Enum(self)249}250251fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {252#bevy_reflect_path::ReflectMut::Enum(self)253}254255fn reflect_owned(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::ReflectOwned {256#bevy_reflect_path::ReflectOwned::Enum(self)257}258259#common_methods260261#clone_fn262}263}264}265266struct EnumImpls {267enum_field: Vec<proc_macro2::TokenStream>,268enum_field_mut: Vec<proc_macro2::TokenStream>,269enum_field_at: Vec<proc_macro2::TokenStream>,270enum_field_at_mut: Vec<proc_macro2::TokenStream>,271enum_index_of: Vec<proc_macro2::TokenStream>,272enum_name_at: Vec<proc_macro2::TokenStream>,273enum_field_len: Vec<proc_macro2::TokenStream>,274enum_variant_name: Vec<proc_macro2::TokenStream>,275enum_variant_index: Vec<proc_macro2::TokenStream>,276enum_variant_type: Vec<proc_macro2::TokenStream>,277}278279fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Ident) -> EnumImpls {280let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();281282let mut enum_field = Vec::new();283let mut enum_field_mut = Vec::new();284let mut enum_field_at = Vec::new();285let mut enum_field_at_mut = Vec::new();286let mut enum_index_of = Vec::new();287let mut enum_name_at = Vec::new();288let mut enum_field_len = Vec::new();289let mut enum_variant_name = Vec::new();290let mut enum_variant_index = Vec::new();291let mut enum_variant_type = Vec::new();292293for (variant_index, variant) in reflect_enum.variants().iter().enumerate() {294let ident = &variant.data.ident;295let name = ident.to_string();296let unit = reflect_enum.get_unit(ident);297298let variant_type_ident = match variant.data.fields {299Fields::Unit => Ident::new("Unit", Span::call_site()),300Fields::Unnamed(..) => Ident::new("Tuple", Span::call_site()),301Fields::Named(..) => Ident::new("Struct", Span::call_site()),302};303304enum_variant_name.push(quote! {305#unit{..} => #name306});307enum_variant_index.push(quote! {308#unit{..} => #variant_index309});310enum_variant_type.push(quote! {311#unit{..} => #bevy_reflect_path::VariantType::#variant_type_ident312});313314fn process_fields(315fields: &[StructField],316mut f: impl FnMut(&StructField) + Sized,317) -> usize {318let mut field_len = 0;319for field in fields.iter() {320if field.attrs.ignore.is_ignored() {321// Ignored field322continue;323};324325f(field);326327field_len += 1;328}329330field_len331}332333/// Process the field value to account for remote types.334///335/// If the field is a remote type, then the value will be transmuted accordingly.336fn process_field_value(337ident: &Ident,338field: &StructField,339is_mutable: bool,340bevy_reflect_path: &Path,341) -> proc_macro2::TokenStream {342let method = if is_mutable {343quote!(as_wrapper_mut)344} else {345quote!(as_wrapper)346};347348field349.attrs350.remote351.as_ref()352.map(|ty| quote!(<#ty as #bevy_reflect_path::ReflectRemote>::#method(#ident)))353.unwrap_or_else(|| quote!(#ident))354}355356match &variant.fields {357EnumVariantFields::Unit => {358let field_len = process_fields(&[], |_| {});359360enum_field_len.push(quote! {361#unit{..} => #field_len362});363}364EnumVariantFields::Unnamed(fields) => {365let field_len = process_fields(fields, |field: &StructField| {366let reflection_index = field367.reflection_index368.expect("reflection index should exist for active field");369370let declare_field = syn::Index::from(field.declaration_index);371372let __value = Ident::new("__value", Span::call_site());373let value_ref = process_field_value(&__value, field, false, bevy_reflect_path);374let value_mut = process_field_value(&__value, field, true, bevy_reflect_path);375376enum_field_at.push(quote! {377#unit { #declare_field : #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_ref)378});379enum_field_at_mut.push(quote! {380#unit { #declare_field : #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_mut)381});382});383384enum_field_len.push(quote! {385#unit{..} => #field_len386});387}388EnumVariantFields::Named(fields) => {389let field_len = process_fields(fields, |field: &StructField| {390let field_ident = field.data.ident.as_ref().unwrap();391let field_name = field_ident.to_string();392let reflection_index = field393.reflection_index394.expect("reflection index should exist for active field");395396let __value = Ident::new("__value", Span::call_site());397let value_ref = process_field_value(&__value, field, false, bevy_reflect_path);398let value_mut = process_field_value(&__value, field, true, bevy_reflect_path);399400enum_field.push(quote! {401#unit{ #field_ident: #__value, .. } if #ref_name == #field_name => #FQOption::Some(#value_ref)402});403enum_field_mut.push(quote! {404#unit{ #field_ident: #__value, .. } if #ref_name == #field_name => #FQOption::Some(#value_mut)405});406enum_field_at.push(quote! {407#unit{ #field_ident: #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_ref)408});409enum_field_at_mut.push(quote! {410#unit{ #field_ident: #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_mut)411});412enum_index_of.push(quote! {413#unit{ .. } if #ref_name == #field_name => #FQOption::Some(#reflection_index)414});415enum_name_at.push(quote! {416#unit{ .. } if #ref_index == #reflection_index => #FQOption::Some(#field_name)417});418});419420enum_field_len.push(quote! {421#unit{..} => #field_len422});423}424};425}426427EnumImpls {428enum_field,429enum_field_mut,430enum_field_at,431enum_field_at_mut,432enum_index_of,433enum_name_at,434enum_field_len,435enum_variant_name,436enum_variant_index,437enum_variant_type,438}439}440441442