auto_tuple_traits/lib.rs
1/*!
2[](https://docs.rs/auto-tuple-traits/) [](https://crates.io/crates/auto-tuple-traits) [](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}