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