Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/derive/src/from_reflect.rs
6599 views
1
use crate::{
2
container_attributes::REFLECT_DEFAULT,
3
derive_data::ReflectEnum,
4
enum_utility::{EnumVariantOutputData, FromReflectVariantBuilder, VariantBuilder},
5
field_attributes::DefaultBehavior,
6
ident::ident_or_index,
7
where_clause_options::WhereClauseOptions,
8
ReflectMeta, ReflectStruct,
9
};
10
use bevy_macro_utils::fq_std::{FQClone, FQDefault, FQOption};
11
use proc_macro2::Span;
12
use quote::{quote, ToTokens};
13
use syn::{Field, Ident, Lit, LitInt, LitStr, Member};
14
15
/// Implements `FromReflect` for the given struct
16
pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {
17
impl_struct_internal(reflect_struct, false)
18
}
19
20
/// Implements `FromReflect` for the given tuple struct
21
pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenStream {
22
impl_struct_internal(reflect_struct, true)
23
}
24
25
pub(crate) fn impl_opaque(meta: &ReflectMeta) -> proc_macro2::TokenStream {
26
let type_path = meta.type_path();
27
let bevy_reflect_path = meta.bevy_reflect_path();
28
let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl();
29
let where_from_reflect_clause = WhereClauseOptions::new(meta).extend_where_clause(where_clause);
30
31
let downcast = match meta.remote_ty() {
32
Some(remote) => {
33
let remote_ty = remote.type_path();
34
quote! {
35
<Self as #bevy_reflect_path::ReflectRemote>::into_wrapper(
36
#FQClone::clone(
37
<dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<#remote_ty>(reflect)?
38
)
39
)
40
}
41
}
42
None => quote! {
43
#FQClone::clone(
44
<dyn #bevy_reflect_path::PartialReflect>::try_downcast_ref::<#type_path #ty_generics>(reflect)?
45
)
46
},
47
};
48
49
quote! {
50
impl #impl_generics #bevy_reflect_path::FromReflect for #type_path #ty_generics #where_from_reflect_clause {
51
fn from_reflect(reflect: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<Self> {
52
#FQOption::Some(#downcast)
53
}
54
}
55
}
56
}
57
58
/// Implements `FromReflect` for the given enum type
59
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream {
60
let fqoption = FQOption.into_token_stream();
61
62
let enum_path = reflect_enum.meta().type_path();
63
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
64
65
let ref_value = Ident::new("__param0", Span::call_site());
66
67
let EnumVariantOutputData {
68
variant_names,
69
variant_constructors,
70
..
71
} = FromReflectVariantBuilder::new(reflect_enum).build(&ref_value);
72
73
let match_branches = if reflect_enum.meta().is_remote_wrapper() {
74
quote! {
75
#(#variant_names => #fqoption::Some(Self(#variant_constructors)),)*
76
}
77
} else {
78
quote! {
79
#(#variant_names => #fqoption::Some(#variant_constructors),)*
80
}
81
};
82
83
let (impl_generics, ty_generics, where_clause) = enum_path.generics().split_for_impl();
84
85
// Add FromReflect bound for each active field
86
let where_from_reflect_clause = reflect_enum
87
.where_clause_options()
88
.extend_where_clause(where_clause);
89
90
quote! {
91
impl #impl_generics #bevy_reflect_path::FromReflect for #enum_path #ty_generics #where_from_reflect_clause {
92
fn from_reflect(#ref_value: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<Self> {
93
if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) =
94
#bevy_reflect_path::PartialReflect::reflect_ref(#ref_value)
95
{
96
match #bevy_reflect_path::Enum::variant_name(#ref_value) {
97
#match_branches
98
name => panic!("variant with name `{}` does not exist on enum `{}`", name, <Self as #bevy_reflect_path::TypePath>::type_path()),
99
}
100
} else {
101
#FQOption::None
102
}
103
}
104
}
105
}
106
}
107
108
/// Container for a struct's members (field name or index) and their
109
/// corresponding values.
110
struct MemberValuePair(Vec<Member>, Vec<proc_macro2::TokenStream>);
111
112
impl MemberValuePair {
113
pub fn new(items: (Vec<Member>, Vec<proc_macro2::TokenStream>)) -> Self {
114
Self(items.0, items.1)
115
}
116
}
117
118
fn impl_struct_internal(
119
reflect_struct: &ReflectStruct,
120
is_tuple: bool,
121
) -> proc_macro2::TokenStream {
122
let fqoption = FQOption.into_token_stream();
123
124
let struct_path = reflect_struct.meta().type_path();
125
let remote_ty = reflect_struct.meta().remote_ty();
126
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
127
128
let ref_struct = Ident::new("__ref_struct", Span::call_site());
129
let ref_struct_type = if is_tuple {
130
Ident::new("TupleStruct", Span::call_site())
131
} else {
132
Ident::new("Struct", Span::call_site())
133
};
134
135
let MemberValuePair(active_members, active_values) =
136
get_active_fields(reflect_struct, &ref_struct, &ref_struct_type, is_tuple);
137
138
let is_defaultable = reflect_struct.meta().attrs().contains(REFLECT_DEFAULT);
139
140
// The constructed "Self" ident
141
let __this = Ident::new("__this", Span::call_site());
142
143
// The reflected type: either `Self` or a remote type
144
let (reflect_ty, constructor, retval) = if let Some(remote_ty) = remote_ty {
145
let constructor = match remote_ty.as_expr_path() {
146
Ok(path) => path,
147
Err(err) => return err.into_compile_error(),
148
};
149
let remote_ty = remote_ty.type_path();
150
151
(
152
quote!(#remote_ty),
153
quote!(#constructor),
154
quote!(Self(#__this)),
155
)
156
} else {
157
(quote!(Self), quote!(Self), quote!(#__this))
158
};
159
160
let constructor = if is_defaultable {
161
quote! {
162
let mut #__this = <#reflect_ty as #FQDefault>::default();
163
#(
164
// The closure catches any failing `?` within `active_values`.
165
if let #fqoption::Some(__field) = (|| #active_values)() {
166
// Iff field exists -> use its value
167
#__this.#active_members = __field;
168
}
169
)*
170
#FQOption::Some(#retval)
171
}
172
} else {
173
let MemberValuePair(ignored_members, ignored_values) = get_ignored_fields(reflect_struct);
174
175
quote! {
176
let #__this = #constructor {
177
#(#active_members: #active_values?,)*
178
#(#ignored_members: #ignored_values,)*
179
};
180
#FQOption::Some(#retval)
181
}
182
};
183
184
let (impl_generics, ty_generics, where_clause) = reflect_struct
185
.meta()
186
.type_path()
187
.generics()
188
.split_for_impl();
189
190
// Add FromReflect bound for each active field
191
let where_from_reflect_clause = reflect_struct
192
.where_clause_options()
193
.extend_where_clause(where_clause);
194
195
quote! {
196
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_path #ty_generics #where_from_reflect_clause {
197
fn from_reflect(reflect: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<Self> {
198
if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct)
199
= #bevy_reflect_path::PartialReflect::reflect_ref(reflect)
200
{
201
#constructor
202
} else {
203
#FQOption::None
204
}
205
}
206
}
207
}
208
}
209
210
/// Get the collection of ignored field definitions
211
///
212
/// Each value of the `MemberValuePair` is a token stream that generates a
213
/// a default value for the ignored field.
214
fn get_ignored_fields(reflect_struct: &ReflectStruct) -> MemberValuePair {
215
MemberValuePair::new(
216
reflect_struct
217
.ignored_fields()
218
.map(|field| {
219
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
220
221
let value = match &field.attrs.default {
222
DefaultBehavior::Func(path) => quote! {#path()},
223
_ => quote! {#FQDefault::default()},
224
};
225
226
(member, value)
227
})
228
.unzip(),
229
)
230
}
231
232
/// Get the collection of active field definitions.
233
///
234
/// Each value of the `MemberValuePair` is a token stream that generates a
235
/// closure of type `fn() -> Option<T>` where `T` is that field's type.
236
fn get_active_fields(
237
reflect_struct: &ReflectStruct,
238
dyn_struct_name: &Ident,
239
struct_type: &Ident,
240
is_tuple: bool,
241
) -> MemberValuePair {
242
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
243
244
MemberValuePair::new(
245
reflect_struct
246
.active_fields()
247
.map(|field| {
248
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
249
let accessor = get_field_accessor(
250
field.data,
251
field.reflection_index.expect("field should be active"),
252
is_tuple,
253
);
254
let ty = field.reflected_type().clone();
255
let real_ty = &field.data.ty;
256
257
let get_field = quote! {
258
#bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)
259
};
260
261
let into_remote = |value: proc_macro2::TokenStream| {
262
if field.attrs.is_remote_generic().unwrap_or_default() {
263
quote! {
264
#FQOption::Some(
265
// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type
266
unsafe {
267
::core::mem::transmute_copy::<#ty, #real_ty>(
268
&::core::mem::ManuallyDrop::new(#value?)
269
)
270
}
271
)
272
}
273
} else if field.attrs().remote.is_some() {
274
quote! {
275
#FQOption::Some(
276
// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type
277
unsafe {
278
::core::mem::transmute::<#ty, #real_ty>(#value?)
279
}
280
)
281
}
282
} else {
283
value
284
}
285
};
286
287
let value = match &field.attrs.default {
288
DefaultBehavior::Func(path) => {
289
let value = into_remote(quote! {
290
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
291
});
292
quote! {
293
if let #FQOption::Some(field) = #get_field {
294
#value
295
} else {
296
#FQOption::Some(#path())
297
}
298
}
299
}
300
DefaultBehavior::Default => {
301
let value = into_remote(quote! {
302
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
303
});
304
quote! {
305
if let #FQOption::Some(field) = #get_field {
306
#value
307
} else {
308
#FQOption::Some(#FQDefault::default())
309
}
310
}
311
}
312
DefaultBehavior::Required => {
313
let value = into_remote(quote! {
314
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?)
315
});
316
quote! {
317
#value
318
}
319
}
320
};
321
322
(member, value)
323
})
324
.unzip(),
325
)
326
}
327
328
/// Returns the accessor for a given field of a struct or tuple struct.
329
///
330
/// This differs from a member in that it needs to be a number for tuple structs
331
/// and a string for standard structs.
332
fn get_field_accessor(field: &Field, index: usize, is_tuple: bool) -> Lit {
333
if is_tuple {
334
Lit::Int(LitInt::new(&index.to_string(), Span::call_site()))
335
} else {
336
field
337
.ident
338
.as_ref()
339
.map(|ident| Lit::Str(LitStr::new(&ident.to_string(), Span::call_site())))
340
.unwrap_or_else(|| Lit::Str(LitStr::new(&index.to_string(), Span::call_site())))
341
}
342
}
343
344