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
9408 views
1
use crate::{
2
container_attributes::REFLECT_DEFAULT,
3
derive_data::ReflectEnum,
4
enum_utility::{EnumVariantOutputData, FromReflectVariantBuilder, VariantBuilder},
5
field_attributes::DefaultBehavior,
6
where_clause_options::WhereClauseOptions,
7
ReflectMeta, ReflectStruct,
8
};
9
use bevy_macro_utils::as_member;
10
use bevy_macro_utils::fq_std::{FQClone, FQDefault, FQOption};
11
use proc_macro2::Span;
12
use quote::{quote, ToTokens};
13
use syn::{parse_str, Field, Ident, Lit, LitInt, LitStr, Member, Path};
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::enums::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, ref_struct_path) = if is_tuple {
130
(
131
Ident::new("TupleStruct", Span::call_site()),
132
parse_str("tuple_struct::TupleStruct").expect("should be a valid path"),
133
)
134
} else {
135
(
136
Ident::new("Struct", Span::call_site()),
137
parse_str("structs::Struct").expect("should be a valid path"),
138
)
139
};
140
141
let MemberValuePair(active_members, active_values) =
142
get_active_fields(reflect_struct, &ref_struct, &ref_struct_path, is_tuple);
143
144
let is_defaultable = reflect_struct.meta().attrs().contains(REFLECT_DEFAULT);
145
146
// The constructed "Self" ident
147
let __this = Ident::new("__this", Span::call_site());
148
149
// Workaround for rustfmt issue: https://github.com/rust-lang/rustfmt/issues/6779
150
// `quote!(Self(#__this))` causes rustfmt to panic in Rust 1.93.0+
151
// TODO: not needed after Rust 1.94
152
let self_ty = quote!(Self);
153
154
// The reflected type: either `Self` or a remote type
155
let (reflect_ty, constructor, retval) = if let Some(remote_ty) = remote_ty {
156
let constructor = match remote_ty.as_expr_path() {
157
Ok(path) => path,
158
Err(err) => return err.into_compile_error(),
159
};
160
let remote_ty = remote_ty.type_path();
161
162
(
163
quote!(#remote_ty),
164
quote!(#constructor),
165
quote!(#self_ty(#__this)),
166
)
167
} else {
168
(quote!(#self_ty), quote!(#self_ty), quote!(#__this))
169
};
170
171
let constructor = if is_defaultable {
172
quote! {
173
let mut #__this = <#reflect_ty as #FQDefault>::default();
174
#(
175
// The closure catches any failing `?` within `active_values`.
176
if let #fqoption::Some(__field) = (|| #active_values)() {
177
// Iff field exists -> use its value
178
#__this.#active_members = __field;
179
}
180
)*
181
#FQOption::Some(#retval)
182
}
183
} else {
184
let MemberValuePair(ignored_members, ignored_values) = get_ignored_fields(reflect_struct);
185
186
quote! {
187
let #__this = #constructor {
188
#(#active_members: #active_values?,)*
189
#(#ignored_members: #ignored_values,)*
190
};
191
#FQOption::Some(#retval)
192
}
193
};
194
195
let (impl_generics, ty_generics, where_clause) = reflect_struct
196
.meta()
197
.type_path()
198
.generics()
199
.split_for_impl();
200
201
// Add FromReflect bound for each active field
202
let where_from_reflect_clause = reflect_struct
203
.where_clause_options()
204
.extend_where_clause(where_clause);
205
206
quote! {
207
impl #impl_generics #bevy_reflect_path::FromReflect for #struct_path #ty_generics #where_from_reflect_clause {
208
fn from_reflect(reflect: &dyn #bevy_reflect_path::PartialReflect) -> #FQOption<Self> {
209
if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct)
210
= #bevy_reflect_path::PartialReflect::reflect_ref(reflect)
211
{
212
#constructor
213
} else {
214
#FQOption::None
215
}
216
}
217
}
218
}
219
}
220
221
/// Get the collection of ignored field definitions
222
///
223
/// Each value of the `MemberValuePair` is a token stream that generates a
224
/// default value for the ignored field.
225
fn get_ignored_fields(reflect_struct: &ReflectStruct) -> MemberValuePair {
226
MemberValuePair::new(
227
reflect_struct
228
.ignored_fields()
229
.map(|field| {
230
let member = as_member(field.data.ident.as_ref(), field.declaration_index);
231
232
let value = match &field.attrs.default {
233
DefaultBehavior::Func(path) => quote! {#path()},
234
_ => quote! {#FQDefault::default()},
235
};
236
237
(member, value)
238
})
239
.unzip(),
240
)
241
}
242
243
/// Get the collection of active field definitions.
244
///
245
/// Each value of the `MemberValuePair` is a token stream that generates a
246
/// closure of type `fn() -> Option<T>` where `T` is that field's type.
247
fn get_active_fields(
248
reflect_struct: &ReflectStruct,
249
dyn_struct_name: &Ident,
250
struct_type: &Path,
251
is_tuple: bool,
252
) -> MemberValuePair {
253
let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path();
254
255
MemberValuePair::new(
256
reflect_struct
257
.active_fields()
258
.map(|field| {
259
let member = as_member(field.data.ident.as_ref(), field.declaration_index);
260
let accessor = get_field_accessor(
261
field.data,
262
field.reflection_index.expect("field should be active"),
263
is_tuple,
264
);
265
let ty = field.reflected_type().clone();
266
let real_ty = &field.data.ty;
267
268
let get_field = quote! {
269
#bevy_reflect_path::#struct_type::field(#dyn_struct_name, #accessor)
270
};
271
272
let into_remote = |value: proc_macro2::TokenStream| {
273
if field.attrs.is_remote_generic().unwrap_or_default() {
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_copy::<#ty, #real_ty>(
279
&::core::mem::ManuallyDrop::new(#value?)
280
)
281
}
282
)
283
}
284
} else if field.attrs().remote.is_some() {
285
quote! {
286
#FQOption::Some(
287
// SAFETY: The remote type should always be a `#[repr(transparent)]` for the actual field type
288
unsafe {
289
::core::mem::transmute::<#ty, #real_ty>(#value?)
290
}
291
)
292
}
293
} else {
294
value
295
}
296
};
297
298
let value = match &field.attrs.default {
299
DefaultBehavior::Func(path) => {
300
let value = into_remote(quote! {
301
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
302
});
303
quote! {
304
if let #FQOption::Some(field) = #get_field {
305
#value
306
} else {
307
#FQOption::Some(#path())
308
}
309
}
310
}
311
DefaultBehavior::Default => {
312
let value = into_remote(quote! {
313
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(field)
314
});
315
quote! {
316
if let #FQOption::Some(field) = #get_field {
317
#value
318
} else {
319
#FQOption::Some(#FQDefault::default())
320
}
321
}
322
}
323
DefaultBehavior::Required => {
324
let value = into_remote(quote! {
325
<#ty as #bevy_reflect_path::FromReflect>::from_reflect(#get_field?)
326
});
327
quote! {
328
#value
329
}
330
}
331
};
332
333
(member, value)
334
})
335
.unzip(),
336
)
337
}
338
339
/// Returns the accessor for a given field of a struct or tuple struct.
340
///
341
/// This differs from a member in that it needs to be a number for tuple structs
342
/// and a string for standard structs.
343
fn get_field_accessor(field: &Field, index: usize, is_tuple: bool) -> Lit {
344
if is_tuple {
345
Lit::Int(LitInt::new(&index.to_string(), Span::call_site()))
346
} else {
347
field
348
.ident
349
.as_ref()
350
.map(|ident| Lit::Str(LitStr::new(&ident.to_string(), Span::call_site())))
351
.unwrap_or_else(|| Lit::Str(LitStr::new(&index.to_string(), Span::call_site())))
352
}
353
}
354
355