Path: blob/main/crates/bevy_reflect/derive/src/enum_utility.rs
6599 views
use crate::field_attributes::CloneBehavior;1use crate::{2derive_data::ReflectEnum, derive_data::StructField, field_attributes::DefaultBehavior,3ident::ident_or_index,4};5use bevy_macro_utils::fq_std::{FQClone, FQDefault, FQOption, FQResult};6use proc_macro2::{Ident, TokenStream};7use quote::{format_ident, quote, ToTokens};89pub(crate) struct EnumVariantOutputData {10/// The names of each variant as a string.11///12/// For example, `Some` and `None` for the `Option` enum.13pub variant_names: Vec<String>,14/// The pattern matching portion of each variant.15///16/// For example, `Option::Some { 0: _0 }` and `Option::None {}` for the `Option` enum.17pub variant_patterns: Vec<TokenStream>,18/// The constructor portion of each variant.19///20/// For example, `Option::Some { 0: value }` and `Option::None {}` for the `Option` enum.21pub variant_constructors: Vec<TokenStream>,22}2324#[derive(Copy, Clone)]25pub(crate) struct VariantField<'a, 'b> {26/// The alias for the field.27///28/// This should be used whenever the field needs to be referenced in a token stream.29pub alias: &'a Ident,30/// The name of the variant that contains the field.31pub variant_name: &'a str,32/// The field data.33pub field: &'a StructField<'b>,34}3536/// Trait used to control how enum variants are built.37pub(crate) trait VariantBuilder: Sized {38/// Returns the enum data.39fn reflect_enum(&self) -> &ReflectEnum<'_>;4041/// Returns a token stream that accesses a field of a variant as an `Option<dyn Reflect>`.42///43/// The default implementation of this method will return a token stream44/// which gets the field dynamically so as to support `dyn Enum`.45///46/// # Parameters47/// * `this`: The identifier of the enum48/// * `field`: The field to access49fn access_field(&self, this: &Ident, field: VariantField) -> TokenStream {50if let Some(field_ident) = &field.field.data.ident {51let name = field_ident.to_string();52quote!(#this.field(#name))53} else if let Some(field_index) = field.field.reflection_index {54quote!(#this.field_at(#field_index))55} else {56quote!(::core::compile_error!(57"internal bevy_reflect error: field should be active"58))59}60}6162/// Returns a token stream that unwraps a field of a variant as a `&dyn Reflect`63/// (from an `Option<dyn Reflect>`).64///65/// # Parameters66/// * `field`: The field to access67fn unwrap_field(&self, field: VariantField) -> TokenStream;6869/// Returns a token stream that constructs a field of a variant as a concrete type70/// (from a `&dyn Reflect`).71///72/// # Parameters73/// * `field`: The field to access74fn construct_field(&self, field: VariantField) -> TokenStream;7576/// Returns a token stream that constructs an instance of an active field.77///78/// # Parameters79/// * `this`: The identifier of the enum80/// * `field`: The field to access81fn on_active_field(&self, this: &Ident, field: VariantField) -> TokenStream {82let bevy_reflect_path = self.reflect_enum().meta().bevy_reflect_path();83let field_accessor = self.access_field(this, field);8485let alias = field.alias;86let field_ty = field.field.reflected_type();87let field_constructor = self.construct_field(field);8889let construction = match &field.field.attrs.default {90DefaultBehavior::Func(path) => quote! {91if let #FQOption::Some(#alias) = #field_accessor {92#field_constructor93} else {94#path()95}96},97DefaultBehavior::Default => quote! {98if let #FQOption::Some(#alias) = #field_accessor {99#field_constructor100} else {101#FQDefault::default()102}103},104DefaultBehavior::Required => {105let field_unwrapper = self.unwrap_field(field);106107quote! {{108// `#alias` is used by both the unwrapper and constructor109let #alias = #field_accessor;110let #alias = #field_unwrapper;111#field_constructor112}}113}114};115116if field.field.attrs().remote.is_some() {117quote! {118<#field_ty as #bevy_reflect_path::ReflectRemote>::into_remote(#construction)119}120} else {121construction122}123}124125/// Returns a token stream that constructs an instance of an ignored field.126///127/// # Parameters128/// * `field`: The field to access129fn on_ignored_field(&self, field: VariantField) -> TokenStream {130match &field.field.attrs.default {131DefaultBehavior::Func(path) => quote! { #path() },132_ => quote! { #FQDefault::default() },133}134}135136/// Builds the enum variant output data.137fn build(&self, this: &Ident) -> EnumVariantOutputData {138let variants = self.reflect_enum().variants();139140let mut variant_names = Vec::with_capacity(variants.len());141let mut variant_patterns = Vec::with_capacity(variants.len());142let mut variant_constructors = Vec::with_capacity(variants.len());143144for variant in variants {145let variant_ident = &variant.data.ident;146let variant_name = variant_ident.to_string();147let variant_path = self.reflect_enum().get_unit(variant_ident);148149let fields = variant.fields();150151let mut field_patterns = Vec::with_capacity(fields.len());152let mut field_constructors = Vec::with_capacity(fields.len());153154for field in fields {155let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);156let alias = format_ident!("_{}", member);157158let variant_field = VariantField {159alias: &alias,160variant_name: &variant_name,161field,162};163164let value = if field.attrs.ignore.is_ignored() {165self.on_ignored_field(variant_field)166} else {167self.on_active_field(this, variant_field)168};169170field_patterns.push(quote! {171#member: #alias172});173174field_constructors.push(quote! {175#member: #value176});177}178179let pattern = quote! {180#variant_path { #( #field_patterns ),* }181};182183let constructor = quote! {184#variant_path {185#( #field_constructors ),*186}187};188189variant_names.push(variant_name);190variant_patterns.push(pattern);191variant_constructors.push(constructor);192}193194EnumVariantOutputData {195variant_names,196variant_patterns,197variant_constructors,198}199}200}201202/// Generates the enum variant output data needed to build the `FromReflect::from_reflect` implementation.203pub(crate) struct FromReflectVariantBuilder<'a> {204reflect_enum: &'a ReflectEnum<'a>,205}206207impl<'a> FromReflectVariantBuilder<'a> {208pub fn new(reflect_enum: &'a ReflectEnum) -> Self {209Self { reflect_enum }210}211}212213impl<'a> VariantBuilder for FromReflectVariantBuilder<'a> {214fn reflect_enum(&self) -> &ReflectEnum<'_> {215self.reflect_enum216}217218fn unwrap_field(&self, field: VariantField) -> TokenStream {219let alias = field.alias;220quote!(#alias?)221}222223fn construct_field(&self, field: VariantField) -> TokenStream {224let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();225let field_ty = field.field.reflected_type();226let alias = field.alias;227228quote! {229<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)?230}231}232}233234/// Generates the enum variant output data needed to build the `PartialReflect::try_apply` implementation.235pub(crate) struct TryApplyVariantBuilder<'a> {236reflect_enum: &'a ReflectEnum<'a>,237}238239impl<'a> TryApplyVariantBuilder<'a> {240pub fn new(reflect_enum: &'a ReflectEnum) -> Self {241Self { reflect_enum }242}243}244245impl<'a> VariantBuilder for TryApplyVariantBuilder<'a> {246fn reflect_enum(&self) -> &ReflectEnum<'_> {247self.reflect_enum248}249250fn unwrap_field(&self, field: VariantField) -> TokenStream {251let VariantField {252alias,253variant_name,254field,255..256} = field;257258let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();259260let field_name = match &field.data.ident {261Some(ident) => format!("{ident}"),262None => format!(".{}", field.declaration_index),263};264265quote! {266#alias.ok_or(#bevy_reflect_path::ApplyError::MissingEnumField {267variant_name: ::core::convert::Into::into(#variant_name),268field_name: ::core::convert::Into::into(#field_name)269})?270}271}272273fn construct_field(&self, field: VariantField) -> TokenStream {274let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();275let alias = field.alias;276let field_ty = field.field.reflected_type();277278quote! {279<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)280.ok_or(#bevy_reflect_path::ApplyError::MismatchedTypes {281from_type: ::core::convert::Into::into(282#bevy_reflect_path::DynamicTypePath::reflect_type_path(#alias)283),284to_type: ::core::convert::Into::into(<#field_ty as #bevy_reflect_path::TypePath>::type_path())285})?286}287}288}289290/// Generates the enum variant output data needed to build the `Reflect::reflect_clone` implementation.291pub(crate) struct ReflectCloneVariantBuilder<'a> {292reflect_enum: &'a ReflectEnum<'a>,293}294295impl<'a> ReflectCloneVariantBuilder<'a> {296pub fn new(reflect_enum: &'a ReflectEnum) -> Self {297Self { reflect_enum }298}299}300301impl<'a> VariantBuilder for ReflectCloneVariantBuilder<'a> {302fn reflect_enum(&self) -> &ReflectEnum<'_> {303self.reflect_enum304}305306fn access_field(&self, _ident: &Ident, field: VariantField) -> TokenStream {307let alias = field.alias;308quote!(#FQOption::Some(#alias))309}310311fn unwrap_field(&self, field: VariantField) -> TokenStream {312let alias = field.alias;313quote!(#alias.unwrap())314}315316fn construct_field(&self, field: VariantField) -> TokenStream {317let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();318let field_ty = field.field.reflected_type();319let alias = field.alias;320let alias = match &field.field.attrs.remote {321Some(wrapper_ty) => {322quote! {323<#wrapper_ty as #bevy_reflect_path::ReflectRemote>::as_wrapper(#alias)324}325}326None => alias.to_token_stream(),327};328329match &field.field.attrs.clone {330CloneBehavior::Default => {331quote! {332<#field_ty as #bevy_reflect_path::PartialReflect>::reflect_clone_and_take(#alias)?333}334}335CloneBehavior::Trait => {336quote! {337#FQClone::clone(#alias)338}339}340CloneBehavior::Func(clone_fn) => {341quote! {342#clone_fn(#alias)343}344}345}346}347348fn on_active_field(&self, _this: &Ident, field: VariantField) -> TokenStream {349self.construct_field(field)350}351352fn on_ignored_field(&self, field: VariantField) -> TokenStream {353let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();354let variant_name = field.variant_name;355let alias = field.alias;356357match &field.field.attrs.clone {358CloneBehavior::Default => {359let field_id = field.field.field_id(bevy_reflect_path);360361quote! {362return #FQResult::Err(363#bevy_reflect_path::ReflectCloneError::FieldNotCloneable {364field: #field_id,365variant: #FQOption::Some(#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#variant_name)),366container_type_path: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(<Self as #bevy_reflect_path::TypePath>::type_path())367}368)369}370}371CloneBehavior::Trait => quote! { #FQClone::clone(#alias) },372CloneBehavior::Func(clone_fn) => quote! { #clone_fn() },373}374}375}376377378