Skip to main content

auto_tuple_traits/
lib.rs

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