excel_emulator_macro/
lib.rs

1use 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