1extern crate proc_macro;
4
5mod component;
6mod fetch;
7pub(crate) mod utils;
8
9use crate::fetch::derive_world_query_impl;
10use proc_macro::TokenStream;
11use proc_macro2::Span;
12use quote::{format_ident, quote};
13use syn::{
14 parse::{Parse, ParseStream},
15 parse_macro_input,
16 punctuated::Punctuated,
17 spanned::Spanned,
18 token::Comma,
19 DeriveInput, Field, GenericParam, Ident, Index, LitInt, Meta, MetaList, NestedMeta, Result,
20 Token, TypeParam,
21};
22use utils::{derive_label, get_named_struct_fields, BevyManifest};
23
24struct AllTuples {
25 macro_ident: Ident,
26 start: usize,
27 end: usize,
28 idents: Vec<Ident>,
29}
30
31impl Parse for AllTuples {
32 fn parse(input: ParseStream) -> Result<Self> {
33 let macro_ident = input.parse::<Ident>()?;
34 input.parse::<Comma>()?;
35 let start = input.parse::<LitInt>()?.base10_parse()?;
36 input.parse::<Comma>()?;
37 let end = input.parse::<LitInt>()?.base10_parse()?;
38 input.parse::<Comma>()?;
39 let mut idents = vec![input.parse::<Ident>()?];
40 while input.parse::<Comma>().is_ok() {
41 idents.push(input.parse::<Ident>()?);
42 }
43
44 Ok(AllTuples {
45 macro_ident,
46 start,
47 end,
48 idents,
49 })
50 }
51}
52
53#[proc_macro]
54pub fn all_tuples(input: TokenStream) -> TokenStream {
55 let input = parse_macro_input!(input as AllTuples);
56 let len = input.end - input.start;
57 let mut ident_tuples = Vec::with_capacity(len);
58 for i in input.start..=input.end {
59 let idents = input
60 .idents
61 .iter()
62 .map(|ident| format_ident!("{}{}", ident, i));
63 if input.idents.len() < 2 {
64 ident_tuples.push(quote! {
65 #(#idents)*
66 });
67 } else {
68 ident_tuples.push(quote! {
69 (#(#idents),*)
70 });
71 }
72 }
73
74 let macro_ident = &input.macro_ident;
75 let invocations = (input.start..=input.end).map(|i| {
76 let ident_tuples = &ident_tuples[..i];
77 quote! {
78 #macro_ident!(#(#ident_tuples),*);
79 }
80 });
81 TokenStream::from(quote! {
82 #(
83 #invocations
84 )*
85 })
86}
87
88enum BundleFieldKind {
89 Component,
90 Ignore,
91}
92
93const BUNDLE_ATTRIBUTE_NAME: &str = "bundle";
94const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore";
95
96#[proc_macro_derive(Bundle, attributes(bundle))]
97pub fn derive_bundle(input: TokenStream) -> TokenStream {
98 let ast = parse_macro_input!(input as DeriveInput);
99 let ecs_path = azalea_ecs_path();
100
101 let named_fields = match get_named_struct_fields(&ast.data) {
102 Ok(fields) => &fields.named,
103 Err(e) => return e.into_compile_error().into(),
104 };
105
106 let mut field_kind = Vec::with_capacity(named_fields.len());
107
108 'field_loop: for field in named_fields.iter() {
109 for attr in &field.attrs {
110 if attr.path.is_ident(BUNDLE_ATTRIBUTE_NAME) {
111 if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() {
112 if let Some(&NestedMeta::Meta(Meta::Path(ref path))) = nested.first() {
113 if path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
114 field_kind.push(BundleFieldKind::Ignore);
115 continue 'field_loop;
116 }
117
118 return syn::Error::new(
119 path.span(),
120 format!(
121 "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
122 ),
123 )
124 .into_compile_error()
125 .into();
126 }
127
128 return syn::Error::new(attr.span(), format!("Invalid bundle attribute. Use `#[{BUNDLE_ATTRIBUTE_NAME}({BUNDLE_ATTRIBUTE_IGNORE_NAME})]`")).into_compile_error().into();
129 }
130 }
131 }
132
133 field_kind.push(BundleFieldKind::Component);
134 }
135
136 let field = named_fields
137 .iter()
138 .map(|field| field.ident.as_ref().unwrap())
139 .collect::<Vec<_>>();
140 let field_type = named_fields
141 .iter()
142 .map(|field| &field.ty)
143 .collect::<Vec<_>>();
144
145 let mut field_component_ids = Vec::new();
146 let mut field_get_components = Vec::new();
147 let mut field_from_components = Vec::new();
148 for ((field_type, field_kind), field) in
149 field_type.iter().zip(field_kind.iter()).zip(field.iter())
150 {
151 match field_kind {
152 BundleFieldKind::Component => {
153 field_component_ids.push(quote! {
154 <#field_type as #ecs_path::bundle::BevyBundle>::component_ids(components, storages, &mut *ids);
155 });
156 field_get_components.push(quote! {
157 self.#field.get_components(&mut *func);
158 });
159 field_from_components.push(quote! {
160 #field: <#field_type as #ecs_path::bundle::BevyBundle>::from_components(ctx, &mut *func),
161 });
162 }
163
164 BundleFieldKind::Ignore => {
165 field_from_components.push(quote! {
166 #field: ::std::default::Default::default(),
167 });
168 }
169 }
170 }
171 let generics = ast.generics;
172 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
173 let struct_name = &ast.ident;
174
175 TokenStream::from(quote! {
176 unsafe impl #impl_generics #ecs_path::bundle::BevyBundle for #struct_name #ty_generics #where_clause {
178 fn component_ids(
179 components: &mut #ecs_path::component::Components,
180 storages: &mut #ecs_path::storage::Storages,
181 ids: &mut impl FnMut(#ecs_path::component::ComponentId)
182 ){
183 #(#field_component_ids)*
184 }
185
186 #[allow(unused_variables, non_snake_case)]
187 unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self
188 where
189 __F: FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_>
190 {
191 Self {
192 #(#field_from_components)*
193 }
194 }
195
196 #[allow(unused_variables)]
197 fn get_components(self, func: &mut impl FnMut(#ecs_path::ptr::OwningPtr<'_>)) {
198 #(#field_get_components)*
199 }
200 }
201 })
202}
203
204fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec<Ident> {
205 (0..count)
206 .map(|i| Ident::new(&fmt_string(i), Span::call_site()))
207 .collect::<Vec<Ident>>()
208}
209
210#[proc_macro]
211pub fn impl_param_set(_input: TokenStream) -> TokenStream {
212 let mut tokens = TokenStream::new();
213 let max_params = 8;
214 let params = get_idents(|i| format!("P{i}"), max_params);
215 let params_fetch = get_idents(|i| format!("PF{i}"), max_params);
216 let metas = get_idents(|i| format!("m{i}"), max_params);
217 let mut param_fn_muts = Vec::new();
218 for (i, param) in params.iter().enumerate() {
219 let fn_name = Ident::new(&format!("p{i}"), Span::call_site());
220 let index = Index::from(i);
221 param_fn_muts.push(quote! {
222 pub fn #fn_name<'a>(&'a mut self) -> <#param::Fetch as SystemParamFetch<'a, 'a>>::Item {
223 unsafe {
227 <#param::Fetch as SystemParamFetch<'a, 'a>>::get_param(&mut self.param_states.#index, &self.system_meta, self.world, self.change_tick)
228 }
229 }
230 });
231 }
232
233 for param_count in 1..=max_params {
234 let param = ¶ms[0..param_count];
235 let param_fetch = ¶ms_fetch[0..param_count];
236 let meta = &metas[0..param_count];
237 let param_fn_mut = ¶m_fn_muts[0..param_count];
238 tokens.extend(TokenStream::from(quote! {
239 impl<'w, 's, #(#param: SystemParam,)*> SystemParam for ParamSet<'w, 's, (#(#param,)*)>
240 {
241 type Fetch = ParamSetState<(#(#param::Fetch,)*)>;
242 }
243
244 unsafe impl<#(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> ReadOnlySystemParamFetch for ParamSetState<(#(#param_fetch,)*)>
247 where #(#param_fetch: ReadOnlySystemParamFetch,)*
248 { }
249
250 unsafe impl<#(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> SystemParamState for ParamSetState<(#(#param_fetch,)*)>
254 {
255 fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
256 #(
257 let mut #meta = system_meta.clone();
259 #meta.component_access_set.clear();
260 #meta.archetype_component_access.clear();
261 #param_fetch::init(world, &mut #meta);
262 let #param = #param_fetch::init(world, &mut system_meta.clone());
263 )*
264 #(
265 system_meta
266 .component_access_set
267 .extend(#meta.component_access_set);
268 system_meta
269 .archetype_component_access
270 .extend(&#meta.archetype_component_access);
271 )*
272 ParamSetState((#(#param,)*))
273 }
274
275 fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) {
276 let (#(#param,)*) = &mut self.0;
277 #(
278 #param.new_archetype(archetype, system_meta);
279 )*
280 }
281
282 fn apply(&mut self, world: &mut World) {
283 self.0.apply(world)
284 }
285 }
286
287
288
289 impl<'w, 's, #(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> SystemParamFetch<'w, 's> for ParamSetState<(#(#param_fetch,)*)>
290 {
291 type Item = ParamSet<'w, 's, (#(<#param_fetch as SystemParamFetch<'w, 's>>::Item,)*)>;
292
293 #[inline]
294 unsafe fn get_param(
295 state: &'s mut Self,
296 system_meta: &SystemMeta,
297 world: &'w World,
298 change_tick: u32,
299 ) -> Self::Item {
300 ParamSet {
301 param_states: &mut state.0,
302 system_meta: system_meta.clone(),
303 world,
304 change_tick,
305 }
306 }
307 }
308
309 impl<'w, 's, #(#param: SystemParam,)*> ParamSet<'w, 's, (#(#param,)*)>
310 {
311
312 #(#param_fn_mut)*
313 }
314 }));
315 }
316
317 tokens
318}
319
320#[derive(Default)]
321struct SystemParamFieldAttributes {
322 pub ignore: bool,
323}
324
325static SYSTEM_PARAM_ATTRIBUTE_NAME: &str = "system_param";
326
327#[proc_macro_derive(SystemParam, attributes(system_param))]
329pub fn derive_system_param(input: TokenStream) -> TokenStream {
330 let ast = parse_macro_input!(input as DeriveInput);
331 let fields = match get_named_struct_fields(&ast.data) {
332 Ok(fields) => &fields.named,
333 Err(e) => return e.into_compile_error().into(),
334 };
335 let path = azalea_ecs_path();
336
337 let field_attributes = fields
338 .iter()
339 .map(|field| {
340 (
341 field,
342 field
343 .attrs
344 .iter()
345 .find(|a| *a.path.get_ident().as_ref().unwrap() == SYSTEM_PARAM_ATTRIBUTE_NAME)
346 .map_or_else(SystemParamFieldAttributes::default, |a| {
347 syn::custom_keyword!(ignore);
348 let mut attributes = SystemParamFieldAttributes::default();
349 a.parse_args_with(|input: ParseStream| {
350 if input.parse::<Option<ignore>>()?.is_some() {
351 attributes.ignore = true;
352 }
353 Ok(())
354 })
355 .expect("Invalid 'system_param' attribute format.");
356
357 attributes
358 }),
359 )
360 })
361 .collect::<Vec<(&Field, SystemParamFieldAttributes)>>();
362 let mut fields = Vec::new();
363 let mut field_indices = Vec::new();
364 let mut field_types = Vec::new();
365 let mut ignored_fields = Vec::new();
366 let mut ignored_field_types = Vec::new();
367 for (i, (field, attrs)) in field_attributes.iter().enumerate() {
368 if attrs.ignore {
369 ignored_fields.push(field.ident.as_ref().unwrap());
370 ignored_field_types.push(&field.ty);
371 } else {
372 fields.push(field.ident.as_ref().unwrap());
373 field_types.push(&field.ty);
374 field_indices.push(Index::from(i));
375 }
376 }
377
378 let generics = ast.generics;
379 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
380
381 let lifetimeless_generics: Vec<_> = generics
382 .params
383 .iter()
384 .filter(|g| matches!(g, GenericParam::Type(_)))
385 .collect();
386
387 let mut punctuated_generics = Punctuated::<_, Token![,]>::new();
388 punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g {
389 GenericParam::Type(g) => GenericParam::Type(TypeParam {
390 default: None,
391 ..g.clone()
392 }),
393 _ => unreachable!(),
394 }));
395
396 let mut punctuated_generic_idents = Punctuated::<_, Token![,]>::new();
397 punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
398 GenericParam::Type(g) => &g.ident,
399 _ => unreachable!(),
400 }));
401
402 let struct_name = &ast.ident;
403 let fetch_struct_visibility = &ast.vis;
404
405 TokenStream::from(quote! {
406 const _: () = {
410 impl #impl_generics #path::system::SystemParam for #struct_name #ty_generics #where_clause {
411 type Fetch = FetchState <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents>;
412 }
413
414 #[doc(hidden)]
415 #fetch_struct_visibility struct FetchState <TSystemParamState, #punctuated_generic_idents> {
416 state: TSystemParamState,
417 marker: std::marker::PhantomData<fn()->(#punctuated_generic_idents)>
418 }
419
420 unsafe impl<TSystemParamState: #path::system::SystemParamState, #punctuated_generics> #path::system::SystemParamState for FetchState <TSystemParamState, #punctuated_generic_idents> #where_clause {
421 fn init(world: &mut #path::world::World, system_meta: &mut #path::system::SystemMeta) -> Self {
422 Self {
423 state: TSystemParamState::init(world, system_meta),
424 marker: std::marker::PhantomData,
425 }
426 }
427
428 fn new_archetype(&mut self, archetype: &#path::archetype::Archetype, system_meta: &mut #path::system::SystemMeta) {
429 self.state.new_archetype(archetype, system_meta)
430 }
431
432 fn apply(&mut self, world: &mut #path::world::World) {
433 self.state.apply(world)
434 }
435 }
436
437 impl #impl_generics #path::system::SystemParamFetch<'w, 's> for FetchState <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents> #where_clause {
438 type Item = #struct_name #ty_generics;
439 unsafe fn get_param(
440 state: &'s mut Self,
441 system_meta: &#path::system::SystemMeta,
442 world: &'w #path::world::World,
443 change_tick: u32,
444 ) -> Self::Item {
445 #struct_name {
446 #(#fields: <<#field_types as #path::system::SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)*
447 #(#ignored_fields: <#ignored_field_types>::default(),)*
448 }
449 }
450 }
451
452 unsafe impl<TSystemParamState: #path::system::SystemParamState + #path::system::ReadOnlySystemParamFetch, #punctuated_generics> #path::system::ReadOnlySystemParamFetch for FetchState <TSystemParamState, #punctuated_generic_idents> #where_clause {}
454 };
455 })
456}
457
458#[proc_macro_derive(WorldQuery, attributes(world_query))]
460pub fn derive_world_query(input: TokenStream) -> TokenStream {
461 let ast = parse_macro_input!(input as DeriveInput);
462 derive_world_query_impl(ast)
463}
464
465#[proc_macro_derive(SystemLabel, attributes(system_label))]
471pub fn derive_system_label(input: TokenStream) -> TokenStream {
472 let input = parse_macro_input!(input as DeriveInput);
473 let mut trait_path = azalea_ecs_path();
474 trait_path.segments.push(format_ident!("schedule").into());
475 trait_path
476 .segments
477 .push(format_ident!("SystemLabel").into());
478 derive_label(input, &trait_path, "system_label")
479}
480
481#[proc_macro_derive(StageLabel, attributes(stage_label))]
487pub fn derive_stage_label(input: TokenStream) -> TokenStream {
488 let input = parse_macro_input!(input as DeriveInput);
489 let mut trait_path = azalea_ecs_path();
490 trait_path.segments.push(format_ident!("schedule").into());
491 trait_path.segments.push(format_ident!("StageLabel").into());
492 derive_label(input, &trait_path, "stage_label")
493}
494
495#[proc_macro_derive(RunCriteriaLabel, attributes(run_criteria_label))]
501pub fn derive_run_criteria_label(input: TokenStream) -> TokenStream {
502 let input = parse_macro_input!(input as DeriveInput);
503 let mut trait_path = azalea_ecs_path();
504 trait_path.segments.push(format_ident!("schedule").into());
505 trait_path
506 .segments
507 .push(format_ident!("RunCriteriaLabel").into());
508 derive_label(input, &trait_path, "run_criteria_label")
509}
510
511pub(crate) fn azalea_ecs_path() -> syn::Path {
512 BevyManifest::default().get_path("azalea_ecs")
513}
514
515#[proc_macro_derive(Resource)]
516pub fn derive_resource(input: TokenStream) -> TokenStream {
517 component::derive_resource(input)
518}
519
520#[proc_macro_derive(Component, attributes(component))]
521pub fn derive_component(input: TokenStream) -> TokenStream {
522 component::derive_component(input)
523}