Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/derive/src/impls/enums.rs
6600 views
1
use crate::{
2
derive_data::{EnumVariantFields, ReflectEnum, StructField},
3
enum_utility::{EnumVariantOutputData, TryApplyVariantBuilder, VariantBuilder},
4
impls::{common_partial_reflect_methods, impl_full_reflect, impl_type_path, impl_typed},
5
};
6
use bevy_macro_utils::fq_std::{FQOption, FQResult};
7
use proc_macro2::{Ident, Span};
8
use quote::quote;
9
use syn::{Fields, Path};
10
11
pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream {
12
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
13
let enum_path = reflect_enum.meta().type_path();
14
let is_remote = reflect_enum.meta().is_remote_wrapper();
15
16
// For `match self` expressions where self is a reference
17
let match_this = if is_remote {
18
quote!(&self.0)
19
} else {
20
quote!(self)
21
};
22
// For `match self` expressions where self is a mutable reference
23
let match_this_mut = if is_remote {
24
quote!(&mut self.0)
25
} else {
26
quote!(self)
27
};
28
// For `*self` assignments
29
let deref_this = if is_remote {
30
quote!(self.0)
31
} else {
32
quote!(*self)
33
};
34
35
let ref_name = Ident::new("__name_param", Span::call_site());
36
let ref_index = Ident::new("__index_param", Span::call_site());
37
let ref_value = Ident::new("__value_param", Span::call_site());
38
39
let EnumImpls {
40
enum_field,
41
enum_field_mut,
42
enum_field_at,
43
enum_field_at_mut,
44
enum_index_of,
45
enum_name_at,
46
enum_field_len,
47
enum_variant_name,
48
enum_variant_index,
49
enum_variant_type,
50
} = generate_impls(reflect_enum, &ref_index, &ref_name);
51
52
let EnumVariantOutputData {
53
variant_names,
54
variant_constructors,
55
..
56
} = TryApplyVariantBuilder::new(reflect_enum).build(&ref_value);
57
58
let where_clause_options = reflect_enum.where_clause_options();
59
let typed_impl = impl_typed(&where_clause_options, reflect_enum.to_info_tokens());
60
61
let type_path_impl = impl_type_path(reflect_enum.meta());
62
let full_reflect_impl = impl_full_reflect(&where_clause_options);
63
let common_methods = common_partial_reflect_methods(
64
reflect_enum.meta(),
65
|| Some(quote!(#bevy_reflect_path::enum_partial_eq)),
66
|| Some(quote!(#bevy_reflect_path::enum_hash)),
67
);
68
let clone_fn = reflect_enum.get_clone_impl();
69
70
#[cfg(not(feature = "functions"))]
71
let function_impls = None::<proc_macro2::TokenStream>;
72
#[cfg(feature = "functions")]
73
let function_impls = crate::impls::impl_function_traits(&where_clause_options);
74
75
let get_type_registration_impl = reflect_enum.get_type_registration(&where_clause_options);
76
77
let (impl_generics, ty_generics, where_clause) =
78
reflect_enum.meta().type_path().generics().split_for_impl();
79
80
#[cfg(not(feature = "auto_register"))]
81
let auto_register = None::<proc_macro2::TokenStream>;
82
#[cfg(feature = "auto_register")]
83
let auto_register = crate::impls::reflect_auto_registration(reflect_enum.meta());
84
85
let where_reflect_clause = where_clause_options.extend_where_clause(where_clause);
86
87
quote! {
88
#get_type_registration_impl
89
90
#typed_impl
91
92
#type_path_impl
93
94
#full_reflect_impl
95
96
#function_impls
97
98
#auto_register
99
100
impl #impl_generics #bevy_reflect_path::Enum for #enum_path #ty_generics #where_reflect_clause {
101
fn field(&self, #ref_name: &str) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
102
match #match_this {
103
#(#enum_field,)*
104
_ => #FQOption::None,
105
}
106
}
107
108
fn field_at(&self, #ref_index: usize) -> #FQOption<&dyn #bevy_reflect_path::PartialReflect> {
109
match #match_this {
110
#(#enum_field_at,)*
111
_ => #FQOption::None,
112
}
113
}
114
115
fn field_mut(&mut self, #ref_name: &str) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
116
match #match_this_mut {
117
#(#enum_field_mut,)*
118
_ => #FQOption::None,
119
}
120
}
121
122
fn field_at_mut(&mut self, #ref_index: usize) -> #FQOption<&mut dyn #bevy_reflect_path::PartialReflect> {
123
match #match_this_mut {
124
#(#enum_field_at_mut,)*
125
_ => #FQOption::None,
126
}
127
}
128
129
fn index_of(&self, #ref_name: &str) -> #FQOption<usize> {
130
match #match_this {
131
#(#enum_index_of,)*
132
_ => #FQOption::None,
133
}
134
}
135
136
fn name_at(&self, #ref_index: usize) -> #FQOption<&str> {
137
match #match_this {
138
#(#enum_name_at,)*
139
_ => #FQOption::None,
140
}
141
}
142
143
fn iter_fields(&self) -> #bevy_reflect_path::VariantFieldIter {
144
#bevy_reflect_path::VariantFieldIter::new(self)
145
}
146
147
#[inline]
148
fn field_len(&self) -> usize {
149
match #match_this {
150
#(#enum_field_len,)*
151
_ => 0,
152
}
153
}
154
155
#[inline]
156
fn variant_name(&self) -> &str {
157
match #match_this {
158
#(#enum_variant_name,)*
159
_ => unreachable!(),
160
}
161
}
162
163
#[inline]
164
fn variant_index(&self) -> usize {
165
match #match_this {
166
#(#enum_variant_index,)*
167
_ => unreachable!(),
168
}
169
}
170
171
#[inline]
172
fn variant_type(&self) -> #bevy_reflect_path::VariantType {
173
match #match_this {
174
#(#enum_variant_type,)*
175
_ => unreachable!(),
176
}
177
}
178
179
fn to_dynamic_enum(&self) -> #bevy_reflect_path::DynamicEnum {
180
#bevy_reflect_path::DynamicEnum::from_ref::<Self>(self)
181
}
182
}
183
184
impl #impl_generics #bevy_reflect_path::PartialReflect for #enum_path #ty_generics #where_reflect_clause {
185
#[inline]
186
fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> {
187
#FQOption::Some(<Self as #bevy_reflect_path::Typed>::type_info())
188
}
189
190
#[inline]
191
fn try_apply(
192
&mut self,
193
#ref_value: &dyn #bevy_reflect_path::PartialReflect
194
) -> #FQResult<(), #bevy_reflect_path::ApplyError> {
195
if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) =
196
#bevy_reflect_path::PartialReflect::reflect_ref(#ref_value) {
197
if #bevy_reflect_path::Enum::variant_name(self) == #bevy_reflect_path::Enum::variant_name(#ref_value) {
198
// Same variant -> just update fields
199
match #bevy_reflect_path::Enum::variant_type(#ref_value) {
200
#bevy_reflect_path::VariantType::Struct => {
201
for field in #bevy_reflect_path::Enum::iter_fields(#ref_value) {
202
let name = field.name().unwrap();
203
if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_mut(self, name) {
204
#bevy_reflect_path::PartialReflect::try_apply(v, field.value())?;
205
}
206
}
207
}
208
#bevy_reflect_path::VariantType::Tuple => {
209
for (index, field) in ::core::iter::Iterator::enumerate(#bevy_reflect_path::Enum::iter_fields(#ref_value)) {
210
if let #FQOption::Some(v) = #bevy_reflect_path::Enum::field_at_mut(self, index) {
211
#bevy_reflect_path::PartialReflect::try_apply(v, field.value())?;
212
}
213
}
214
}
215
_ => {}
216
}
217
} else {
218
// New variant -> perform a switch
219
match #bevy_reflect_path::Enum::variant_name(#ref_value) {
220
#(#variant_names => {
221
#deref_this = #variant_constructors
222
})*
223
name => {
224
return #FQResult::Err(
225
#bevy_reflect_path::ApplyError::UnknownVariant {
226
enum_name: ::core::convert::Into::into(#bevy_reflect_path::DynamicTypePath::reflect_type_path(self)),
227
variant_name: ::core::convert::Into::into(name),
228
}
229
);
230
}
231
}
232
}
233
} else {
234
return #FQResult::Err(
235
#bevy_reflect_path::ApplyError::MismatchedKinds {
236
from_kind: #bevy_reflect_path::PartialReflect::reflect_kind(#ref_value),
237
to_kind: #bevy_reflect_path::ReflectKind::Enum,
238
}
239
);
240
}
241
#FQResult::Ok(())
242
}
243
244
fn reflect_kind(&self) -> #bevy_reflect_path::ReflectKind {
245
#bevy_reflect_path::ReflectKind::Enum
246
}
247
248
fn reflect_ref(&self) -> #bevy_reflect_path::ReflectRef {
249
#bevy_reflect_path::ReflectRef::Enum(self)
250
}
251
252
fn reflect_mut(&mut self) -> #bevy_reflect_path::ReflectMut {
253
#bevy_reflect_path::ReflectMut::Enum(self)
254
}
255
256
fn reflect_owned(self: #bevy_reflect_path::__macro_exports::alloc_utils::Box<Self>) -> #bevy_reflect_path::ReflectOwned {
257
#bevy_reflect_path::ReflectOwned::Enum(self)
258
}
259
260
#common_methods
261
262
#clone_fn
263
}
264
}
265
}
266
267
struct EnumImpls {
268
enum_field: Vec<proc_macro2::TokenStream>,
269
enum_field_mut: Vec<proc_macro2::TokenStream>,
270
enum_field_at: Vec<proc_macro2::TokenStream>,
271
enum_field_at_mut: Vec<proc_macro2::TokenStream>,
272
enum_index_of: Vec<proc_macro2::TokenStream>,
273
enum_name_at: Vec<proc_macro2::TokenStream>,
274
enum_field_len: Vec<proc_macro2::TokenStream>,
275
enum_variant_name: Vec<proc_macro2::TokenStream>,
276
enum_variant_index: Vec<proc_macro2::TokenStream>,
277
enum_variant_type: Vec<proc_macro2::TokenStream>,
278
}
279
280
fn generate_impls(reflect_enum: &ReflectEnum, ref_index: &Ident, ref_name: &Ident) -> EnumImpls {
281
let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path();
282
283
let mut enum_field = Vec::new();
284
let mut enum_field_mut = Vec::new();
285
let mut enum_field_at = Vec::new();
286
let mut enum_field_at_mut = Vec::new();
287
let mut enum_index_of = Vec::new();
288
let mut enum_name_at = Vec::new();
289
let mut enum_field_len = Vec::new();
290
let mut enum_variant_name = Vec::new();
291
let mut enum_variant_index = Vec::new();
292
let mut enum_variant_type = Vec::new();
293
294
for (variant_index, variant) in reflect_enum.variants().iter().enumerate() {
295
let ident = &variant.data.ident;
296
let name = ident.to_string();
297
let unit = reflect_enum.get_unit(ident);
298
299
let variant_type_ident = match variant.data.fields {
300
Fields::Unit => Ident::new("Unit", Span::call_site()),
301
Fields::Unnamed(..) => Ident::new("Tuple", Span::call_site()),
302
Fields::Named(..) => Ident::new("Struct", Span::call_site()),
303
};
304
305
enum_variant_name.push(quote! {
306
#unit{..} => #name
307
});
308
enum_variant_index.push(quote! {
309
#unit{..} => #variant_index
310
});
311
enum_variant_type.push(quote! {
312
#unit{..} => #bevy_reflect_path::VariantType::#variant_type_ident
313
});
314
315
fn process_fields(
316
fields: &[StructField],
317
mut f: impl FnMut(&StructField) + Sized,
318
) -> usize {
319
let mut field_len = 0;
320
for field in fields.iter() {
321
if field.attrs.ignore.is_ignored() {
322
// Ignored field
323
continue;
324
};
325
326
f(field);
327
328
field_len += 1;
329
}
330
331
field_len
332
}
333
334
/// Process the field value to account for remote types.
335
///
336
/// If the field is a remote type, then the value will be transmuted accordingly.
337
fn process_field_value(
338
ident: &Ident,
339
field: &StructField,
340
is_mutable: bool,
341
bevy_reflect_path: &Path,
342
) -> proc_macro2::TokenStream {
343
let method = if is_mutable {
344
quote!(as_wrapper_mut)
345
} else {
346
quote!(as_wrapper)
347
};
348
349
field
350
.attrs
351
.remote
352
.as_ref()
353
.map(|ty| quote!(<#ty as #bevy_reflect_path::ReflectRemote>::#method(#ident)))
354
.unwrap_or_else(|| quote!(#ident))
355
}
356
357
match &variant.fields {
358
EnumVariantFields::Unit => {
359
let field_len = process_fields(&[], |_| {});
360
361
enum_field_len.push(quote! {
362
#unit{..} => #field_len
363
});
364
}
365
EnumVariantFields::Unnamed(fields) => {
366
let field_len = process_fields(fields, |field: &StructField| {
367
let reflection_index = field
368
.reflection_index
369
.expect("reflection index should exist for active field");
370
371
let declare_field = syn::Index::from(field.declaration_index);
372
373
let __value = Ident::new("__value", Span::call_site());
374
let value_ref = process_field_value(&__value, field, false, bevy_reflect_path);
375
let value_mut = process_field_value(&__value, field, true, bevy_reflect_path);
376
377
enum_field_at.push(quote! {
378
#unit { #declare_field : #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_ref)
379
});
380
enum_field_at_mut.push(quote! {
381
#unit { #declare_field : #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_mut)
382
});
383
});
384
385
enum_field_len.push(quote! {
386
#unit{..} => #field_len
387
});
388
}
389
EnumVariantFields::Named(fields) => {
390
let field_len = process_fields(fields, |field: &StructField| {
391
let field_ident = field.data.ident.as_ref().unwrap();
392
let field_name = field_ident.to_string();
393
let reflection_index = field
394
.reflection_index
395
.expect("reflection index should exist for active field");
396
397
let __value = Ident::new("__value", Span::call_site());
398
let value_ref = process_field_value(&__value, field, false, bevy_reflect_path);
399
let value_mut = process_field_value(&__value, field, true, bevy_reflect_path);
400
401
enum_field.push(quote! {
402
#unit{ #field_ident: #__value, .. } if #ref_name == #field_name => #FQOption::Some(#value_ref)
403
});
404
enum_field_mut.push(quote! {
405
#unit{ #field_ident: #__value, .. } if #ref_name == #field_name => #FQOption::Some(#value_mut)
406
});
407
enum_field_at.push(quote! {
408
#unit{ #field_ident: #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_ref)
409
});
410
enum_field_at_mut.push(quote! {
411
#unit{ #field_ident: #__value, .. } if #ref_index == #reflection_index => #FQOption::Some(#value_mut)
412
});
413
enum_index_of.push(quote! {
414
#unit{ .. } if #ref_name == #field_name => #FQOption::Some(#reflection_index)
415
});
416
enum_name_at.push(quote! {
417
#unit{ .. } if #ref_index == #reflection_index => #FQOption::Some(#field_name)
418
});
419
});
420
421
enum_field_len.push(quote! {
422
#unit{..} => #field_len
423
});
424
}
425
};
426
}
427
428
EnumImpls {
429
enum_field,
430
enum_field_mut,
431
enum_field_at,
432
enum_field_at_mut,
433
enum_index_of,
434
enum_name_at,
435
enum_field_len,
436
enum_variant_name,
437
enum_variant_index,
438
enum_variant_type,
439
}
440
}
441
442