Path: blob/main/crates/bevy_reflect/derive/src/from_reflect.rs
9408 views
use crate::{1container_attributes::REFLECT_DEFAULT,2derive_data::ReflectEnum,3enum_utility::{EnumVariantOutputData, FromReflectVariantBuilder, VariantBuilder},4field_attributes::DefaultBehavior,5where_clause_options::WhereClauseOptions,6ReflectMeta, ReflectStruct,7};8use bevy_macro_utils::as_member;9use bevy_macro_utils::fq_std::{FQClone, FQDefault, FQOption};10use proc_macro2::Span;11use quote::{quote, ToTokens};12use syn::{parse_str, Field, Ident, Lit, LitInt, LitStr, Member, Path};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::enums::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, ref_struct_path) = if is_tuple {129(130Ident::new("TupleStruct", Span::call_site()),131parse_str("tuple_struct::TupleStruct").expect("should be a valid path"),132)133} else {134(135Ident::new("Struct", Span::call_site()),136parse_str("structs::Struct").expect("should be a valid path"),137)138};139140let MemberValuePair(active_members, active_values) =141get_active_fields(reflect_struct, &ref_struct, &ref_struct_path, is_tuple);142143let is_defaultable = reflect_struct.meta().attrs().contains(REFLECT_DEFAULT);144145// The constructed "Self" ident146let __this = Ident::new("__this", Span::call_site());147148// Workaround for rustfmt issue: https://github.com/rust-lang/rustfmt/issues/6779149// `quote!(Self(#__this))` causes rustfmt to panic in Rust 1.93.0+150// TODO: not needed after Rust 1.94151let self_ty = quote!(Self);152153// The reflected type: either `Self` or a remote type154let (reflect_ty, constructor, retval) = if let Some(remote_ty) = remote_ty {155let constructor = match remote_ty.as_expr_path() {156Ok(path) => path,157Err(err) => return err.into_compile_error(),158};159let remote_ty = remote_ty.type_path();160161(162quote!(#remote_ty),163quote!(#constructor),164quote!(#self_ty(#__this)),165)166} else {167(quote!(#self_ty), quote!(#self_ty), quote!(#__this))168};169170let constructor = if is_defaultable {171quote! {172let mut #__this = <#reflect_ty as #FQDefault>::default();173#(174// The closure catches any failing `?` within `active_values`.175if let #fqoption::Some(__field) = (|| #active_values)() {176// Iff field exists -> use its value177#__this.#active_members = __field;178}179)*180#FQOption::Some(#retval)181}182} else {183let MemberValuePair(ignored_members, ignored_values) = get_ignored_fields(reflect_struct);184185quote! {186let #__this = #constructor {187#(#active_members: #active_values?,)*188#(#ignored_members: #ignored_values,)*189};190#FQOption::Some(#retval)191}192};193194let (impl_generics, ty_generics, where_clause) = reflect_struct195.meta()196.type_path()197.generics()198.split_for_impl();199200// Add FromReflect bound for each active field201let where_from_reflect_clause = reflect_struct202.where_clause_options()203.extend_where_clause(where_clause);204205quote! {206impl #impl_generics #bevy_reflect_path::FromReflect for #struct_path #ty_generics #where_from_reflect_clause {207fn from_reflect(reflect: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<Self> {208if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct)209= #bevy_reflect_path::PartialReflect::reflect_ref(reflect)210{211#constructor212} else {213#FQOption::None214}215}216}217}218}219220/// Get the collection of ignored field definitions221///222/// Each value of the `MemberValuePair` is a token stream that generates a223/// default value for the ignored field.224fn get_ignored_fields(reflect_struct: &ReflectStruct) -> MemberValuePair {225MemberValuePair::new(226reflect_struct227.ignored_fields()228.map(|field| {229let member = as_member(field.data.ident.as_ref(), field.declaration_index);230231let value = match &field.attrs.default {232DefaultBehavior::Func(path) => quote! {#path()},233_ => quote! {#FQDefault::default()},234};235236(member, value)237})238.unzip(),239)240}241242/// Get the collection of active field definitions.243///244/// Each value of the `MemberValuePair` is a token stream that generates a245/// closure of type `fn() -> Option<T>` where `T` is that field's type.246fn get_active_fields(247reflect_struct: &ReflectStruct,248dyn_struct_name: &Ident,249struct_type: &Path,250is_tuple: bool,251) -> MemberValuePair {252let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();253254MemberValuePair::new(255reflect_struct256.active_fields()257.map(|field| {258let member = as_member(field.data.ident.as_ref(), field.declaration_index);259let accessor = get_field_accessor(260field.data,261field.reflection_index.expect("field should be active"),262is_tuple,263);264let ty = field.reflected_type().clone();265let real_ty = &field.data.ty;266267let get_field = quote! {268#bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)269};270271let into_remote = |value: proc_macro2::TokenStream| {272if field.attrs.is_remote_generic().unwrap_or_default() {273quote! {274#FQOption::Some(275// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type276unsafe {277::core::mem::transmute_copy::<#ty, #real_ty>(278&::core::mem::ManuallyDrop::new(#value?)279)280}281)282}283} else if field.attrs().remote.is_some() {284quote! {285#FQOption::Some(286// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type287unsafe {288::core::mem::transmute::<#ty, #real_ty>(#value?)289}290)291}292} else {293value294}295};296297let value = match &field.attrs.default {298DefaultBehavior::Func(path) => {299let value = into_remote(quote! {300<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)301});302quote! {303if let #FQOption::Some(field) = #get_field {304#value305} else {306#FQOption::Some(#path())307}308}309}310DefaultBehavior::Default => {311let value = into_remote(quote! {312<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)313});314quote! {315if let #FQOption::Some(field) = #get_field {316#value317} else {318#FQOption::Some(#FQDefault::default())319}320}321}322DefaultBehavior::Required => {323let value = into_remote(quote! {324<#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?)325});326quote! {327#value328}329}330};331332(member, value)333})334.unzip(),335)336}337338/// Returns the accessor for a given field of a struct or tuple struct.339///340/// This differs from a member in that it needs to be a number for tuple structs341/// and a string for standard structs.342fn get_field_accessor(field: &Field, index: usize, is_tuple: bool) -> Lit {343if is_tuple {344Lit::Int(LitInt::new(&index.to_string(), Span::call_site()))345} else {346field347.ident348.as_ref()349.map(|ident| Lit::Str(LitStr::new(&ident.to_string(), Span::call_site())))350.unwrap_or_else(|| Lit::Str(LitStr::new(&index.to_string(), Span::call_site())))351}352}353354355