impl_trait_for_tuples/
full_automatic.rs1use crate::utils::add_tuple_element_generics;
8
9use proc_macro2::{Span, TokenStream};
10
11use std::iter::repeat;
12
13use syn::{
14 parse_quote,
15 spanned::Spanned,
16 visit::{self, Visit},
17 Error, FnArg, Generics, Ident, Index, ItemTrait, Pat, Result, ReturnType, Signature, TraitItem,
18 TraitItemMethod, Type,
19};
20
21use quote::quote;
22
23pub fn full_automatic_impl(
25 definition: ItemTrait,
26 tuple_elements: Vec<Ident>,
27 min: Option<usize>,
28) -> Result<TokenStream> {
29 check_trait_declaration(&definition)?;
30
31 let impls = (min.unwrap_or(0)..=tuple_elements.len())
32 .map(|i| generate_tuple_impl(&definition, &tuple_elements[0..i]));
33
34 Ok(quote!(
35 #definition
36 #( #impls )*
37 ))
38}
39
40fn check_trait_declaration(trait_decl: &ItemTrait) -> Result<()> {
41 let mut visitor = CheckTraitDeclaration { errors: Vec::new() };
42 visit::visit_item_trait(&mut visitor, trait_decl);
43
44 match visitor.errors.pop() {
45 Some(init) => Err(visitor.errors.into_iter().fold(init, |mut old, new| {
46 old.combine(new);
47 old
48 })),
49 None => Ok(()),
50 }
51}
52
53struct CheckTraitDeclaration {
55 errors: Vec<Error>,
57}
58
59impl CheckTraitDeclaration {
60 fn add_error<T: Spanned>(&mut self, span: &T) {
61 self.errors.push(Error::new(span.span(), CHECK_ERROR_MSG));
62 }
63}
64
65const CHECK_ERROR_MSG: &str =
66 "Not supported by full-automatic tuple implementation. \
67 Use semi-automatic tuple implementation for more control of the implementation.";
68
69impl<'ast> Visit<'ast> for CheckTraitDeclaration {
70 fn visit_trait_item(&mut self, ti: &'ast TraitItem) {
71 match ti {
72 TraitItem::Method(m) => visit::visit_trait_item_method(self, m),
73 _ => self.add_error(ti),
74 }
75 }
76
77 fn visit_return_type(&mut self, rt: &'ast ReturnType) {
78 match rt {
79 ReturnType::Default => {}
80 ReturnType::Type(_, ty) => match **ty {
81 Type::Tuple(ref tuple) if tuple.elems.is_empty() => {}
82 _ => self.add_error(rt),
83 },
84 }
85 }
86}
87
88fn generate_tuple_impl(definition: &ItemTrait, tuple_elements: &[Ident]) -> TokenStream {
89 let name = &definition.ident;
90 let unsafety = &definition.unsafety;
91 let generics = generate_generics(definition, tuple_elements);
92 let ty_generics = definition.generics.split_for_impl().1;
93 let (impl_generics, _, where_clause) = generics.split_for_impl();
94 let fns = definition.items.iter().filter_map(|i| match i {
95 TraitItem::Method(method) => Some(generate_delegate_method(method, tuple_elements)),
96 _ => None,
97 });
98
99 quote!(
100 #[allow(unused)]
101 #unsafety impl #impl_generics #name #ty_generics for ( #( #tuple_elements, )* ) #where_clause {
102 #( #fns )*
103 }
104 )
105}
106
107struct CollectNonReferenceArgTypes {
109 result: Vec<Type>,
110}
111
112impl<'ast> Visit<'ast> for CollectNonReferenceArgTypes {
113 fn visit_type(&mut self, ty: &'ast Type) {
114 if !is_reference_type(ty) {
115 self.result.push(ty.clone());
116 }
117 }
118}
119
120fn generate_generics(definition: &ItemTrait, tuple_elements: &[Ident]) -> Generics {
121 let mut generics = definition.generics.clone();
122 let name = &definition.ident;
123 let ty_generics = definition.generics.split_for_impl().1;
124
125 let mut visitor = CollectNonReferenceArgTypes { result: Vec::new() };
127 definition
128 .items
129 .iter()
130 .for_each(|item| visit::visit_trait_item(&mut visitor, item));
131 visitor.result.dedup();
132 {
133 let where_clause = generics.make_where_clause();
134 visitor
135 .result
136 .into_iter()
137 .for_each(|ty| where_clause.predicates.push(parse_quote!(#ty: Clone)));
138 }
139
140 add_tuple_element_generics(
141 tuple_elements,
142 Some(quote!(#name #ty_generics)),
143 &mut generics,
144 );
145
146 generics
147}
148
149fn generate_delegate_method(method: &TraitItemMethod, tuple_elements: &[Ident]) -> TokenStream {
150 let name = repeat(&method.sig.ident);
151 let self_arg = method
152 .sig
153 .inputs
154 .first()
155 .map(|a| match a {
156 FnArg::Receiver(_) => true,
157 _ => false,
158 })
159 .unwrap_or(false);
160 let (sig, arg_names, arg_clones) = update_signature_and_extract_arg_infos(method.sig.clone());
161 let arg_names_per_tuple = repeat(&arg_names);
162 let arg_clones_per_tuple = repeat(&arg_clones);
163 let tuple_access = tuple_elements.iter().enumerate().map(|(i, te)| {
164 if self_arg {
165 let index = Index::from(i);
166 quote!(self.#index.)
167 } else {
168 quote!(#te::)
169 }
170 });
171
172 let mut res = method.clone();
173 res.sig = sig;
174 res.semi_token = None;
175 res.default = Some(parse_quote!(
176 { #( #tuple_access #name ( #( #arg_names_per_tuple #arg_clones_per_tuple ),* ); )* }
177 ));
178
179 quote!( #res )
180}
181
182fn update_signature_and_extract_arg_infos(
185 mut sig: Signature,
186) -> (Signature, Vec<Ident>, Vec<TokenStream>) {
187 let mut unique_id = 0;
188 let mut arg_clone = Vec::new();
189 let mut push_arg_clone = |is_ref: bool| {
190 if is_ref {
191 arg_clone.push(quote!())
192 } else {
193 arg_clone.push(quote!(.clone()))
194 }
195 };
196
197 let arg_names = sig
198 .inputs
199 .iter_mut()
200 .filter_map(|a| match a {
201 FnArg::Typed(ref mut arg) => match &mut *arg.pat {
202 Pat::Ident(p) => {
203 push_arg_clone(is_reference_type(&arg.ty));
204 Some(p.ident.clone())
205 }
206 Pat::Wild(_) => {
207 push_arg_clone(is_reference_type(&arg.ty));
208 let ident = Ident::new(
209 &format!("tuple_element_unique_ident_{}", unique_id),
210 Span::call_site(),
211 );
212 unique_id += 1;
213 let ident2 = ident.clone();
214
215 arg.pat = parse_quote!(#ident);
216 Some(ident2)
217 }
218 _ => None,
219 },
220 _ => None,
221 })
222 .collect::<Vec<_>>();
223
224 (sig, arg_names, arg_clone)
225}
226
227fn is_reference_type(ty: &Type) -> bool {
228 match ty {
229 Type::Reference(_) => true,
230 _ => false,
231 }
232}