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