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