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