elo_rust/codegen/
functions.rs1use proc_macro2::TokenStream;
4use quote::quote;
5
6#[allow(dead_code)]
9const REGEX_TIMEOUT_MS: u64 = 1000;
10
11#[derive(Debug)]
13pub struct FunctionGenerator;
14
15impl FunctionGenerator {
16 pub fn new() -> Self {
18 Self
19 }
20
21 pub fn call(&self, name: &str, args: Vec<TokenStream>) -> TokenStream {
23 match name {
24 "matches" | "contains" | "length" | "uppercase" | "lowercase" | "trim"
26 | "starts_with" | "ends_with" => self.string_function(name, args),
27 "today" | "now" | "age" | "days_since" | "date" => self.datetime_function(name, args),
29 "any" | "all" => self.array_function(name, args),
31 _ => quote!(),
32 }
33 }
34
35 pub fn string_function(&self, name: &str, args: Vec<TokenStream>) -> TokenStream {
37 match name {
38 "matches" => {
39 if args.len() < 2 {
40 return quote!();
41 }
42 let subject = &args[0];
43 let pattern = &args[1];
44 quote! {
45 {
46 use regex::Regex;
47 match Regex::new(#pattern) {
49 Ok(re) => {
50 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
53 re.is_match(#subject)
54 })) {
55 Ok(result) => result,
56 Err(_) => {
57 eprintln!(
58 "⚠️ Regex matching failed: pattern may cause performance issues"
59 );
60 false
61 }
62 }
63 }
64 Err(_) => {
65 eprintln!("⚠️ Invalid regex pattern: {}", #pattern);
66 false
67 }
68 }
69 }
70 }
71 }
72 "contains" => {
73 if args.len() < 2 {
74 return quote!();
75 }
76 let subject = &args[0];
77 let substring = &args[1];
78 quote!(#subject.contains(#substring))
79 }
80 "length" => {
81 if args.is_empty() {
82 return quote!();
83 }
84 let subject = &args[0];
85 quote!(#subject.len())
86 }
87 "uppercase" => {
88 if args.is_empty() {
89 return quote!();
90 }
91 let subject = &args[0];
92 quote!(#subject.to_uppercase())
93 }
94 "lowercase" => {
95 if args.is_empty() {
96 return quote!();
97 }
98 let subject = &args[0];
99 quote!(#subject.to_lowercase())
100 }
101 "trim" => {
102 if args.is_empty() {
103 return quote!();
104 }
105 let subject = &args[0];
106 quote!(#subject.trim())
107 }
108 "starts_with" => {
109 if args.len() < 2 {
110 return quote!();
111 }
112 let subject = &args[0];
113 let prefix = &args[1];
114 quote!(#subject.starts_with(#prefix))
115 }
116 "ends_with" => {
117 if args.len() < 2 {
118 return quote!();
119 }
120 let subject = &args[0];
121 let suffix = &args[1];
122 quote!(#subject.ends_with(#suffix))
123 }
124 _ => quote!(),
125 }
126 }
127
128 pub fn datetime_function(&self, name: &str, args: Vec<TokenStream>) -> TokenStream {
130 match name {
131 "today" => {
132 quote! {
133 {
134 use chrono::Local;
135 Local::now().date_naive()
136 }
137 }
138 }
139 "now" => {
140 quote! {
141 {
142 use chrono::Utc;
143 Utc::now()
144 }
145 }
146 }
147 "age" => {
148 if args.is_empty() {
149 return quote!();
150 }
151 let birth_date = &args[0];
152 quote! {
153 {
154 use chrono::Local;
155 let today = Local::now().date_naive();
156 let mut age = today.year() - #birth_date.year();
157 if (today.month(), today.day()) < (#birth_date.month(), #birth_date.day()) {
158 age -= 1;
159 }
160 age as u32
161 }
162 }
163 }
164 "days_since" => {
165 if args.is_empty() {
166 return quote!();
167 }
168 let date = &args[0];
169 quote! {
170 {
171 use chrono::Local;
172 (Local::now().date_naive() - #date).num_days()
173 }
174 }
175 }
176 "date" => {
177 if args.is_empty() {
178 return quote!();
179 }
180 let date_str = &args[0];
181 quote! {
182 {
183 use chrono::NaiveDate;
184 NaiveDate::parse_from_str(#date_str, "%Y-%m-%d")
185 .expect("Invalid date format")
186 }
187 }
188 }
189 _ => quote!(),
190 }
191 }
192
193 pub fn array_function(&self, name: &str, args: Vec<TokenStream>) -> TokenStream {
195 match name {
196 "contains" => {
197 if args.len() < 2 {
198 return quote!();
199 }
200 let array = &args[0];
201 let value = &args[1];
202 quote!(#array.contains(&#value))
203 }
204 "any" => {
205 if args.len() < 2 {
206 return quote!();
207 }
208 let array = &args[0];
209 let predicate = &args[1];
210 quote!(#array.iter().any(|item| #predicate))
211 }
212 "all" => {
213 if args.len() < 2 {
214 return quote!();
215 }
216 let array = &args[0];
217 let predicate = &args[1];
218 quote!(#array.iter().all(|item| #predicate))
219 }
220 "length" => {
221 if args.is_empty() {
222 return quote!();
223 }
224 let array = &args[0];
225 quote!(#array.len())
226 }
227 "is_empty" => {
228 if args.is_empty() {
229 return quote!();
230 }
231 let array = &args[0];
232 quote!(#array.is_empty())
233 }
234 "is_null" => {
236 if args.is_empty() {
237 return quote!();
238 }
239 let value = &args[0];
240 quote!(#value.is_none())
241 }
242 "is_some" => {
243 if args.is_empty() {
244 return quote!();
245 }
246 let value = &args[0];
247 quote!(#value.is_some())
248 }
249 _ => quote!(),
250 }
251 }
252}
253
254impl Default for FunctionGenerator {
255 fn default() -> Self {
256 Self::new()
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 #[test]
265 fn test_function_generator_creation() {
266 let _gen = FunctionGenerator::new();
267 }
268}