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