1 #![allow(dead_code)] // TODO: remove
2 
3 // This is inspired from `synstructure`, but `synstructure` is not adapted in severals ways
4 // including:
5 //     * `&mut` everywhere
6 //     * not generic, we use our own `ast`, `synstructure` only knows about `syn`
7 //     * missing information (what arm are we in?, what attributes? etc.)
8 
9 use proc_macro2::{self, TokenStream};
10 use quote::ToTokens;
11 use syn;
12 
13 use ast;
14 use attr;
15 use quote;
16 
17 /// The type of binding to use when generating a pattern.
18 #[derive(Debug, Copy, Clone)]
19 pub enum BindingStyle {
20     /// `x`
21     Move,
22     /// `mut x`
23     MoveMut,
24     /// `ref x`
25     Ref,
26     /// `ref mut x`
27     RefMut,
28 }
29 
30 impl BindingStyle {
with_packed(self, is_packed: bool) -> BindingStyle31     fn with_packed(self, is_packed: bool) -> BindingStyle {
32         match self {
33             BindingStyle::Move | BindingStyle::MoveMut => self,
34             BindingStyle::Ref if is_packed => BindingStyle::Move,
35             BindingStyle::RefMut if is_packed => BindingStyle::MoveMut,
36             BindingStyle::Ref | BindingStyle::RefMut => self,
37         }
38     }
39 }
40 
41 impl quote::ToTokens for BindingStyle {
to_tokens(&self, tokens: &mut TokenStream)42     fn to_tokens(&self, tokens: &mut TokenStream) {
43         match *self {
44             BindingStyle::Move => (),
45             BindingStyle::MoveMut => tokens.extend(quote!(mut)),
46             BindingStyle::Ref => tokens.extend(quote!(ref)),
47             BindingStyle::RefMut => {
48                 tokens.extend(quote!(ref mut));
49             }
50         }
51     }
52 }
53 
54 #[derive(Debug)]
55 pub struct BindingInfo<'a> {
56     pub expr: TokenStream,
57     pub ident: syn::Ident,
58     pub field: &'a ast::Field<'a>,
59 }
60 
61 #[derive(Debug)]
62 pub struct CommonVariant<'a> {
63     path: syn::Path,
64     name: &'a syn::Ident,
65     style: ast::Style,
66     attrs: &'a attr::Input,
67 }
68 
69 pub struct Matcher<T> {
70     binding_name: String,
71     binding_style: BindingStyle,
72     is_packed: bool,
73     field_filter: T,
74 }
75 
76 impl Matcher<fn (&ast::Field) -> bool> {
new(style: BindingStyle, is_packed: bool) -> Self77     pub fn new(style: BindingStyle, is_packed: bool) -> Self {
78         Matcher {
79             binding_name: "__arg".into(),
80             binding_style: style.with_packed(is_packed),
81             is_packed,
82             field_filter: |_| true,
83         }
84     }
85 }
86 
87 impl<T: Fn (&ast::Field) -> bool> Matcher<T> {
with_name(self, name: String) -> Self88     pub fn with_name(self, name: String) -> Self {
89         Matcher {
90             binding_name: name,
91             ..self
92         }
93     }
94 
with_field_filter<P>(self, field_filter: P) -> Matcher<P>95     pub fn with_field_filter<P>(self, field_filter: P) -> Matcher<P> {
96         Matcher {
97             field_filter,
98             binding_name: self.binding_name,
99             binding_style: self.binding_style,
100             is_packed: self.is_packed,
101         }
102     }
103 
build_arms<F>(self, input: &ast::Input, binding_name: &str, f: F) -> TokenStream where F: Fn( syn::Path, usize, &syn::Ident, ast::Style, &attr::Input, Vec<BindingInfo>, ) -> TokenStream,104     pub fn build_arms<F>(self, input: &ast::Input, binding_name: &str, f: F) -> TokenStream
105     where
106         F: Fn(
107             syn::Path,
108             usize,
109             &syn::Ident,
110             ast::Style,
111             &attr::Input,
112             Vec<BindingInfo>,
113         ) -> TokenStream,
114     {
115         let variants = self.build_match_pattern(input, binding_name);
116 
117         // Now that we have the patterns, generate the actual branches of the match
118         // expression
119         let mut t = TokenStream::new();
120         for (i, (variant, (pat, bindings))) in variants.into_iter().enumerate() {
121             let body = f(
122                 variant.path,
123                 i,
124                 variant.name,
125                 variant.style,
126                 variant.attrs,
127                 bindings,
128             );
129             quote!(#pat => { #body }).to_tokens(&mut t);
130         }
131 
132         t
133     }
134 
build_2_arms<F>( self, (left_matched_expr, right_matched_expr): (TokenStream, TokenStream), left: (&ast::Input, &str), right: (&ast::Input, &str), f: F, ) -> TokenStream where F: Fn( usize, CommonVariant, CommonVariant, (Vec<BindingInfo>, Vec<BindingInfo>), ) -> TokenStream,135     pub fn build_2_arms<F>(
136         self,
137         (left_matched_expr, right_matched_expr): (TokenStream, TokenStream),
138         left: (&ast::Input, &str),
139         right: (&ast::Input, &str),
140         f: F,
141     ) -> TokenStream
142     where
143         F: Fn(
144             usize,
145             CommonVariant,
146             CommonVariant,
147             (Vec<BindingInfo>, Vec<BindingInfo>),
148         ) -> TokenStream,
149     {
150         let left_variants = self.build_match_pattern(left.0, left.1);
151         let right_variants = self.build_match_pattern(right.0, right.1);
152 
153         assert_eq!(left_variants.len(), right_variants.len());
154 
155         if left_variants.len() == 1 {
156             let (left, (left_pat, left_bindings)) = left_variants.into_iter().next().unwrap();
157             let (right, (right_pat, right_bindings)) = right_variants.into_iter().next().unwrap();
158 
159             let body = f(0, left, right, (left_bindings, right_bindings));
160 
161             quote! {
162                 match #left_matched_expr {
163                     #left_pat => match #right_matched_expr {
164                         #right_pat => #body,
165                     },
166                 }
167             }
168         } else {
169             // Now that we have the patterns, generate the actual branches of the match
170             // expression
171             let mut t = TokenStream::new();
172             for (i, (left, right)) in left_variants.into_iter().zip(right_variants).enumerate() {
173                 let (left, (left_pat, left_bindings)) = left;
174                 let (right, (right_pat, right_bindings)) = right;
175 
176                 let body = f(i, left, right, (left_bindings, right_bindings));
177                 quote!((#left_pat, #right_pat) => { #body }).to_tokens(&mut t);
178             }
179 
180             quote! {
181                 match (&#left_matched_expr, &#right_matched_expr) {
182                     #t
183                     _ => unreachable!(),
184                 }
185             }
186         }
187     }
188 
189     /// Generate patterns for matching against all of the variants
build_match_pattern<'a>( &self, input: &'a ast::Input, binding_name: &str, ) -> Vec<(CommonVariant<'a>, (TokenStream, Vec<BindingInfo<'a>>))>190     pub fn build_match_pattern<'a>(
191         &self,
192         input: &'a ast::Input,
193         binding_name: &str,
194     ) -> Vec<(CommonVariant<'a>, (TokenStream, Vec<BindingInfo<'a>>))> {
195         let ident = &input.ident;
196 
197         match input.body {
198             ast::Body::Enum(ref variants) => variants
199                 .iter()
200                 .map(|variant| {
201                     let variant_ident = &variant.ident;
202                     let path = parse_quote!(#ident::#variant_ident);
203 
204                     let pat = self.build_match_pattern_impl(
205                         &path,
206                         variant.style,
207                         &variant.fields,
208                         binding_name,
209                     );
210 
211                     (
212                         CommonVariant {
213                             path,
214                             name: variant_ident,
215                             style: variant.style,
216                             attrs: &variant.attrs,
217                         },
218                         pat,
219                     )
220                 })
221                 .collect(),
222             ast::Body::Struct(style, ref vd) => {
223                 let path = parse_quote!(#ident);
224                 vec![(
225                     CommonVariant {
226                         path,
227                         name: ident,
228                         style,
229                         attrs: &input.attrs,
230                     },
231                     self.build_match_pattern_impl(ident, style, vd, binding_name),
232                 )]
233             }
234         }
235     }
236 
build_match_pattern_impl<'a, N>( &self, name: &N, style: ast::Style, fields: &'a [ast::Field<'a>], binding_name: &str, ) -> (TokenStream, Vec<BindingInfo<'a>>) where N: quote::ToTokens,237     fn build_match_pattern_impl<'a, N>(
238         &self,
239         name: &N,
240         style: ast::Style,
241         fields: &'a [ast::Field<'a>],
242         binding_name: &str,
243     ) -> (TokenStream, Vec<BindingInfo<'a>>)
244     where
245         N: quote::ToTokens,
246     {
247         let (stream, matches) = match style {
248             ast::Style::Unit => (TokenStream::new(), Vec::new()),
249             ast::Style::Tuple => {
250                 let (stream, matches) = fields.iter().enumerate().fold(
251                     (TokenStream::new(), Vec::new()),
252                     |(stream, matches), field| {
253                         self.build_inner_pattern(
254                             (stream, matches),
255                             field,
256                             binding_name,
257                             |f, ident, binding| {
258                                 if (self.field_filter)(f) {
259                                     quote!(#binding #ident ,)
260                                 } else {
261                                     quote!(_ ,)
262                                 }
263                             },
264                         )
265                     },
266                 );
267 
268                 (quote! { ( #stream ) }, matches)
269             }
270             ast::Style::Struct => {
271                 let (stream, matches) = fields.iter().enumerate().fold(
272                     (TokenStream::new(), Vec::new()),
273                     |(stream, matches), field| {
274                         self.build_inner_pattern(
275                             (stream, matches),
276                             field,
277                             binding_name,
278                             |field, ident, binding| {
279                                 let field_name = field.ident.as_ref().unwrap();
280                                 if (self.field_filter)(field) {
281                                     quote!(#field_name : #binding #ident ,)
282                                 } else {
283                                     quote!(#field_name : _ ,)
284                                 }
285                             },
286                         )
287                     },
288                 );
289 
290                 (quote! { { #stream } }, matches)
291             }
292         };
293 
294         let mut all_tokens = TokenStream::new();
295         name.to_tokens(&mut all_tokens);
296         all_tokens.extend(stream);
297 
298         (all_tokens, matches)
299     }
300 
build_inner_pattern<'a>( &self, (mut stream, mut matches): (TokenStream, Vec<BindingInfo<'a>>), (i, field): (usize, &'a ast::Field), binding_name: &str, f: impl FnOnce(&ast::Field, &syn::Ident, BindingStyle) -> TokenStream, ) -> (TokenStream, Vec<BindingInfo<'a>>)301     fn build_inner_pattern<'a>(
302         &self,
303         (mut stream, mut matches): (TokenStream, Vec<BindingInfo<'a>>),
304         (i, field): (usize, &'a ast::Field),
305         binding_name: &str,
306         f: impl FnOnce(&ast::Field, &syn::Ident, BindingStyle) -> TokenStream,
307     ) -> (TokenStream, Vec<BindingInfo<'a>>) {
308         let binding_style = self.binding_style;
309 
310         let ident: syn::Ident = syn::Ident::new(
311             &format!("{}_{}", binding_name, i),
312             proc_macro2::Span::call_site(),
313         );
314         let expr = syn::Expr::Path(syn::ExprPath {
315             attrs: vec![],
316             qself: None,
317             path: syn::Path::from(ident.clone())
318         });
319 
320         let expr = if self.is_packed {
321             expr.into_token_stream()
322         } else {
323             quote!((*#expr))
324         };
325 
326         f(field, &ident, binding_style).to_tokens(&mut stream);
327 
328         matches.push(BindingInfo {
329             expr,
330             ident,
331             field,
332         });
333 
334         (stream, matches)
335     }
336 }
337