1use crate::path_utils;
2use ahash::AHashMap;
3use serde::Serialize;
4use serde_json::Value;
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(
61 Box<CompiledLogic>,
62 Box<CompiledLogic>,
63 Option<Box<CompiledLogic>>,
64 ), Missing(Vec<String>),
68 MissingSome(Box<CompiledLogic>, Vec<String>), Abs(Box<CompiledLogic>),
72 Max(Vec<CompiledLogic>),
73 Min(Vec<CompiledLogic>),
74 Pow(Box<CompiledLogic>, Box<CompiledLogic>),
75 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>),
85 Search(
86 Box<CompiledLogic>,
87 Box<CompiledLogic>,
88 Option<Box<CompiledLogic>>,
89 ), Left(Box<CompiledLogic>, Option<Box<CompiledLogic>>), Right(Box<CompiledLogic>, Option<Box<CompiledLogic>>), Mid(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), Len(Box<CompiledLogic>),
94 SplitText(
95 Box<CompiledLogic>,
96 Box<CompiledLogic>,
97 Option<Box<CompiledLogic>>,
98 ), Concat(Vec<CompiledLogic>),
100 SplitValue(Box<CompiledLogic>, Box<CompiledLogic>), StringFormat(
102 Box<CompiledLogic>,
103 Option<Box<CompiledLogic>>,
104 Option<Box<CompiledLogic>>,
105 Option<Box<CompiledLogic>>,
106 Option<Box<CompiledLogic>>,
107 ), Xor(Box<CompiledLogic>, Box<CompiledLogic>),
111 IfNull(Box<CompiledLogic>, Box<CompiledLogic>),
112 IsEmpty(Box<CompiledLogic>),
113 Empty,
114
115 Today,
117 Now,
118 Days(Box<CompiledLogic>, Box<CompiledLogic>), Year(Box<CompiledLogic>),
120 Month(Box<CompiledLogic>),
121 Day(Box<CompiledLogic>),
122 Date(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), DateFormat(Box<CompiledLogic>, Option<Box<CompiledLogic>>), Sum(
127 Box<CompiledLogic>,
128 Option<Box<CompiledLogic>>,
129 Option<Box<CompiledLogic>>,
130 ), For(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), ValueAt(
135 Box<CompiledLogic>,
136 Box<CompiledLogic>,
137 Option<Box<CompiledLogic>>,
138 ), MaxAt(Box<CompiledLogic>, Box<CompiledLogic>), IndexAt(
141 Box<CompiledLogic>,
142 Box<CompiledLogic>,
143 Box<CompiledLogic>,
144 Option<Box<CompiledLogic>>,
145 ), 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(
157 Box<CompiledLogic>,
158 Box<CompiledLogic>,
159 Option<Box<CompiledLogic>>,
160 ), DateDif(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), RangeOptions(Box<CompiledLogic>, Box<CompiledLogic>), MapOptions(Box<CompiledLogic>, Box<CompiledLogic>, Box<CompiledLogic>), MapOptionsIf(
167 Box<CompiledLogic>,
168 Box<CompiledLogic>,
169 Box<CompiledLogic>,
170 Vec<CompiledLogic>,
171 ), Return(Box<Value>), }
174
175impl CompiledLogic {
176 pub fn compile(logic: &Value) -> Result<Self, String> {
178 match logic {
179 Value::Null => Ok(CompiledLogic::Null),
180 Value::Bool(b) => Ok(CompiledLogic::Bool(*b)),
181 Value::Number(n) => {
182 Ok(CompiledLogic::Number(n.to_string()))
184 }
185 Value::String(s) => Ok(CompiledLogic::String(s.clone())),
186 Value::Array(arr) => {
187 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
188 Ok(CompiledLogic::Array(compiled?))
189 }
190 Value::Object(obj) => {
191 if obj.is_empty() {
192 return Ok(CompiledLogic::Null);
193 }
194
195 let (op, args) = obj.iter().next().unwrap();
197
198 Self::compile_operator(op, args)
199 }
200 }
201 }
202
203 fn compile_operator(op: &str, args: &Value) -> Result<Self, String> {
204 match op {
205 "var" => {
207 if let Value::String(name) = args {
208 let normalized = path_utils::normalize_to_json_pointer(name);
210 Ok(CompiledLogic::Var(normalized, None))
211 } else if let Value::Array(arr) = args {
212 if arr.is_empty() {
213 return Err("var requires at least one argument".to_string());
214 }
215 let name = arr[0].as_str().ok_or("var name must be a string")?;
216 let normalized = path_utils::normalize_to_json_pointer(name);
218 let default = if arr.len() > 1 {
219 Some(Box::new(Self::compile(&arr[1])?))
220 } else {
221 None
222 };
223 Ok(CompiledLogic::Var(normalized, default))
224 } else {
225 Err("var requires string or array".to_string())
226 }
227 }
228 "$ref" | "ref" => {
229 if let Value::String(path) = args {
230 let normalized = path_utils::normalize_to_json_pointer(path);
232 Ok(CompiledLogic::Ref(normalized, None))
233 } else if let Value::Array(arr) = args {
234 if arr.is_empty() {
235 return Err("$ref requires at least one argument".to_string());
236 }
237 let path = arr[0].as_str().ok_or("$ref path must be a string")?;
238 let normalized = path_utils::normalize_to_json_pointer(path);
240 let default = if arr.len() > 1 {
241 Some(Box::new(Self::compile(&arr[1])?))
242 } else {
243 None
244 };
245 Ok(CompiledLogic::Ref(normalized, default))
246 } else {
247 Err("$ref requires string or array".to_string())
248 }
249 }
250
251 "and" => {
253 let arr = args.as_array().ok_or("and requires array")?;
254 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
255 let items = compiled?;
256 Ok(CompiledLogic::And(Self::flatten_and(items)))
258 }
259 "or" => {
260 let arr = args.as_array().ok_or("or requires array")?;
261 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
262 let items = compiled?;
263 Ok(CompiledLogic::Or(Self::flatten_or(items)))
265 }
266 "!" | "not" => {
267 let value_to_negate = if let Value::Array(arr) = args {
269 if arr.is_empty() {
270 return Err("! requires an argument".to_string());
271 }
272 &arr[0]
273 } else {
274 args
275 };
276
277 let inner = Self::compile(value_to_negate)?;
278 if let CompiledLogic::Not(inner_expr) = inner {
280 Ok(*inner_expr)
281 } else {
282 Ok(CompiledLogic::Not(Box::new(inner)))
283 }
284 }
285 "if" => {
286 let arr = args.as_array().ok_or("if requires array")?;
287 if arr.len() < 3 {
288 return Err("if requires at least 3 arguments".to_string());
289 }
290 Ok(CompiledLogic::If(
291 Box::new(Self::compile(&arr[0])?),
292 Box::new(Self::compile(&arr[1])?),
293 Box::new(Self::compile(&arr[2])?),
294 ))
295 }
296
297 "==" => Self::compile_binary(args, |a, b| CompiledLogic::Equal(a, b)),
299 "===" => Self::compile_binary(args, |a, b| CompiledLogic::StrictEqual(a, b)),
300 "!=" => Self::compile_binary(args, |a, b| CompiledLogic::NotEqual(a, b)),
301 "!==" => Self::compile_binary(args, |a, b| CompiledLogic::StrictNotEqual(a, b)),
302 "<" => Self::compile_binary(args, |a, b| CompiledLogic::LessThan(a, b)),
303 "<=" => Self::compile_binary(args, |a, b| CompiledLogic::LessThanOrEqual(a, b)),
304 ">" => Self::compile_binary(args, |a, b| CompiledLogic::GreaterThan(a, b)),
305 ">=" => Self::compile_binary(args, |a, b| CompiledLogic::GreaterThanOrEqual(a, b)),
306
307 "+" => {
309 let arr = args.as_array().ok_or("+ requires array")?;
310 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
311 let items = compiled?;
312 Ok(CompiledLogic::Add(Self::flatten_add(items)))
314 }
315 "-" => {
316 let arr = args.as_array().ok_or("- requires array")?;
317 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
318 Ok(CompiledLogic::Subtract(compiled?))
319 }
320 "*" => {
321 let arr = args.as_array().ok_or("* requires array")?;
322 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
323 let items = compiled?;
324 Ok(CompiledLogic::Multiply(Self::flatten_multiply(items)))
326 }
327 "/" => {
328 let arr = args.as_array().ok_or("/ requires array")?;
329 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
330 Ok(CompiledLogic::Divide(compiled?))
331 }
332 "%" => Self::compile_binary(args, |a, b| CompiledLogic::Modulo(a, b)),
333 "^" => Self::compile_binary(args, |a, b| CompiledLogic::Power(a, b)),
334
335 "map" => Self::compile_binary(args, |a, b| CompiledLogic::Map(a, b)),
337 "filter" => Self::compile_binary(args, |a, b| CompiledLogic::Filter(a, b)),
338 "reduce" => {
339 let arr = args.as_array().ok_or("reduce requires array")?;
340 if arr.len() < 3 {
341 return Err("reduce requires 3 arguments".to_string());
342 }
343 Ok(CompiledLogic::Reduce(
344 Box::new(Self::compile(&arr[0])?),
345 Box::new(Self::compile(&arr[1])?),
346 Box::new(Self::compile(&arr[2])?),
347 ))
348 }
349 "all" => Self::compile_binary(args, |a, b| CompiledLogic::All(a, b)),
350 "some" => Self::compile_binary(args, |a, b| CompiledLogic::Some(a, b)),
351 "none" => Self::compile_binary(args, |a, b| CompiledLogic::None(a, b)),
352 "merge" => {
353 let arr = args.as_array().ok_or("merge requires array")?;
354 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
355 Ok(CompiledLogic::Merge(compiled?))
356 }
357 "in" => Self::compile_binary(args, |a, b| CompiledLogic::In(a, b)),
358
359 "cat" => {
361 let arr = args.as_array().ok_or("cat requires array")?;
362 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
363 let items = compiled?;
364 Ok(CompiledLogic::Cat(Self::flatten_cat(items)))
366 }
367 "substr" => {
368 let arr = args.as_array().ok_or("substr requires array")?;
369 if arr.len() < 2 {
370 return Err("substr requires at least 2 arguments".to_string());
371 }
372 let length = if arr.len() > 2 {
373 Some(Box::new(Self::compile(&arr[2])?))
374 } else {
375 None
376 };
377 Ok(CompiledLogic::Substr(
378 Box::new(Self::compile(&arr[0])?),
379 Box::new(Self::compile(&arr[1])?),
380 length,
381 ))
382 }
383
384 "missing" => {
386 let keys = if let Value::Array(arr) = args {
387 arr.iter()
388 .map(|v| {
389 v.as_str()
390 .ok_or("missing key must be string")
391 .map(|s| s.to_string())
392 })
393 .collect::<Result<Vec<_>, _>>()?
394 } else if let Value::String(s) = args {
395 vec![s.clone()]
396 } else {
397 return Err("missing requires string or array".to_string());
398 };
399 Ok(CompiledLogic::Missing(keys))
400 }
401 "missing_some" => {
402 let arr = args.as_array().ok_or("missing_some requires array")?;
403 if arr.len() < 2 {
404 return Err("missing_some requires at least 2 arguments".to_string());
405 }
406 let minimum = Box::new(Self::compile(&arr[0])?);
407 let keys = if let Value::Array(key_arr) = &arr[1] {
408 key_arr
409 .iter()
410 .map(|v| {
411 v.as_str()
412 .ok_or("key must be string")
413 .map(|s| s.to_string())
414 })
415 .collect::<Result<Vec<_>, _>>()?
416 } else {
417 return Err("missing_some keys must be array".to_string());
418 };
419 Ok(CompiledLogic::MissingSome(minimum, keys))
420 }
421
422 "abs" => {
424 let arg = if let Value::Array(arr) = args {
425 if arr.is_empty() {
426 return Err("abs requires at least one argument".to_string());
427 }
428 &arr[0]
429 } else {
430 args
431 };
432 Ok(CompiledLogic::Abs(Box::new(Self::compile(arg)?)))
433 }
434 "max" => {
435 let arr = args.as_array().ok_or("max requires array")?;
436 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
437 Ok(CompiledLogic::Max(compiled?))
438 }
439 "min" => {
440 let arr = args.as_array().ok_or("min requires array")?;
441 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
442 Ok(CompiledLogic::Min(compiled?))
443 }
444 "pow" | "**" => Self::compile_binary(args, |a, b| CompiledLogic::Pow(a, b)),
445 "round" | "ROUND" => {
446 if let Value::Array(arr) = args {
447 let decimals = if arr.len() > 1 {
448 Some(Box::new(Self::compile(&arr[1])?))
449 } else {
450 None
451 };
452 Ok(CompiledLogic::Round(
453 Box::new(Self::compile(&arr[0])?),
454 decimals,
455 ))
456 } else {
457 Ok(CompiledLogic::Round(Box::new(Self::compile(args)?), None))
458 }
459 }
460 "roundup" | "ROUNDUP" => {
461 if let Value::Array(arr) = args {
462 let decimals = if arr.len() > 1 {
463 Some(Box::new(Self::compile(&arr[1])?))
464 } else {
465 None
466 };
467 Ok(CompiledLogic::RoundUp(
468 Box::new(Self::compile(&arr[0])?),
469 decimals,
470 ))
471 } else {
472 Ok(CompiledLogic::RoundUp(Box::new(Self::compile(args)?), None))
473 }
474 }
475 "rounddown" | "ROUNDDOWN" => {
476 if let Value::Array(arr) = args {
477 let decimals = if arr.len() > 1 {
478 Some(Box::new(Self::compile(&arr[1])?))
479 } else {
480 None
481 };
482 Ok(CompiledLogic::RoundDown(
483 Box::new(Self::compile(&arr[0])?),
484 decimals,
485 ))
486 } else {
487 Ok(CompiledLogic::RoundDown(
488 Box::new(Self::compile(args)?),
489 None,
490 ))
491 }
492 }
493 "ceiling" | "CEILING" => {
494 if let Value::Array(arr) = args {
495 let significance = if arr.len() > 1 {
496 Some(Box::new(Self::compile(&arr[1])?))
497 } else {
498 None
499 };
500 Ok(CompiledLogic::Ceiling(
501 Box::new(Self::compile(&arr[0])?),
502 significance,
503 ))
504 } else {
505 Ok(CompiledLogic::Ceiling(Box::new(Self::compile(args)?), None))
506 }
507 }
508 "floor" | "FLOOR" => {
509 if let Value::Array(arr) = args {
510 let significance = if arr.len() > 1 {
511 Some(Box::new(Self::compile(&arr[1])?))
512 } else {
513 None
514 };
515 Ok(CompiledLogic::Floor(
516 Box::new(Self::compile(&arr[0])?),
517 significance,
518 ))
519 } else {
520 Ok(CompiledLogic::Floor(Box::new(Self::compile(args)?), None))
521 }
522 }
523 "trunc" | "TRUNC" => {
524 if let Value::Array(arr) = args {
525 let decimals = if arr.len() > 1 {
526 Some(Box::new(Self::compile(&arr[1])?))
527 } else {
528 None
529 };
530 Ok(CompiledLogic::Trunc(
531 Box::new(Self::compile(&arr[0])?),
532 decimals,
533 ))
534 } else {
535 Ok(CompiledLogic::Trunc(Box::new(Self::compile(args)?), None))
536 }
537 }
538 "mround" | "MROUND" => Self::compile_binary(args, |a, b| CompiledLogic::Mround(a, b)),
539
540 "length" => {
542 let arg = if let Value::Array(arr) = args {
543 if arr.is_empty() {
544 return Err("length requires at least one argument".to_string());
545 }
546 &arr[0]
547 } else {
548 args
549 };
550 Ok(CompiledLogic::Length(Box::new(Self::compile(arg)?)))
551 }
552 "len" | "LEN" => {
553 let arg = if let Value::Array(arr) = args {
554 if arr.is_empty() {
555 return Err("len requires at least one argument".to_string());
556 }
557 &arr[0]
558 } else {
559 args
560 };
561 Ok(CompiledLogic::Len(Box::new(Self::compile(arg)?)))
562 }
563 "search" | "SEARCH" => {
564 let arr = args.as_array().ok_or("search requires array")?;
565 if arr.len() < 2 {
566 return Err("search requires at least 2 arguments".to_string());
567 }
568 let start_num = if arr.len() > 2 {
569 Some(Box::new(Self::compile(&arr[2])?))
570 } else {
571 None
572 };
573 Ok(CompiledLogic::Search(
574 Box::new(Self::compile(&arr[0])?),
575 Box::new(Self::compile(&arr[1])?),
576 start_num,
577 ))
578 }
579 "left" | "LEFT" => {
580 if let Value::Array(arr) = args {
581 let num_chars = if arr.len() > 1 {
582 Some(Box::new(Self::compile(&arr[1])?))
583 } else {
584 None
585 };
586 Ok(CompiledLogic::Left(
587 Box::new(Self::compile(&arr[0])?),
588 num_chars,
589 ))
590 } else {
591 Ok(CompiledLogic::Left(Box::new(Self::compile(args)?), None))
592 }
593 }
594 "right" | "RIGHT" => {
595 if let Value::Array(arr) = args {
596 let num_chars = if arr.len() > 1 {
597 Some(Box::new(Self::compile(&arr[1])?))
598 } else {
599 None
600 };
601 Ok(CompiledLogic::Right(
602 Box::new(Self::compile(&arr[0])?),
603 num_chars,
604 ))
605 } else {
606 Ok(CompiledLogic::Right(Box::new(Self::compile(args)?), None))
607 }
608 }
609 "mid" | "MID" => {
610 let arr = args.as_array().ok_or("mid requires array")?;
611 if arr.len() < 3 {
612 return Err("mid requires 3 arguments".to_string());
613 }
614 Ok(CompiledLogic::Mid(
615 Box::new(Self::compile(&arr[0])?),
616 Box::new(Self::compile(&arr[1])?),
617 Box::new(Self::compile(&arr[2])?),
618 ))
619 }
620 "splittext" | "SPLITTEXT" => {
621 let arr = args.as_array().ok_or("splittext requires array")?;
622 if arr.len() < 2 {
623 return Err("splittext requires at least 2 arguments".to_string());
624 }
625 let index = if arr.len() > 2 {
626 Some(Box::new(Self::compile(&arr[2])?))
627 } else {
628 None
629 };
630 Ok(CompiledLogic::SplitText(
631 Box::new(Self::compile(&arr[0])?),
632 Box::new(Self::compile(&arr[1])?),
633 index,
634 ))
635 }
636 "concat" | "CONCAT" => {
637 let arr = args.as_array().ok_or("concat requires array")?;
638 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
639 Ok(CompiledLogic::Concat(compiled?))
640 }
641 "splitvalue" | "SPLITVALUE" => {
642 Self::compile_binary(args, |a, b| CompiledLogic::SplitValue(a, b))
643 }
644 "stringformat" | "STRINGFORMAT" => {
645 let arr = args.as_array().ok_or("stringformat requires array")?;
646 if arr.is_empty() {
647 return Err("stringformat requires at least 1 argument".to_string());
648 }
649 let decimals = if arr.len() > 1 {
650 Some(Box::new(Self::compile(&arr[1])?))
651 } else {
652 None
653 };
654 let prefix = if arr.len() > 2 {
655 Some(Box::new(Self::compile(&arr[2])?))
656 } else {
657 None
658 };
659 let suffix = if arr.len() > 3 {
660 Some(Box::new(Self::compile(&arr[3])?))
661 } else {
662 None
663 };
664 let thousands_sep = if arr.len() > 4 {
665 Some(Box::new(Self::compile(&arr[4])?))
666 } else {
667 None
668 };
669 Ok(CompiledLogic::StringFormat(
670 Box::new(Self::compile(&arr[0])?),
671 decimals,
672 prefix,
673 suffix,
674 thousands_sep,
675 ))
676 }
677
678 "xor" => Self::compile_binary(args, |a, b| CompiledLogic::Xor(a, b)),
680 "ifnull" | "IFNULL" => Self::compile_binary(args, |a, b| CompiledLogic::IfNull(a, b)),
681 "isempty" | "ISEMPTY" => {
682 let arg = if let Value::Array(arr) = args {
683 if arr.is_empty() {
684 return Err("ISEMPTY requires at least one argument".to_string());
685 }
686 &arr[0]
687 } else {
688 args
689 };
690 Ok(CompiledLogic::IsEmpty(Box::new(Self::compile(arg)?)))
691 }
692 "empty" | "EMPTY" => Ok(CompiledLogic::Empty),
693
694 "today" | "TODAY" => Ok(CompiledLogic::Today),
696 "now" | "NOW" => Ok(CompiledLogic::Now),
697 "days" | "DAYS" => Self::compile_binary(args, |a, b| CompiledLogic::Days(a, b)),
698 "year" | "YEAR" => {
699 let arg = if let Value::Array(arr) = args {
700 if arr.is_empty() {
701 return Err("year requires at least one argument".to_string());
702 }
703 &arr[0]
704 } else {
705 args
706 };
707 Ok(CompiledLogic::Year(Box::new(Self::compile(arg)?)))
708 }
709 "month" | "MONTH" => {
710 let arg = if let Value::Array(arr) = args {
711 if arr.is_empty() {
712 return Err("month requires at least one argument".to_string());
713 }
714 &arr[0]
715 } else {
716 args
717 };
718 Ok(CompiledLogic::Month(Box::new(Self::compile(arg)?)))
719 }
720 "day" | "DAY" => {
721 let arg = if let Value::Array(arr) = args {
722 if arr.is_empty() {
723 return Err("day requires at least one argument".to_string());
724 }
725 &arr[0]
726 } else {
727 args
728 };
729 Ok(CompiledLogic::Day(Box::new(Self::compile(arg)?)))
730 }
731 "date" | "DATE" => {
732 let arr = args.as_array().ok_or("date requires array")?;
733 if arr.len() < 3 {
734 return Err("date requires 3 arguments".to_string());
735 }
736 Ok(CompiledLogic::Date(
737 Box::new(Self::compile(&arr[0])?),
738 Box::new(Self::compile(&arr[1])?),
739 Box::new(Self::compile(&arr[2])?),
740 ))
741 }
742 "dateformat" | "DATEFORMAT" => {
743 if let Value::Array(arr) = args {
744 if arr.is_empty() {
745 return Err("dateformat requires at least 1 argument".to_string());
746 }
747 let format = if arr.len() > 1 {
748 Some(Box::new(Self::compile(&arr[1])?))
749 } else {
750 None
751 };
752 Ok(CompiledLogic::DateFormat(
753 Box::new(Self::compile(&arr[0])?),
754 format,
755 ))
756 } else {
757 Ok(CompiledLogic::DateFormat(
758 Box::new(Self::compile(args)?),
759 None,
760 ))
761 }
762 }
763
764 "sum" | "SUM" => {
766 if let Value::Array(arr) = args {
767 if arr.is_empty() {
768 return Err("sum requires at least 1 argument".to_string());
769 }
770 let field = if arr.len() > 1 {
771 Some(Box::new(Self::compile(&arr[1])?))
772 } else {
773 None
774 };
775 let threshold = if arr.len() > 2 {
776 Some(Box::new(Self::compile(&arr[2])?))
777 } else {
778 None
779 };
780 Ok(CompiledLogic::Sum(
781 Box::new(Self::compile(&arr[0])?),
782 field,
783 threshold,
784 ))
785 } else {
786 Ok(CompiledLogic::Sum(
787 Box::new(Self::compile(args)?),
788 None,
789 None,
790 ))
791 }
792 }
793 "FOR" => {
794 let arr = args.as_array().ok_or("FOR requires array")?;
795 if arr.len() < 3 {
796 return Err("FOR requires 3 arguments: start, end, logic".to_string());
797 }
798 Ok(CompiledLogic::For(
799 Box::new(Self::compile(&arr[0])?),
800 Box::new(Self::compile(&arr[1])?),
801 Box::new(Self::compile(&arr[2])?),
802 ))
803 }
804
805 "VALUEAT" => {
807 let arr = args.as_array().ok_or("VALUEAT requires array")?;
808 if arr.len() < 2 {
809 return Err("VALUEAT requires at least 2 arguments".to_string());
810 }
811 let col_name = if arr.len() > 2 {
812 Some(Box::new(Self::compile(&arr[2])?))
813 } else {
814 None
815 };
816 Ok(CompiledLogic::ValueAt(
817 Box::new(Self::compile(&arr[0])?),
818 Box::new(Self::compile(&arr[1])?),
819 col_name,
820 ))
821 }
822 "MAXAT" => Self::compile_binary(args, |a, b| CompiledLogic::MaxAt(a, b)),
823 "INDEXAT" => {
824 let arr = args.as_array().ok_or("INDEXAT requires array")?;
825 if arr.len() < 3 {
826 return Err("INDEXAT requires at least 3 arguments".to_string());
827 }
828 let range = if arr.len() > 3 {
829 Some(Box::new(Self::compile(&arr[3])?))
830 } else {
831 None
832 };
833 Ok(CompiledLogic::IndexAt(
834 Box::new(Self::compile(&arr[0])?),
835 Box::new(Self::compile(&arr[1])?),
836 Box::new(Self::compile(&arr[2])?),
837 range,
838 ))
839 }
840 "MATCH" => {
841 let arr = args.as_array().ok_or("MATCH requires array")?;
842 if arr.is_empty() {
843 return Err("MATCH requires at least 1 argument".to_string());
844 }
845 let table = Box::new(Self::compile(&arr[0])?);
846 let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
847 Ok(CompiledLogic::Match(table, conditions?))
848 }
849 "MATCHRANGE" => {
850 let arr = args.as_array().ok_or("MATCHRANGE requires array")?;
851 if arr.is_empty() {
852 return Err("MATCHRANGE requires at least 1 argument".to_string());
853 }
854 let table = Box::new(Self::compile(&arr[0])?);
855 let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
856 Ok(CompiledLogic::MatchRange(table, conditions?))
857 }
858 "CHOOSE" => {
859 let arr = args.as_array().ok_or("CHOOSE requires array")?;
860 if arr.is_empty() {
861 return Err("CHOOSE requires at least 1 argument".to_string());
862 }
863 let table = Box::new(Self::compile(&arr[0])?);
864 let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
865 Ok(CompiledLogic::Choose(table, conditions?))
866 }
867 "FINDINDEX" => {
868 let arr = args.as_array().ok_or("FINDINDEX requires array")?;
869 if arr.len() < 2 {
870 return Err("FINDINDEX requires at least 2 arguments".to_string());
871 }
872 let table = Box::new(Self::compile(&arr[0])?);
873 let conditions: Result<Vec<_>, _> = arr[1..]
876 .iter()
877 .map(|cond| Self::compile(&Self::preprocess_table_condition(cond)))
878 .collect();
879 Ok(CompiledLogic::FindIndex(table, conditions?))
880 }
881
882 "MULTIPLIES" => {
884 let arr = args.as_array().ok_or("MULTIPLIES requires array")?;
885 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
886 Ok(CompiledLogic::Multiplies(compiled?))
887 }
888 "DIVIDES" => {
889 let arr = args.as_array().ok_or("DIVIDES requires array")?;
890 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
891 Ok(CompiledLogic::Divides(compiled?))
892 }
893
894 "YEARFRAC" => {
896 let arr = args.as_array().ok_or("YEARFRAC requires array")?;
897 if arr.len() < 2 {
898 return Err("YEARFRAC requires at least 2 arguments".to_string());
899 }
900 let basis = if arr.len() > 2 {
901 Some(Box::new(Self::compile(&arr[2])?))
902 } else {
903 None
904 };
905 Ok(CompiledLogic::YearFrac(
906 Box::new(Self::compile(&arr[0])?),
907 Box::new(Self::compile(&arr[1])?),
908 basis,
909 ))
910 }
911 "DATEDIF" => {
912 let arr = args.as_array().ok_or("DATEDIF requires array")?;
913 if arr.len() < 3 {
914 return Err("DATEDIF requires 3 arguments".to_string());
915 }
916 Ok(CompiledLogic::DateDif(
917 Box::new(Self::compile(&arr[0])?),
918 Box::new(Self::compile(&arr[1])?),
919 Box::new(Self::compile(&arr[2])?),
920 ))
921 }
922
923 "RANGEOPTIONS" => Self::compile_binary(args, |a, b| CompiledLogic::RangeOptions(a, b)),
925 "MAPOPTIONS" => {
926 let arr = args.as_array().ok_or("MAPOPTIONS requires array")?;
927 if arr.len() < 3 {
928 return Err("MAPOPTIONS requires 3 arguments".to_string());
929 }
930 Ok(CompiledLogic::MapOptions(
931 Box::new(Self::compile(&arr[0])?),
932 Box::new(Self::compile(&arr[1])?),
933 Box::new(Self::compile(&arr[2])?),
934 ))
935 }
936 "MAPOPTIONSIF" => {
937 let arr = args.as_array().ok_or("MAPOPTIONSIF requires array")?;
938 if arr.len() < 4 {
939 return Err("MAPOPTIONSIF requires at least 4 arguments".to_string());
940 }
941 let table = Box::new(Self::compile(&arr[0])?);
942 let field_label = Box::new(Self::compile(&arr[1])?);
943 let field_value = Box::new(Self::compile(&arr[2])?);
944
945 let condition_args = &arr[3..];
947 let mut conditions = Vec::new();
948
949 let mut i = 0;
950 while i + 2 < condition_args.len() {
951 let value = &condition_args[i];
952 let operator = &condition_args[i + 1];
953 let field = &condition_args[i + 2];
954
955 if let Value::String(op) = operator {
956 let field_var = if let Value::String(f) = field {
958 serde_json::json!({"var": f})
959 } else {
960 field.clone()
961 };
962
963 let comparison = serde_json::json!({
964 op: [value.clone(), field_var]
965 });
966
967 conditions.push(Self::compile(&comparison)?);
968 }
969
970 i += 3;
971 }
972
973 while i < condition_args.len() {
975 conditions.push(Self::compile(&Self::preprocess_table_condition(
976 &condition_args[i],
977 ))?);
978 i += 1;
979 }
980
981 Ok(CompiledLogic::MapOptionsIf(
982 table,
983 field_label,
984 field_value,
985 conditions,
986 ))
987 }
988 "return" => Ok(CompiledLogic::Return(Box::new(args.clone()))),
989
990 _ => Err(format!("Unknown operator: {}", op)),
991 }
992 }
993
994 fn compile_binary<F>(args: &Value, f: F) -> Result<Self, String>
995 where
996 F: FnOnce(Box<CompiledLogic>, Box<CompiledLogic>) -> CompiledLogic,
997 {
998 let arr = args.as_array().ok_or("Binary operator requires array")?;
999 if arr.len() != 2 {
1000 return Err("Binary operator requires exactly 2 arguments".to_string());
1001 }
1002 Ok(f(
1003 Box::new(Self::compile(&arr[0])?),
1004 Box::new(Self::compile(&arr[1])?),
1005 ))
1006 }
1007
1008 fn preprocess_table_condition(value: &Value) -> Value {
1016 match value {
1017 Value::String(s) => {
1018 serde_json::json!({"var": s})
1020 }
1021 Value::Array(arr) => {
1022 if !arr.is_empty() {
1024 if let Some(op_str) = arr[0].as_str() {
1025 let is_comparison = matches!(
1027 op_str,
1028 "==" | "!=" | "===" | "!==" | "<" | ">" | "<=" | ">="
1029 );
1030
1031 if is_comparison && arr.len() >= 3 {
1032 let value_arg = arr[1].clone();
1036
1037 let col_arg = if let Value::String(col) = &arr[2] {
1039 serde_json::json!({"var": col})
1041 } else {
1042 Self::preprocess_table_condition(&arr[2])
1044 };
1045
1046 let mut obj = serde_json::Map::new();
1048 obj.insert(op_str.to_string(), Value::Array(vec![col_arg, value_arg]));
1049 return Value::Object(obj);
1050 }
1051
1052 let canonical_op = match op_str {
1054 "&&" => Some("and"),
1055 "||" => Some("or"),
1056 "and" | "or" | "!" | "not" | "if" => Some(op_str),
1057 _ => None,
1058 };
1059
1060 if let Some(op_name) = canonical_op {
1061 let args: Vec<Value> = arr[1..]
1063 .iter()
1064 .map(Self::preprocess_table_condition)
1065 .collect();
1066 let mut obj = serde_json::Map::new();
1067 obj.insert(op_name.to_string(), Value::Array(args));
1068 return Value::Object(obj);
1069 }
1070 }
1071 }
1072 Value::Array(arr.iter().map(Self::preprocess_table_condition).collect())
1074 }
1075 Value::Object(obj) => {
1076 let mut new_obj = serde_json::Map::new();
1078 for (key, val) in obj {
1079 if key == "$ref" || key == "ref" || key == "var" {
1081 new_obj.insert(key.clone(), val.clone());
1082 } else {
1083 new_obj.insert(key.clone(), Self::preprocess_table_condition(val));
1084 }
1085 }
1086 Value::Object(new_obj)
1087 }
1088 _ => value.clone(),
1089 }
1090 }
1091
1092 pub fn is_simple_ref(&self) -> bool {
1094 matches!(
1095 self,
1096 CompiledLogic::Ref(_, None) | CompiledLogic::Var(_, None)
1097 )
1098 }
1099
1100 pub fn referenced_vars(&self) -> Vec<String> {
1102 let mut vars = Vec::new();
1103 self.collect_vars(&mut vars);
1104 vars.sort();
1105 vars.dedup();
1106 vars
1107 }
1108
1109 fn flatten_and(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1111 let mut flattened = Vec::new();
1112 for item in items {
1113 match item {
1114 CompiledLogic::And(nested) => {
1115 flattened.extend(Self::flatten_and(nested));
1117 }
1118 _ => flattened.push(item),
1119 }
1120 }
1121 flattened
1122 }
1123
1124 fn flatten_or(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1126 let mut flattened = Vec::new();
1127 for item in items {
1128 match item {
1129 CompiledLogic::Or(nested) => {
1130 flattened.extend(Self::flatten_or(nested));
1132 }
1133 _ => flattened.push(item),
1134 }
1135 }
1136 flattened
1137 }
1138
1139 fn flatten_add(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1141 let mut flattened = Vec::new();
1142 for item in items {
1143 match item {
1144 CompiledLogic::Add(nested) => {
1145 flattened.extend(Self::flatten_add(nested));
1147 }
1148 _ => flattened.push(item),
1149 }
1150 }
1151 flattened
1152 }
1153
1154 fn flatten_multiply(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1156 let mut flattened = Vec::new();
1157 for item in items {
1158 match item {
1159 CompiledLogic::Multiply(nested) => {
1160 flattened.extend(Self::flatten_multiply(nested));
1162 }
1163 _ => flattened.push(item),
1164 }
1165 }
1166 flattened
1167 }
1168
1169 fn flatten_cat(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1172 let mut flattened = Vec::new();
1173 for item in items {
1174 match &item {
1175 CompiledLogic::Cat(nested) => {
1176 flattened.extend(Self::flatten_cat(nested.clone()));
1178 }
1179 _ => flattened.push(item),
1180 }
1181 }
1182 flattened
1183 }
1184
1185 pub fn has_forward_reference(&self) -> bool {
1188 let result = self.check_forward_reference();
1189 result
1190 }
1191
1192 fn check_forward_reference(&self) -> bool {
1193 match self {
1194 CompiledLogic::ValueAt(table, idx_logic, col_name) => {
1196 let has_fwd = idx_logic.contains_iteration_plus_positive();
1198 if has_fwd {
1199 return true;
1200 }
1201 let table_fwd = table.check_forward_reference();
1203 let idx_fwd = idx_logic.check_forward_reference();
1204 let col_fwd = col_name
1205 .as_ref()
1206 .map(|c| c.check_forward_reference())
1207 .unwrap_or(false);
1208 table_fwd || idx_fwd || col_fwd
1209 }
1210 CompiledLogic::Array(arr) => arr.iter().any(|item| item.check_forward_reference()),
1212 CompiledLogic::And(items)
1213 | CompiledLogic::Or(items)
1214 | CompiledLogic::Add(items)
1215 | CompiledLogic::Subtract(items)
1216 | CompiledLogic::Multiply(items)
1217 | CompiledLogic::Divide(items)
1218 | CompiledLogic::Merge(items)
1219 | CompiledLogic::Cat(items)
1220 | CompiledLogic::Max(items)
1221 | CompiledLogic::Min(items)
1222 | CompiledLogic::Concat(items)
1223 | CompiledLogic::Multiplies(items)
1224 | CompiledLogic::Divides(items) => {
1225 items.iter().any(|item| item.check_forward_reference())
1226 }
1227 CompiledLogic::Not(a)
1228 | CompiledLogic::Abs(a)
1229 | CompiledLogic::Length(a)
1230 | CompiledLogic::Len(a)
1231 | CompiledLogic::IsEmpty(a)
1232 | CompiledLogic::Year(a)
1233 | CompiledLogic::Month(a)
1234 | CompiledLogic::Day(a) => a.check_forward_reference(),
1235 CompiledLogic::Round(a, decimals)
1236 | CompiledLogic::RoundUp(a, decimals)
1237 | CompiledLogic::RoundDown(a, decimals)
1238 | CompiledLogic::Ceiling(a, decimals)
1239 | CompiledLogic::Floor(a, decimals)
1240 | CompiledLogic::Trunc(a, decimals)
1241 | CompiledLogic::DateFormat(a, decimals) => {
1242 a.check_forward_reference()
1243 || decimals
1244 .as_ref()
1245 .map_or(false, |d| d.check_forward_reference())
1246 }
1247 CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1248 a.check_forward_reference()
1249 || decimals
1250 .as_ref()
1251 .map_or(false, |d| d.check_forward_reference())
1252 || prefix
1253 .as_ref()
1254 .map_or(false, |p| p.check_forward_reference())
1255 || suffix
1256 .as_ref()
1257 .map_or(false, |s| s.check_forward_reference())
1258 || sep.as_ref().map_or(false, |s| s.check_forward_reference())
1259 }
1260 CompiledLogic::Mround(a, b) => {
1261 a.check_forward_reference() || b.check_forward_reference()
1262 }
1263 CompiledLogic::Return(_) => false, CompiledLogic::If(cond, then_val, else_val) => {
1265 cond.check_forward_reference()
1266 || then_val.check_forward_reference()
1267 || else_val.check_forward_reference()
1268 }
1269 CompiledLogic::Equal(a, b)
1270 | CompiledLogic::StrictEqual(a, b)
1271 | CompiledLogic::NotEqual(a, b)
1272 | CompiledLogic::StrictNotEqual(a, b)
1273 | CompiledLogic::LessThan(a, b)
1274 | CompiledLogic::LessThanOrEqual(a, b)
1275 | CompiledLogic::GreaterThan(a, b)
1276 | CompiledLogic::GreaterThanOrEqual(a, b)
1277 | CompiledLogic::Modulo(a, b)
1278 | CompiledLogic::Power(a, b)
1279 | CompiledLogic::Map(a, b)
1280 | CompiledLogic::Filter(a, b)
1281 | CompiledLogic::All(a, b)
1282 | CompiledLogic::Some(a, b)
1283 | CompiledLogic::None(a, b)
1284 | CompiledLogic::In(a, b)
1285 | CompiledLogic::Pow(a, b)
1286 | CompiledLogic::Xor(a, b)
1287 | CompiledLogic::IfNull(a, b)
1288 | CompiledLogic::Days(a, b)
1289 | CompiledLogic::SplitValue(a, b)
1290 | CompiledLogic::MaxAt(a, b)
1291 | CompiledLogic::RangeOptions(a, b) => {
1292 a.check_forward_reference() || b.check_forward_reference()
1293 }
1294 CompiledLogic::Reduce(a, b, c)
1295 | CompiledLogic::Mid(a, b, c)
1296 | CompiledLogic::Date(a, b, c)
1297 | CompiledLogic::DateDif(a, b, c)
1298 | CompiledLogic::MapOptions(a, b, c)
1299 | CompiledLogic::For(a, b, c) => {
1300 a.check_forward_reference()
1301 || b.check_forward_reference()
1302 || c.check_forward_reference()
1303 }
1304 CompiledLogic::Substr(s, start, len)
1305 | CompiledLogic::Search(s, start, len)
1306 | CompiledLogic::SplitText(s, start, len)
1307 | CompiledLogic::YearFrac(s, start, len) => {
1308 s.check_forward_reference()
1309 || start.check_forward_reference()
1310 || len
1311 .as_ref()
1312 .map(|l| l.check_forward_reference())
1313 .unwrap_or(false)
1314 }
1315 CompiledLogic::Left(a, opt) | CompiledLogic::Right(a, opt) => {
1316 a.check_forward_reference()
1317 || opt
1318 .as_ref()
1319 .map(|o| o.check_forward_reference())
1320 .unwrap_or(false)
1321 }
1322 CompiledLogic::Sum(a, opt1, opt2) => {
1323 a.check_forward_reference()
1324 || opt1
1325 .as_ref()
1326 .map(|o| o.check_forward_reference())
1327 .unwrap_or(false)
1328 || opt2
1329 .as_ref()
1330 .map(|o| o.check_forward_reference())
1331 .unwrap_or(false)
1332 }
1333 CompiledLogic::IndexAt(a, b, c, opt) => {
1334 a.check_forward_reference()
1335 || b.check_forward_reference()
1336 || c.check_forward_reference()
1337 || opt
1338 .as_ref()
1339 .map(|o| o.check_forward_reference())
1340 .unwrap_or(false)
1341 }
1342 CompiledLogic::Match(table, conds)
1343 | CompiledLogic::MatchRange(table, conds)
1344 | CompiledLogic::Choose(table, conds)
1345 | CompiledLogic::FindIndex(table, conds) => {
1346 table.check_forward_reference() || conds.iter().any(|c| c.check_forward_reference())
1347 }
1348 CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1349 table.check_forward_reference()
1350 || label.check_forward_reference()
1351 || value.check_forward_reference()
1352 || conds.iter().any(|c| c.check_forward_reference())
1353 }
1354 CompiledLogic::MissingSome(min, _) => min.check_forward_reference(),
1355 _ => false,
1356 }
1357 }
1358
1359 fn contains_iteration_plus_positive(&self) -> bool {
1361 match self {
1362 CompiledLogic::Add(items) => {
1363 let has_iteration = items
1365 .iter()
1366 .any(|item| item.referenced_vars().iter().any(|v| v == "$iteration"));
1367
1368 let has_positive = items.iter().any(|item| match item {
1369 CompiledLogic::Number(n) => n.parse::<f64>().unwrap_or(0.0) > 0.0,
1370 _ => false,
1371 });
1372
1373 let result = has_iteration && has_positive;
1374 result
1375 }
1376 _ => false,
1377 }
1378 }
1379
1380 fn normalize_ref_path(path: &str) -> String {
1384 let mut normalized = path.to_string();
1385
1386 if normalized.starts_with("#/") {
1388 normalized = normalized[2..].to_string();
1389 } else if normalized.starts_with('/') {
1390 normalized = normalized[1..].to_string();
1391 }
1392
1393 normalized = normalized.replace('/', ".");
1395
1396 normalized = normalized.replace("properties.", "");
1398 normalized = normalized.replace(".properties.", ".");
1399
1400 while normalized.contains("..") {
1402 normalized = normalized.replace("..", ".");
1403 }
1404
1405 normalized = normalized.trim_matches('.').to_string();
1407
1408 normalized
1409 }
1410
1411 pub fn collect_vars(&self, vars: &mut Vec<String>) {
1412 match self {
1413 CompiledLogic::Var(name, default) => {
1414 vars.push(name.clone());
1415 if let Some(def) = default {
1416 def.collect_vars(vars);
1417 }
1418 }
1419 CompiledLogic::Ref(path, default) => {
1420 vars.push(Self::normalize_ref_path(path));
1422 if let Some(def) = default {
1423 def.collect_vars(vars);
1424 }
1425 }
1426 CompiledLogic::Array(arr) => {
1427 for item in arr {
1428 item.collect_vars(vars);
1429 }
1430 }
1431 CompiledLogic::And(items)
1432 | CompiledLogic::Or(items)
1433 | CompiledLogic::Add(items)
1434 | CompiledLogic::Subtract(items)
1435 | CompiledLogic::Multiply(items)
1436 | CompiledLogic::Divide(items)
1437 | CompiledLogic::Merge(items)
1438 | CompiledLogic::Cat(items)
1439 | CompiledLogic::Max(items)
1440 | CompiledLogic::Min(items)
1441 | CompiledLogic::Concat(items) => {
1442 for item in items {
1443 item.collect_vars(vars);
1444 }
1445 }
1446 CompiledLogic::Not(a)
1447 | CompiledLogic::Abs(a)
1448 | CompiledLogic::Length(a)
1449 | CompiledLogic::Len(a)
1450 | CompiledLogic::IsEmpty(a)
1451 | CompiledLogic::Year(a)
1452 | CompiledLogic::Month(a)
1453 | CompiledLogic::Day(a) => {
1454 a.collect_vars(vars);
1455 }
1456 CompiledLogic::Round(a, decimals)
1457 | CompiledLogic::RoundUp(a, decimals)
1458 | CompiledLogic::RoundDown(a, decimals)
1459 | CompiledLogic::Ceiling(a, decimals)
1460 | CompiledLogic::Floor(a, decimals)
1461 | CompiledLogic::Trunc(a, decimals)
1462 | CompiledLogic::DateFormat(a, decimals) => {
1463 a.collect_vars(vars);
1464 if let Some(d) = decimals {
1465 d.collect_vars(vars);
1466 }
1467 }
1468 CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1469 a.collect_vars(vars);
1470 if let Some(d) = decimals {
1471 d.collect_vars(vars);
1472 }
1473 if let Some(p) = prefix {
1474 p.collect_vars(vars);
1475 }
1476 if let Some(s) = suffix {
1477 s.collect_vars(vars);
1478 }
1479 if let Some(s) = sep {
1480 s.collect_vars(vars);
1481 }
1482 }
1483 CompiledLogic::Mround(a, b) => {
1484 a.collect_vars(vars);
1485 b.collect_vars(vars);
1486 }
1487 CompiledLogic::Return(_) => {} CompiledLogic::If(cond, then_val, else_val) => {
1489 cond.collect_vars(vars);
1490 then_val.collect_vars(vars);
1491 else_val.collect_vars(vars);
1492 }
1493 CompiledLogic::Equal(a, b)
1494 | CompiledLogic::StrictEqual(a, b)
1495 | CompiledLogic::NotEqual(a, b)
1496 | CompiledLogic::StrictNotEqual(a, b)
1497 | CompiledLogic::LessThan(a, b)
1498 | CompiledLogic::LessThanOrEqual(a, b)
1499 | CompiledLogic::GreaterThan(a, b)
1500 | CompiledLogic::GreaterThanOrEqual(a, b)
1501 | CompiledLogic::Modulo(a, b)
1502 | CompiledLogic::Power(a, b)
1503 | CompiledLogic::Map(a, b)
1504 | CompiledLogic::Filter(a, b)
1505 | CompiledLogic::All(a, b)
1506 | CompiledLogic::Some(a, b)
1507 | CompiledLogic::None(a, b)
1508 | CompiledLogic::In(a, b)
1509 | CompiledLogic::Pow(a, b)
1510 | CompiledLogic::Xor(a, b)
1511 | CompiledLogic::IfNull(a, b)
1512 | CompiledLogic::Days(a, b)
1513 | CompiledLogic::SplitValue(a, b)
1514 | CompiledLogic::MaxAt(a, b)
1515 | CompiledLogic::RangeOptions(a, b) => {
1516 a.collect_vars(vars);
1517 b.collect_vars(vars);
1518 }
1519 CompiledLogic::Reduce(a, b, c)
1520 | CompiledLogic::Mid(a, b, c)
1521 | CompiledLogic::Date(a, b, c)
1522 | CompiledLogic::DateDif(a, b, c)
1523 | CompiledLogic::MapOptions(a, b, c)
1524 | CompiledLogic::For(a, b, c) => {
1525 a.collect_vars(vars);
1526 b.collect_vars(vars);
1527 c.collect_vars(vars);
1528 }
1529 CompiledLogic::Substr(s, start, len)
1530 | CompiledLogic::Search(s, start, len)
1531 | CompiledLogic::SplitText(s, start, len)
1532 | CompiledLogic::YearFrac(s, start, len) => {
1533 s.collect_vars(vars);
1534 start.collect_vars(vars);
1535 if let Some(l) = len {
1536 l.collect_vars(vars);
1537 }
1538 }
1539 CompiledLogic::Left(a, opt)
1540 | CompiledLogic::Right(a, opt)
1541 | CompiledLogic::ValueAt(a, _, opt) => {
1542 a.collect_vars(vars);
1543 if let Some(o) = opt {
1544 o.collect_vars(vars);
1545 }
1546 }
1547 CompiledLogic::Sum(a, opt1, opt2) => {
1548 a.collect_vars(vars);
1549 if let Some(o) = opt1 {
1550 o.collect_vars(vars);
1551 }
1552 if let Some(o) = opt2 {
1553 o.collect_vars(vars);
1554 }
1555 }
1556 CompiledLogic::IndexAt(a, b, c, opt) => {
1557 a.collect_vars(vars);
1558 b.collect_vars(vars);
1559 c.collect_vars(vars);
1560 if let Some(o) = opt {
1561 o.collect_vars(vars);
1562 }
1563 }
1564 CompiledLogic::Match(table, conds)
1565 | CompiledLogic::MatchRange(table, conds)
1566 | CompiledLogic::Choose(table, conds)
1567 | CompiledLogic::FindIndex(table, conds) => {
1568 table.collect_vars(vars);
1569 for cond in conds {
1570 cond.collect_vars(vars);
1571 }
1572 }
1573 CompiledLogic::Multiplies(items) | CompiledLogic::Divides(items) => {
1574 for item in items {
1575 item.collect_vars(vars);
1576 }
1577 }
1578 CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1579 table.collect_vars(vars);
1580 label.collect_vars(vars);
1581 value.collect_vars(vars);
1582 for cond in conds {
1583 cond.collect_vars(vars);
1584 }
1585 }
1586 CompiledLogic::MissingSome(min, _) => {
1587 min.collect_vars(vars);
1588 }
1589 _ => {}
1590 }
1591 }
1592}
1593
1594pub struct CompiledLogicStore {
1600 next_id: u64,
1601 store: AHashMap<LogicId, CompiledLogic>,
1602 dependencies: AHashMap<LogicId, Vec<String>>,
1603}
1604
1605impl CompiledLogicStore {
1606 pub fn new() -> Self {
1607 Self {
1608 next_id: 0,
1609 store: AHashMap::default(),
1610 dependencies: AHashMap::default(),
1611 }
1612 }
1613
1614 pub fn compile(&mut self, logic: &Value) -> Result<LogicId, String> {
1620 let _global_id = super::compiled_logic_store::compile_logic_value(logic)?;
1622
1623 let compiled = super::compiled_logic_store::get_compiled_logic(_global_id)
1625 .ok_or_else(|| "Failed to retrieve compiled logic from global store".to_string())?;
1626
1627 let deps = compiled.referenced_vars();
1629
1630 let id = LogicId(self.next_id);
1632 self.next_id += 1;
1633
1634 self.store.insert(id, compiled);
1636 self.dependencies.insert(id, deps);
1637
1638 Ok(id)
1639 }
1640
1641 pub fn get(&self, id: &LogicId) -> Option<&CompiledLogic> {
1643 self.store.get(id)
1644 }
1645
1646 pub fn remove(&mut self, id: &LogicId) -> Option<CompiledLogic> {
1648 self.dependencies.remove(id);
1649 self.store.remove(id)
1650 }
1651
1652 pub fn get_dependencies(&self, id: &LogicId) -> Option<&[String]> {
1654 self.dependencies.get(id).map(|v| v.as_slice())
1655 }
1656}
1657
1658impl Default for CompiledLogicStore {
1659 fn default() -> Self {
1660 Self::new()
1661 }
1662}