Skip to content

Commit ead8d19

Browse files
Merge pull request #2 from mweatherley/enumerating
Add enumerating version of `all_tuples`
2 parents 338e8c4 + 9a87c68 commit ead8d19

File tree

1 file changed

+96
-1
lines changed

1 file changed

+96
-1
lines changed

src/lib.rs

+96-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
44

55
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};
77
use quote::{format_ident, quote};
88
use syn::{
99
parse::{Parse, ParseStream},
@@ -191,6 +191,81 @@ pub fn all_tuples(input: TokenStream) -> TokenStream {
191191
})
192192
}
193193

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+
194269
/// Helper macro to generate tuple pyramids with their length. Useful to generate scaffolding to
195270
/// work around Rust lacking variadics. Invoking `all_tuples_with_size!(impl_foo, start, end, P, Q, ..)`
196271
/// 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
372447
}
373448
}
374449

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+
375464
fn to_ident_tuple(idents: impl Iterator<Item = Ident>, len: usize) -> TokenStream2 {
376465
if len < 2 {
377466
quote! { #(#idents)* }
@@ -380,6 +469,12 @@ fn to_ident_tuple(idents: impl Iterator<Item = Ident>, len: usize) -> TokenStrea
380469
}
381470
}
382471

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+
383478
fn fake_variadic_attrs(len: usize, i: usize) -> TokenStream2 {
384479
let cfg = quote! { any(docsrs, docsrs_dep) };
385480
match i {

0 commit comments

Comments
 (0)