Path: blob/main/crates/bevy_reflect/derive/src/from_reflect.rs
6599 views
use crate::{1container_attributes::REFLECT_DEFAULT,2derive_data::ReflectEnum,3enum_utility::{EnumVariantOutputData, FromReflectVariantBuilder, VariantBuilder},4field_attributes::DefaultBehavior,5ident::ident_or_index,6where_clause_options::WhereClauseOptions,7ReflectMeta, ReflectStruct,8};9use bevy_macro_utils::fq_std::{FQClone, FQDefault, FQOption};10use proc_macro2::Span;11use quote::{quote, ToTokens};12use syn::{Field, Ident, Lit, LitInt, LitStr, Member};1314/// Implements `FromReflect` for the given struct15pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {16impl_struct_internal(reflect_struct, false)17}1819/// Implements `FromReflect` for the given tuple struct20pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {21impl_struct_internal(reflect_struct, true)22}2324pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream {25let type_path = meta.type_path();26let bevy_reflect_path = meta.bevy_reflect_path();27let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();28let where_from_reflect_clause = WhereClauseOptions::new(meta).extend_where_clause(where_clause);2930let downcast = match meta.remote_ty() {31Some(remote) => {32let remote_ty = remote.type_path();33quote! {34<Self as #bevy_reflect_path::ReflectRemote>::into_wrapper(35#FQClone::clone(36<dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<#remote_ty>(reflect)?37)38)39}40}41None => quote! {42#FQClone::clone(43<dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<#type_path #ty_generics>(reflect)?44)45},46};4748quote! {49impl #impl_generics #bevy_reflect_path::FromReflect for #type_path #ty_generics #where_from_reflect_clause {50fn from_reflect(reflect: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<Self> {51#FQOption::Some(#downcast)52}53}54}55}5657/// Implements `FromReflect` for the given enum type58pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream {59let fqoption = FQOption.into_token_stream();6061let enum_path = reflect_enum.meta().type_path();62let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();6364let ref_value = Ident::new("__param0", Span::call_site());6566let EnumVariantOutputData {67variant_names,68variant_constructors,69..70} = FromReflectVariantBuilder::new(reflect_enum).build(&ref_value);7172let match_branches = if reflect_enum.meta().is_remote_wrapper() {73quote! {74#(#variant_names => #fqoption::Some(Self(#variant_constructors)),)*75}76} else {77quote! {78#(#variant_names => #fqoption::Some(#variant_constructors),)*79}80};8182let (impl_generics, ty_generics, where_clause) = enum_path.generics().split_for_impl();8384// Add FromReflect bound for each active field85let where_from_reflect_clause = reflect_enum86.where_clause_options()87.extend_where_clause(where_clause);8889quote! {90impl #impl_generics #bevy_reflect_path::FromReflect for #enum_path #ty_generics #where_from_reflect_clause {91fn from_reflect(#ref_value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<Self> {92if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) =93#bevy_reflect_path::PartialReflect::reflect_ref(#ref_value)94{95match #bevy_reflect_path::Enum::variant_name(#ref_value) {96#match_branches97name => panic!("variant with name `{}` does not exist on enum `{}`", name, <Self as #bevy_reflect_path::TypePath>::type_path()),98}99} else {100#FQOption::None101}102}103}104}105}106107/// Container for a struct's members (field name or index) and their108/// corresponding values.109struct MemberValuePair(Vec<Member>, Vec<proc_macro2::TokenStream>);110111impl MemberValuePair {112pub fn new(items: (Vec<Member>, Vec<proc_macro2::TokenStream>)) -> Self {113Self(items.0, items.1)114}115}116117fn impl_struct_internal(118reflect_struct: &ReflectStruct,119is_tuple: bool,120) -> proc_macro2::TokenStream {121let fqoption = FQOption.into_token_stream();122123let struct_path = reflect_struct.meta().type_path();124let remote_ty = reflect_struct.meta().remote_ty();125let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();126127let ref_struct = Ident::new("__ref_struct", Span::call_site());128let ref_struct_type = if is_tuple {129Ident::new("TupleStruct", Span::call_site())130} else {131Ident::new("Struct", Span::call_site())132};133134let MemberValuePair(active_members, active_values) =135get_active_fields(reflect_struct, &ref_struct, &ref_struct_type, is_tuple);136137let is_defaultable = reflect_struct.meta().attrs().contains(REFLECT_DEFAULT);138139// The constructed "Self" ident140let __this = Ident::new("__this", Span::call_site());141142// The reflected type: either `Self` or a remote type143let (reflect_ty, constructor, retval) = if let Some(remote_ty) = remote_ty {144let constructor = match remote_ty.as_expr_path() {145Ok(path) => path,146Err(err) => return err.into_compile_error(),147};148let remote_ty = remote_ty.type_path();149150(151quote!(#remote_ty),152quote!(#constructor),153quote!(Self(#__this)),154)155} else {156(quote!(Self), quote!(Self), quote!(#__this))157};158159let constructor = if is_defaultable {160quote! {161let mut #__this = <#reflect_ty as #FQDefault>::default();162#(163// The closure catches any failing `?` within `active_values`.164if let #fqoption::Some(__field) = (|| #active_values)() {165// Iff field exists -> use its value166#__this.#active_members = __field;167}168)*169#FQOption::Some(#retval)170}171} else {172let MemberValuePair(ignored_members, ignored_values) = get_ignored_fields(reflect_struct);173174quote! {175let #__this = #constructor {176#(#active_members: #active_values?,)*177#(#ignored_members: #ignored_values,)*178};179#FQOption::Some(#retval)180}181};182183let (impl_generics, ty_generics, where_clause) = reflect_struct184.meta()185.type_path()186.generics()187.split_for_impl();188189// Add FromReflect bound for each active field190let where_from_reflect_clause = reflect_struct191.where_clause_options()192.extend_where_clause(where_clause);193194quote! {195impl #impl_generics #bevy_reflect_path::FromReflect for #struct_path #ty_generics #where_from_reflect_clause {196fn from_reflect(reflect: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<Self> {197if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct)198= #bevy_reflect_path::PartialReflect::reflect_ref(reflect)199{200#constructor201} else {202#FQOption::None203}204}205}206}207}208209/// Get the collection of ignored field definitions210///211/// Each value of the `MemberValuePair` is a token stream that generates a212/// a default value for the ignored field.213fn get_ignored_fields(reflect_struct: &ReflectStruct) -> MemberValuePair {214MemberValuePair::new(215reflect_struct216.ignored_fields()217.map(|field| {218let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);219220let value = match &field.attrs.default {221DefaultBehavior::Func(path) => quote! {#path()},222_ => quote! {#FQDefault::default()},223};224225(member, value)226})227.unzip(),228)229}230231/// Get the collection of active field definitions.232///233/// Each value of the `MemberValuePair` is a token stream that generates a234/// closure of type `fn() -> Option<T>` where `T` is that field's type.235fn get_active_fields(236reflect_struct: &ReflectStruct,237dyn_struct_name: &Ident,238struct_type: &Ident,239is_tuple: bool,240) -> MemberValuePair {241let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();242243MemberValuePair::new(244reflect_struct245.active_fields()246.map(|field| {247let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);248let accessor = get_field_accessor(249field.data,250field.reflection_index.expect("field should be active"),251is_tuple,252);253let ty = field.reflected_type().clone();254let real_ty = &field.data.ty;255256let get_field = quote! {257#bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)258};259260let into_remote = |value: proc_macro2::TokenStream| {261if field.attrs.is_remote_generic().unwrap_or_default() {262quote! {263#FQOption::Some(264// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type265unsafe {266::core::mem::transmute_copy::<#ty, #real_ty>(267&::core::mem::ManuallyDrop::new(#value?)268)269}270)271}272} else if field.attrs().remote.is_some() {273quote! {274#FQOption::Some(275// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type276unsafe {277::core::mem::transmute::<#ty, #real_ty>(#value?)278}279)280}281} else {282value283}284};285286let value = match &field.attrs.default {287DefaultBehavior::Func(path) => {288let value = into_remote(quote! {289<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)290});291quote! {292if let #FQOption::Some(field) = #get_field {293#value294} else {295#FQOption::Some(#path())296}297}298}299DefaultBehavior::Default => {300let value = into_remote(quote! {301<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)302});303quote! {304if let #FQOption::Some(field) = #get_field {305#value306} else {307#FQOption::Some(#FQDefault::default())308}309}310}311DefaultBehavior::Required => {312let value = into_remote(quote! {313<#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?)314});315quote! {316#value317}318}319};320321(member, value)322})323.unzip(),324)325}326327/// Returns the accessor for a given field of a struct or tuple struct.328///329/// This differs from a member in that it needs to be a number for tuple structs330/// and a string for standard structs.331fn get_field_accessor(field: &Field, index: usize, is_tuple: bool) -> Lit {332if is_tuple {333Lit::Int(LitInt::new(&index.to_string(), Span::call_site()))334} else {335field336.ident337.as_ref()338.map(|ident| Lit::Str(LitStr::new(&ident.to_string(), Span::call_site())))339.unwrap_or_else(|| Lit::Str(LitStr::new(&index.to_string(), Span::call_site())))340}341}342343344