Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/macro_logic/src/component.rs
30636 views
1
use bevy_macro_utils::fq_std::{FQDefault, FQOption, FQSend, FQSync};
2
use proc_macro2::{Span, TokenStream};
3
use quote::{quote, ToTokens};
4
use std::collections::HashSet;
5
use syn::{
6
braced, parenthesized,
7
parse::Parse,
8
parse_quote,
9
punctuated::Punctuated,
10
spanned::Spanned,
11
token::{Brace, Comma, Paren},
12
Data, DataStruct, DeriveInput, Expr, ExprCall, ExprPath, Field, Fields, Ident, LitStr, Member,
13
Path, Result, Token, Type, Visibility,
14
};
15
16
use crate::map_entities::{map_entities, MapEntitiesAttributeKind};
17
18
/// Whether the derive macro may contain a `component(storage = "…")` attribute.
19
pub enum StorageAttribute {
20
/// User can overwrite the storage type
21
Allowed,
22
/// User cannot overwrite the storage type
23
Disallowed,
24
}
25
26
/// Derived `Component` trait specification, which can be used to generate a component implementation.
27
pub struct DeriveComponent {
28
/// The storage type of the component.
29
pub storage: Option<StorageTy>,
30
/// The parsed punctuated list of required components.
31
pub requires: Option<Punctuated<Require, Comma>>,
32
/// The `on_add` hook.
33
pub on_add: Option<HookAttributeKind>,
34
/// The `on_insert` hook.
35
pub on_insert: Option<HookAttributeKind>,
36
/// The `on_discard` hook.
37
pub on_discard: Option<HookAttributeKind>,
38
/// The `on_remove` hook.
39
pub on_remove: Option<HookAttributeKind>,
40
/// The `on_despawn` hook.
41
pub on_despawn: Option<HookAttributeKind>,
42
/// The relationship attribute information.
43
pub relationship: Option<Relationship>,
44
/// The relationship target attribute information.
45
pub relationship_target: Option<RelationshipTarget>,
46
/// Whether or not this component is immutable.
47
pub immutable: bool,
48
/// The clone behavior for this component.
49
pub clone_behavior: Option<Expr>,
50
/// The `map_entities` attribute information.
51
pub map_entities: Option<MapEntitiesAttributeKind>,
52
/// Additional required component registrations that are added in `Component::register_required_components`
53
pub additional_requires: Vec<TokenStream>,
54
}
55
56
impl DeriveComponent {
57
/// Parse [`DeriveComponent`] from the given `ast`.
58
pub fn parse(ast: &DeriveInput, storage_attr: StorageAttribute) -> Result<DeriveComponent> {
59
let mut attrs = DeriveComponent {
60
storage: None,
61
on_add: None,
62
on_insert: None,
63
on_discard: None,
64
on_remove: None,
65
on_despawn: None,
66
requires: None,
67
relationship: None,
68
relationship_target: None,
69
immutable: false,
70
clone_behavior: None,
71
map_entities: None,
72
additional_requires: Vec::new(),
73
};
74
75
let mut require_paths = HashSet::new();
76
for attr in ast.attrs.iter() {
77
if attr.path().is_ident(COMPONENT) {
78
attr.parse_nested_meta(|nested| {
79
if let StorageAttribute::Allowed = storage_attr
80
&& nested.path.is_ident(STORAGE)
81
{
82
attrs.storage = Some(match nested.value()?.parse::<LitStr>()?.value() {
83
s if s == TABLE => StorageTy::Table,
84
s if s == SPARSE_SET => StorageTy::SparseSet,
85
s => {
86
return Err(nested.error(format!(
87
"Invalid storage type `{s}`, expected '{TABLE}' or '{SPARSE_SET}'.",
88
)));
89
}
90
});
91
Ok(())
92
} else if nested.path.is_ident(ON_ADD) {
93
attrs.on_add = Some(HookAttributeKind::parse(nested.input, || {
94
parse_quote! { Self::on_add }
95
})?);
96
Ok(())
97
} else if nested.path.is_ident(ON_INSERT) {
98
attrs.on_insert = Some(HookAttributeKind::parse(nested.input, || {
99
parse_quote! { Self::on_insert }
100
})?);
101
Ok(())
102
} else if nested.path.is_ident(ON_DISCARD) {
103
attrs.on_discard = Some(HookAttributeKind::parse(nested.input, || {
104
parse_quote! { Self::on_discard }
105
})?);
106
Ok(())
107
} else if nested.path.is_ident(ON_REMOVE) {
108
attrs.on_remove = Some(HookAttributeKind::parse(nested.input, || {
109
parse_quote! { Self::on_remove }
110
})?);
111
Ok(())
112
} else if nested.path.is_ident(ON_DESPAWN) {
113
attrs.on_despawn = Some(HookAttributeKind::parse(nested.input, || {
114
parse_quote! { Self::on_despawn }
115
})?);
116
Ok(())
117
} else if nested.path.is_ident(IMMUTABLE) {
118
attrs.immutable = true;
119
Ok(())
120
} else if nested.path.is_ident(CLONE_BEHAVIOR) {
121
attrs.clone_behavior = Some(nested.value()?.parse()?);
122
Ok(())
123
} else if nested.path.is_ident(MAP_ENTITIES) {
124
attrs.map_entities =
125
Some(nested.input.parse::<MapEntitiesAttributeKind>()?);
126
Ok(())
127
} else {
128
Err(nested.error("Unsupported attribute"))
129
}
130
})?;
131
} else if attr.path().is_ident(REQUIRE) {
132
let punctuated =
133
attr.parse_args_with(Punctuated::<Require, Comma>::parse_terminated)?;
134
for require in punctuated.iter() {
135
if !require_paths.insert(require.path.to_token_stream().to_string()) {
136
return Err(syn::Error::new(
137
require.path.span(),
138
"Duplicate required components are not allowed.",
139
));
140
}
141
}
142
if let Some(current) = &mut attrs.requires {
143
current.extend(punctuated);
144
} else {
145
attrs.requires = Some(punctuated);
146
}
147
} else if attr.path().is_ident(RELATIONSHIP) {
148
let relationship = attr.parse_args::<Relationship>()?;
149
attrs.relationship = Some(relationship);
150
} else if attr.path().is_ident(RELATIONSHIP_TARGET) {
151
let relationship_target = attr.parse_args::<RelationshipTarget>()?;
152
attrs.relationship_target = Some(relationship_target);
153
}
154
}
155
156
if attrs.relationship_target.is_some() && attrs.clone_behavior.is_some() {
157
return Err(syn::Error::new(
158
attrs.clone_behavior.span(),
159
"A Relationship Target already has its own clone behavior, please remove `clone_behavior = ...`",
160
));
161
}
162
163
Ok(attrs)
164
}
165
166
/// Generates a new `Component` trait implementation from this specification.
167
///
168
/// Note that this will add Send + Sync + 'static to the where clause
169
pub fn impl_component(
170
self,
171
ast: &mut DeriveInput,
172
bevy_ecs: &Path,
173
default_storage: StorageTy,
174
) -> Result<TokenStream> {
175
// We want to raise a compile time error when the generic lifetimes
176
// are not bound to 'static lifetime
177
let non_static_lifetime_error = ast
178
.generics
179
.lifetimes()
180
.filter(|lifetime| !lifetime.bounds.iter().any(|bound| bound.ident == "static"))
181
.map(|param| syn::Error::new(param.span(), "Lifetimes must be 'static"))
182
.reduce(|mut err_acc, err| {
183
err_acc.combine(err);
184
err_acc
185
});
186
if let Some(err) = non_static_lifetime_error {
187
return Err(err);
188
}
189
190
let relationship = match self.derive_relationship(ast, bevy_ecs) {
191
Ok(value) => value,
192
Err(err) => Some(err.into_compile_error()),
193
};
194
let relationship_target = match self.derive_relationship_target(ast, bevy_ecs) {
195
Ok(value) => value,
196
Err(err) => Some(err.into_compile_error()),
197
};
198
199
let map_entities = map_entities(
200
&ast.data,
201
bevy_ecs,
202
Ident::new("this", Span::call_site()),
203
relationship.is_some(),
204
relationship_target.is_some(),
205
self.map_entities,
206
)
207
.map(|map_entities_impl| {
208
quote! {
209
fn map_entities<M: #bevy_ecs::entity::EntityMapper>(this: &mut Self, mapper: &mut M) {
210
use #bevy_ecs::entity::MapEntities;
211
#map_entities_impl
212
}
213
}
214
});
215
216
let storage = storage_path(bevy_ecs, self.storage.unwrap_or(default_storage));
217
218
let on_add_path = Vec::from_iter(self.on_add.map(|path| path.to_token_stream(bevy_ecs)));
219
let on_remove_path =
220
Vec::from_iter(self.on_remove.map(|path| path.to_token_stream(bevy_ecs)));
221
222
let mut on_insert_path =
223
Vec::from_iter(self.on_insert.map(|path| path.to_token_stream(bevy_ecs)));
224
225
let mut on_discard_path =
226
Vec::from_iter(self.on_discard.map(|path| path.to_token_stream(bevy_ecs)));
227
228
let mut on_despawn_path =
229
Vec::from_iter(self.on_despawn.map(|path| path.to_token_stream(bevy_ecs)));
230
231
if relationship.is_some() {
232
on_insert_path.push(quote!(<Self as #bevy_ecs::relationship::Relationship>::on_insert));
233
on_discard_path
234
.push(quote!(<Self as #bevy_ecs::relationship::Relationship>::on_discard));
235
}
236
if let Some(target) = self.relationship_target {
237
on_discard_path
238
.push(quote!(<Self as #bevy_ecs::relationship::RelationshipTarget>::on_discard));
239
if target.linked_spawn {
240
on_despawn_path.push(
241
quote!(<Self as #bevy_ecs::relationship::RelationshipTarget>::on_despawn),
242
);
243
}
244
}
245
246
let on_add = hook_register_function_call(bevy_ecs, quote! {on_add}, &on_add_path);
247
let on_insert = hook_register_function_call(bevy_ecs, quote! {on_insert}, &on_insert_path);
248
let on_discard =
249
hook_register_function_call(bevy_ecs, quote! {on_discard}, &on_discard_path);
250
let on_remove = hook_register_function_call(bevy_ecs, quote! {on_remove}, &on_remove_path);
251
let on_despawn =
252
hook_register_function_call(bevy_ecs, quote! {on_despawn}, &on_despawn_path);
253
254
let requires = &self.requires;
255
let mut register_required = Vec::with_capacity(self.requires.iter().len());
256
if let Some(requires) = requires {
257
for require in requires {
258
let ident = &require.path;
259
let constructor = match &require.func {
260
Some(func) => quote! { || { let x: #ident = (#func)().into(); x } },
261
None => quote! { <#ident as #FQDefault>::default },
262
};
263
register_required.push(quote! {
264
required_components.register_required::<#ident>(#constructor);
265
});
266
}
267
}
268
let additional_requires = &self.additional_requires;
269
let struct_name = &ast.ident;
270
ast.generics
271
.make_where_clause()
272
.predicates
273
.push(parse_quote! { Self: #FQSend + #FQSync + 'static });
274
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
275
276
let required_component_docs = self.requires.map(|r| {
277
let paths = r
278
.iter()
279
.map(|r| format!("[`{}`]", r.path.to_token_stream()))
280
.collect::<Vec<_>>()
281
.join(", ");
282
let doc = format!("**Required Components**: {paths}. \n\n A component's Required Components are inserted whenever it is inserted. Note that this will also insert the required components _of_ the required components, recursively, in depth-first order.");
283
quote! {
284
#[doc = #doc]
285
}
286
});
287
288
let mutable_type = (self.immutable || relationship.is_some())
289
.then_some(quote! { #bevy_ecs::component::Immutable })
290
.unwrap_or(quote! { #bevy_ecs::component::Mutable });
291
292
let clone_behavior = if relationship_target.is_some() || relationship.is_some() {
293
quote!(
294
use #bevy_ecs::relationship::{
295
RelationshipCloneBehaviorBase, RelationshipCloneBehaviorViaClone, RelationshipCloneBehaviorViaReflect,
296
RelationshipTargetCloneBehaviorViaClone, RelationshipTargetCloneBehaviorViaReflect, RelationshipTargetCloneBehaviorHierarchy
297
};
298
(&&&&&&&#bevy_ecs::relationship::RelationshipCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
299
)
300
} else if let Some(behavior) = self.clone_behavior {
301
quote!(#bevy_ecs::component::ComponentCloneBehavior::#behavior)
302
} else {
303
quote!(
304
use #bevy_ecs::component::{DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone};
305
(&&&#bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
306
)
307
};
308
309
let relationship_accessor = if (relationship.is_some() || relationship_target.is_some())
310
&& let Data::Struct(DataStruct {
311
fields,
312
struct_token,
313
..
314
}) = &ast.data
315
&& let Ok(field) = relationship_field(fields, "Relationship", struct_token.span())
316
{
317
let relationship_member = field.ident.clone().map_or(Member::from(0), Member::Named);
318
if relationship.is_some() {
319
quote! {
320
#FQOption::Some(
321
// Safety: we pass valid offset of a field containing Entity (obtained via offset_off!)
322
unsafe {
323
#bevy_ecs::relationship::ComponentRelationshipAccessor::<Self>::relationship(
324
::core::mem::offset_of!(Self, #relationship_member)
325
)
326
}
327
)
328
}
329
} else {
330
quote! {
331
#FQOption::Some(#bevy_ecs::relationship::ComponentRelationshipAccessor::<Self>::relationship_target())
332
}
333
}
334
} else {
335
quote! {#FQOption::None}
336
};
337
Ok(quote! {
338
#required_component_docs
339
impl #impl_generics #bevy_ecs::component::Component for #struct_name #type_generics #where_clause {
340
const STORAGE_TYPE: #bevy_ecs::component::StorageType = #storage;
341
type Mutability = #mutable_type;
342
fn register_required_components(
343
_requiree: #bevy_ecs::component::ComponentId,
344
required_components: &mut #bevy_ecs::component::RequiredComponentsRegistrator,
345
) {
346
#(#register_required)*
347
#(#additional_requires)*
348
}
349
350
#on_add
351
#on_insert
352
#on_discard
353
#on_remove
354
#on_despawn
355
356
fn clone_behavior() -> #bevy_ecs::component::ComponentCloneBehavior {
357
#clone_behavior
358
}
359
360
#map_entities
361
362
fn relationship_accessor() -> #FQOption<#bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
363
#relationship_accessor
364
}
365
}
366
367
#relationship
368
369
#relationship_target
370
})
371
}
372
fn derive_relationship(
373
&self,
374
ast: &DeriveInput,
375
bevy_ecs: &Path,
376
) -> Result<Option<TokenStream>> {
377
let Some(relationship) = &self.relationship else {
378
return Ok(None);
379
};
380
let Data::Struct(DataStruct {
381
fields,
382
struct_token,
383
..
384
}) = &ast.data
385
else {
386
return Err(syn::Error::new(
387
ast.span(),
388
"Relationship can only be derived for structs.",
389
));
390
};
391
let field = relationship_field(fields, "Relationship", struct_token.span())?;
392
393
let relationship_member = field.ident.clone().map_or(Member::from(0), Member::Named);
394
let members = fields
395
.members()
396
.filter(|member| member != &relationship_member);
397
398
let struct_name = &ast.ident;
399
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
400
401
let relationship_target = &relationship.relationship_target;
402
let allow_self_referential = relationship.allow_self_referential;
403
404
let fqdefault = FQDefault.into_token_stream();
405
406
Ok(Some(quote! {
407
impl #impl_generics #bevy_ecs::relationship::Relationship for #struct_name #type_generics #where_clause {
408
type RelationshipTarget = #relationship_target;
409
const ALLOW_SELF_REFERENTIAL: bool = #allow_self_referential;
410
411
#[inline(always)]
412
fn get(&self) -> #bevy_ecs::entity::Entity {
413
self.#relationship_member
414
}
415
416
#[inline]
417
fn from(entity: #bevy_ecs::entity::Entity) -> Self {
418
Self {
419
#(#members: #fqdefault::default(),)*
420
#relationship_member: entity
421
}
422
}
423
424
#[inline]
425
fn set_risky(&mut self, entity: #bevy_ecs::entity::Entity) {
426
self.#relationship_member = entity;
427
}
428
}
429
}))
430
}
431
432
fn derive_relationship_target(
433
&self,
434
ast: &DeriveInput,
435
bevy_ecs: &Path,
436
) -> Result<Option<TokenStream>> {
437
let Some(relationship_target) = &self.relationship_target else {
438
return Ok(None);
439
};
440
441
let Data::Struct(DataStruct {
442
fields,
443
struct_token,
444
..
445
}) = &ast.data
446
else {
447
return Err(syn::Error::new(
448
ast.span(),
449
"RelationshipTarget can only be derived for structs.",
450
));
451
};
452
let field = relationship_field(fields, "RelationshipTarget", struct_token.span())?;
453
454
if field.vis != Visibility::Inherited {
455
return Err(syn::Error::new(field.span(), "The collection in RelationshipTarget must be private to prevent users from directly mutating it, which could invalidate the correctness of relationships."));
456
}
457
let collection = &field.ty;
458
let relationship_member = field.ident.clone().map_or(Member::from(0), Member::Named);
459
460
let members = fields
461
.members()
462
.filter(|member| member != &relationship_member);
463
464
let relationship = &relationship_target.relationship;
465
let struct_name = &ast.ident;
466
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
467
let linked_spawn = relationship_target.linked_spawn;
468
let fqdefault = FQDefault.into_token_stream();
469
Ok(Some(quote! {
470
impl #impl_generics #bevy_ecs::relationship::RelationshipTarget for #struct_name #type_generics #where_clause {
471
const LINKED_SPAWN: bool = #linked_spawn;
472
type Relationship = #relationship;
473
type Collection = #collection;
474
475
#[inline]
476
fn collection(&self) -> &Self::Collection {
477
&self.#relationship_member
478
}
479
480
#[inline]
481
fn collection_mut_risky(&mut self) -> &mut Self::Collection {
482
&mut self.#relationship_member
483
}
484
485
#[inline]
486
fn from_collection_risky(collection: Self::Collection) -> Self {
487
Self {
488
#(#members: #fqdefault::default(),)*
489
#relationship_member: collection
490
}
491
}
492
}
493
}))
494
}
495
}
496
497
const COMPONENT: &str = "component";
498
const MAP_ENTITIES: &str = "map_entities";
499
const STORAGE: &str = "storage";
500
const REQUIRE: &str = "require";
501
const RELATIONSHIP: &str = "relationship";
502
const RELATIONSHIP_TARGET: &str = "relationship_target";
503
504
const ON_ADD: &str = "on_add";
505
const ON_INSERT: &str = "on_insert";
506
const ON_DISCARD: &str = "on_discard";
507
const ON_REMOVE: &str = "on_remove";
508
const ON_DESPAWN: &str = "on_despawn";
509
510
const IMMUTABLE: &str = "immutable";
511
const CLONE_BEHAVIOR: &str = "clone_behavior";
512
513
/// All allowed attribute value expression kinds for component hooks.
514
/// This doesn't simply use general expressions because of conflicting needs:
515
/// - we want to be able to use `Self` & generic parameters in paths
516
/// - call expressions producing a closure need to be wrapped in a function
517
/// to turn them into function pointers, which prevents access to the outer generic params
518
#[derive(Debug)]
519
pub enum HookAttributeKind {
520
/// expressions like function or struct names
521
///
522
/// structs will throw compile errors on the code generation so this is safe
523
Path(ExprPath),
524
/// function call like expressions
525
Call(ExprCall),
526
}
527
528
impl HookAttributeKind {
529
fn parse(
530
input: syn::parse::ParseStream,
531
default_hook_path: impl FnOnce() -> ExprPath,
532
) -> Result<Self> {
533
if input.peek(Token![=]) {
534
input.parse::<Token![=]>()?;
535
input.parse::<Expr>().and_then(Self::from_expr)
536
} else {
537
Ok(Self::Path(default_hook_path()))
538
}
539
}
540
541
fn from_expr(value: Expr) -> Result<Self> {
542
match value {
543
Expr::Path(path) => Ok(HookAttributeKind::Path(path)),
544
Expr::Call(call) => Ok(HookAttributeKind::Call(call)),
545
// throw meaningful error on all other expressions
546
_ => Err(syn::Error::new(
547
value.span(),
548
[
549
"Not supported in this position, please use one of the following:",
550
"- path to function",
551
"- call to function yielding closure",
552
]
553
.join("\n"),
554
)),
555
}
556
}
557
558
fn to_token_stream(&self, bevy_ecs_path: &Path) -> TokenStream {
559
match self {
560
HookAttributeKind::Path(path) => path.to_token_stream(),
561
HookAttributeKind::Call(call) => {
562
quote!({
563
fn _internal_hook(world: #bevy_ecs_path::world::DeferredWorld, ctx: #bevy_ecs_path::lifecycle::HookContext) {
564
(#call)(world, ctx)
565
}
566
_internal_hook
567
})
568
}
569
}
570
}
571
}
572
573
/// The derived component storage type
574
#[derive(Clone, Copy)]
575
pub enum StorageTy {
576
/// Table storage
577
Table,
578
/// Sparse set storage
579
SparseSet,
580
}
581
582
/// Derived required component from the `#[require]` attribute.
583
pub struct Require {
584
path: Path,
585
func: Option<TokenStream>,
586
}
587
588
/// Derived `#[relationship]` attribute information.
589
pub struct Relationship {
590
relationship_target: Type,
591
allow_self_referential: bool,
592
}
593
594
/// Derived `#[relationship_target]` attribute information.
595
pub struct RelationshipTarget {
596
relationship: Type,
597
linked_spawn: bool,
598
}
599
600
// values for `storage` attribute
601
const TABLE: &str = "Table";
602
const SPARSE_SET: &str = "SparseSet";
603
604
impl Parse for Require {
605
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
606
let mut path = input.parse::<Path>()?;
607
let mut last_segment_is_lower = false;
608
let mut is_constructor_call = false;
609
610
// Use the case of the type name to check if it's an enum
611
// This doesn't match everything that can be an enum according to the rust spec
612
// but it matches what clippy is OK with
613
let is_enum = {
614
let mut first_chars = path
615
.segments
616
.iter()
617
.rev()
618
.filter_map(|s| s.ident.to_string().chars().next());
619
if let Some(last) = first_chars.next() {
620
if last.is_uppercase() {
621
if let Some(last) = first_chars.next() {
622
last.is_uppercase()
623
} else {
624
false
625
}
626
} else {
627
last_segment_is_lower = true;
628
false
629
}
630
} else {
631
false
632
}
633
};
634
635
let func = if input.peek(Token![=]) {
636
// If there is an '=', then this is a "function style" require
637
input.parse::<Token![=]>()?;
638
let expr: Expr = input.parse()?;
639
Some(quote!(|| #expr ))
640
} else if input.peek(Brace) {
641
// This is a "value style" named-struct-like require
642
let content;
643
braced!(content in input);
644
let content = content.parse::<TokenStream>()?;
645
Some(quote!(|| #path { #content }))
646
} else if input.peek(Paren) {
647
// This is a "value style" tuple-struct-like require
648
let content;
649
parenthesized!(content in input);
650
let content = content.parse::<TokenStream>()?;
651
is_constructor_call = last_segment_is_lower;
652
Some(quote!(|| #path (#content)))
653
} else if is_enum {
654
// if this is an enum, then it is an inline enum component declaration
655
Some(quote!(|| #path))
656
} else {
657
// if this isn't any of the above, then it is a component ident, which will use Default
658
None
659
};
660
if is_enum || is_constructor_call {
661
path.segments.pop();
662
path.segments.pop_punct();
663
}
664
Ok(Require { path, func })
665
}
666
}
667
668
fn storage_path(bevy_ecs_path: &Path, ty: StorageTy) -> TokenStream {
669
let storage_type = match ty {
670
StorageTy::Table => Ident::new("Table", Span::call_site()),
671
StorageTy::SparseSet => Ident::new("SparseSet", Span::call_site()),
672
};
673
674
quote! { #bevy_ecs_path::component::StorageType::#storage_type }
675
}
676
677
fn hook_register_function_call(
678
bevy_ecs_path: &Path,
679
hook: TokenStream,
680
functions: &[TokenStream],
681
) -> TokenStream {
682
let hook_function = match functions {
683
[] => return TokenStream::new(),
684
[single] => single.clone(),
685
multiple => {
686
quote! {
687
|mut world: #bevy_ecs_path::world::DeferredWorld, context: #bevy_ecs_path::lifecycle::HookContext| {
688
#(#multiple(world.reborrow(), context.clone());)*
689
}
690
}
691
}
692
};
693
quote! {
694
fn #hook() -> #FQOption<#bevy_ecs_path::lifecycle::ComponentHook> {
695
#FQOption::Some(#hook_function)
696
}
697
}
698
}
699
700
mod kw {
701
syn::custom_keyword!(relationship_target);
702
syn::custom_keyword!(relationship);
703
syn::custom_keyword!(linked_spawn);
704
syn::custom_keyword!(allow_self_referential);
705
}
706
707
impl Parse for Relationship {
708
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
709
let mut relationship_target: Option<Type> = None;
710
let mut allow_self_referential: bool = false;
711
712
while !input.is_empty() {
713
let lookahead = input.lookahead1();
714
if lookahead.peek(kw::allow_self_referential) {
715
input.parse::<kw::allow_self_referential>()?;
716
allow_self_referential = true;
717
} else if lookahead.peek(kw::relationship_target) {
718
input.parse::<kw::relationship_target>()?;
719
input.parse::<Token![=]>()?;
720
relationship_target = Some(input.parse()?);
721
} else {
722
return Err(lookahead.error());
723
}
724
if !input.is_empty() {
725
input.parse::<Token![,]>()?;
726
}
727
}
728
Ok(Relationship {
729
relationship_target: relationship_target.ok_or_else(|| {
730
syn::Error::new(input.span(), "Missing `relationship_target = X` attribute")
731
})?,
732
allow_self_referential,
733
})
734
}
735
}
736
737
impl Parse for RelationshipTarget {
738
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
739
let mut relationship: Option<Type> = None;
740
let mut linked_spawn: bool = false;
741
742
while !input.is_empty() {
743
let lookahead = input.lookahead1();
744
if lookahead.peek(kw::linked_spawn) {
745
input.parse::<kw::linked_spawn>()?;
746
linked_spawn = true;
747
} else if lookahead.peek(kw::relationship) {
748
input.parse::<kw::relationship>()?;
749
input.parse::<Token![=]>()?;
750
relationship = Some(input.parse()?);
751
} else {
752
return Err(lookahead.error());
753
}
754
if !input.is_empty() {
755
input.parse::<Token![,]>()?;
756
}
757
}
758
Ok(RelationshipTarget {
759
relationship: relationship.ok_or_else(|| {
760
syn::Error::new(input.span(), "Missing `relationship = X` attribute")
761
})?,
762
linked_spawn,
763
})
764
}
765
}
766
767
/// Returns the field with the `#[relationship]` attribute, the only field if unnamed,
768
/// or the only field in a [`Fields::Named`] with one field, otherwise `Err`.
769
pub(crate) fn relationship_field<'a>(
770
fields: &'a Fields,
771
derive: &'static str,
772
span: Span,
773
) -> Result<&'a Field> {
774
match fields {
775
Fields::Named(fields) if fields.named.len() == 1 => Ok(fields.named.first().unwrap()),
776
Fields::Named(fields) => fields.named.iter().find(|field| {
777
field
778
.attrs
779
.iter()
780
.any(|attr| attr.path().is_ident(RELATIONSHIP))
781
}).ok_or(syn::Error::new(
782
span,
783
format!("{derive} derive expected named structs with a single field or with a field annotated with #[relationship].")
784
)),
785
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => Ok(fields.unnamed.first().unwrap()),
786
Fields::Unnamed(fields) => fields.unnamed.iter().find(|field| {
787
field
788
.attrs
789
.iter()
790
.any(|attr| attr.path().is_ident(RELATIONSHIP))
791
})
792
.ok_or(syn::Error::new(
793
span,
794
format!("{derive} derive expected unnamed structs with one field or with a field annotated with #[relationship]."),
795
)),
796
Fields::Unit => Err(syn::Error::new(
797
span,
798
format!("{derive} derive expected named or unnamed struct, found unit struct."),
799
)),
800
}
801
}
802
803