Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/macros/src/template.rs
30636 views
1
use bevy_macro_utils::{fq_std::FQDefault, BevyManifest};
2
use proc_macro::TokenStream;
3
use quote::{format_ident, quote};
4
use syn::{
5
parse::ParseStream, parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned,
6
Data, DeriveInput, Fields, FieldsUnnamed, Ident, Index, Path, Result, Token, WhereClause,
7
};
8
9
const TEMPLATE_DEFAULT_ATTRIBUTE: &str = "default";
10
const TEMPLATE_ATTRIBUTE: &str = "template";
11
const BUILT_IN_ATTRIBUTE: &str = "built_in";
12
13
pub(crate) fn derive_from_template(input: TokenStream) -> TokenStream {
14
let ast = parse_macro_input!(input as DeriveInput);
15
let bevy_ecs = BevyManifest::shared(|manifest| manifest.get_path("bevy_ecs"));
16
17
let type_ident = &ast.ident;
18
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
19
20
let template_ident = format_ident!("{type_ident}Template");
21
22
let is_pub = matches!(ast.vis, syn::Visibility::Public(_));
23
let maybe_pub = if is_pub { quote!(pub) } else { quote!() };
24
25
let template = match &ast.data {
26
Data::Struct(data_struct) => {
27
let result = match struct_impl(&data_struct.fields, &bevy_ecs, false) {
28
Ok(result) => result,
29
Err(err) => return err.into_compile_error().into(),
30
};
31
let StructImpl {
32
template_fields,
33
template_field_builds,
34
template_field_defaults,
35
template_field_clones,
36
..
37
} = result;
38
match &data_struct.fields {
39
Fields::Named(_) => {
40
quote! {
41
#[allow(missing_docs)]
42
#maybe_pub struct #template_ident #impl_generics #where_clause {
43
#(#template_fields,)*
44
}
45
46
impl #impl_generics #bevy_ecs::template::Template for #template_ident #type_generics #where_clause {
47
type Output = #type_ident #type_generics;
48
fn build_template(&self, context: &mut #bevy_ecs::template::TemplateContext) -> #bevy_ecs::error::Result<Self::Output> {
49
#bevy_ecs::error::Result::Ok(#type_ident {
50
#(#template_field_builds,)*
51
})
52
}
53
54
fn clone_template(&self) -> Self {
55
Self {
56
#(#template_field_clones,)*
57
}
58
}
59
}
60
61
impl #impl_generics #FQDefault for #template_ident #type_generics #where_clause {
62
fn default() -> Self {
63
Self {
64
#(#template_field_defaults,)*
65
}
66
}
67
}
68
}
69
}
70
Fields::Unnamed(_) => {
71
quote! {
72
#[allow(missing_docs)]
73
#maybe_pub struct #template_ident #impl_generics (
74
#(#template_fields,)*
75
) #where_clause;
76
77
impl #impl_generics #bevy_ecs::template::Template for #template_ident #type_generics #where_clause {
78
type Output = #type_ident #type_generics;
79
fn build_template(&self, context: &mut #bevy_ecs::template::TemplateContext) -> #bevy_ecs::error::Result<Self::Output> {
80
#bevy_ecs::error::Result::Ok(#type_ident (
81
#(#template_field_builds,)*
82
))
83
}
84
85
fn clone_template(&self) -> Self {
86
Self(
87
#(#template_field_clones,)*
88
)
89
}
90
}
91
92
impl #impl_generics #FQDefault for #template_ident #type_generics #where_clause {
93
fn default() -> Self {
94
Self (
95
#(#template_field_defaults,)*
96
)
97
}
98
}
99
}
100
}
101
Fields::Unit => {
102
quote! {
103
#[allow(missing_docs)]
104
#maybe_pub struct #template_ident;
105
106
impl #impl_generics #bevy_ecs::template::Template for #template_ident #type_generics #where_clause {
107
type Output = #type_ident;
108
fn build_template(&self, context: &mut #bevy_ecs::template::TemplateContext) -> #bevy_ecs::error::Result<Self::Output> {
109
#bevy_ecs::error::Result::Ok(#type_ident)
110
}
111
112
fn clone_template(&self) -> Self {
113
Self
114
}
115
}
116
117
impl #impl_generics #FQDefault for #template_ident #type_generics #where_clause {
118
fn default() -> Self {
119
Self
120
}
121
}
122
}
123
}
124
}
125
}
126
Data::Enum(data_enum) => {
127
let mut variant_definitions = Vec::new();
128
let mut variant_builds = Vec::new();
129
let mut variant_clones = Vec::new();
130
let mut variant_default_ident = None;
131
let mut variant_defaults = Vec::new();
132
for variant in &data_enum.variants {
133
let result = match struct_impl(&variant.fields, &bevy_ecs, true) {
134
Ok(result) => result,
135
Err(err) => return err.into_compile_error().into(),
136
};
137
let StructImpl {
138
template_fields,
139
template_field_builds,
140
template_field_defaults,
141
template_field_clones,
142
..
143
} = result;
144
145
let is_default = variant
146
.attrs
147
.iter()
148
.any(|a| a.path().is_ident(TEMPLATE_DEFAULT_ATTRIBUTE));
149
if is_default && variant_default_ident.is_some() {
150
panic!("Cannot have multiple default variants");
151
}
152
let variant_ident = &variant.ident;
153
let variant_name_lower = variant_ident.to_string().to_lowercase();
154
let variant_default_name = format_ident!("default_{}", variant_name_lower);
155
match &variant.fields {
156
Fields::Named(fields) => {
157
variant_definitions.push(quote! {
158
#variant_ident {
159
#(#template_fields,)*
160
}
161
});
162
let field_idents = fields.named.iter().map(|f| &f.ident);
163
variant_builds.push(quote! {
164
// TODO: proper assignments here
165
#template_ident::#variant_ident {
166
#(#field_idents,)*
167
} => {
168
#type_ident::#variant_ident {
169
#(#template_field_builds,)*
170
}
171
}
172
});
173
174
let field_idents = fields.named.iter().map(|f| &f.ident);
175
variant_clones.push(quote! {
176
// TODO: proper assignments here
177
#template_ident::#variant_ident {
178
#(#field_idents,)*
179
} => {
180
#template_ident::#variant_ident {
181
#(#template_field_clones,)*
182
}
183
}
184
});
185
186
if is_default {
187
variant_default_ident = Some(quote! {
188
Self::#variant_ident {
189
#(#template_field_defaults,)*
190
}
191
});
192
}
193
variant_defaults.push(quote! {
194
/// Default value for this variant, generated by a `FromTemplate` derive.
195
#maybe_pub fn #variant_default_name() -> Self {
196
Self::#variant_ident {
197
#(#template_field_defaults,)*
198
}
199
}
200
});
201
}
202
Fields::Unnamed(FieldsUnnamed { unnamed: f, .. }) => {
203
let field_idents = f
204
.iter()
205
.enumerate()
206
.map(|(i, _)| format_ident!("t{}", i))
207
.collect::<Vec<_>>();
208
variant_definitions.push(quote! {
209
#variant_ident(#(#template_fields,)*)
210
});
211
variant_builds.push(quote! {
212
// TODO: proper assignments here
213
#template_ident::#variant_ident(
214
#(#field_idents,)*
215
) => {
216
#type_ident::#variant_ident(
217
#(#template_field_builds,)*
218
)
219
}
220
});
221
variant_clones.push(quote! {
222
#template_ident::#variant_ident(
223
#(#field_idents,)*
224
) => {
225
#template_ident::#variant_ident(
226
#(#template_field_clones,)*
227
)
228
}
229
});
230
if is_default {
231
variant_default_ident = Some(quote! {
232
Self::#variant_ident(
233
#(#template_field_defaults,)*
234
)
235
});
236
}
237
238
variant_defaults.push(quote! {
239
/// Default value for this variant, generated by a `FromTemplate` derive.
240
#maybe_pub fn #variant_default_name() -> Self {
241
Self::#variant_ident(
242
#(#template_field_defaults,)*
243
)
244
}
245
});
246
}
247
Fields::Unit => {
248
variant_definitions.push(quote! {#variant_ident});
249
variant_builds.push(
250
quote! {#template_ident::#variant_ident => #type_ident::#variant_ident},
251
);
252
variant_clones.push(
253
quote! {#template_ident::#variant_ident => #template_ident::#variant_ident},
254
);
255
if is_default {
256
variant_default_ident = Some(quote! {
257
Self::#variant_ident
258
});
259
}
260
variant_defaults.push(quote! {
261
/// Default value for this variant, generated by a `FromTemplate` derive.
262
#maybe_pub fn #variant_default_name() -> Self {
263
Self::#variant_ident
264
}
265
});
266
}
267
}
268
}
269
270
if variant_default_ident.is_none() {
271
panic!("Deriving Template for enums requires picking a default variant using #[default]");
272
}
273
274
quote! {
275
#[allow(missing_docs)]
276
#maybe_pub enum #template_ident #type_generics #where_clause {
277
#(#variant_definitions,)*
278
}
279
280
impl #impl_generics #template_ident #type_generics #where_clause {
281
#(#variant_defaults)*
282
}
283
284
impl #impl_generics #bevy_ecs::template::Template for #template_ident #type_generics #where_clause {
285
type Output = #type_ident #type_generics;
286
fn build_template(&self, context: &mut #bevy_ecs::template::TemplateContext) -> #bevy_ecs::error::Result<Self::Output> {
287
#bevy_ecs::error::Result::Ok(match self {
288
#(#variant_builds,)*
289
})
290
}
291
292
fn clone_template(&self) -> Self {
293
match self {
294
#(#variant_clones,)*
295
}
296
}
297
}
298
299
impl #impl_generics #FQDefault for #template_ident #type_generics #where_clause {
300
fn default() -> Self {
301
#variant_default_ident
302
}
303
}
304
}
305
}
306
Data::Union(_) => panic!("Union types are not supported yet."),
307
};
308
309
let mut unpin_where_clause = where_clause.cloned().unwrap_or_else(|| WhereClause {
310
where_token: <Token![where]>::default(),
311
predicates: Punctuated::new(),
312
});
313
314
unpin_where_clause
315
.predicates
316
.push(parse_quote! { for<'a> [()]: #bevy_ecs::template::SpecializeFromTemplate });
317
318
TokenStream::from(quote! {
319
impl #impl_generics #bevy_ecs::template::FromTemplate for #type_ident #type_generics #where_clause {
320
type Template = #template_ident #type_generics;
321
}
322
323
impl #impl_generics ::core::marker::Unpin for #type_ident #type_generics #unpin_where_clause {}
324
325
#template
326
})
327
}
328
329
struct StructImpl {
330
template_fields: Vec<proc_macro2::TokenStream>,
331
template_field_builds: Vec<proc_macro2::TokenStream>,
332
template_field_defaults: Vec<proc_macro2::TokenStream>,
333
template_field_clones: Vec<proc_macro2::TokenStream>,
334
}
335
336
enum TemplateType {
337
FromTemplate,
338
BuiltIn,
339
Manual(Path),
340
}
341
342
fn struct_impl(fields: &Fields, bevy_ecs: &Path, is_enum: bool) -> Result<StructImpl> {
343
let mut template_fields = Vec::with_capacity(fields.len());
344
let mut template_field_builds = Vec::with_capacity(fields.len());
345
let mut template_field_defaults = Vec::with_capacity(fields.len());
346
let mut template_field_clones = Vec::with_capacity(fields.len());
347
let is_named = matches!(fields, Fields::Named(_));
348
for (index, field) in fields.iter().enumerate() {
349
let is_pub = matches!(field.vis, syn::Visibility::Public(_));
350
let field_maybe_pub = if is_pub { quote!(pub) } else { quote!() };
351
let ident = &field.ident;
352
let ty = &field.ty;
353
let index = Index::from(index);
354
let mut template_type = TemplateType::FromTemplate;
355
for attr in &field.attrs {
356
if attr.path().is_ident(TEMPLATE_ATTRIBUTE) {
357
attr.parse_args_with(|stream: ParseStream| {
358
let forked = stream.fork();
359
let ident = forked.parse::<Ident>()?;
360
if ident == BUILT_IN_ATTRIBUTE {
361
stream.parse::<Ident>()?;
362
template_type = TemplateType::BuiltIn;
363
} else {
364
if let Ok(path) = stream.parse::<Path>() {
365
template_type = TemplateType::Manual(path);
366
} else {
367
return Err(syn::Error::new(
368
attr.span(),
369
"Expected a Template type path",
370
));
371
}
372
}
373
Ok(())
374
})?;
375
}
376
}
377
378
let template_type = match template_type {
379
TemplateType::FromTemplate => {
380
quote!(<#ty as #bevy_ecs::template::FromTemplate>::Template)
381
}
382
TemplateType::BuiltIn => {
383
quote!(<#ty as #bevy_ecs::template::BuiltInTemplate>::Template)
384
}
385
TemplateType::Manual(path) => quote! {#path},
386
};
387
388
if is_named {
389
template_fields.push(quote! {
390
#field_maybe_pub #ident: #template_type
391
});
392
if is_enum {
393
template_field_builds.push(quote! {
394
#ident: #ident.build_template(context)?
395
});
396
template_field_clones.push(quote! {
397
#ident: #bevy_ecs::template::Template::clone_template(#ident)
398
});
399
} else {
400
template_field_builds.push(quote! {
401
#ident: self.#ident.build_template(context)?
402
});
403
template_field_clones.push(quote! {
404
#ident: #bevy_ecs::template::Template::clone_template(&self.#ident)
405
});
406
}
407
408
template_field_defaults.push(quote! {
409
#ident: #FQDefault::default()
410
});
411
} else {
412
template_fields.push(quote! {
413
#field_maybe_pub #template_type
414
});
415
if is_enum {
416
let enum_tuple_ident = format_ident!("t{}", index);
417
template_field_builds.push(quote! {
418
#enum_tuple_ident.build_template(context)?
419
});
420
template_field_clones.push(quote! {
421
#bevy_ecs::template::Template::clone_template(#enum_tuple_ident)
422
});
423
} else {
424
template_field_builds.push(quote! {
425
self.#index.build_template(context)?
426
});
427
template_field_clones.push(quote! {
428
#bevy_ecs::template::Template::clone_template(&self.#index)
429
});
430
}
431
template_field_defaults.push(quote! {
432
#FQDefault::default()
433
});
434
}
435
}
436
Ok(StructImpl {
437
template_fields,
438
template_field_builds,
439
template_field_defaults,
440
template_field_clones,
441
})
442
}
443
444