bevy_utils_proc_macros/
lib.rs

1// FIXME(3492): remove once docs are ready
2#![allow(missing_docs)]
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4
5use proc_macro::TokenStream;
6use quote::{format_ident, quote};
7use syn::{
8    parse::{Parse, ParseStream},
9    parse_macro_input,
10    token::Comma,
11    Ident, LitInt, Result,
12};
13struct AllTuples {
14    macro_ident: Ident,
15    start: usize,
16    end: usize,
17    idents: Vec<Ident>,
18}
19
20impl Parse for AllTuples {
21    fn parse(input: ParseStream) -> Result<Self> {
22        let macro_ident = input.parse::<Ident>()?;
23        input.parse::<Comma>()?;
24        let start = input.parse::<LitInt>()?.base10_parse()?;
25        input.parse::<Comma>()?;
26        let end = input.parse::<LitInt>()?.base10_parse()?;
27        input.parse::<Comma>()?;
28        let mut idents = vec![input.parse::<Ident>()?];
29        while input.parse::<Comma>().is_ok() {
30            idents.push(input.parse::<Ident>()?);
31        }
32
33        Ok(AllTuples {
34            macro_ident,
35            start,
36            end,
37            idents,
38        })
39    }
40}
41
42/// Helper macro to generate tuple pyramids. Useful to generate scaffolding to work around Rust
43/// lacking variadics. Invoking `all_tuples!(impl_foo, start, end, P, Q, ..)`
44/// invokes `impl_foo` providing ident tuples through arity `start..=end`.
45/// # Examples
46/// A single parameter.
47/// ```
48/// use std::marker::PhantomData;
49/// use bevy_utils_proc_macros::all_tuples;
50///
51/// struct Foo<T> {
52///     // ..
53///     _phantom: PhantomData<T>
54/// }
55///
56/// trait WrappedInFoo {
57///     type Tup;
58/// }
59///
60/// macro_rules! impl_wrapped_in_foo {
61///     ($($T:ident),*) => {
62///         impl<$($T),*> WrappedInFoo for ($($T,)*) {
63///             type Tup = ($(Foo<$T>,)*);
64///         }
65///     };
66/// }
67///
68/// all_tuples!(impl_wrapped_in_foo, 0, 15, T);
69/// // impl_wrapped_in_foo!();
70/// // impl_wrapped_in_foo!(P0);
71/// // impl_wrapped_in_foo!(P0, P1);
72/// // ..
73/// // impl_wrapped_in_foo!(P0 .. P14);
74/// ```
75/// Multiple parameters.
76/// ```
77/// use bevy_utils_proc_macros::all_tuples;
78///
79/// trait Append {
80///     type Out<Item>;
81///     fn append<Item>(tup: Self, item: Item) -> Self::Out<Item>;
82/// }
83///
84/// impl Append for () {
85///     type Out<Item> = (Item,);
86///     fn append<Item>(_: Self, item: Item) -> Self::Out<Item> {
87///         (item,)
88///     }
89/// }
90///
91/// macro_rules! impl_append {
92///     ($(($P:ident, $p:ident)),*) => {
93///         impl<$($P),*> Append for ($($P,)*) {
94///             type Out<Item> = ($($P),*, Item);
95///             fn append<Item>(($($p,)*): Self, item: Item) -> Self::Out<Item> {
96///                 ($($p),*, item)
97///             }
98///         }
99///     }
100/// }
101///
102/// all_tuples!(impl_append, 1, 15, P, p);
103/// // impl_append!((P0, p0));
104/// // impl_append!((P0, p0), (P1, p1));
105/// // impl_append!((P0, p0), (P1, p1), (P2, p2));
106/// // ..
107/// // impl_append!((P0, p0) .. (P14, p14));
108/// ````
109#[proc_macro]
110pub fn all_tuples(input: TokenStream) -> TokenStream {
111    let input = parse_macro_input!(input as AllTuples);
112    let len = 1 + input.end - input.start;
113    let mut ident_tuples = Vec::with_capacity(len);
114    for i in 0..=len {
115        let idents = input
116            .idents
117            .iter()
118            .map(|ident| format_ident!("{}{}", ident, i));
119        if input.idents.len() < 2 {
120            ident_tuples.push(quote! {
121                #(#idents)*
122            });
123        } else {
124            ident_tuples.push(quote! {
125                (#(#idents),*)
126            });
127        }
128    }
129
130    let macro_ident = &input.macro_ident;
131    let invocations = (input.start..=input.end).map(|i| {
132        let ident_tuples = &ident_tuples[..i];
133        quote! {
134            #macro_ident!(#(#ident_tuples),*);
135        }
136    });
137    TokenStream::from(quote! {
138        #(
139            #invocations
140        )*
141    })
142}
143
144#[proc_macro]
145pub fn all_tuples_with_size(input: TokenStream) -> TokenStream {
146    let input = parse_macro_input!(input as AllTuples);
147    let len = 1 + input.end - input.start;
148    let mut ident_tuples = Vec::with_capacity(len);
149    for i in 0..=len {
150        let idents = input
151            .idents
152            .iter()
153            .map(|ident| format_ident!("{}{}", ident, i));
154        if input.idents.len() < 2 {
155            ident_tuples.push(quote! {
156                #(#idents)*
157            });
158        } else {
159            ident_tuples.push(quote! {
160                (#(#idents),*)
161            });
162        }
163    }
164
165    let macro_ident = &input.macro_ident;
166    let invocations = (input.start..=input.end).map(|i| {
167        let ident_tuples = &ident_tuples[..i];
168        quote! {
169            #macro_ident!(#i, #(#ident_tuples),*);
170        }
171    });
172    TokenStream::from(quote! {
173        #(
174            #invocations
175        )*
176    })
177}