3
3
#![ cfg_attr( docsrs, feature( doc_auto_cfg) ) ]
4
4
5
5
use proc_macro:: TokenStream ;
6
- use proc_macro2:: { Span as Span2 , TokenStream as TokenStream2 } ;
6
+ use proc_macro2:: { Literal , Span as Span2 , TokenStream as TokenStream2 } ;
7
7
use quote:: { format_ident, quote} ;
8
8
use syn:: {
9
9
parse:: { Parse , ParseStream } ,
@@ -191,6 +191,81 @@ pub fn all_tuples(input: TokenStream) -> TokenStream {
191
191
} )
192
192
}
193
193
194
+ /// A variant of [`all_tuples!`] that enumerates its output.
195
+ ///
196
+ /// In particular, the tuples used by the inner macro will themselves be composed
197
+ /// of tuples which contain the index.
198
+ ///
199
+ /// For example, with a single parameter:
200
+ /// ```
201
+ /// # use variadics_please::all_tuples_enumerated;
202
+ ///
203
+ /// trait Squawk {
204
+ /// fn squawk(&self);
205
+ /// }
206
+ ///
207
+ /// // If every type in a tuple is `Squawk`, the tuple can squawk by having its
208
+ /// // constituents squawk sequentially:
209
+ /// macro_rules! impl_squawk {
210
+ /// ($(($n:tt, $T:ident)),*) => {
211
+ /// impl<$($T: Squawk),*> Squawk for ($($T,)*) {
212
+ /// fn squawk(&self) {
213
+ /// $(
214
+ /// self.$n.squawk();
215
+ /// )*
216
+ /// }
217
+ /// }
218
+ /// };
219
+ /// }
220
+ ///
221
+ /// all_tuples_enumerated!(impl_squawk, 1, 15, T);
222
+ /// // impl_squawk!((0, T0));
223
+ /// // impl_squawk!((0, T0), (1, T1));
224
+ /// // ..
225
+ /// // impl_squawk!((0, T0) .. (14, T14));
226
+ /// ```
227
+ ///
228
+ /// With multiple parameters, the result is similar, but with the additional parameters
229
+ /// included in each tuple; e.g.:
230
+ /// ```ignore
231
+ /// all_tuples_enumerated!(impl_squawk, 1, 15, P, p);
232
+ /// // impl_squawk!((0, P0, p0));
233
+ /// // impl_squawk!((0, P0, p0), (1, P1, p1));
234
+ /// // ..
235
+ /// // impl_squawk!((0, P0, p0) .. (14, P14, p14));
236
+ /// ```
237
+ #[ proc_macro]
238
+ pub fn all_tuples_enumerated ( input : TokenStream ) -> TokenStream {
239
+ let input = parse_macro_input ! ( input as AllTuples ) ;
240
+ let len = 1 + input. end - input. start ;
241
+ let mut ident_tuples = Vec :: with_capacity ( len) ;
242
+ for i in 0 ..=len {
243
+ let idents = input
244
+ . idents
245
+ . iter ( )
246
+ . map ( |ident| format_ident ! ( "{}{}" , ident, i) ) ;
247
+ ident_tuples. push ( to_ident_tuple_enumerated ( idents, i) ) ;
248
+ }
249
+
250
+ let macro_ident = & input. macro_ident ;
251
+ let invocations = ( input. start ..=input. end ) . map ( |i| {
252
+ let ident_tuples = choose_ident_tuples_enumerated ( & input, & ident_tuples, i) ;
253
+ let attrs = if input. fake_variadic {
254
+ fake_variadic_attrs ( len, i)
255
+ } else {
256
+ TokenStream2 :: default ( )
257
+ } ;
258
+ quote ! {
259
+ #macro_ident!( #attrs #ident_tuples) ;
260
+ }
261
+ } ) ;
262
+ TokenStream :: from ( quote ! {
263
+ #(
264
+ #invocations
265
+ ) *
266
+ } )
267
+ }
268
+
194
269
/// Helper macro to generate tuple pyramids with their length. Useful to generate scaffolding to
195
270
/// work around Rust lacking variadics. Invoking `all_tuples_with_size!(impl_foo, start, end, P, Q, ..)`
196
271
/// invokes `impl_foo` providing ident tuples through arity `start..=end` preceded by their length.
@@ -372,6 +447,20 @@ fn choose_ident_tuples(input: &AllTuples, ident_tuples: &[TokenStream2], i: usiz
372
447
}
373
448
}
374
449
450
+ fn choose_ident_tuples_enumerated (
451
+ input : & AllTuples ,
452
+ ident_tuples : & [ TokenStream2 ] ,
453
+ i : usize ,
454
+ ) -> TokenStream2 {
455
+ if input. fake_variadic && i == 1 {
456
+ let ident_tuple = to_ident_tuple_enumerated ( input. idents . iter ( ) . cloned ( ) , 0 ) ;
457
+ quote ! { #ident_tuple }
458
+ } else {
459
+ let ident_tuples = & ident_tuples[ ..i] ;
460
+ quote ! { #( #ident_tuples) , * }
461
+ }
462
+ }
463
+
375
464
fn to_ident_tuple ( idents : impl Iterator < Item = Ident > , len : usize ) -> TokenStream2 {
376
465
if len < 2 {
377
466
quote ! { #( #idents) * }
@@ -380,6 +469,12 @@ fn to_ident_tuple(idents: impl Iterator<Item = Ident>, len: usize) -> TokenStrea
380
469
}
381
470
}
382
471
472
+ /// Like `to_ident_tuple`, but it enumerates the identifiers
473
+ fn to_ident_tuple_enumerated ( idents : impl Iterator < Item = Ident > , idx : usize ) -> TokenStream2 {
474
+ let idx = Literal :: usize_unsuffixed ( idx) ;
475
+ quote ! { ( #idx, #( #idents) , * ) }
476
+ }
477
+
383
478
fn fake_variadic_attrs ( len : usize , i : usize ) -> TokenStream2 {
384
479
let cfg = quote ! { any( docsrs, docsrs_dep) } ;
385
480
match i {
0 commit comments