zengine_macro/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5use quote::{format_ident, quote};
6use syn::{
7    parse::{Parse, ParseStream},
8    parse_macro_input,
9    token::Comma,
10    DeriveInput, Ident, Index, ItemFn, LitInt, Path, Result,
11};
12
13mod zengine_manifest;
14use zengine_manifest::ZENgineManifest;
15
16pub(crate) fn zengine_ecs_path() -> syn::Path {
17    ZENgineManifest::default().get_path("zengine_ecs")
18}
19
20/// Generates an impl of the `Component` trait.
21#[proc_macro_derive(Component)]
22pub fn component_macro_derive(input: TokenStream) -> TokenStream {
23    let input = parse_macro_input!(input as DeriveInput);
24    let zengine_ecs_path: Path = crate::zengine_ecs_path();
25
26    let name = input.ident;
27    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
28
29    let expanded = quote! {
30        impl #impl_generics #zengine_ecs_path::Component for #name #ty_generics #where_clause {}
31    };
32
33    TokenStream::from(expanded)
34}
35
36/// Generates an impl of the `Resource` trait.
37#[proc_macro_derive(Resource)]
38pub fn resource_macro_derive(input: TokenStream) -> TokenStream {
39    let input = parse_macro_input!(input as DeriveInput);
40    let zengine_ecs_path: Path = crate::zengine_ecs_path();
41
42    let name = input.ident;
43    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
44
45    let expanded = quote! {
46        impl #impl_generics #zengine_ecs_path::Resource for #name #ty_generics #where_clause {}
47    };
48
49    TokenStream::from(expanded)
50}
51
52/// Generates an impl of the `UnsendableResource` trait.
53#[proc_macro_derive(UnsendableResource)]
54pub fn unsendable_resource_macro_derive(input: TokenStream) -> TokenStream {
55    let input = parse_macro_input!(input as DeriveInput);
56    let zengine_ecs_path: Path = crate::zengine_ecs_path();
57
58    let name = input.ident;
59    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
60
61    let expanded = quote! {
62        impl #impl_generics #zengine_ecs_path::UnsendableResource for #name #ty_generics #where_clause {}
63    };
64
65    TokenStream::from(expanded)
66}
67
68/// Generates an impl of the `Asset` trait.
69#[proc_macro_derive(Asset)]
70pub fn asset_macro_derive(input: TokenStream) -> TokenStream {
71    let input = parse_macro_input!(input as DeriveInput);
72    let zengine_asset_path: Path = ZENgineManifest::default().get_path("zengine_asset");
73
74    let name = input.ident;
75    let asset_counter = format_ident!("{}_COUNTER", name.to_string().to_uppercase());
76    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
77
78    let expanded = quote! {
79        static #asset_counter: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
80        impl #impl_generics #zengine_asset_path::Asset for #name #ty_generics #where_clause {
81            fn next_counter() -> u64
82            where
83                Self: Sized,
84            {
85                #asset_counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
86            }
87        }
88    };
89
90    TokenStream::from(expanded)
91}
92
93/// Generates an impl of the `InputType` trait.
94#[proc_macro_derive(InputType)]
95pub fn input_type_macro_derive(input: TokenStream) -> TokenStream {
96    let input = parse_macro_input!(input as DeriveInput);
97    let zengine_input_path = ZENgineManifest::default().get_path("zengine_input");
98
99    let name = input.ident;
100    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
101
102    let expanded = quote! {
103        impl #impl_generics #zengine_input_path::InputType for #name #ty_generics #where_clause {}
104    };
105
106    TokenStream::from(expanded)
107}
108
109struct AllTuples {
110    macro_ident: Ident,
111    start: usize,
112    end: usize,
113    ident: Ident,
114}
115
116impl Parse for AllTuples {
117    fn parse(input: ParseStream) -> Result<Self> {
118        let macro_ident = input.parse::<Ident>()?;
119        input.parse::<Comma>()?;
120        let start = input.parse::<LitInt>()?.base10_parse()?;
121        input.parse::<Comma>()?;
122        let end = input.parse::<LitInt>()?.base10_parse()?;
123        input.parse::<Comma>()?;
124        let ident = input.parse::<Ident>()?;
125
126        Ok(AllTuples {
127            macro_ident,
128            start,
129            end,
130            ident,
131        })
132    }
133}
134
135#[doc(hidden)]
136#[proc_macro]
137pub fn all_tuples(input: TokenStream) -> TokenStream {
138    let input = parse_macro_input!(input as AllTuples);
139    let len = input.end - input.start;
140    let mut ident_tuples = Vec::with_capacity(len);
141    for i in input.start..=input.end {
142        let ident = format_ident!("{}{}", input.ident, i);
143        ident_tuples.push(quote! {
144            #ident
145        });
146    }
147
148    let macro_ident = &input.macro_ident;
149    let invocations = (input.start..=input.end).map(|i| {
150        let ident_tuples = &ident_tuples[0..i - input.start];
151        quote! {
152            #macro_ident!(#(#ident_tuples),*);
153        }
154    });
155    TokenStream::from(quote! {
156        #(
157            #invocations
158        )*
159    })
160}
161
162#[doc(hidden)]
163#[proc_macro]
164pub fn all_tuples_with_idexes(input: TokenStream) -> TokenStream {
165    let input = parse_macro_input!(input as AllTuples);
166    let len = input.end - input.start;
167    let mut ident_tuples = Vec::with_capacity(len);
168    for i in input.start..=input.end {
169        let ident = format_ident!("{}{}", input.ident, i);
170        let position: Index = syn::Index {
171            index: i as u32,
172            span: Span::call_site(),
173        };
174        ident_tuples.push(quote! {
175            #ident => #position
176        });
177    }
178
179    let macro_ident = &input.macro_ident;
180    let invocations = (input.start..=input.end).map(|i| {
181        let ident_tuples = &ident_tuples[0..i - input.start];
182        quote! {
183            #macro_ident!(#(#ident_tuples),*);
184        }
185    });
186    TokenStream::from(quote! {
187        #(
188            #invocations
189        )*
190    })
191}
192
193#[doc(hidden)]
194#[proc_macro]
195pub fn all_positional_tuples(input: TokenStream) -> TokenStream {
196    let input = parse_macro_input!(input as AllTuples);
197    let len = input.end - input.start;
198    let mut ident_tuples = Vec::with_capacity(len);
199    for i in input.start..=input.end {
200        let position: Index = syn::Index {
201            index: i as u32,
202            span: Span::call_site(),
203        };
204        let ident = format_ident!("{}{}", input.ident, i);
205        ident_tuples.push(quote! {
206            #ident => #position
207        });
208    }
209
210    let macro_ident = &input.macro_ident;
211    let invocations = (input.start..=input.end).map(|i| {
212        let ident_tuples = &ident_tuples[0..i - input.start];
213        quote! {
214            #macro_ident!(#(#ident_tuples),*);
215        }
216    });
217
218    TokenStream::from(quote! {
219        #(
220            #invocations
221        )*
222    })
223}
224
225struct ZipInput {
226    end: usize,
227}
228
229impl Parse for ZipInput {
230    fn parse(input: ParseStream) -> Result<Self> {
231        let end = input.parse::<LitInt>()?.base10_parse()?;
232
233        Ok(ZipInput { end })
234    }
235}
236
237#[doc(hidden)]
238#[proc_macro]
239pub fn generate_zip(input: TokenStream) -> TokenStream {
240    let input = parse_macro_input!(input as ZipInput);
241    let mut expanded = quote! {};
242
243    for zip_number in 3..input.end {
244        let name = format_ident!("Zip{}", zip_number);
245
246        let identity = format_ident!("Z{}", 0_usize);
247        let identity_lower = format_ident!("z{}", 0_usize);
248
249        let mut generics_with_where = quote!( #identity: Iterator );
250        let mut generics = quote!( #identity );
251        let mut generics_args = quote!( #identity_lower: #identity );
252        for i in 1..zip_number {
253            let identity = format_ident!("Z{}", i);
254            let identity_lower = format_ident!("z{}", i);
255
256            generics_with_where.extend(quote! { , #identity: Iterator });
257            generics.extend(quote! { , #identity });
258            generics_args.extend(quote! { , #identity_lower: #identity })
259        }
260
261        let generics = quote!( < #generics > );
262        let generics_with_where = quote!( < #generics_with_where > );
263
264        let mut zip_type = quote! {};
265        for _i in 0..zip_number - 1 {
266            zip_type.extend(quote! { std::iter::Zip< });
267        }
268        zip_type.extend(quote! { Z0, Z1> });
269
270        for i in 2..zip_number {
271            let identity = format_ident!("Z{}", i);
272            zip_type.extend(quote! { , #identity > });
273        }
274
275        let identity1_lower = format_ident!("z{}", 0_usize);
276        let identity2_lower = format_ident!("z{}", 1_usize);
277        let mut zip_constructor = quote! { std::iter::zip(#identity1_lower, #identity2_lower) };
278        for i in 2..zip_number {
279            let identity_lower = format_ident!("z{}", i);
280            zip_constructor = quote! { std::iter::zip(#zip_constructor, #identity_lower) };
281        }
282
283        let mut map_constructor_args = quote! { (#identity1_lower, #identity2_lower) };
284        for i in 2..zip_number {
285            let identity_lower = format_ident!("z{}", i);
286            map_constructor_args = quote! { (#map_constructor_args, #identity_lower) };
287        }
288
289        let identity_lower = format_ident!("z{}", 0_usize);
290        let mut map_constructor_res = quote! { #identity_lower };
291        for i in 1..zip_number {
292            let identity_lower = format_ident!("z{}", i);
293            map_constructor_res.extend(quote! { , #identity_lower });
294        }
295
296        let identity = format_ident!("Z{}", 0_usize);
297        let mut iter_output = quote! { #identity ::Item };
298        for i in 1..zip_number {
299            let identity = format_ident!("Z{}", i);
300            iter_output.extend(quote! { , #identity ::Item });
301        }
302
303        let map_constructor = quote! { |#map_constructor_args| ( #map_constructor_res ) };
304
305        expanded.extend(quote! {
306            #[doc(hidden)]
307            pub struct #name #generics_with_where {
308                inner: #zip_type,
309            }
310
311            impl #generics_with_where #name #generics {
312                #[allow(clippy::too_many_arguments)]
313                pub fn new (#generics_args) -> Self {
314                    Self {
315                        inner: #zip_constructor
316                    }
317                }
318            }
319
320            impl #generics_with_where Iterator for #name #generics {
321                type Item = (#iter_output);
322
323                #[inline(always)]
324                fn next(&mut self) -> Option<Self::Item> {
325                    self.inner.next().map(#map_constructor)
326                }
327                #[inline]
328                fn size_hint(&self) -> (usize, Option<usize>) {
329                    self.inner.size_hint()
330                }
331            }
332
333        });
334    }
335
336    TokenStream::from(expanded)
337}
338
339struct QueryIterInput {
340    end: usize,
341}
342
343impl Parse for QueryIterInput {
344    fn parse(input: ParseStream) -> Result<Self> {
345        let end = input.parse::<LitInt>()?.base10_parse()?;
346
347        Ok(QueryIterInput { end })
348    }
349}
350
351#[doc(hidden)]
352#[proc_macro]
353pub fn query_iter_for_tuple(input: TokenStream) -> TokenStream {
354    let input = parse_macro_input!(input as QueryIterInput);
355    let mut expanded = quote! {};
356
357    expanded.extend(quote!{
358        impl<'a, 'b, Z: QueryParameter> QueryIter<'b> for Query<'a, (Z,)>
359        where
360            <<Z as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem: QueryIter<'b>,
361        {
362            type Iter = QueryIterator<
363            <<<Z as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem as QueryIter<
364            'b,
365        >>::Iter
366            >;
367            fn iter(&'b self) -> Self::Iter {
368                QueryIterator::new(self.data.iter().map(|a| a.iter()).collect())
369            }
370        }
371    });
372
373    expanded.extend(quote!{
374        impl<'a, 'b, Z0: QueryParameter, Z1: QueryParameter> QueryIter<'b> for Query<'a, (Z0, Z1)>
375            where
376                <<Z0 as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem: QueryIter<'b>,
377                <<Z1 as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem: QueryIter<'b>,
378            {
379                type Iter = QueryIterator<
380                    Zip<
381                        <<<Z0 as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem as QueryIter<
382                            'b,
383                        >>::Iter,
384                        <<<Z1 as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem as QueryIter<
385                            'b,
386                        >>::Iter,
387                    >,
388                >;
389                fn iter(&'b self) -> Self::Iter {
390                    QueryIterator::new(
391                        self.data
392                            .iter()
393                            .map(|(z0, z1)| zip(z0.iter(), z1.iter()))
394                            .collect(),
395                    )
396                }
397            }
398    });
399
400    for zip_number in 3..input.end {
401        let zip_type = format_ident!("Zip{}", zip_number);
402
403        let identity = format_ident!("Z{}", 0_usize);
404        let identity_lowercase = format_ident!("z{}", 0_usize);
405        let mut generics = quote! { #identity: QueryParameter };
406        let mut tuple = quote! { #identity };
407        let mut where_clause = quote! { <<#identity as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem: QueryIter<'b> };
408        let mut zip_args = quote! { <<<#identity as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem as QueryIter<'b,>>::Iter };
409        let mut tuple_args = quote! { #identity_lowercase };
410        let mut tuple_iter = quote! { #identity_lowercase.iter() };
411        for i in 1..zip_number {
412            let identity = format_ident!("Z{}", i);
413            let identity_lowercase = format_ident!("z{}", i);
414            generics.extend(quote! { , #identity: QueryParameter });
415            tuple.extend(quote! { , #identity });
416            where_clause.extend(quote! { , <<#identity as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem: QueryIter<'b> });
417            zip_args.extend(quote! { , <<<#identity as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem as QueryIter<'b,>>::Iter });
418            tuple_args.extend(quote! { , #identity_lowercase });
419            tuple_iter.extend(quote! { , #identity_lowercase.iter() });
420        }
421
422        expanded.extend(quote! {
423            impl<'a, 'b, #generics> QueryIter<'b> for Query<'a, ( #tuple )>
424        where #where_clause
425        {
426            type Iter = QueryIterator<
427                #zip_type<#zip_args>,
428            >;
429            fn iter(&'b self) -> Self::Iter {
430                QueryIterator::new(
431                    self.data
432                        .iter()
433                        .map(|( #tuple_args )| #zip_type::new( #tuple_iter ))
434                        .collect(),
435                )
436            }
437        }
438        })
439    }
440
441    TokenStream::from(expanded)
442}
443
444#[doc(hidden)]
445#[proc_macro]
446pub fn query_iter_mut_for_tuple(input: TokenStream) -> TokenStream {
447    let input = parse_macro_input!(input as QueryIterInput);
448    let mut expanded = quote! {};
449
450    expanded.extend(quote!{
451        impl<'a, 'b, Z: QueryParameter> QueryIterMut<'b> for Query<'a, (Z,)>
452        where
453            <<Z as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem: QueryIterMut<'b>,
454        {
455            type Iter = QueryIterator<
456            <<<Z as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem as QueryIterMut<
457            'b,
458        >>::Iter
459            >;
460            fn iter_mut(&'b mut self) -> Self::Iter {
461                QueryIterator::new(self.data.iter_mut().map(|a| a.iter_mut()).collect())
462            }
463        }
464    });
465
466    expanded.extend(quote!{
467        impl<'a, 'b, Z0: QueryParameter, Z1: QueryParameter> QueryIterMut<'b> for Query<'a, (Z0, Z1)>
468            where
469                <<Z0 as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem: QueryIterMut<'b>,
470                <<Z1 as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem: QueryIterMut<'b>,
471            {
472                type Iter = QueryIterator<
473                    Zip<
474                        <<<Z0 as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem as QueryIterMut<
475                            'b,
476                        >>::Iter,
477                        <<<Z1 as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem as QueryIterMut<
478                            'b,
479                        >>::Iter,
480                    >,
481                >;
482                fn iter_mut(&'b mut self) -> Self::Iter {
483                    QueryIterator::new(
484                        self.data
485                            .iter_mut()
486                            .map(|(z0, z1)| zip(z0.iter_mut(), z1.iter_mut()))
487                            .collect(),
488                    )
489                }
490            }
491    });
492
493    for zip_number in 3..input.end {
494        let zip_type = format_ident!("Zip{}", zip_number);
495
496        let identity = format_ident!("Z{}", 0_usize);
497        let identity_lowercase = format_ident!("z{}", 0_usize);
498        let mut generics = quote! { #identity: QueryParameter };
499        let mut tuple = quote! { #identity };
500        let mut where_clause = quote! { <<#identity as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem: QueryIterMut<'b> };
501        let mut zip_args = quote! { <<<#identity as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem as QueryIterMut<'b,>>::Iter };
502        let mut tuple_args = quote! { #identity_lowercase };
503        let mut tuple_iter = quote! { #identity_lowercase.iter_mut() };
504        for i in 1..zip_number {
505            let identity = format_ident!("Z{}", i);
506            let identity_lowercase = format_ident!("z{}", i);
507            generics.extend(quote! { , #identity: QueryParameter });
508            tuple.extend(quote! { , #identity });
509            where_clause.extend(quote! { , <<#identity as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem: QueryIterMut<'b> });
510            zip_args.extend(quote! { , <<<#identity as QueryParameter>::Item as QueryParameterFetchFromArchetype<'a>>::ArchetypeFetchItem as QueryIterMut<'b,>>::Iter });
511            tuple_args.extend(quote! { , #identity_lowercase });
512            tuple_iter.extend(quote! { , #identity_lowercase.iter_mut() });
513        }
514
515        expanded.extend(quote! {
516            impl<'a, 'b, #generics> QueryIterMut<'b> for Query<'a, ( #tuple )>
517        where #where_clause
518        {
519            type Iter = QueryIterator<
520                #zip_type<#zip_args>,
521            >;
522            fn iter_mut(&'b mut self) -> Self::Iter {
523                QueryIterator::new(
524                    self.data
525                        .iter_mut()
526                        .map(|( #tuple_args )| #zip_type::new( #tuple_iter ))
527                        .collect(),
528                )
529            }
530        }
531        })
532    }
533
534    TokenStream::from(expanded)
535}
536
537/// Generate the required Android boilerplate.
538#[proc_macro_attribute]
539pub fn zengine_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
540    let input = parse_macro_input!(item as ItemFn);
541    assert!(
542        input.sig.ident == "main",
543        "`zengine_main` can only be used on a function called 'main'.",
544    );
545
546    TokenStream::from(quote! {
547        #[cfg(target_os = "android")]
548        #[cfg_attr(target_os = "android", zengine::ndk_glue::main(backtrace = "on", ndk_glue = "zengine::ndk_glue"))]
549        fn android_main() {
550            main()
551        }
552
553        #[allow(unused)]
554        #input
555    })
556}