Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_reflect/derive/src/enum_utility.rs
6599 views
1
use crate::field_attributes::CloneBehavior;
2
use crate::{
3
derive_data::ReflectEnum, derive_data::StructField, field_attributes::DefaultBehavior,
4
ident::ident_or_index,
5
};
6
use bevy_macro_utils::fq_std::{FQClone, FQDefault, FQOption, FQResult};
7
use proc_macro2::{Ident, TokenStream};
8
use quote::{format_ident, quote, ToTokens};
9
10
pub(crate) struct EnumVariantOutputData {
11
/// The names of each variant as a string.
12
///
13
/// For example, `Some` and `None` for the `Option` enum.
14
pub variant_names: Vec<String>,
15
/// The pattern matching portion of each variant.
16
///
17
/// For example, `Option::Some { 0: _0 }` and `Option::None {}` for the `Option` enum.
18
pub variant_patterns: Vec<TokenStream>,
19
/// The constructor portion of each variant.
20
///
21
/// For example, `Option::Some { 0: value }` and `Option::None {}` for the `Option` enum.
22
pub variant_constructors: Vec<TokenStream>,
23
}
24
25
#[derive(Copy, Clone)]
26
pub(crate) struct VariantField<'a, 'b> {
27
/// The alias for the field.
28
///
29
/// This should be used whenever the field needs to be referenced in a token stream.
30
pub alias: &'a Ident,
31
/// The name of the variant that contains the field.
32
pub variant_name: &'a str,
33
/// The field data.
34
pub field: &'a StructField<'b>,
35
}
36
37
/// Trait used to control how enum variants are built.
38
pub(crate) trait VariantBuilder: Sized {
39
/// Returns the enum data.
40
fn reflect_enum(&self) -> &ReflectEnum<'_>;
41
42
/// Returns a token stream that accesses a field of a variant as an `Option<dyn Reflect>`.
43
///
44
/// The default implementation of this method will return a token stream
45
/// which gets the field dynamically so as to support `dyn Enum`.
46
///
47
/// # Parameters
48
/// * `this`: The identifier of the enum
49
/// * `field`: The field to access
50
fn access_field(&self, this: &Ident, field: VariantField) -> TokenStream {
51
if let Some(field_ident) = &field.field.data.ident {
52
let name = field_ident.to_string();
53
quote!(#this.field(#name))
54
} else if let Some(field_index) = field.field.reflection_index {
55
quote!(#this.field_at(#field_index))
56
} else {
57
quote!(::core::compile_error!(
58
"internal bevy_reflect error: field should be active"
59
))
60
}
61
}
62
63
/// Returns a token stream that unwraps a field of a variant as a `&dyn Reflect`
64
/// (from an `Option<dyn Reflect>`).
65
///
66
/// # Parameters
67
/// * `field`: The field to access
68
fn unwrap_field(&self, field: VariantField) -> TokenStream;
69
70
/// Returns a token stream that constructs a field of a variant as a concrete type
71
/// (from a `&dyn Reflect`).
72
///
73
/// # Parameters
74
/// * `field`: The field to access
75
fn construct_field(&self, field: VariantField) -> TokenStream;
76
77
/// Returns a token stream that constructs an instance of an active field.
78
///
79
/// # Parameters
80
/// * `this`: The identifier of the enum
81
/// * `field`: The field to access
82
fn on_active_field(&self, this: &Ident, field: VariantField) -> TokenStream {
83
let bevy_reflect_path = self.reflect_enum().meta().bevy_reflect_path();
84
let field_accessor = self.access_field(this, field);
85
86
let alias = field.alias;
87
let field_ty = field.field.reflected_type();
88
let field_constructor = self.construct_field(field);
89
90
let construction = match &field.field.attrs.default {
91
DefaultBehavior::Func(path) => quote! {
92
if let #FQOption::Some(#alias) = #field_accessor {
93
#field_constructor
94
} else {
95
#path()
96
}
97
},
98
DefaultBehavior::Default => quote! {
99
if let #FQOption::Some(#alias) = #field_accessor {
100
#field_constructor
101
} else {
102
#FQDefault::default()
103
}
104
},
105
DefaultBehavior::Required => {
106
let field_unwrapper = self.unwrap_field(field);
107
108
quote! {{
109
// `#alias` is used by both the unwrapper and constructor
110
let #alias = #field_accessor;
111
let #alias = #field_unwrapper;
112
#field_constructor
113
}}
114
}
115
};
116
117
if field.field.attrs().remote.is_some() {
118
quote! {
119
<#field_ty as #bevy_reflect_path::ReflectRemote>::into_remote(#construction)
120
}
121
} else {
122
construction
123
}
124
}
125
126
/// Returns a token stream that constructs an instance of an ignored field.
127
///
128
/// # Parameters
129
/// * `field`: The field to access
130
fn on_ignored_field(&self, field: VariantField) -> TokenStream {
131
match &field.field.attrs.default {
132
DefaultBehavior::Func(path) => quote! { #path() },
133
_ => quote! { #FQDefault::default() },
134
}
135
}
136
137
/// Builds the enum variant output data.
138
fn build(&self, this: &Ident) -> EnumVariantOutputData {
139
let variants = self.reflect_enum().variants();
140
141
let mut variant_names = Vec::with_capacity(variants.len());
142
let mut variant_patterns = Vec::with_capacity(variants.len());
143
let mut variant_constructors = Vec::with_capacity(variants.len());
144
145
for variant in variants {
146
let variant_ident = &variant.data.ident;
147
let variant_name = variant_ident.to_string();
148
let variant_path = self.reflect_enum().get_unit(variant_ident);
149
150
let fields = variant.fields();
151
152
let mut field_patterns = Vec::with_capacity(fields.len());
153
let mut field_constructors = Vec::with_capacity(fields.len());
154
155
for field in fields {
156
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
157
let alias = format_ident!("_{}", member);
158
159
let variant_field = VariantField {
160
alias: &alias,
161
variant_name: &variant_name,
162
field,
163
};
164
165
let value = if field.attrs.ignore.is_ignored() {
166
self.on_ignored_field(variant_field)
167
} else {
168
self.on_active_field(this, variant_field)
169
};
170
171
field_patterns.push(quote! {
172
#member: #alias
173
});
174
175
field_constructors.push(quote! {
176
#member: #value
177
});
178
}
179
180
let pattern = quote! {
181
#variant_path { #( #field_patterns ),* }
182
};
183
184
let constructor = quote! {
185
#variant_path {
186
#( #field_constructors ),*
187
}
188
};
189
190
variant_names.push(variant_name);
191
variant_patterns.push(pattern);
192
variant_constructors.push(constructor);
193
}
194
195
EnumVariantOutputData {
196
variant_names,
197
variant_patterns,
198
variant_constructors,
199
}
200
}
201
}
202
203
/// Generates the enum variant output data needed to build the `FromReflect::from_reflect` implementation.
204
pub(crate) struct FromReflectVariantBuilder<'a> {
205
reflect_enum: &'a ReflectEnum<'a>,
206
}
207
208
impl<'a> FromReflectVariantBuilder<'a> {
209
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
210
Self { reflect_enum }
211
}
212
}
213
214
impl<'a> VariantBuilder for FromReflectVariantBuilder<'a> {
215
fn reflect_enum(&self) -> &ReflectEnum<'_> {
216
self.reflect_enum
217
}
218
219
fn unwrap_field(&self, field: VariantField) -> TokenStream {
220
let alias = field.alias;
221
quote!(#alias?)
222
}
223
224
fn construct_field(&self, field: VariantField) -> TokenStream {
225
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
226
let field_ty = field.field.reflected_type();
227
let alias = field.alias;
228
229
quote! {
230
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)?
231
}
232
}
233
}
234
235
/// Generates the enum variant output data needed to build the `PartialReflect::try_apply` implementation.
236
pub(crate) struct TryApplyVariantBuilder<'a> {
237
reflect_enum: &'a ReflectEnum<'a>,
238
}
239
240
impl<'a> TryApplyVariantBuilder<'a> {
241
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
242
Self { reflect_enum }
243
}
244
}
245
246
impl<'a> VariantBuilder for TryApplyVariantBuilder<'a> {
247
fn reflect_enum(&self) -> &ReflectEnum<'_> {
248
self.reflect_enum
249
}
250
251
fn unwrap_field(&self, field: VariantField) -> TokenStream {
252
let VariantField {
253
alias,
254
variant_name,
255
field,
256
..
257
} = field;
258
259
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
260
261
let field_name = match &field.data.ident {
262
Some(ident) => format!("{ident}"),
263
None => format!(".{}", field.declaration_index),
264
};
265
266
quote! {
267
#alias.ok_or(#bevy_reflect_path::ApplyError::MissingEnumField {
268
variant_name: ::core::convert::Into::into(#variant_name),
269
field_name: ::core::convert::Into::into(#field_name)
270
})?
271
}
272
}
273
274
fn construct_field(&self, field: VariantField) -> TokenStream {
275
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
276
let alias = field.alias;
277
let field_ty = field.field.reflected_type();
278
279
quote! {
280
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)
281
.ok_or(#bevy_reflect_path::ApplyError::MismatchedTypes {
282
from_type: ::core::convert::Into::into(
283
#bevy_reflect_path::DynamicTypePath::reflect_type_path(#alias)
284
),
285
to_type: ::core::convert::Into::into(<#field_ty as #bevy_reflect_path::TypePath>::type_path())
286
})?
287
}
288
}
289
}
290
291
/// Generates the enum variant output data needed to build the `Reflect::reflect_clone` implementation.
292
pub(crate) struct ReflectCloneVariantBuilder<'a> {
293
reflect_enum: &'a ReflectEnum<'a>,
294
}
295
296
impl<'a> ReflectCloneVariantBuilder<'a> {
297
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
298
Self { reflect_enum }
299
}
300
}
301
302
impl<'a> VariantBuilder for ReflectCloneVariantBuilder<'a> {
303
fn reflect_enum(&self) -> &ReflectEnum<'_> {
304
self.reflect_enum
305
}
306
307
fn access_field(&self, _ident: &Ident, field: VariantField) -> TokenStream {
308
let alias = field.alias;
309
quote!(#FQOption::Some(#alias))
310
}
311
312
fn unwrap_field(&self, field: VariantField) -> TokenStream {
313
let alias = field.alias;
314
quote!(#alias.unwrap())
315
}
316
317
fn construct_field(&self, field: VariantField) -> TokenStream {
318
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
319
let field_ty = field.field.reflected_type();
320
let alias = field.alias;
321
let alias = match &field.field.attrs.remote {
322
Some(wrapper_ty) => {
323
quote! {
324
<#wrapper_ty as #bevy_reflect_path::ReflectRemote>::as_wrapper(#alias)
325
}
326
}
327
None => alias.to_token_stream(),
328
};
329
330
match &field.field.attrs.clone {
331
CloneBehavior::Default => {
332
quote! {
333
<#field_ty as #bevy_reflect_path::PartialReflect>::reflect_clone_and_take(#alias)?
334
}
335
}
336
CloneBehavior::Trait => {
337
quote! {
338
#FQClone::clone(#alias)
339
}
340
}
341
CloneBehavior::Func(clone_fn) => {
342
quote! {
343
#clone_fn(#alias)
344
}
345
}
346
}
347
}
348
349
fn on_active_field(&self, _this: &Ident, field: VariantField) -> TokenStream {
350
self.construct_field(field)
351
}
352
353
fn on_ignored_field(&self, field: VariantField) -> TokenStream {
354
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
355
let variant_name = field.variant_name;
356
let alias = field.alias;
357
358
match &field.field.attrs.clone {
359
CloneBehavior::Default => {
360
let field_id = field.field.field_id(bevy_reflect_path);
361
362
quote! {
363
return #FQResult::Err(
364
#bevy_reflect_path::ReflectCloneError::FieldNotCloneable {
365
field: #field_id,
366
variant: #FQOption::Some(#bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(#variant_name)),
367
container_type_path: #bevy_reflect_path::__macro_exports::alloc_utils::Cow::Borrowed(<Self as #bevy_reflect_path::TypePath>::type_path())
368
}
369
)
370
}
371
}
372
CloneBehavior::Trait => quote! { #FQClone::clone(#alias) },
373
CloneBehavior::Func(clone_fn) => quote! { #clone_fn() },
374
}
375
}
376
}
377
378