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}