Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/macros/src/event.rs
9401 views
1
use proc_macro::TokenStream;
2
use quote::quote;
3
use syn::{
4
parse_macro_input, parse_quote, spanned::Spanned, Data, DataStruct, DeriveInput, Fields, Index,
5
Member, Path, Result, Token, Type,
6
};
7
8
pub const EVENT: &str = "event";
9
pub const ENTITY_EVENT: &str = "entity_event";
10
pub const PROPAGATE: &str = "propagate";
11
pub const AUTO_PROPAGATE: &str = "auto_propagate";
12
pub const TRIGGER: &str = "trigger";
13
pub const EVENT_TARGET: &str = "event_target";
14
15
pub fn derive_event(input: TokenStream) -> TokenStream {
16
let mut ast = parse_macro_input!(input as DeriveInput);
17
let bevy_ecs_path: Path = crate::bevy_ecs_path();
18
19
ast.generics
20
.make_where_clause()
21
.predicates
22
.push(parse_quote! { Self: Send + Sync + 'static });
23
24
let mut processed_attrs = Vec::new();
25
let mut trigger: Option<Type> = None;
26
27
for attr in ast.attrs.iter().filter(|attr| attr.path().is_ident(EVENT)) {
28
if let Err(e) = attr.parse_nested_meta(|meta| match meta.path.get_ident() {
29
Some(ident) if processed_attrs.iter().any(|i| ident == i) => {
30
Err(meta.error(format!("duplicate attribute: {ident}")))
31
}
32
Some(ident) if ident == TRIGGER => {
33
trigger = Some(meta.value()?.parse()?);
34
processed_attrs.push(TRIGGER);
35
Ok(())
36
}
37
Some(ident) => Err(meta.error(format!("unsupported attribute: {ident}"))),
38
None => Err(meta.error("expected identifier")),
39
}) {
40
return e.to_compile_error().into();
41
}
42
}
43
44
let trigger = if let Some(trigger) = trigger {
45
quote! {#trigger}
46
} else {
47
quote! {#bevy_ecs_path::event::GlobalTrigger}
48
};
49
50
let struct_name = &ast.ident;
51
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
52
53
TokenStream::from(quote! {
54
impl #impl_generics #bevy_ecs_path::event::Event for #struct_name #type_generics #where_clause {
55
type Trigger<'a> = #trigger;
56
}
57
})
58
}
59
60
pub fn derive_entity_event(input: TokenStream) -> TokenStream {
61
let mut ast = parse_macro_input!(input as DeriveInput);
62
63
ast.generics
64
.make_where_clause()
65
.predicates
66
.push(parse_quote! { Self: Send + Sync + 'static });
67
68
let mut auto_propagate = false;
69
let mut propagate = false;
70
let mut traversal: Option<Type> = None;
71
let mut trigger: Option<Type> = None;
72
let bevy_ecs_path: Path = crate::bevy_ecs_path();
73
74
let mut processed_attrs = Vec::new();
75
76
for attr in ast
77
.attrs
78
.iter()
79
.filter(|attr| attr.path().is_ident(ENTITY_EVENT))
80
{
81
if let Err(e) = attr.parse_nested_meta(|meta| match meta.path.get_ident() {
82
Some(ident) if processed_attrs.iter().any(|i| ident == i) => {
83
Err(meta.error(format!("duplicate attribute: {ident}")))
84
}
85
Some(ident) if ident == AUTO_PROPAGATE => {
86
propagate = true;
87
auto_propagate = true;
88
processed_attrs.push(AUTO_PROPAGATE);
89
Ok(())
90
}
91
Some(ident) if ident == PROPAGATE => {
92
propagate = true;
93
if meta.input.peek(Token![=]) {
94
traversal = Some(meta.value()?.parse()?);
95
}
96
processed_attrs.push(PROPAGATE);
97
Ok(())
98
}
99
Some(ident) if ident == TRIGGER => {
100
trigger = Some(meta.value()?.parse()?);
101
processed_attrs.push(TRIGGER);
102
Ok(())
103
}
104
Some(ident) => Err(meta.error(format!("unsupported attribute: {ident}"))),
105
None => Err(meta.error("expected identifier")),
106
}) {
107
return e.to_compile_error().into();
108
}
109
}
110
111
if trigger.is_some() && propagate {
112
return syn::Error::new(
113
ast.span(),
114
"Cannot define both #[entity_event(trigger)] and #[entity_event(propagate)]",
115
)
116
.into_compile_error()
117
.into();
118
}
119
120
let entity_field = match get_event_target_field(&ast) {
121
Ok(value) => value,
122
Err(err) => return err.into_compile_error().into(),
123
};
124
125
let struct_name = &ast.ident;
126
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
127
128
let trigger = if let Some(trigger) = trigger {
129
quote! {#trigger}
130
} else if propagate {
131
let traversal = traversal
132
.unwrap_or_else(|| parse_quote! { &'static #bevy_ecs_path::hierarchy::ChildOf});
133
quote! {#bevy_ecs_path::event::PropagateEntityTrigger<#auto_propagate, Self, #traversal>}
134
} else {
135
quote! {#bevy_ecs_path::event::EntityTrigger}
136
};
137
138
let set_entity_event_target_impl = if propagate {
139
quote! {
140
impl #impl_generics #bevy_ecs_path::event::SetEntityEventTarget for #struct_name #type_generics #where_clause {
141
fn set_event_target(&mut self, entity: #bevy_ecs_path::entity::Entity) {
142
self.#entity_field = Into::into(entity);
143
}
144
}
145
}
146
} else {
147
quote! {}
148
};
149
150
TokenStream::from(quote! {
151
impl #impl_generics #bevy_ecs_path::event::Event for #struct_name #type_generics #where_clause {
152
type Trigger<'a> = #trigger;
153
}
154
155
impl #impl_generics #bevy_ecs_path::event::EntityEvent for #struct_name #type_generics #where_clause {
156
fn event_target(&self) -> #bevy_ecs_path::entity::Entity {
157
#bevy_ecs_path::entity::ContainsEntity::entity(&self.#entity_field)
158
}
159
}
160
161
#set_entity_event_target_impl
162
})
163
}
164
165
/// Returns the field with the `#[event_target]` attribute, the only field if unnamed,
166
/// or the field with the name "entity".
167
fn get_event_target_field(ast: &DeriveInput) -> Result<Member> {
168
let Data::Struct(DataStruct { fields, .. }) = &ast.data else {
169
return Err(syn::Error::new(
170
ast.span(),
171
"EntityEvent can only be derived for structs.",
172
));
173
};
174
match fields {
175
Fields::Named(fields) => fields.named.iter().find_map(|field| {
176
if field.ident.as_ref().is_some_and(|i| i == "entity") || field
177
.attrs
178
.iter()
179
.any(|attr| attr.path().is_ident(EVENT_TARGET)) {
180
Some(Member::Named(field.ident.clone()?))
181
} else {
182
None
183
}
184
}).ok_or(syn::Error::new(
185
fields.span(),
186
"EntityEvent derive expected a field name 'entity' or a field annotated with #[event_target]."
187
)),
188
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => Ok(Member::Unnamed(Index::from(0))),
189
Fields::Unnamed(fields) => fields.unnamed.iter().enumerate().find_map(|(index, field)| {
190
if field
191
.attrs
192
.iter()
193
.any(|attr| attr.path().is_ident(EVENT_TARGET)) {
194
Some(Member::Unnamed(Index::from(index)))
195
} else {
196
None
197
}
198
})
199
.ok_or(syn::Error::new(
200
fields.span(),
201
"EntityEvent derive expected unnamed structs with one field or with a field annotated with #[event_target].",
202
)),
203
Fields::Unit => Err(syn::Error::new(
204
fields.span(),
205
"EntityEvent derive does not work on unit structs. Your type must have a field to store the `Entity` target, such as `Attack(Entity)` or `Attack { entity: Entity }`.",
206
)),
207
}
208
}
209
210