impl_trait_for_tuples/
lib.rs

1/*!
2[![](https://docs.rs/impl-trait-for-tuples/badge.svg)](https://docs.rs/impl-trait-for-tuples/) [![](https://img.shields.io/crates/v/impl-trait-for-tuples.svg)](https://crates.io/crates/impl-trait-for-tuples) [![](https://img.shields.io/crates/d/impl-trait-for-tuples.png)](https://crates.io/crates/impl-trait-for-tuples)
3
4Attribute macro to implement a trait for tuples
5
6* [Introduction](#introduction)
7* [Syntax](#syntax)
8* [Limitations](#limitations)
9* [Example](#example)
10* [License](#license)
11
12## Introduction
13
14When wanting to implement a trait for combinations of tuples, Rust requires the trait to be implemented
15for each combination manually. With this crate you just need to place `#[impl_for_tuples(5)]` above
16your trait declaration (in full-automatic mode) to implement the trait for the tuple combinations
17`(), (T0), (T0, T1), (T0, T1, T2), (T0, T1, T2, T3), (T0, T1, T2, T3, T4, T5)`. The number of tuples is the
18parameter given to the attribute and can be chosen freely.
19
20This crate provides two modes full-automatic and semi-automatic. The full-automatic mode just requires
21the trait definition to implement the trait for the tuple combinations. While being much easier to
22use, it also comes with some restrictions like no associated types, no return values or no associated
23consts. To support these, the semi-automatic mode is provided. This mode requires a dummy implementation
24block of the trait that is expanded to all the tuple combinations implementations. To express the
25tuple access in this dummy implementation a special syntax is required `for_tuples!( #( Tuple::function(); )* )`.
26This would expand to `Tuple::function();` for each tuple while `Tuple` is chosen by the user and will be
27replaced by the corresponding tuple identifier per iteration.
28
29## Syntax
30
31The attribute macro can be called with one `#[impl_for_tuples(5)]` or with two `#[impl_for_tuples(2, 5)]`
32parameters. The former instructs the macro to generate up to a tuple of five elements and the later instructs it
33to generate from a tuple with two element up to five elements.
34
35### Semi-automatic syntax
36
37```
38# use impl_trait_for_tuples::impl_for_tuples;
39trait Trait {
40    type Ret;
41    type Arg;
42    type FixedType;
43    const VALUE: u32;
44
45    fn test(arg: Self::Arg) -> Self::Ret;
46
47    fn test_with_self(&self) -> Result<(), ()>;
48}
49
50#[impl_for_tuples(1, 5)]
51impl Trait for Tuple {
52    // Here we expand the `Ret` and `Arg` associated types.
53    for_tuples!( type Ret = ( #( Tuple::Ret ),* ); );
54    for_tuples!( type Arg = ( #( Tuple::Arg ),* ); );
55    for_tuples!( const VALUE: u32 = #( Tuple::VALUE )+*; );
56
57    // Here we set the `FixedType` to `u32` and add a custom where bound that forces the same
58    // `FixedType` for all tuple types.
59    type FixedType = u32;
60    for_tuples!( where #( Tuple: Trait<FixedType=u32> )* );
61
62    fn test(arg: Self::Arg) -> Self::Ret {
63        for_tuples!( ( #( Tuple::test(arg.Tuple) ),* ) )
64    }
65
66    fn test_with_self(&self) -> Result<(), ()> {
67        for_tuples!( #( Tuple.test_with_self()?; )* );
68        Ok(())
69    }
70}
71
72# fn main() {}
73```
74
75The given example shows all supported combinations of `for_tuples!`. When accessing a method from the
76`self` tuple instance, `self.Tuple` is the required syntax and is replaced by `self.0`, `self.1`, etc.
77The placeholder tuple identifer is taken from the self type given to the implementation block. So, it
78is up to the user to chose any valid identifier.
79
80The separator given to `#( Tuple::something() )SEPARATOR*` can be chosen from `,`, `+`, `-`,
81`*`, `/`, `|`, `&` or nothing for no separator.
82
83By adding the `#[tuple_types_no_default_trait_bound]` above the impl block, the macro will not add the
84automatic bound to the implemented trait for each tuple type.
85
86The trait bound can be customized using `#[tuple_types_custom_trait_bound(NewBound)]`.
87The new bound will be used instead of the impleted trait for each tuple type.
88
89## Limitations
90
91The macro does not supports `for_tuples!` calls in a different macro, so stuff like
92`vec![ for_tuples!( bla ) ]` will generate invalid code.
93
94## Example
95
96### Full-automatic
97
98```
99# use impl_trait_for_tuples::impl_for_tuples;
100#[impl_for_tuples(5)]
101trait Notify {
102    fn notify(&self);
103}
104
105# fn main() {}
106```
107
108### Semi-automatic
109
110```
111# use impl_trait_for_tuples::impl_for_tuples;
112trait Notify {
113    fn notify(&self) -> Result<(), ()>;
114}
115
116#[impl_for_tuples(5)]
117impl Notify for TupleIdentifier {
118    fn notify(&self) -> Result<(), ()> {
119        for_tuples!( #( TupleIdentifier.notify()?; )* );
120        Ok(())
121    }
122}
123
124# fn main() {}
125```
126
127## License
128Licensed under either of
129 * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
130 * [MIT license](http://opensource.org/licenses/MIT)
131at your option.
132*/
133
134extern crate proc_macro;
135
136use proc_macro2::{Span, TokenStream};
137
138use syn::{
139    parse::{Parse, ParseStream},
140    parse_macro_input,
141    punctuated::Punctuated,
142    token, Attribute, Error, Ident, ItemImpl, ItemTrait, LitInt, Result,
143};
144
145mod full_automatic;
146mod semi_automatic;
147mod utils;
148
149/// Enum to parse the input and to distinguish between full/semi-automatic mode.
150enum FullOrSemiAutomatic {
151    /// Full-automatic trait implementation for tuples uses the trait definition.
152    Full(ItemTrait),
153    /// Sem-automatic trait implementation for tuples uses a trait implementation.
154    Semi(ItemImpl),
155}
156
157impl Parse for FullOrSemiAutomatic {
158    fn parse(input: ParseStream) -> Result<Self> {
159        // We need to parse any attributes first, before we know what we actually can parse.
160        let fork = input.fork();
161        fork.call(Attribute::parse_outer)?;
162
163        // If there is a `unsafe` in front of, just skip it.
164        if fork.peek(token::Unsafe) {
165            fork.parse::<token::Unsafe>()?;
166        }
167
168        let lookahead1 = fork.lookahead1();
169
170        if lookahead1.peek(token::Impl) {
171            Ok(Self::Semi(input.parse()?))
172        } else if lookahead1.peek(token::Trait) || lookahead1.peek(token::Pub) {
173            Ok(Self::Full(input.parse()?))
174        } else {
175            Err(lookahead1.error())
176        }
177    }
178}
179
180/// The minimum and maximum given as two `LitInt`'s to the macro as arguments.
181struct MinMax {
182    min: Option<usize>,
183    max: usize,
184}
185
186impl Parse for MinMax {
187    fn parse(input: ParseStream) -> Result<Self> {
188        let args = Punctuated::<LitInt, token::Comma>::parse_terminated(input)?;
189
190        if args.is_empty() {
191            Err(Error::new(
192                Span::call_site(),
193                "Expected at least one argument to the macro!",
194            ))
195        } else if args.len() == 1 {
196            Ok(Self {
197                max: args[0].base10_parse()?,
198                min: None,
199            })
200        } else if args.len() == 2 {
201            let min = args[0].base10_parse()?;
202            let max = args[1].base10_parse()?;
203
204            if min >= max {
205                Err(Error::new(
206                    Span::call_site(),
207                    "It is expected that `min` comes before `max` and that `max > min` is true!",
208                ))
209            } else {
210                Ok(Self {
211                    min: Some(min),
212                    max,
213                })
214            }
215        } else {
216            Err(Error::new(
217                Span::call_site(),
218                "Too many arguments given to the macro!",
219            ))
220        }
221    }
222}
223
224/// See [crate](index.html) documentation.
225#[proc_macro_attribute]
226pub fn impl_for_tuples(
227    args: proc_macro::TokenStream,
228    input: proc_macro::TokenStream,
229) -> proc_macro::TokenStream {
230    let input = parse_macro_input!(input as FullOrSemiAutomatic);
231    let min_max = parse_macro_input!(args as MinMax);
232
233    impl_for_tuples_impl(input, min_max)
234        .unwrap_or_else(|e| e.to_compile_error())
235        .into()
236}
237
238fn impl_for_tuples_impl(input: FullOrSemiAutomatic, min_max: MinMax) -> Result<TokenStream> {
239    let tuple_elements = (0usize..min_max.max)
240        .map(|i| generate_tuple_element_ident(i))
241        .collect::<Vec<_>>();
242
243    match input {
244        FullOrSemiAutomatic::Full(definition) => {
245            full_automatic::full_automatic_impl(definition, tuple_elements, min_max.min)
246        }
247        FullOrSemiAutomatic::Semi(trait_impl) => {
248            semi_automatic::semi_automatic_impl(trait_impl, tuple_elements, min_max.min)
249        }
250    }
251}
252
253fn generate_tuple_element_ident(num: usize) -> Ident {
254    Ident::new(&format!("TupleElement{}", num), Span::call_site())
255}