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