1extern crate proc_macro;
2
3use proc_macro2::{Ident, Span, TokenStream};
4use proc_macro_hack::proc_macro_hack;
5use proc_quote::quote;
6
7#[proc_macro_hack]
8pub fn vecmerge(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
9 let expr = syn::parse_macro_input!(input as syn::Expr);
10 let mut visitor = Visitor::new();
11 visitor.visit(&expr);
12 let (capacity, parts) = visitor.get();
13 proc_macro::TokenStream::from(quote!({
14 let mut vec = Vec::with_capacity(#(#capacity)+*);
15 #(#parts)*
16 vec
17 }))
18}
19
20struct Visitor {
21 capacity: Vec<TokenStream>,
22 parts: Vec<TokenStream>,
23}
24
25impl Visitor {
26 fn new() -> Self {
27 Self {
28 capacity: Vec::new(),
29 parts: Vec::new(),
30 }
31 }
32
33 fn get(self) -> (Vec<TokenStream>, Vec<TokenStream>) {
34 (self.capacity, self.parts)
35 }
36
37 fn visit(&mut self, expr: &syn::Expr) {
38 match expr {
39 syn::Expr::Binary(ref binary_expr) => match binary_expr.op {
40 syn::BinOp::Add(..) => {
41 self.visit(binary_expr.left.as_ref());
42 self.visit(binary_expr.right.as_ref());
43 }
44 op => self.parts.push(quote! {
45 compile_error!(format!("Unexpected operator {}", #op))
46 }),
47 },
48 syn::Expr::Try(ref try_expr) => match try_expr.expr.as_ref() {
49 syn::Expr::Array(array_expr) => self.visit_try_array_expr(array_expr),
50 _ => self.visit_expr(expr),
51 },
52 syn::Expr::Array(array_expr) => self.visit_array_expr(array_expr),
53 _ => self.visit_expr(expr),
54 }
55 }
56
57 fn visit_array_expr(&mut self, array_expr: &syn::ExprArray) {
58 let len = array_expr.elems.len();
59 self.capacity.push(quote!(#len));
60 if len == 1 {
61 let elem = &array_expr.elems[0];
62 self.parts.push(quote!(vec.push(#elem);));
63 } else {
64 self.parts
65 .push(quote!(vec.extend(<[_]>::into_vec(std::boxed::Box::new(#array_expr)));));
66 }
67 }
68
69 fn visit_try_array_expr(&mut self, try_array_expr: &syn::ExprArray) {
70 let len = try_array_expr.elems.len();
71 let elems = &try_array_expr.elems;
72 if len == 1 {
73 let elem = &elems[0];
74 self.parts.push(quote! {
75 if let Some(elem) = #elem {
76 vec.push(elem);
77 }
78 });
79 } else {
80 let names = (0..elems.len())
81 .into_iter()
82 .map(|index| Ident::new(&format!("elem_{}", index), Span::call_site()))
83 .collect::<Vec<Ident>>();
84 self.parts.push(quote! {
85 if let (#(Some(#names)),*) = (#elems) {
86 vec.extend(<[_]>::into_vec(std::boxed::Box::new([#(#names,)*])));
87 }
88 });
89 }
90 }
91
92 fn visit_expr(&mut self, expr: &syn::Expr) {
93 self.capacity.push(quote!(#expr.len()));
94 self.parts.push(quote!(vec.extend(#expr);));
95 }
96}