1use ahash::AHashMap;
2use serde::Serialize;
3use serde_json::Value;
4use crate::path_utils;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
8pub struct LogicId(pub(crate) u64);
9
10#[derive(Debug, Clone)]
12pub enum CompiledLogic {
13 Null,
15 Bool(bool),
16 Number(String), String(String),
18 Array(Vec<CompiledLogic>),
19
20 Var(String, Option<Box<CompiledLogic>>), Ref(String, Option<Box<CompiledLogic>>), And(Vec<CompiledLogic>),
26 Or(Vec<CompiledLogic>),
27 Not(Box<CompiledLogic>),
28 If(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), Equal(Box<CompiledLogic>, Box<CompiledLogic>),
32 StrictEqual(Box<CompiledLogic>, Box<CompiledLogic>),
33 NotEqual(Box<CompiledLogic>, Box<CompiledLogic>),
34 StrictNotEqual(Box<CompiledLogic>, Box<CompiledLogic>),
35 LessThan(Box<CompiledLogic>, Box<CompiledLogic>),
36 LessThanOrEqual(Box<CompiledLogic>, Box<CompiledLogic>),
37 GreaterThan(Box<CompiledLogic>, Box<CompiledLogic>),
38 GreaterThanOrEqual(Box<CompiledLogic>, Box<CompiledLogic>),
39
40 Add(Vec<CompiledLogic>),
42 Subtract(Vec<CompiledLogic>),
43 Multiply(Vec<CompiledLogic>),
44 Divide(Vec<CompiledLogic>),
45 Modulo(Box<CompiledLogic>, Box<CompiledLogic>),
46 Power(Box<CompiledLogic>, Box<CompiledLogic>),
47
48 Map(Box<CompiledLogic>, Box<CompiledLogic>), Filter(Box<CompiledLogic>, Box<CompiledLogic>), Reduce(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), All(Box<CompiledLogic>, Box<CompiledLogic>), Some(Box<CompiledLogic>, Box<CompiledLogic>), None(Box<CompiledLogic>, Box<CompiledLogic>), Merge(Vec<CompiledLogic>),
56 In(Box<CompiledLogic>, Box<CompiledLogic>), Cat(Vec<CompiledLogic>),
60 Substr(Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), Missing(Vec<String>),
64 MissingSome(Box<CompiledLogic>, Vec<String>), Abs(Box<CompiledLogic>),
68 Max(Vec<CompiledLogic>),
69 Min(Vec<CompiledLogic>),
70 Pow(Box<CompiledLogic>, Box<CompiledLogic>),
71 Round(Box<CompiledLogic>, Option<Box<CompiledLogic>>), RoundUp(Box<CompiledLogic>, Option<Box<CompiledLogic>>), RoundDown(Box<CompiledLogic>, Option<Box<CompiledLogic>>), Ceiling(Box<CompiledLogic>, Option<Box<CompiledLogic>>), Floor(Box<CompiledLogic>, Option<Box<CompiledLogic>>), Trunc(Box<CompiledLogic>, Option<Box<CompiledLogic>>), Mround(Box<CompiledLogic>, Box<CompiledLogic>), Length(Box<CompiledLogic>),
81 Search(Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), Left(Box<CompiledLogic>, Option<Box<CompiledLogic>>), Right(Box<CompiledLogic>, Option<Box<CompiledLogic>>), Mid(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), Len(Box<CompiledLogic>),
86 SplitText(Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), Concat(Vec<CompiledLogic>),
88 SplitValue(Box<CompiledLogic>, Box<CompiledLogic>), StringFormat(Box<CompiledLogic>, Option<Box<CompiledLogic>>, Option<Box<CompiledLogic>>, Option<Box<CompiledLogic>>, Option<Box<CompiledLogic>>), Xor(Box<CompiledLogic>, Box<CompiledLogic>),
93 IfNull(Box<CompiledLogic>, Box<CompiledLogic>),
94 IsEmpty(Box<CompiledLogic>),
95 Empty,
96
97 Today,
99 Now,
100 Days(Box<CompiledLogic>, Box<CompiledLogic>), Year(Box<CompiledLogic>),
102 Month(Box<CompiledLogic>),
103 Day(Box<CompiledLogic>),
104 Date(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), DateFormat(Box<CompiledLogic>, Option<Box<CompiledLogic>>), Sum(Box<CompiledLogic>, Option<Box<CompiledLogic>>, Option<Box<CompiledLogic>>), For(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), ValueAt(Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), MaxAt(Box<CompiledLogic>, Box<CompiledLogic>), IndexAt(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), Match(Box<CompiledLogic>, Vec<CompiledLogic>), MatchRange(Box<CompiledLogic>, Vec<CompiledLogic>), Choose(Box<CompiledLogic>, Vec<CompiledLogic>), FindIndex(Box<CompiledLogic>, Vec<CompiledLogic>), Multiplies(Vec<CompiledLogic>), Divides(Vec<CompiledLogic>), YearFrac(Box<CompiledLogic>, Box<CompiledLogic>, Option<Box<CompiledLogic>>), DateDif(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), RangeOptions(Box<CompiledLogic>, Box<CompiledLogic>), MapOptions(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), MapOptionsIf(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>, Vec<CompiledLogic>), Return(Box<Value>), }
134
135impl CompiledLogic {
136 pub fn compile(logic: &Value) -> Result<Self, String> {
138 match logic {
139 Value::Null => Ok(CompiledLogic::Null),
140 Value::Bool(b) => Ok(CompiledLogic::Bool(*b)),
141 Value::Number(n) => {
142 Ok(CompiledLogic::Number(n.to_string()))
144 }
145 Value::String(s) => Ok(CompiledLogic::String(s.clone())),
146 Value::Array(arr) => {
147 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
148 Ok(CompiledLogic::Array(compiled?))
149 }
150 Value::Object(obj) => {
151 if obj.is_empty() {
152 return Ok(CompiledLogic::Null);
153 }
154
155 let (op, args) = obj.iter().next().unwrap();
157
158 Self::compile_operator(op, args)
159 }
160 }
161 }
162
163 fn compile_operator(op: &str, args: &Value) -> Result<Self, String> {
164 match op {
165 "var" => {
167 if let Value::String(name) = args {
168 let normalized = path_utils::normalize_to_json_pointer(name);
170 Ok(CompiledLogic::Var(normalized, None))
171 } else if let Value::Array(arr) = args {
172 if arr.is_empty() {
173 return Err("var requires at least one argument".to_string());
174 }
175 let name = arr[0].as_str()
176 .ok_or("var name must be a string")?;
177 let normalized = path_utils::normalize_to_json_pointer(name);
179 let default = if arr.len() > 1 {
180 Some(Box::new(Self::compile(&arr[1])?))
181 } else {
182 None
183 };
184 Ok(CompiledLogic::Var(normalized, default))
185 } else {
186 Err("var requires string or array".to_string())
187 }
188 }
189 "$ref" | "ref" => {
190 if let Value::String(path) = args {
191 let normalized = path_utils::normalize_to_json_pointer(path);
193 Ok(CompiledLogic::Ref(normalized, None))
194 } else if let Value::Array(arr) = args {
195 if arr.is_empty() {
196 return Err("$ref requires at least one argument".to_string());
197 }
198 let path = arr[0].as_str()
199 .ok_or("$ref path must be a string")?;
200 let normalized = path_utils::normalize_to_json_pointer(path);
202 let default = if arr.len() > 1 {
203 Some(Box::new(Self::compile(&arr[1])?))
204 } else {
205 None
206 };
207 Ok(CompiledLogic::Ref(normalized, default))
208 } else {
209 Err("$ref requires string or array".to_string())
210 }
211 }
212
213 "and" => {
215 let arr = args.as_array().ok_or("and requires array")?;
216 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
217 let items = compiled?;
218 Ok(CompiledLogic::And(Self::flatten_and(items)))
220 }
221 "or" => {
222 let arr = args.as_array().ok_or("or requires array")?;
223 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
224 let items = compiled?;
225 Ok(CompiledLogic::Or(Self::flatten_or(items)))
227 }
228 "!" | "not" => {
229 let value_to_negate = if let Value::Array(arr) = args {
231 if arr.is_empty() {
232 return Err("! requires an argument".to_string());
233 }
234 &arr[0]
235 } else {
236 args
237 };
238
239 let inner = Self::compile(value_to_negate)?;
240 if let CompiledLogic::Not(inner_expr) = inner {
242 Ok(*inner_expr)
243 } else {
244 Ok(CompiledLogic::Not(Box::new(inner)))
245 }
246 }
247 "if" => {
248 let arr = args.as_array().ok_or("if requires array")?;
249 if arr.len() < 3 {
250 return Err("if requires at least 3 arguments".to_string());
251 }
252 Ok(CompiledLogic::If(
253 Box::new(Self::compile(&arr[0])?),
254 Box::new(Self::compile(&arr[1])?),
255 Box::new(Self::compile(&arr[2])?),
256 ))
257 }
258
259 "==" => Self::compile_binary(args, |a, b| CompiledLogic::Equal(a, b)),
261 "===" => Self::compile_binary(args, |a, b| CompiledLogic::StrictEqual(a, b)),
262 "!=" => Self::compile_binary(args, |a, b| CompiledLogic::NotEqual(a, b)),
263 "!==" => Self::compile_binary(args, |a, b| CompiledLogic::StrictNotEqual(a, b)),
264 "<" => Self::compile_binary(args, |a, b| CompiledLogic::LessThan(a, b)),
265 "<=" => Self::compile_binary(args, |a, b| CompiledLogic::LessThanOrEqual(a, b)),
266 ">" => Self::compile_binary(args, |a, b| CompiledLogic::GreaterThan(a, b)),
267 ">=" => Self::compile_binary(args, |a, b| CompiledLogic::GreaterThanOrEqual(a, b)),
268
269 "+" => {
271 let arr = args.as_array().ok_or("+ requires array")?;
272 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
273 let items = compiled?;
274 Ok(CompiledLogic::Add(Self::flatten_add(items)))
276 }
277 "-" => {
278 let arr = args.as_array().ok_or("- requires array")?;
279 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
280 Ok(CompiledLogic::Subtract(compiled?))
281 }
282 "*" => {
283 let arr = args.as_array().ok_or("* requires array")?;
284 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
285 let items = compiled?;
286 Ok(CompiledLogic::Multiply(Self::flatten_multiply(items)))
288 }
289 "/" => {
290 let arr = args.as_array().ok_or("/ requires array")?;
291 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
292 Ok(CompiledLogic::Divide(compiled?))
293 }
294 "%" => Self::compile_binary(args, |a, b| CompiledLogic::Modulo(a, b)),
295 "^" => Self::compile_binary(args, |a, b| CompiledLogic::Power(a, b)),
296
297 "map" => Self::compile_binary(args, |a, b| CompiledLogic::Map(a, b)),
299 "filter" => Self::compile_binary(args, |a, b| CompiledLogic::Filter(a, b)),
300 "reduce" => {
301 let arr = args.as_array().ok_or("reduce requires array")?;
302 if arr.len() < 3 {
303 return Err("reduce requires 3 arguments".to_string());
304 }
305 Ok(CompiledLogic::Reduce(
306 Box::new(Self::compile(&arr[0])?),
307 Box::new(Self::compile(&arr[1])?),
308 Box::new(Self::compile(&arr[2])?),
309 ))
310 }
311 "all" => Self::compile_binary(args, |a, b| CompiledLogic::All(a, b)),
312 "some" => Self::compile_binary(args, |a, b| CompiledLogic::Some(a, b)),
313 "none" => Self::compile_binary(args, |a, b| CompiledLogic::None(a, b)),
314 "merge" => {
315 let arr = args.as_array().ok_or("merge requires array")?;
316 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
317 Ok(CompiledLogic::Merge(compiled?))
318 }
319 "in" => Self::compile_binary(args, |a, b| CompiledLogic::In(a, b)),
320
321 "cat" => {
323 let arr = args.as_array().ok_or("cat requires array")?;
324 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
325 let items = compiled?;
326 Ok(CompiledLogic::Cat(Self::flatten_cat(items)))
328 }
329 "substr" => {
330 let arr = args.as_array().ok_or("substr requires array")?;
331 if arr.len() < 2 {
332 return Err("substr requires at least 2 arguments".to_string());
333 }
334 let length = if arr.len() > 2 {
335 Some(Box::new(Self::compile(&arr[2])?))
336 } else {
337 None
338 };
339 Ok(CompiledLogic::Substr(
340 Box::new(Self::compile(&arr[0])?),
341 Box::new(Self::compile(&arr[1])?),
342 length,
343 ))
344 }
345
346 "missing" => {
348 let keys = if let Value::Array(arr) = args {
349 arr.iter()
350 .map(|v| v.as_str().ok_or("missing key must be string").map(|s| s.to_string()))
351 .collect::<Result<Vec<_>, _>>()?
352 } else if let Value::String(s) = args {
353 vec![s.clone()]
354 } else {
355 return Err("missing requires string or array".to_string());
356 };
357 Ok(CompiledLogic::Missing(keys))
358 }
359 "missing_some" => {
360 let arr = args.as_array().ok_or("missing_some requires array")?;
361 if arr.len() < 2 {
362 return Err("missing_some requires at least 2 arguments".to_string());
363 }
364 let minimum = Box::new(Self::compile(&arr[0])?);
365 let keys = if let Value::Array(key_arr) = &arr[1] {
366 key_arr.iter()
367 .map(|v| v.as_str().ok_or("key must be string").map(|s| s.to_string()))
368 .collect::<Result<Vec<_>, _>>()?
369 } else {
370 return Err("missing_some keys must be array".to_string());
371 };
372 Ok(CompiledLogic::MissingSome(minimum, keys))
373 }
374
375 "abs" => {
377 let arg = if let Value::Array(arr) = args {
378 if arr.is_empty() {
379 return Err("abs requires at least one argument".to_string());
380 }
381 &arr[0]
382 } else {
383 args
384 };
385 Ok(CompiledLogic::Abs(Box::new(Self::compile(arg)?)))
386 }
387 "max" => {
388 let arr = args.as_array().ok_or("max requires array")?;
389 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
390 Ok(CompiledLogic::Max(compiled?))
391 }
392 "min" => {
393 let arr = args.as_array().ok_or("min requires array")?;
394 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
395 Ok(CompiledLogic::Min(compiled?))
396 }
397 "pow" | "**" => Self::compile_binary(args, |a, b| CompiledLogic::Pow(a, b)),
398 "round" | "ROUND" => {
399 if let Value::Array(arr) = args {
400 let decimals = if arr.len() > 1 {
401 Some(Box::new(Self::compile(&arr[1])?))
402 } else {
403 None
404 };
405 Ok(CompiledLogic::Round(Box::new(Self::compile(&arr[0])?), decimals))
406 } else {
407 Ok(CompiledLogic::Round(Box::new(Self::compile(args)?), None))
408 }
409 }
410 "roundup" | "ROUNDUP" => {
411 if let Value::Array(arr) = args {
412 let decimals = if arr.len() > 1 {
413 Some(Box::new(Self::compile(&arr[1])?))
414 } else {
415 None
416 };
417 Ok(CompiledLogic::RoundUp(Box::new(Self::compile(&arr[0])?), decimals))
418 } else {
419 Ok(CompiledLogic::RoundUp(Box::new(Self::compile(args)?), None))
420 }
421 }
422 "rounddown" | "ROUNDDOWN" => {
423 if let Value::Array(arr) = args {
424 let decimals = if arr.len() > 1 {
425 Some(Box::new(Self::compile(&arr[1])?))
426 } else {
427 None
428 };
429 Ok(CompiledLogic::RoundDown(Box::new(Self::compile(&arr[0])?), decimals))
430 } else {
431 Ok(CompiledLogic::RoundDown(Box::new(Self::compile(args)?), None))
432 }
433 }
434 "ceiling" | "CEILING" => {
435 if let Value::Array(arr) = args {
436 let significance = if arr.len() > 1 {
437 Some(Box::new(Self::compile(&arr[1])?))
438 } else {
439 None
440 };
441 Ok(CompiledLogic::Ceiling(Box::new(Self::compile(&arr[0])?), significance))
442 } else {
443 Ok(CompiledLogic::Ceiling(Box::new(Self::compile(args)?), None))
444 }
445 }
446 "floor" | "FLOOR" => {
447 if let Value::Array(arr) = args {
448 let significance = if arr.len() > 1 {
449 Some(Box::new(Self::compile(&arr[1])?))
450 } else {
451 None
452 };
453 Ok(CompiledLogic::Floor(Box::new(Self::compile(&arr[0])?), significance))
454 } else {
455 Ok(CompiledLogic::Floor(Box::new(Self::compile(args)?), None))
456 }
457 }
458 "trunc" | "TRUNC" => {
459 if let Value::Array(arr) = args {
460 let decimals = if arr.len() > 1 {
461 Some(Box::new(Self::compile(&arr[1])?))
462 } else {
463 None
464 };
465 Ok(CompiledLogic::Trunc(Box::new(Self::compile(&arr[0])?), decimals))
466 } else {
467 Ok(CompiledLogic::Trunc(Box::new(Self::compile(args)?), None))
468 }
469 }
470 "mround" | "MROUND" => Self::compile_binary(args, |a, b| CompiledLogic::Mround(a, b)),
471
472 "length" => {
474 let arg = if let Value::Array(arr) = args {
475 if arr.is_empty() {
476 return Err("length requires at least one argument".to_string());
477 }
478 &arr[0]
479 } else {
480 args
481 };
482 Ok(CompiledLogic::Length(Box::new(Self::compile(arg)?)))
483 }
484 "len" | "LEN" => {
485 let arg = if let Value::Array(arr) = args {
486 if arr.is_empty() {
487 return Err("len requires at least one argument".to_string());
488 }
489 &arr[0]
490 } else {
491 args
492 };
493 Ok(CompiledLogic::Len(Box::new(Self::compile(arg)?)))
494 }
495 "search" | "SEARCH" => {
496 let arr = args.as_array().ok_or("search requires array")?;
497 if arr.len() < 2 {
498 return Err("search requires at least 2 arguments".to_string());
499 }
500 let start_num = if arr.len() > 2 {
501 Some(Box::new(Self::compile(&arr[2])?))
502 } else {
503 None
504 };
505 Ok(CompiledLogic::Search(
506 Box::new(Self::compile(&arr[0])?),
507 Box::new(Self::compile(&arr[1])?),
508 start_num,
509 ))
510 }
511 "left" | "LEFT" => {
512 if let Value::Array(arr) = args {
513 let num_chars = if arr.len() > 1 {
514 Some(Box::new(Self::compile(&arr[1])?))
515 } else {
516 None
517 };
518 Ok(CompiledLogic::Left(Box::new(Self::compile(&arr[0])?), num_chars))
519 } else {
520 Ok(CompiledLogic::Left(Box::new(Self::compile(args)?), None))
521 }
522 }
523 "right" | "RIGHT" => {
524 if let Value::Array(arr) = args {
525 let num_chars = if arr.len() > 1 {
526 Some(Box::new(Self::compile(&arr[1])?))
527 } else {
528 None
529 };
530 Ok(CompiledLogic::Right(Box::new(Self::compile(&arr[0])?), num_chars))
531 } else {
532 Ok(CompiledLogic::Right(Box::new(Self::compile(args)?), None))
533 }
534 }
535 "mid" | "MID" => {
536 let arr = args.as_array().ok_or("mid requires array")?;
537 if arr.len() < 3 {
538 return Err("mid requires 3 arguments".to_string());
539 }
540 Ok(CompiledLogic::Mid(
541 Box::new(Self::compile(&arr[0])?),
542 Box::new(Self::compile(&arr[1])?),
543 Box::new(Self::compile(&arr[2])?),
544 ))
545 }
546 "splittext" | "SPLITTEXT" => {
547 let arr = args.as_array().ok_or("splittext requires array")?;
548 if arr.len() < 2 {
549 return Err("splittext requires at least 2 arguments".to_string());
550 }
551 let index = if arr.len() > 2 {
552 Some(Box::new(Self::compile(&arr[2])?))
553 } else {
554 None
555 };
556 Ok(CompiledLogic::SplitText(
557 Box::new(Self::compile(&arr[0])?),
558 Box::new(Self::compile(&arr[1])?),
559 index,
560 ))
561 }
562 "concat" | "CONCAT" => {
563 let arr = args.as_array().ok_or("concat requires array")?;
564 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
565 Ok(CompiledLogic::Concat(compiled?))
566 }
567 "splitvalue" | "SPLITVALUE" => Self::compile_binary(args, |a, b| CompiledLogic::SplitValue(a, b)),
568 "stringformat" | "STRINGFORMAT" => {
569 let arr = args.as_array().ok_or("stringformat requires array")?;
570 if arr.is_empty() {
571 return Err("stringformat requires at least 1 argument".to_string());
572 }
573 let decimals = if arr.len() > 1 {
574 Some(Box::new(Self::compile(&arr[1])?))
575 } else {
576 None
577 };
578 let prefix = if arr.len() > 2 {
579 Some(Box::new(Self::compile(&arr[2])?))
580 } else {
581 None
582 };
583 let suffix = if arr.len() > 3 {
584 Some(Box::new(Self::compile(&arr[3])?))
585 } else {
586 None
587 };
588 let thousands_sep = if arr.len() > 4 {
589 Some(Box::new(Self::compile(&arr[4])?))
590 } else {
591 None
592 };
593 Ok(CompiledLogic::StringFormat(
594 Box::new(Self::compile(&arr[0])?),
595 decimals,
596 prefix,
597 suffix,
598 thousands_sep,
599 ))
600 }
601
602 "xor" => Self::compile_binary(args, |a, b| CompiledLogic::Xor(a, b)),
604 "ifnull" | "IFNULL" => Self::compile_binary(args, |a, b| CompiledLogic::IfNull(a, b)),
605 "isempty" | "ISEMPTY" => {
606 let arg = if let Value::Array(arr) = args {
607 if arr.is_empty() {
608 return Err("ISEMPTY requires at least one argument".to_string());
609 }
610 &arr[0]
611 } else {
612 args
613 };
614 Ok(CompiledLogic::IsEmpty(Box::new(Self::compile(arg)?)))
615 }
616 "empty" | "EMPTY" => Ok(CompiledLogic::Empty),
617
618 "today" | "TODAY" => Ok(CompiledLogic::Today),
620 "now" | "NOW" => Ok(CompiledLogic::Now),
621 "days" | "DAYS" => Self::compile_binary(args, |a, b| CompiledLogic::Days(a, b)),
622 "year" | "YEAR" => {
623 let arg = if let Value::Array(arr) = args {
624 if arr.is_empty() {
625 return Err("year requires at least one argument".to_string());
626 }
627 &arr[0]
628 } else {
629 args
630 };
631 Ok(CompiledLogic::Year(Box::new(Self::compile(arg)?)))
632 }
633 "month" | "MONTH" => {
634 let arg = if let Value::Array(arr) = args {
635 if arr.is_empty() {
636 return Err("month requires at least one argument".to_string());
637 }
638 &arr[0]
639 } else {
640 args
641 };
642 Ok(CompiledLogic::Month(Box::new(Self::compile(arg)?)))
643 }
644 "day" | "DAY" => {
645 let arg = if let Value::Array(arr) = args {
646 if arr.is_empty() {
647 return Err("day requires at least one argument".to_string());
648 }
649 &arr[0]
650 } else {
651 args
652 };
653 Ok(CompiledLogic::Day(Box::new(Self::compile(arg)?)))
654 }
655 "date" | "DATE" => {
656 let arr = args.as_array().ok_or("date requires array")?;
657 if arr.len() < 3 {
658 return Err("date requires 3 arguments".to_string());
659 }
660 Ok(CompiledLogic::Date(
661 Box::new(Self::compile(&arr[0])?),
662 Box::new(Self::compile(&arr[1])?),
663 Box::new(Self::compile(&arr[2])?),
664 ))
665 }
666 "dateformat" | "DATEFORMAT" => {
667 if let Value::Array(arr) = args {
668 if arr.is_empty() {
669 return Err("dateformat requires at least 1 argument".to_string());
670 }
671 let format = if arr.len() > 1 {
672 Some(Box::new(Self::compile(&arr[1])?))
673 } else {
674 None
675 };
676 Ok(CompiledLogic::DateFormat(Box::new(Self::compile(&arr[0])?), format))
677 } else {
678 Ok(CompiledLogic::DateFormat(Box::new(Self::compile(args)?), None))
679 }
680 }
681
682 "sum" | "SUM" => {
684 if let Value::Array(arr) = args {
685 if arr.is_empty() {
686 return Err("sum requires at least 1 argument".to_string());
687 }
688 let field = if arr.len() > 1 {
689 Some(Box::new(Self::compile(&arr[1])?))
690 } else {
691 None
692 };
693 let threshold = if arr.len() > 2 {
694 Some(Box::new(Self::compile(&arr[2])?))
695 } else {
696 None
697 };
698 Ok(CompiledLogic::Sum(Box::new(Self::compile(&arr[0])?), field, threshold))
699 } else {
700 Ok(CompiledLogic::Sum(Box::new(Self::compile(args)?), None, None))
701 }
702 }
703 "FOR" => {
704 let arr = args.as_array().ok_or("FOR requires array")?;
705 if arr.len() < 3 {
706 return Err("FOR requires 3 arguments: start, end, logic".to_string());
707 }
708 Ok(CompiledLogic::For(
709 Box::new(Self::compile(&arr[0])?),
710 Box::new(Self::compile(&arr[1])?),
711 Box::new(Self::compile(&arr[2])?),
712 ))
713 }
714
715 "VALUEAT" => {
717 let arr = args.as_array().ok_or("VALUEAT requires array")?;
718 if arr.len() < 2 {
719 return Err("VALUEAT requires at least 2 arguments".to_string());
720 }
721 let col_name = if arr.len() > 2 {
722 Some(Box::new(Self::compile(&arr[2])?))
723 } else {
724 None
725 };
726 Ok(CompiledLogic::ValueAt(
727 Box::new(Self::compile(&arr[0])?),
728 Box::new(Self::compile(&arr[1])?),
729 col_name,
730 ))
731 }
732 "MAXAT" => Self::compile_binary(args, |a, b| CompiledLogic::MaxAt(a, b)),
733 "INDEXAT" => {
734 let arr = args.as_array().ok_or("INDEXAT requires array")?;
735 if arr.len() < 3 {
736 return Err("INDEXAT requires at least 3 arguments".to_string());
737 }
738 let range = if arr.len() > 3 {
739 Some(Box::new(Self::compile(&arr[3])?))
740 } else {
741 None
742 };
743 Ok(CompiledLogic::IndexAt(
744 Box::new(Self::compile(&arr[0])?),
745 Box::new(Self::compile(&arr[1])?),
746 Box::new(Self::compile(&arr[2])?),
747 range,
748 ))
749 }
750 "MATCH" => {
751 let arr = args.as_array().ok_or("MATCH requires array")?;
752 if arr.is_empty() {
753 return Err("MATCH requires at least 1 argument".to_string());
754 }
755 let table = Box::new(Self::compile(&arr[0])?);
756 let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
757 Ok(CompiledLogic::Match(table, conditions?))
758 }
759 "MATCHRANGE" => {
760 let arr = args.as_array().ok_or("MATCHRANGE requires array")?;
761 if arr.is_empty() {
762 return Err("MATCHRANGE requires at least 1 argument".to_string());
763 }
764 let table = Box::new(Self::compile(&arr[0])?);
765 let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
766 Ok(CompiledLogic::MatchRange(table, conditions?))
767 }
768 "CHOOSE" => {
769 let arr = args.as_array().ok_or("CHOOSE requires array")?;
770 if arr.is_empty() {
771 return Err("CHOOSE requires at least 1 argument".to_string());
772 }
773 let table = Box::new(Self::compile(&arr[0])?);
774 let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
775 Ok(CompiledLogic::Choose(table, conditions?))
776 }
777 "FINDINDEX" => {
778 let arr = args.as_array().ok_or("FINDINDEX requires array")?;
779 if arr.len() < 2 {
780 return Err("FINDINDEX requires at least 2 arguments".to_string());
781 }
782 let table = Box::new(Self::compile(&arr[0])?);
783 let conditions: Result<Vec<_>, _> = arr[1..]
786 .iter()
787 .map(|cond| Self::compile(&Self::preprocess_table_condition(cond)))
788 .collect();
789 Ok(CompiledLogic::FindIndex(table, conditions?))
790 }
791
792 "MULTIPLIES" => {
794 let arr = args.as_array().ok_or("MULTIPLIES requires array")?;
795 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
796 Ok(CompiledLogic::Multiplies(compiled?))
797 }
798 "DIVIDES" => {
799 let arr = args.as_array().ok_or("DIVIDES requires array")?;
800 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
801 Ok(CompiledLogic::Divides(compiled?))
802 }
803
804 "YEARFRAC" => {
806 let arr = args.as_array().ok_or("YEARFRAC requires array")?;
807 if arr.len() < 2 {
808 return Err("YEARFRAC requires at least 2 arguments".to_string());
809 }
810 let basis = if arr.len() > 2 {
811 Some(Box::new(Self::compile(&arr[2])?))
812 } else {
813 None
814 };
815 Ok(CompiledLogic::YearFrac(
816 Box::new(Self::compile(&arr[0])?),
817 Box::new(Self::compile(&arr[1])?),
818 basis,
819 ))
820 }
821 "DATEDIF" => {
822 let arr = args.as_array().ok_or("DATEDIF requires array")?;
823 if arr.len() < 3 {
824 return Err("DATEDIF requires 3 arguments".to_string());
825 }
826 Ok(CompiledLogic::DateDif(
827 Box::new(Self::compile(&arr[0])?),
828 Box::new(Self::compile(&arr[1])?),
829 Box::new(Self::compile(&arr[2])?),
830 ))
831 }
832
833 "RANGEOPTIONS" => Self::compile_binary(args, |a, b| CompiledLogic::RangeOptions(a, b)),
835 "MAPOPTIONS" => {
836 let arr = args.as_array().ok_or("MAPOPTIONS requires array")?;
837 if arr.len() < 3 {
838 return Err("MAPOPTIONS requires 3 arguments".to_string());
839 }
840 Ok(CompiledLogic::MapOptions(
841 Box::new(Self::compile(&arr[0])?),
842 Box::new(Self::compile(&arr[1])?),
843 Box::new(Self::compile(&arr[2])?),
844 ))
845 }
846 "MAPOPTIONSIF" => {
847 let arr = args.as_array().ok_or("MAPOPTIONSIF requires array")?;
848 if arr.len() < 4 {
849 return Err("MAPOPTIONSIF requires at least 4 arguments".to_string());
850 }
851 let table = Box::new(Self::compile(&arr[0])?);
852 let field_label = Box::new(Self::compile(&arr[1])?);
853 let field_value = Box::new(Self::compile(&arr[2])?);
854
855 let condition_args = &arr[3..];
857 let mut conditions = Vec::new();
858
859 let mut i = 0;
860 while i + 2 < condition_args.len() {
861 let value = &condition_args[i];
862 let operator = &condition_args[i + 1];
863 let field = &condition_args[i + 2];
864
865 if let Value::String(op) = operator {
866 let field_var = if let Value::String(f) = field {
868 serde_json::json!({"var": f})
869 } else {
870 field.clone()
871 };
872
873 let comparison = serde_json::json!({
874 op: [value.clone(), field_var]
875 });
876
877 conditions.push(Self::compile(&comparison)?);
878 }
879
880 i += 3;
881 }
882
883 while i < condition_args.len() {
885 conditions.push(Self::compile(&Self::preprocess_table_condition(&condition_args[i]))?);
886 i += 1;
887 }
888
889 Ok(CompiledLogic::MapOptionsIf(table, field_label, field_value, conditions))
890 }
891 "return" => Ok(CompiledLogic::Return(Box::new(args.clone()))),
892
893 _ => Err(format!("Unknown operator: {}", op)),
894 }
895 }
896
897 fn compile_binary<F>(args: &Value, f: F) -> Result<Self, String>
898 where
899 F: FnOnce(Box<CompiledLogic>, Box<CompiledLogic>) -> CompiledLogic,
900 {
901 let arr = args.as_array().ok_or("Binary operator requires array")?;
902 if arr.len() != 2 {
903 return Err("Binary operator requires exactly 2 arguments".to_string());
904 }
905 Ok(f(
906 Box::new(Self::compile(&arr[0])?),
907 Box::new(Self::compile(&arr[1])?),
908 ))
909 }
910
911 fn preprocess_table_condition(value: &Value) -> Value {
919 match value {
920 Value::String(s) => {
921 serde_json::json!({"var": s})
923 }
924 Value::Array(arr) => {
925 if !arr.is_empty() {
927 if let Some(op_str) = arr[0].as_str() {
928 let is_comparison = matches!(op_str, "==" | "!=" | "===" | "!==" | "<" | ">" | "<=" | ">=");
930
931 if is_comparison && arr.len() >= 3 {
932 let value_arg = arr[1].clone();
936
937 let col_arg = if let Value::String(col) = &arr[2] {
939 serde_json::json!({"var": col})
941 } else {
942 Self::preprocess_table_condition(&arr[2])
944 };
945
946 let mut obj = serde_json::Map::new();
948 obj.insert(op_str.to_string(), Value::Array(vec![col_arg, value_arg]));
949 return Value::Object(obj);
950 }
951
952 let canonical_op = match op_str {
954 "&&" => Some("and"),
955 "||" => Some("or"),
956 "and" | "or" | "!" | "not" | "if" => Some(op_str),
957 _ => None,
958 };
959
960 if let Some(op_name) = canonical_op {
961 let args: Vec<Value> = arr[1..].iter()
963 .map(Self::preprocess_table_condition)
964 .collect();
965 let mut obj = serde_json::Map::new();
966 obj.insert(op_name.to_string(), Value::Array(args));
967 return Value::Object(obj);
968 }
969 }
970 }
971 Value::Array(arr.iter().map(Self::preprocess_table_condition).collect())
973 }
974 Value::Object(obj) => {
975 let mut new_obj = serde_json::Map::new();
977 for (key, val) in obj {
978 if key == "$ref" || key == "ref" || key == "var" {
980 new_obj.insert(key.clone(), val.clone());
981 } else {
982 new_obj.insert(key.clone(), Self::preprocess_table_condition(val));
983 }
984 }
985 Value::Object(new_obj)
986 }
987 _ => value.clone(),
988 }
989 }
990
991 pub fn is_simple_ref(&self) -> bool {
993 matches!(self, CompiledLogic::Ref(_, None) | CompiledLogic::Var(_, None))
994 }
995
996 pub fn referenced_vars(&self) -> Vec<String> {
998 let mut vars = Vec::new();
999 self.collect_vars(&mut vars);
1000 vars.sort();
1001 vars.dedup();
1002 vars
1003 }
1004
1005 fn flatten_and(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1007 let mut flattened = Vec::new();
1008 for item in items {
1009 match item {
1010 CompiledLogic::And(nested) => {
1011 flattened.extend(Self::flatten_and(nested));
1013 }
1014 _ => flattened.push(item),
1015 }
1016 }
1017 flattened
1018 }
1019
1020 fn flatten_or(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1022 let mut flattened = Vec::new();
1023 for item in items {
1024 match item {
1025 CompiledLogic::Or(nested) => {
1026 flattened.extend(Self::flatten_or(nested));
1028 }
1029 _ => flattened.push(item),
1030 }
1031 }
1032 flattened
1033 }
1034
1035 fn flatten_add(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1037 let mut flattened = Vec::new();
1038 for item in items {
1039 match item {
1040 CompiledLogic::Add(nested) => {
1041 flattened.extend(Self::flatten_add(nested));
1043 }
1044 _ => flattened.push(item),
1045 }
1046 }
1047 flattened
1048 }
1049
1050 fn flatten_multiply(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1052 let mut flattened = Vec::new();
1053 for item in items {
1054 match item {
1055 CompiledLogic::Multiply(nested) => {
1056 flattened.extend(Self::flatten_multiply(nested));
1058 }
1059 _ => flattened.push(item),
1060 }
1061 }
1062 flattened
1063 }
1064
1065 fn flatten_cat(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1068 let mut flattened = Vec::new();
1069 for item in items {
1070 match &item {
1071 CompiledLogic::Cat(nested) => {
1072 flattened.extend(Self::flatten_cat(nested.clone()));
1074 }
1075 _ => flattened.push(item),
1076 }
1077 }
1078 flattened
1079 }
1080
1081 pub fn has_forward_reference(&self) -> bool {
1084 let result = self.check_forward_reference();
1085 result
1086 }
1087
1088 fn check_forward_reference(&self) -> bool {
1089 match self {
1090 CompiledLogic::ValueAt(table, idx_logic, col_name) => {
1092 let has_fwd = idx_logic.contains_iteration_plus_positive();
1094 if has_fwd {
1095 return true;
1096 }
1097 let table_fwd = table.check_forward_reference();
1099 let idx_fwd = idx_logic.check_forward_reference();
1100 let col_fwd = col_name.as_ref().map(|c| c.check_forward_reference()).unwrap_or(false);
1101 table_fwd || idx_fwd || col_fwd
1102 }
1103 CompiledLogic::Array(arr) => {
1105 arr.iter().any(|item| item.check_forward_reference())
1106 }
1107 CompiledLogic::And(items) | CompiledLogic::Or(items)
1108 | CompiledLogic::Add(items) | CompiledLogic::Subtract(items)
1109 | CompiledLogic::Multiply(items) | CompiledLogic::Divide(items)
1110 | CompiledLogic::Merge(items) | CompiledLogic::Cat(items)
1111 | CompiledLogic::Max(items) | CompiledLogic::Min(items)
1112 | CompiledLogic::Concat(items) | CompiledLogic::Multiplies(items)
1113 | CompiledLogic::Divides(items) => {
1114 items.iter().any(|item| item.check_forward_reference())
1115 }
1116 CompiledLogic::Not(a) | CompiledLogic::Abs(a)
1117 | CompiledLogic::Length(a) | CompiledLogic::Len(a) | CompiledLogic::IsEmpty(a)
1118 | CompiledLogic::Year(a) | CompiledLogic::Month(a) | CompiledLogic::Day(a) => a.check_forward_reference(),
1119 CompiledLogic::Round(a, decimals) | CompiledLogic::RoundUp(a, decimals) | CompiledLogic::RoundDown(a, decimals)
1120 | CompiledLogic::Ceiling(a, decimals) | CompiledLogic::Floor(a, decimals) | CompiledLogic::Trunc(a, decimals)
1121 | CompiledLogic::DateFormat(a, decimals) => {
1122 a.check_forward_reference() || decimals.as_ref().map_or(false, |d| d.check_forward_reference())
1123 }
1124 CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1125 a.check_forward_reference()
1126 || decimals.as_ref().map_or(false, |d| d.check_forward_reference())
1127 || prefix.as_ref().map_or(false, |p| p.check_forward_reference())
1128 || suffix.as_ref().map_or(false, |s| s.check_forward_reference())
1129 || sep.as_ref().map_or(false, |s| s.check_forward_reference())
1130 }
1131 CompiledLogic::Mround(a, b) => a.check_forward_reference() || b.check_forward_reference(),
1132 CompiledLogic::Return(_) => false, CompiledLogic::If(cond, then_val, else_val) => {
1134 cond.check_forward_reference() || then_val.check_forward_reference() || else_val.check_forward_reference()
1135 }
1136 CompiledLogic::Equal(a, b) | CompiledLogic::StrictEqual(a, b)
1137 | CompiledLogic::NotEqual(a, b) | CompiledLogic::StrictNotEqual(a, b)
1138 | CompiledLogic::LessThan(a, b) | CompiledLogic::LessThanOrEqual(a, b)
1139 | CompiledLogic::GreaterThan(a, b) | CompiledLogic::GreaterThanOrEqual(a, b)
1140 | CompiledLogic::Modulo(a, b) | CompiledLogic::Power(a, b)
1141 | CompiledLogic::Map(a, b) | CompiledLogic::Filter(a, b)
1142 | CompiledLogic::All(a, b) | CompiledLogic::Some(a, b)
1143 | CompiledLogic::None(a, b) | CompiledLogic::In(a, b)
1144 | CompiledLogic::Pow(a, b) | CompiledLogic::Xor(a, b)
1145 | CompiledLogic::IfNull(a, b) | CompiledLogic::Days(a, b)
1146 | CompiledLogic::SplitValue(a, b) | CompiledLogic::MaxAt(a, b)
1147 | CompiledLogic::RangeOptions(a, b) => {
1148 a.check_forward_reference() || b.check_forward_reference()
1149 }
1150 CompiledLogic::Reduce(a, b, c) | CompiledLogic::Mid(a, b, c)
1151 | CompiledLogic::Date(a, b, c) | CompiledLogic::DateDif(a, b, c)
1152 | CompiledLogic::MapOptions(a, b, c) | CompiledLogic::For(a, b, c) => {
1153 a.check_forward_reference() || b.check_forward_reference() || c.check_forward_reference()
1154 }
1155 CompiledLogic::Substr(s, start, len) | CompiledLogic::Search(s, start, len)
1156 | CompiledLogic::SplitText(s, start, len) | CompiledLogic::YearFrac(s, start, len) => {
1157 s.check_forward_reference() || start.check_forward_reference()
1158 || len.as_ref().map(|l| l.check_forward_reference()).unwrap_or(false)
1159 }
1160 CompiledLogic::Left(a, opt) | CompiledLogic::Right(a, opt) => {
1161 a.check_forward_reference() || opt.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1162 }
1163 CompiledLogic::Sum(a, opt1, opt2) => {
1164 a.check_forward_reference()
1165 || opt1.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1166 || opt2.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1167 }
1168 CompiledLogic::IndexAt(a, b, c, opt) => {
1169 a.check_forward_reference() || b.check_forward_reference()
1170 || c.check_forward_reference() || opt.as_ref().map(|o| o.check_forward_reference()).unwrap_or(false)
1171 }
1172 CompiledLogic::Match(table, conds) | CompiledLogic::MatchRange(table, conds)
1173 | CompiledLogic::Choose(table, conds) | CompiledLogic::FindIndex(table, conds) => {
1174 table.check_forward_reference() || conds.iter().any(|c| c.check_forward_reference())
1175 }
1176 CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1177 table.check_forward_reference() || label.check_forward_reference()
1178 || value.check_forward_reference() || conds.iter().any(|c| c.check_forward_reference())
1179 }
1180 CompiledLogic::MissingSome(min, _) => {
1181 min.check_forward_reference()
1182 }
1183 _ => false,
1184 }
1185 }
1186
1187 fn contains_iteration_plus_positive(&self) -> bool {
1189 match self {
1190 CompiledLogic::Add(items) => {
1191 let has_iteration = items.iter().any(|item| {
1193 item.referenced_vars().iter().any(|v| v == "$iteration")
1194 });
1195
1196 let has_positive = items.iter().any(|item| match item {
1197 CompiledLogic::Number(n) => {
1198 n.parse::<f64>().unwrap_or(0.0) > 0.0
1199 },
1200 _ => false,
1201 });
1202
1203 let result = has_iteration && has_positive;
1204 result
1205 }
1206 _ => false,
1207 }
1208 }
1209
1210 fn normalize_ref_path(path: &str) -> String {
1214 let mut normalized = path.to_string();
1215
1216 if normalized.starts_with("#/") {
1218 normalized = normalized[2..].to_string();
1219 } else if normalized.starts_with('/') {
1220 normalized = normalized[1..].to_string();
1221 }
1222
1223 normalized = normalized.replace('/', ".");
1225
1226 normalized = normalized.replace("properties.", "");
1228 normalized = normalized.replace(".properties.", ".");
1229
1230 while normalized.contains("..") {
1232 normalized = normalized.replace("..", ".");
1233 }
1234
1235 normalized = normalized.trim_matches('.').to_string();
1237
1238 normalized
1239 }
1240
1241 pub fn collect_vars(&self, vars: &mut Vec<String>) {
1242 match self {
1243 CompiledLogic::Var(name, default) => {
1244 vars.push(name.clone());
1245 if let Some(def) = default {
1246 def.collect_vars(vars);
1247 }
1248 }
1249 CompiledLogic::Ref(path, default) => {
1250 vars.push(Self::normalize_ref_path(path));
1252 if let Some(def) = default {
1253 def.collect_vars(vars);
1254 }
1255 }
1256 CompiledLogic::Array(arr) => {
1257 for item in arr {
1258 item.collect_vars(vars);
1259 }
1260 }
1261 CompiledLogic::And(items) | CompiledLogic::Or(items)
1262 | CompiledLogic::Add(items) | CompiledLogic::Subtract(items)
1263 | CompiledLogic::Multiply(items) | CompiledLogic::Divide(items)
1264 | CompiledLogic::Merge(items) | CompiledLogic::Cat(items)
1265 | CompiledLogic::Max(items) | CompiledLogic::Min(items)
1266 | CompiledLogic::Concat(items) => {
1267 for item in items {
1268 item.collect_vars(vars);
1269 }
1270 }
1271 CompiledLogic::Not(a) | CompiledLogic::Abs(a)
1272 | CompiledLogic::Length(a) | CompiledLogic::Len(a) | CompiledLogic::IsEmpty(a)
1273 | CompiledLogic::Year(a) | CompiledLogic::Month(a) | CompiledLogic::Day(a) => {
1274 a.collect_vars(vars);
1275 }
1276 CompiledLogic::Round(a, decimals) | CompiledLogic::RoundUp(a, decimals) | CompiledLogic::RoundDown(a, decimals)
1277 | CompiledLogic::Ceiling(a, decimals) | CompiledLogic::Floor(a, decimals) | CompiledLogic::Trunc(a, decimals)
1278 | CompiledLogic::DateFormat(a, decimals) => {
1279 a.collect_vars(vars);
1280 if let Some(d) = decimals {
1281 d.collect_vars(vars);
1282 }
1283 }
1284 CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1285 a.collect_vars(vars);
1286 if let Some(d) = decimals { d.collect_vars(vars); }
1287 if let Some(p) = prefix { p.collect_vars(vars); }
1288 if let Some(s) = suffix { s.collect_vars(vars); }
1289 if let Some(s) = sep { s.collect_vars(vars); }
1290 }
1291 CompiledLogic::Mround(a, b) => {
1292 a.collect_vars(vars);
1293 b.collect_vars(vars);
1294 }
1295 CompiledLogic::Return(_) => {} CompiledLogic::If(cond, then_val, else_val) => {
1297 cond.collect_vars(vars);
1298 then_val.collect_vars(vars);
1299 else_val.collect_vars(vars);
1300 }
1301 CompiledLogic::Equal(a, b) | CompiledLogic::StrictEqual(a, b)
1302 | CompiledLogic::NotEqual(a, b) | CompiledLogic::StrictNotEqual(a, b)
1303 | CompiledLogic::LessThan(a, b) | CompiledLogic::LessThanOrEqual(a, b)
1304 | CompiledLogic::GreaterThan(a, b) | CompiledLogic::GreaterThanOrEqual(a, b)
1305 | CompiledLogic::Modulo(a, b) | CompiledLogic::Power(a, b)
1306 | CompiledLogic::Map(a, b) | CompiledLogic::Filter(a, b)
1307 | CompiledLogic::All(a, b) | CompiledLogic::Some(a, b)
1308 | CompiledLogic::None(a, b) | CompiledLogic::In(a, b)
1309 | CompiledLogic::Pow(a, b) | CompiledLogic::Xor(a, b)
1310 | CompiledLogic::IfNull(a, b) | CompiledLogic::Days(a, b)
1311 | CompiledLogic::SplitValue(a, b) | CompiledLogic::MaxAt(a, b)
1312 | CompiledLogic::RangeOptions(a, b) => {
1313 a.collect_vars(vars);
1314 b.collect_vars(vars);
1315 }
1316 CompiledLogic::Reduce(a, b, c) | CompiledLogic::Mid(a, b, c)
1317 | CompiledLogic::Date(a, b, c) | CompiledLogic::DateDif(a, b, c)
1318 | CompiledLogic::MapOptions(a, b, c) | CompiledLogic::For(a, b, c) => {
1319 a.collect_vars(vars);
1320 b.collect_vars(vars);
1321 c.collect_vars(vars);
1322 }
1323 CompiledLogic::Substr(s, start, len) | CompiledLogic::Search(s, start, len)
1324 | CompiledLogic::SplitText(s, start, len) | CompiledLogic::YearFrac(s, start, len) => {
1325 s.collect_vars(vars);
1326 start.collect_vars(vars);
1327 if let Some(l) = len {
1328 l.collect_vars(vars);
1329 }
1330 }
1331 CompiledLogic::Left(a, opt) | CompiledLogic::Right(a, opt)
1332 | CompiledLogic::ValueAt(a, _, opt) => {
1333 a.collect_vars(vars);
1334 if let Some(o) = opt {
1335 o.collect_vars(vars);
1336 }
1337 }
1338 CompiledLogic::Sum(a, opt1, opt2) => {
1339 a.collect_vars(vars);
1340 if let Some(o) = opt1 {
1341 o.collect_vars(vars);
1342 }
1343 if let Some(o) = opt2 {
1344 o.collect_vars(vars);
1345 }
1346 }
1347 CompiledLogic::IndexAt(a, b, c, opt) => {
1348 a.collect_vars(vars);
1349 b.collect_vars(vars);
1350 c.collect_vars(vars);
1351 if let Some(o) = opt {
1352 o.collect_vars(vars);
1353 }
1354 }
1355 CompiledLogic::Match(table, conds) | CompiledLogic::MatchRange(table, conds)
1356 | CompiledLogic::Choose(table, conds) | CompiledLogic::FindIndex(table, conds) => {
1357 table.collect_vars(vars);
1358 for cond in conds {
1359 cond.collect_vars(vars);
1360 }
1361 }
1362 CompiledLogic::Multiplies(items) | CompiledLogic::Divides(items) => {
1363 for item in items {
1364 item.collect_vars(vars);
1365 }
1366 }
1367 CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1368 table.collect_vars(vars);
1369 label.collect_vars(vars);
1370 value.collect_vars(vars);
1371 for cond in conds {
1372 cond.collect_vars(vars);
1373 }
1374 }
1375 CompiledLogic::MissingSome(min, _) => {
1376 min.collect_vars(vars);
1377 }
1378 _ => {}
1379 }
1380 }
1381}
1382
1383pub struct CompiledLogicStore {
1389 next_id: u64,
1390 store: AHashMap<LogicId, CompiledLogic>,
1391 dependencies: AHashMap<LogicId, Vec<String>>,
1392}
1393
1394impl CompiledLogicStore {
1395 pub fn new() -> Self {
1396 Self {
1397 next_id: 0,
1398 store: AHashMap::default(),
1399 dependencies: AHashMap::default(),
1400 }
1401 }
1402
1403 pub fn compile(&mut self, logic: &Value) -> Result<LogicId, String> {
1409 let _global_id = super::compiled_logic_store::compile_logic_value(logic)?;
1411
1412 let compiled = super::compiled_logic_store::get_compiled_logic(_global_id)
1414 .ok_or_else(|| "Failed to retrieve compiled logic from global store".to_string())?;
1415
1416 let deps = compiled.referenced_vars();
1418
1419 let id = LogicId(self.next_id);
1421 self.next_id += 1;
1422
1423 self.store.insert(id, compiled);
1425 self.dependencies.insert(id, deps);
1426
1427 Ok(id)
1428 }
1429
1430 pub fn get(&self, id: &LogicId) -> Option<&CompiledLogic> {
1432 self.store.get(id)
1433 }
1434
1435 pub fn remove(&mut self, id: &LogicId) -> Option<CompiledLogic> {
1437 self.dependencies.remove(id);
1438 self.store.remove(id)
1439 }
1440
1441 pub fn get_dependencies(&self, id: &LogicId) -> Option<&[String]> {
1443 self.dependencies.get(id).map(|v| v.as_slice())
1444 }
1445}
1446
1447impl Default for CompiledLogicStore {
1448 fn default() -> Self {
1449 Self::new()
1450 }
1451}