bevy_reflect_derive/
enum_utility.rsuse crate::{
derive_data::ReflectEnum, derive_data::StructField, field_attributes::DefaultBehavior,
ident::ident_or_index,
};
use bevy_macro_utils::fq_std::{FQDefault, FQOption};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
pub(crate) struct EnumVariantOutputData {
pub variant_names: Vec<String>,
pub variant_constructors: Vec<TokenStream>,
}
#[derive(Copy, Clone)]
pub(crate) struct VariantField<'a, 'b> {
pub alias: &'a Ident,
pub variant_name: &'a str,
pub field: &'a StructField<'b>,
}
pub(crate) trait VariantBuilder: Sized {
fn reflect_enum(&self) -> &ReflectEnum;
fn access_field(&self, this: &Ident, field: VariantField) -> TokenStream {
match &field.field.data.ident {
Some(field_ident) => {
let name = field_ident.to_string();
quote!(#this.field(#name))
}
None => {
if let Some(field_index) = field.field.reflection_index {
quote!(#this.field_at(#field_index))
} else {
quote!(::core::compile_error!(
"internal bevy_reflect error: field should be active"
))
}
}
}
}
fn unwrap_field(&self, field: VariantField) -> TokenStream;
fn construct_field(&self, field: VariantField) -> TokenStream;
fn on_active_field(&self, this: &Ident, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum().meta().bevy_reflect_path();
let field_accessor = self.access_field(this, field);
let alias = field.alias;
let field_ty = field.field.reflected_type();
let field_constructor = self.construct_field(field);
let construction = match &field.field.attrs.default {
DefaultBehavior::Func(path) => quote! {
if let #FQOption::Some(#alias) = #field_accessor {
#field_constructor
} else {
#path()
}
},
DefaultBehavior::Default => quote! {
if let #FQOption::Some(#alias) = #field_accessor {
#field_constructor
} else {
#FQDefault::default()
}
},
DefaultBehavior::Required => {
let field_unwrapper = self.unwrap_field(field);
quote! {{
let #alias = #field_accessor;
let #alias = #field_unwrapper;
#field_constructor
}}
}
};
if field.field.attrs().remote.is_some() {
quote! {
<#field_ty as #bevy_reflect_path::ReflectRemote>::into_remote(#construction)
}
} else {
construction
}
}
fn on_ignored_field(&self, field: VariantField) -> TokenStream {
match &field.field.attrs.default {
DefaultBehavior::Func(path) => quote! { #path() },
_ => quote! { #FQDefault::default() },
}
}
fn build(&self, this: &Ident) -> EnumVariantOutputData {
let variants = self.reflect_enum().variants();
let mut variant_names = Vec::with_capacity(variants.len());
let mut variant_constructors = Vec::with_capacity(variants.len());
for variant in variants {
let variant_ident = &variant.data.ident;
let variant_name = variant_ident.to_string();
let variant_path = self.reflect_enum().get_unit(variant_ident);
let fields = variant.fields();
let field_constructors = fields.iter().map(|field| {
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
let alias = format_ident!("_{}", member);
let variant_field = VariantField {
alias: &alias,
variant_name: &variant_name,
field,
};
let value = if field.attrs.ignore.is_ignored() {
self.on_ignored_field(variant_field)
} else {
self.on_active_field(this, variant_field)
};
let constructor = quote! {
#member: #value
};
constructor
});
let constructor = quote! {
#variant_path {
#( #field_constructors ),*
}
};
variant_names.push(variant_name);
variant_constructors.push(constructor);
}
EnumVariantOutputData {
variant_names,
variant_constructors,
}
}
}
pub(crate) struct FromReflectVariantBuilder<'a> {
reflect_enum: &'a ReflectEnum<'a>,
}
impl<'a> FromReflectVariantBuilder<'a> {
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
Self { reflect_enum }
}
}
impl<'a> VariantBuilder for FromReflectVariantBuilder<'a> {
fn reflect_enum(&self) -> &ReflectEnum {
self.reflect_enum
}
fn unwrap_field(&self, field: VariantField) -> TokenStream {
let alias = field.alias;
quote!(#alias?)
}
fn construct_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let field_ty = field.field.reflected_type();
let alias = field.alias;
quote! {
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)?
}
}
}
pub(crate) struct TryApplyVariantBuilder<'a> {
reflect_enum: &'a ReflectEnum<'a>,
}
impl<'a> TryApplyVariantBuilder<'a> {
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
Self { reflect_enum }
}
}
impl<'a> VariantBuilder for TryApplyVariantBuilder<'a> {
fn reflect_enum(&self) -> &ReflectEnum {
self.reflect_enum
}
fn unwrap_field(&self, field: VariantField) -> TokenStream {
let VariantField {
alias,
variant_name,
field,
..
} = field;
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let field_name = match &field.data.ident {
Some(ident) => format!("{ident}"),
None => format!(".{}", field.declaration_index),
};
quote! {
#alias.ok_or(#bevy_reflect_path::ApplyError::MissingEnumField {
variant_name: ::core::convert::Into::into(#variant_name),
field_name: ::core::convert::Into::into(#field_name)
})?
}
}
fn construct_field(&self, field: VariantField) -> TokenStream {
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
let alias = field.alias;
let field_ty = field.field.reflected_type();
quote! {
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)
.ok_or(#bevy_reflect_path::ApplyError::MismatchedTypes {
from_type: ::core::convert::Into::into(
#bevy_reflect_path::DynamicTypePath::reflect_type_path(#alias)
),
to_type: ::core::convert::Into::into(<#field_ty as #bevy_reflect_path::TypePath>::type_path())
})?
}
}
}