Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/macros/src/lib.rs
6600 views
1
//! Macros for deriving ECS traits.
2
3
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4
5
extern crate proc_macro;
6
7
mod component;
8
mod query_data;
9
mod query_filter;
10
mod world_query;
11
12
use crate::{
13
component::map_entities, query_data::derive_query_data_impl,
14
query_filter::derive_query_filter_impl,
15
};
16
use bevy_macro_utils::{derive_label, ensure_no_collision, get_struct_fields, BevyManifest};
17
use proc_macro::TokenStream;
18
use proc_macro2::{Ident, Span};
19
use quote::{format_ident, quote, ToTokens};
20
use syn::{
21
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma,
22
ConstParam, Data, DataStruct, DeriveInput, GenericParam, Index, TypeParam,
23
};
24
25
enum BundleFieldKind {
26
Component,
27
Ignore,
28
}
29
30
const BUNDLE_ATTRIBUTE_NAME: &str = "bundle";
31
const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore";
32
const BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS: &str = "ignore_from_components";
33
34
#[derive(Debug)]
35
struct BundleAttributes {
36
impl_from_components: bool,
37
}
38
39
impl Default for BundleAttributes {
40
fn default() -> Self {
41
Self {
42
impl_from_components: true,
43
}
44
}
45
}
46
47
/// Implement the `Bundle` trait.
48
#[proc_macro_derive(Bundle, attributes(bundle))]
49
pub fn derive_bundle(input: TokenStream) -> TokenStream {
50
let ast = parse_macro_input!(input as DeriveInput);
51
let ecs_path = bevy_ecs_path();
52
53
let mut errors = vec![];
54
55
let mut attributes = BundleAttributes::default();
56
57
for attr in &ast.attrs {
58
if attr.path().is_ident(BUNDLE_ATTRIBUTE_NAME) {
59
let parsing = attr.parse_nested_meta(|meta| {
60
if meta.path.is_ident(BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS) {
61
attributes.impl_from_components = false;
62
return Ok(());
63
}
64
65
Err(meta.error(format!("Invalid bundle container attribute. Allowed attributes: `{BUNDLE_ATTRIBUTE_NO_FROM_COMPONENTS}`")))
66
});
67
68
if let Err(error) = parsing {
69
errors.push(error.into_compile_error());
70
}
71
}
72
}
73
74
let named_fields = match get_struct_fields(&ast.data, "derive(Bundle)") {
75
Ok(fields) => fields,
76
Err(e) => return e.into_compile_error().into(),
77
};
78
79
let mut field_kind = Vec::with_capacity(named_fields.len());
80
81
for field in named_fields {
82
let mut kind = BundleFieldKind::Component;
83
84
for attr in field
85
.attrs
86
.iter()
87
.filter(|a| a.path().is_ident(BUNDLE_ATTRIBUTE_NAME))
88
{
89
if let Err(error) = attr.parse_nested_meta(|meta| {
90
if meta.path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
91
kind = BundleFieldKind::Ignore;
92
Ok(())
93
} else {
94
Err(meta.error(format!(
95
"Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
96
)))
97
}
98
}) {
99
return error.into_compile_error().into();
100
}
101
}
102
103
field_kind.push(kind);
104
}
105
106
let field = named_fields
107
.iter()
108
.map(|field| field.ident.as_ref())
109
.collect::<Vec<_>>();
110
111
let field_type = named_fields
112
.iter()
113
.map(|field| &field.ty)
114
.collect::<Vec<_>>();
115
116
let mut active_field_types = Vec::new();
117
let mut active_field_tokens = Vec::new();
118
let mut inactive_field_tokens = Vec::new();
119
for (((i, field_type), field_kind), field) in field_type
120
.iter()
121
.enumerate()
122
.zip(field_kind.iter())
123
.zip(field.iter())
124
{
125
let field_tokens = match field {
126
Some(field) => field.to_token_stream(),
127
None => Index::from(i).to_token_stream(),
128
};
129
match field_kind {
130
BundleFieldKind::Component => {
131
active_field_types.push(field_type);
132
active_field_tokens.push(field_tokens);
133
}
134
135
BundleFieldKind::Ignore => inactive_field_tokens.push(field_tokens),
136
}
137
}
138
let generics = ast.generics;
139
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
140
let struct_name = &ast.ident;
141
142
let bundle_impl = quote! {
143
// SAFETY:
144
// - ComponentId is returned in field-definition-order. [get_components] uses field-definition-order
145
// - `Bundle::get_components` is exactly once for each member. Rely's on the Component -> Bundle implementation to properly pass
146
// the correct `StorageType` into the callback.
147
#[allow(deprecated)]
148
unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause {
149
fn component_ids(
150
components: &mut #ecs_path::component::ComponentsRegistrator,
151
ids: &mut impl FnMut(#ecs_path::component::ComponentId)
152
) {
153
#(<#active_field_types as #ecs_path::bundle::Bundle>::component_ids(components, ids);)*
154
}
155
156
fn get_component_ids(
157
components: &#ecs_path::component::Components,
158
ids: &mut impl FnMut(Option<#ecs_path::component::ComponentId>)
159
) {
160
#(<#active_field_types as #ecs_path::bundle::Bundle>::get_component_ids(components, &mut *ids);)*
161
}
162
}
163
};
164
165
let dynamic_bundle_impl = quote! {
166
#[allow(deprecated)]
167
impl #impl_generics #ecs_path::bundle::DynamicBundle for #struct_name #ty_generics #where_clause {
168
type Effect = ();
169
#[allow(unused_variables)]
170
#[inline]
171
fn get_components(
172
self,
173
func: &mut impl FnMut(#ecs_path::component::StorageType, #ecs_path::ptr::OwningPtr<'_>)
174
) {
175
#(<#active_field_types as #ecs_path::bundle::DynamicBundle>::get_components(self.#active_field_tokens, &mut *func);)*
176
}
177
}
178
};
179
180
let from_components_impl = attributes.impl_from_components.then(|| quote! {
181
// SAFETY:
182
// - ComponentId is returned in field-definition-order. [from_components] uses field-definition-order
183
#[allow(deprecated)]
184
unsafe impl #impl_generics #ecs_path::bundle::BundleFromComponents for #struct_name #ty_generics #where_clause {
185
#[allow(unused_variables, non_snake_case)]
186
unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self
187
where
188
__F: FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_>
189
{
190
Self {
191
#(#active_field_tokens: <#active_field_types as #ecs_path::bundle::BundleFromComponents>::from_components(ctx, &mut *func),)*
192
#(#inactive_field_tokens: ::core::default::Default::default(),)*
193
}
194
}
195
}
196
});
197
198
let attribute_errors = &errors;
199
200
TokenStream::from(quote! {
201
#(#attribute_errors)*
202
#bundle_impl
203
#from_components_impl
204
#dynamic_bundle_impl
205
})
206
}
207
208
/// Implement the `MapEntities` trait.
209
#[proc_macro_derive(MapEntities, attributes(entities))]
210
pub fn derive_map_entities(input: TokenStream) -> TokenStream {
211
let ast = parse_macro_input!(input as DeriveInput);
212
let ecs_path = bevy_ecs_path();
213
214
let map_entities_impl = map_entities(
215
&ast.data,
216
&ecs_path,
217
Ident::new("self", Span::call_site()),
218
false,
219
false,
220
None,
221
);
222
223
let struct_name = &ast.ident;
224
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
225
TokenStream::from(quote! {
226
impl #impl_generics #ecs_path::entity::MapEntities for #struct_name #type_generics #where_clause {
227
fn map_entities<M: #ecs_path::entity::EntityMapper>(&mut self, mapper: &mut M) {
228
#map_entities_impl
229
}
230
}
231
})
232
}
233
234
/// Implement `SystemParam` to use a struct as a parameter in a system
235
#[proc_macro_derive(SystemParam, attributes(system_param))]
236
pub fn derive_system_param(input: TokenStream) -> TokenStream {
237
let token_stream = input.clone();
238
let ast = parse_macro_input!(input as DeriveInput);
239
let Data::Struct(DataStruct {
240
fields: field_definitions,
241
..
242
}) = ast.data
243
else {
244
return syn::Error::new(
245
ast.span(),
246
"Invalid `SystemParam` type: expected a `struct`",
247
)
248
.into_compile_error()
249
.into();
250
};
251
let path = bevy_ecs_path();
252
253
let mut field_locals = Vec::new();
254
let mut field_names = Vec::new();
255
let mut fields = Vec::new();
256
let mut field_types = Vec::new();
257
let mut field_messages = Vec::new();
258
for (i, field) in field_definitions.iter().enumerate() {
259
field_locals.push(format_ident!("f{i}"));
260
let i = Index::from(i);
261
let field_value = field
262
.ident
263
.as_ref()
264
.map(|f| quote! { #f })
265
.unwrap_or_else(|| quote! { #i });
266
field_names.push(format!("::{field_value}"));
267
fields.push(field_value);
268
field_types.push(&field.ty);
269
let mut field_message = None;
270
for meta in field
271
.attrs
272
.iter()
273
.filter(|a| a.path().is_ident("system_param"))
274
{
275
if let Err(e) = meta.parse_nested_meta(|nested| {
276
if nested.path.is_ident("validation_message") {
277
field_message = Some(nested.value()?.parse()?);
278
Ok(())
279
} else {
280
Err(nested.error("Unsupported attribute"))
281
}
282
}) {
283
return e.into_compile_error().into();
284
}
285
}
286
field_messages.push(field_message.unwrap_or_else(|| quote! { err.message }));
287
}
288
289
let generics = ast.generics;
290
291
// Emit an error if there's any unrecognized lifetime names.
292
for lt in generics.lifetimes() {
293
let ident = &lt.lifetime.ident;
294
let w = format_ident!("w");
295
let s = format_ident!("s");
296
if ident != &w && ident != &s {
297
return syn::Error::new_spanned(
298
lt,
299
r#"invalid lifetime name: expected `'w` or `'s`
300
'w -- refers to data stored in the World.
301
's -- refers to data stored in the SystemParam's state.'"#,
302
)
303
.into_compile_error()
304
.into();
305
}
306
}
307
308
let (_impl_generics, ty_generics, where_clause) = generics.split_for_impl();
309
310
let lifetimeless_generics: Vec<_> = generics
311
.params
312
.iter()
313
.filter(|g| !matches!(g, GenericParam::Lifetime(_)))
314
.collect();
315
316
let shadowed_lifetimes: Vec<_> = generics.lifetimes().map(|_| quote!('_)).collect();
317
318
let mut punctuated_generics = Punctuated::<_, Comma>::new();
319
punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g {
320
GenericParam::Type(g) => GenericParam::Type(TypeParam {
321
default: None,
322
..g.clone()
323
}),
324
GenericParam::Const(g) => GenericParam::Const(ConstParam {
325
default: None,
326
..g.clone()
327
}),
328
_ => unreachable!(),
329
}));
330
331
let mut punctuated_generic_idents = Punctuated::<_, Comma>::new();
332
punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
333
GenericParam::Type(g) => &g.ident,
334
GenericParam::Const(g) => &g.ident,
335
_ => unreachable!(),
336
}));
337
338
let punctuated_generics_no_bounds: Punctuated<_, Comma> = lifetimeless_generics
339
.iter()
340
.map(|&g| match g.clone() {
341
GenericParam::Type(mut g) => {
342
g.bounds.clear();
343
GenericParam::Type(g)
344
}
345
g => g,
346
})
347
.collect();
348
349
let mut tuple_types: Vec<_> = field_types.iter().map(|x| quote! { #x }).collect();
350
let mut tuple_patterns: Vec<_> = field_locals.iter().map(|x| quote! { #x }).collect();
351
352
// If the number of fields exceeds the 16-parameter limit,
353
// fold the fields into tuples of tuples until we are below the limit.
354
const LIMIT: usize = 16;
355
while tuple_types.len() > LIMIT {
356
let end = Vec::from_iter(tuple_types.drain(..LIMIT));
357
tuple_types.push(parse_quote!( (#(#end,)*) ));
358
359
let end = Vec::from_iter(tuple_patterns.drain(..LIMIT));
360
tuple_patterns.push(parse_quote!( (#(#end,)*) ));
361
}
362
363
// Create a where clause for the `ReadOnlySystemParam` impl.
364
// Ensure that each field implements `ReadOnlySystemParam`.
365
let mut read_only_generics = generics.clone();
366
let read_only_where_clause = read_only_generics.make_where_clause();
367
for field_type in &field_types {
368
read_only_where_clause
369
.predicates
370
.push(syn::parse_quote!(#field_type: #path::system::ReadOnlySystemParam));
371
}
372
373
let fields_alias =
374
ensure_no_collision(format_ident!("__StructFieldsAlias"), token_stream.clone());
375
376
let struct_name = &ast.ident;
377
let state_struct_visibility = &ast.vis;
378
let state_struct_name = ensure_no_collision(format_ident!("FetchState"), token_stream);
379
380
let mut builder_name = None;
381
for meta in ast
382
.attrs
383
.iter()
384
.filter(|a| a.path().is_ident("system_param"))
385
{
386
if let Err(e) = meta.parse_nested_meta(|nested| {
387
if nested.path.is_ident("builder") {
388
builder_name = Some(format_ident!("{struct_name}Builder"));
389
Ok(())
390
} else {
391
Err(nested.error("Unsupported attribute"))
392
}
393
}) {
394
return e.into_compile_error().into();
395
}
396
}
397
398
let builder = builder_name.map(|builder_name| {
399
let builder_type_parameters: Vec<_> = (0..fields.len()).map(|i| format_ident!("B{i}")).collect();
400
let builder_doc_comment = format!("A [`SystemParamBuilder`] for a [`{struct_name}`].");
401
let builder_struct = quote! {
402
#[doc = #builder_doc_comment]
403
struct #builder_name<#(#builder_type_parameters,)*> {
404
#(#fields: #builder_type_parameters,)*
405
}
406
};
407
let lifetimes: Vec<_> = generics.lifetimes().collect();
408
let generic_struct = quote!{ #struct_name <#(#lifetimes,)* #punctuated_generic_idents> };
409
let builder_impl = quote!{
410
// SAFETY: This delegates to the `SystemParamBuilder` for tuples.
411
unsafe impl<
412
#(#lifetimes,)*
413
#(#builder_type_parameters: #path::system::SystemParamBuilder<#field_types>,)*
414
#punctuated_generics
415
> #path::system::SystemParamBuilder<#generic_struct> for #builder_name<#(#builder_type_parameters,)*>
416
#where_clause
417
{
418
fn build(self, world: &mut #path::world::World) -> <#generic_struct as #path::system::SystemParam>::State {
419
let #builder_name { #(#fields: #field_locals,)* } = self;
420
#state_struct_name {
421
state: #path::system::SystemParamBuilder::build((#(#tuple_patterns,)*), world)
422
}
423
}
424
}
425
};
426
(builder_struct, builder_impl)
427
});
428
let (builder_struct, builder_impl) = builder.unzip();
429
430
TokenStream::from(quote! {
431
// We define the FetchState struct in an anonymous scope to avoid polluting the user namespace.
432
// The struct can still be accessed via SystemParam::State, e.g. EventReaderState can be accessed via
433
// <EventReader<'static, 'static, T> as SystemParam>::State
434
const _: () = {
435
// Allows rebinding the lifetimes of each field type.
436
type #fields_alias <'w, 's, #punctuated_generics_no_bounds> = (#(#tuple_types,)*);
437
438
#[doc(hidden)]
439
#state_struct_visibility struct #state_struct_name <#(#lifetimeless_generics,)*>
440
#where_clause {
441
state: <#fields_alias::<'static, 'static, #punctuated_generic_idents> as #path::system::SystemParam>::State,
442
}
443
444
unsafe impl<#punctuated_generics> #path::system::SystemParam for
445
#struct_name <#(#shadowed_lifetimes,)* #punctuated_generic_idents> #where_clause
446
{
447
type State = #state_struct_name<#punctuated_generic_idents>;
448
type Item<'w, 's> = #struct_name #ty_generics;
449
450
fn init_state(world: &mut #path::world::World) -> Self::State {
451
#state_struct_name {
452
state: <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_state(world),
453
}
454
}
455
456
fn init_access(state: &Self::State, system_meta: &mut #path::system::SystemMeta, component_access_set: &mut #path::query::FilteredAccessSet, world: &mut #path::world::World) {
457
<#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_access(&state.state, system_meta, component_access_set, world);
458
}
459
460
fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) {
461
<#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::apply(&mut state.state, system_meta, world);
462
}
463
464
fn queue(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: #path::world::DeferredWorld) {
465
<#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::queue(&mut state.state, system_meta, world);
466
}
467
468
#[inline]
469
unsafe fn validate_param<'w, 's>(
470
state: &'s mut Self::State,
471
_system_meta: &#path::system::SystemMeta,
472
_world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
473
) -> Result<(), #path::system::SystemParamValidationError> {
474
let #state_struct_name { state: (#(#tuple_patterns,)*) } = state;
475
#(
476
<#field_types as #path::system::SystemParam>::validate_param(#field_locals, _system_meta, _world)
477
.map_err(|err| #path::system::SystemParamValidationError::new::<Self>(err.skipped, #field_messages, #field_names))?;
478
)*
479
Result::Ok(())
480
}
481
482
#[inline]
483
unsafe fn get_param<'w, 's>(
484
state: &'s mut Self::State,
485
system_meta: &#path::system::SystemMeta,
486
world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
487
change_tick: #path::component::Tick,
488
) -> Self::Item<'w, 's> {
489
let (#(#tuple_patterns,)*) = <
490
(#(#tuple_types,)*) as #path::system::SystemParam
491
>::get_param(&mut state.state, system_meta, world, change_tick);
492
#struct_name {
493
#(#fields: #field_locals,)*
494
}
495
}
496
}
497
498
// Safety: Each field is `ReadOnlySystemParam`, so this can only read from the `World`
499
unsafe impl<'w, 's, #punctuated_generics> #path::system::ReadOnlySystemParam for #struct_name #ty_generics #read_only_where_clause {}
500
501
#builder_impl
502
};
503
504
#builder_struct
505
})
506
}
507
508
/// Implement `QueryData` to use a struct as a data parameter in a query
509
#[proc_macro_derive(QueryData, attributes(query_data))]
510
pub fn derive_query_data(input: TokenStream) -> TokenStream {
511
derive_query_data_impl(input)
512
}
513
514
/// Implement `QueryFilter` to use a struct as a filter parameter in a query
515
#[proc_macro_derive(QueryFilter, attributes(query_filter))]
516
pub fn derive_query_filter(input: TokenStream) -> TokenStream {
517
derive_query_filter_impl(input)
518
}
519
520
/// Derive macro generating an impl of the trait `ScheduleLabel`.
521
///
522
/// This does not work for unions.
523
#[proc_macro_derive(ScheduleLabel)]
524
pub fn derive_schedule_label(input: TokenStream) -> TokenStream {
525
let input = parse_macro_input!(input as DeriveInput);
526
let mut trait_path = bevy_ecs_path();
527
trait_path.segments.push(format_ident!("schedule").into());
528
trait_path
529
.segments
530
.push(format_ident!("ScheduleLabel").into());
531
derive_label(input, "ScheduleLabel", &trait_path)
532
}
533
534
/// Derive macro generating an impl of the trait `SystemSet`.
535
///
536
/// This does not work for unions.
537
#[proc_macro_derive(SystemSet)]
538
pub fn derive_system_set(input: TokenStream) -> TokenStream {
539
let input = parse_macro_input!(input as DeriveInput);
540
let mut trait_path = bevy_ecs_path();
541
trait_path.segments.push(format_ident!("schedule").into());
542
trait_path.segments.push(format_ident!("SystemSet").into());
543
derive_label(input, "SystemSet", &trait_path)
544
}
545
546
pub(crate) fn bevy_ecs_path() -> syn::Path {
547
BevyManifest::shared().get_path("bevy_ecs")
548
}
549
550
/// Implement the `Event` trait.
551
#[proc_macro_derive(Event)]
552
pub fn derive_event(input: TokenStream) -> TokenStream {
553
component::derive_event(input)
554
}
555
556
/// Cheat sheet for derive syntax,
557
/// see full explanation on `EntityEvent` trait docs.
558
///
559
/// ```ignore
560
/// #[derive(EntityEvent)]
561
/// /// Traversal component
562
/// #[entity_event(traversal = &'static ChildOf)]
563
/// /// Always propagate
564
/// #[entity_event(auto_propagate)]
565
/// struct MyEvent;
566
/// ```
567
#[proc_macro_derive(EntityEvent, attributes(entity_event))]
568
pub fn derive_entity_event(input: TokenStream) -> TokenStream {
569
component::derive_entity_event(input)
570
}
571
572
/// Implement the `BufferedEvent` trait.
573
#[proc_macro_derive(BufferedEvent)]
574
pub fn derive_buffered_event(input: TokenStream) -> TokenStream {
575
component::derive_buffered_event(input)
576
}
577
578
/// Implement the `Resource` trait.
579
#[proc_macro_derive(Resource)]
580
pub fn derive_resource(input: TokenStream) -> TokenStream {
581
component::derive_resource(input)
582
}
583
584
/// Cheat sheet for derive syntax,
585
/// see full explanation and examples on the `Component` trait doc.
586
///
587
/// ## Immutability
588
/// ```ignore
589
/// #[derive(Component)]
590
/// #[component(immutable)]
591
/// struct MyComponent;
592
/// ```
593
///
594
/// ## Sparse instead of table-based storage
595
/// ```ignore
596
/// #[derive(Component)]
597
/// #[component(storage = "SparseSet")]
598
/// struct MyComponent;
599
/// ```
600
///
601
/// ## Required Components
602
///
603
/// ```ignore
604
/// #[derive(Component)]
605
/// #[require(
606
/// // `Default::default()`
607
/// A,
608
/// // tuple structs
609
/// B(1),
610
/// // named-field structs
611
/// C {
612
/// x: 1,
613
/// ..default()
614
/// },
615
/// // unit structs/variants
616
/// D::One,
617
/// // associated consts
618
/// E::ONE,
619
/// // constructors
620
/// F::new(1),
621
/// // arbitrary expressions
622
/// G = make(1, 2, 3)
623
/// )]
624
/// struct MyComponent;
625
/// ```
626
///
627
/// ## Relationships
628
/// ```ignore
629
/// #[derive(Component)]
630
/// #[relationship(relationship_target = Children)]
631
/// pub struct ChildOf {
632
/// // Marking the field is not necessary if there is only one.
633
/// #[relationship]
634
/// pub parent: Entity,
635
/// internal: u8,
636
/// };
637
///
638
/// #[derive(Component)]
639
/// #[relationship_target(relationship = ChildOf)]
640
/// pub struct Children(Vec<Entity>);
641
/// ```
642
///
643
/// On despawn, also despawn all related entities:
644
/// ```ignore
645
/// #[derive(Component)]
646
/// #[relationship_target(relationship_target = Children, linked_spawn)]
647
/// pub struct Children(Vec<Entity>);
648
/// ```
649
///
650
/// ## Hooks
651
/// ```ignore
652
/// #[derive(Component)]
653
/// #[component(hook_name = function)]
654
/// struct MyComponent;
655
/// ```
656
/// where `hook_name` is `on_add`, `on_insert`, `on_replace` or `on_remove`;
657
/// `function` can be either a path, e.g. `some_function::<Self>`,
658
/// or a function call that returns a function that can be turned into
659
/// a `ComponentHook`, e.g. `get_closure("Hi!")`.
660
///
661
/// ## Ignore this component when cloning an entity
662
/// ```ignore
663
/// #[derive(Component)]
664
/// #[component(clone_behavior = Ignore)]
665
/// struct MyComponent;
666
/// ```
667
#[proc_macro_derive(
668
Component,
669
attributes(component, require, relationship, relationship_target, entities)
670
)]
671
pub fn derive_component(input: TokenStream) -> TokenStream {
672
component::derive_component(input)
673
}
674
675
/// Implement the `FromWorld` trait.
676
#[proc_macro_derive(FromWorld, attributes(from_world))]
677
pub fn derive_from_world(input: TokenStream) -> TokenStream {
678
let bevy_ecs_path = bevy_ecs_path();
679
let ast = parse_macro_input!(input as DeriveInput);
680
let name = ast.ident;
681
let (impl_generics, ty_generics, where_clauses) = ast.generics.split_for_impl();
682
683
let (fields, variant_ident) = match &ast.data {
684
Data::Struct(data) => (&data.fields, None),
685
Data::Enum(data) => {
686
match data.variants.iter().find(|variant| {
687
variant
688
.attrs
689
.iter()
690
.any(|attr| attr.path().is_ident("from_world"))
691
}) {
692
Some(variant) => (&variant.fields, Some(&variant.ident)),
693
None => {
694
return syn::Error::new(
695
Span::call_site(),
696
"No variant found with the `#[from_world]` attribute",
697
)
698
.into_compile_error()
699
.into();
700
}
701
}
702
}
703
Data::Union(_) => {
704
return syn::Error::new(
705
Span::call_site(),
706
"#[derive(FromWorld)]` does not support unions",
707
)
708
.into_compile_error()
709
.into();
710
}
711
};
712
713
let field_init_expr = quote!(#bevy_ecs_path::world::FromWorld::from_world(world));
714
let members = fields.members();
715
716
let field_initializers = match variant_ident {
717
Some(variant_ident) => quote!( Self::#variant_ident {
718
#(#members: #field_init_expr),*
719
}),
720
None => quote!( Self {
721
#(#members: #field_init_expr),*
722
}),
723
};
724
725
TokenStream::from(quote! {
726
impl #impl_generics #bevy_ecs_path::world::FromWorld for #name #ty_generics #where_clauses {
727
fn from_world(world: &mut #bevy_ecs_path::world::World) -> Self {
728
#field_initializers
729
}
730
}
731
})
732
}
733
734