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 { |
31 | 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 { |
42 | fn to_tokens(&self, tokens: &mut TokenStream) { |
43 | match *self { |
44 | BindingStyle::Move => (), |
45 | BindingStyle::MoveMut => tokens.extend(iter:quote!(mut)), |
46 | BindingStyle::Ref => tokens.extend(iter:quote!(ref)), |
47 | BindingStyle::RefMut => { |
48 | tokens.extend(iter: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> { |
77 | 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> { |
88 | pub fn with_name(self, name: String) -> Self { |
89 | Matcher { |
90 | binding_name: name, |
91 | ..self |
92 | } |
93 | } |
94 | |
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 | |
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 | |
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 |
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 | |
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 | |
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 | |