excel_emulator_macro/
lib.rs1use proc_macro::{TokenStream, Span};
2use syn::{self, ItemFn, Ident, FnArg};
3use quote::quote;
4
5fn make_ascii_titlecase(s: &mut str) {
6 if let Some(r) = s.get_mut(0..1) {
7 r.make_ascii_uppercase();
8 }
9}
10
11#[proc_macro_attribute]
12pub fn function(_attr: TokenStream, item: TokenStream) -> TokenStream {
13 let ast: ItemFn = syn::parse(item).unwrap();
14 create_excel_function(ast)
15}
16
17fn create_excel_function(ast: ItemFn) -> TokenStream {
18 let function_name = ast.sig.ident.clone();
19 let mut struct_name: String = ast.sig.ident.to_string();
20 make_ascii_titlecase(&mut struct_name);
21 let struct_name_ident: Ident = Ident::new(&struct_name, Span::call_site().into());
22 let fn_args = ast.sig.inputs
23 .iter()
24 .filter(|fnarg| { matches!(fnarg, FnArg::Typed(_)) })
25 .collect::<Vec<&FnArg>>();
26
27 let struct_fields = fn_args.clone().into_iter().map(|fnarg| {
28 quote! {
29 pub #fnarg
30 }
31 });
32
33 let field_declarations = fn_args.clone().into_iter().map(|fnarg| {
34 if let FnArg::Typed(pat_type) = fnarg {
35 let arg_name = *pat_type.pat.clone();
36 let is_optional: bool = match &*pat_type.ty {
37 syn::Type::Path(typepath) => typepath.path.segments.len() == 1 && typepath.path.segments[0].ident.to_string().as_str() == "Option",
38 _ => false
39 };
40 if let syn::Pat::Ident(pat_ident) = arg_name {
41 if pat_ident.ident.to_string() == "args" {
42 quote! {
43 let args = v;
44 }
45 } else if is_optional {
46 quote! {
47 let #fnarg = if v.len() > 0 {
48 Some(v.remove(0))
49 } else {
50 None
51 };
52 }
53 } else {
54 quote! {
55 let #fnarg = Value::from(v.remove(0));
56 }
57 }
58 } else {
59 quote! {}
60 }
61 } else {
62 quote! {}
63 }
64 });
65
66 let self_arg_declarations = fn_args.clone().into_iter().map(|fnarg| {
67 if let FnArg::Typed(pat_type) = fnarg {
68 let arg_name = *pat_type.pat.clone();
69 quote! {
70 self.#arg_name
71 }
72 } else {
73 quote! { }
74 }
75 });
76
77 let error_handling = fn_args.clone().into_iter().map(|fnarg| {
78 if let FnArg::Typed(pat_type) = fnarg {
79 let arg_name = *pat_type.pat.clone();
80 let is_optional: bool = match &*pat_type.ty {
81 syn::Type::Path(typepath) => typepath.path.segments.len() == 1 && typepath.path.segments[0].ident.to_string().as_str() == "Option",
82 _ => false
83 };
84 if let syn::Pat::Ident(ref pat_ident) = arg_name {
85 if pat_ident.ident.to_string() == "args" {
86 quote! {
87 let mut errors = self.args.iter().filter(|x| x.is_err());
88 let error = errors.next();
89 if let Some(e) = error {
90 return e.clone();
91 }
92 }
93 } else if is_optional {
94 quote! {
95 if let Some(x) = self.#arg_name.clone() {
96 if x.is_err() {
97 return x;
98 }
99 }
100 }
101 } else {
102 quote! {
103 if self.#arg_name.is_err() {
104 return self.#arg_name.clone();
105 }
106 }
107 }
108 } else {
109 quote! {}
110 }
111 } else {
112 quote! {}
113 }
114
115
116 });
117
118 let arg_declarations = fn_args.clone().into_iter().map(|fnarg| {
119 if let FnArg::Typed(pat_type) = fnarg {
120 let arg_name = *pat_type.pat.clone();
121 quote! {
122 #arg_name
123 }
124 } else {
125 quote! { }
126 }
127 });
128
129 quote! {
130 pub struct #struct_name_ident {
131 #(#struct_fields),*
132 }
133
134 impl #struct_name_ident {
135 #ast
136 }
137
138 impl Function for #struct_name_ident {
139 fn evaluate(self) -> Value {
140 #(#error_handling)*;
141 Self::#function_name(#(#self_arg_declarations),*)
142 }
143 }
144
145 impl From<Vec<Value>> for #struct_name_ident {
146 fn from(mut v: Vec<Value>) -> #struct_name_ident {
147 #(#field_declarations)*;
148 #struct_name_ident {#(#arg_declarations),*}
149 }
150 }
151 }.into()
152}
153