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).into_owned();
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).into_owned();
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).into_owned();
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).into_owned();
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..];
945 let mut conditions = Vec::new();
946
947 let mut i = 0;
948 while i < condition_args.len() {
949 let arg = &condition_args[i];
950
951 if let Some(arg_arr) = arg.as_array() {
953 if arg_arr.len() == 3 && arg_arr[1].is_string() {
954 let is_comparison = matches!(
955 arg_arr[1].as_str().unwrap(),
956 "==" | "!=" | "===" | "!==" | "<" | ">" | "<=" | ">="
957 );
958 if is_comparison {
959 let value = &arg_arr[0];
960 let operator = arg_arr[1].as_str().unwrap();
961 let field = &arg_arr[2];
962
963 let field_var = if let Value::String(f) = field {
964 serde_json::json!({"var": f})
965 } else {
966 field.clone()
967 };
968
969 let comparison = serde_json::json!({
970 operator: [value.clone(), field_var]
971 });
972
973 conditions.push(Self::compile(&comparison)?);
974 i += 1;
975 continue;
976 }
977 }
978 }
979
980 if i + 2 < condition_args.len() {
982 let value = &condition_args[i];
983 let operator = &condition_args[i + 1];
984 let field = &condition_args[i + 2];
985
986 if let Value::String(op) = operator {
987 let is_comparison = matches!(
988 op.as_str(),
989 "==" | "!=" | "===" | "!==" | "<" | ">" | "<=" | ">="
990 );
991
992 if is_comparison {
993 let field_var = if let Value::String(f) = field {
994 serde_json::json!({"var": f})
995 } else {
996 field.clone()
997 };
998
999 let comparison = serde_json::json!({
1000 op: [value.clone(), field_var]
1001 });
1002
1003 conditions.push(Self::compile(&comparison)?);
1004 i += 3;
1005 continue;
1006 }
1007 }
1008 }
1009
1010 conditions.push(Self::compile(&Self::preprocess_table_condition(
1012 &condition_args[i],
1013 ))?);
1014 i += 1;
1015 }
1016
1017 Ok(CompiledLogic::MapOptionsIf(
1018 table,
1019 field_label,
1020 field_value,
1021 conditions,
1022 ))
1023 }
1024 "return" => Ok(CompiledLogic::Return(Box::new(args.clone()))),
1025
1026 _ => Err(format!("Unknown operator: {}", op)),
1027 }
1028 }
1029
1030 fn compile_binary<F>(args: &Value, f: F) -> Result<Self, String>
1031 where
1032 F: FnOnce(Box<CompiledLogic>, Box<CompiledLogic>) -> CompiledLogic,
1033 {
1034 let arr = args.as_array().ok_or("Binary operator requires array")?;
1035 if arr.len() != 2 {
1036 return Err("Binary operator requires exactly 2 arguments".to_string());
1037 }
1038 Ok(f(
1039 Box::new(Self::compile(&arr[0])?),
1040 Box::new(Self::compile(&arr[1])?),
1041 ))
1042 }
1043
1044 fn preprocess_table_condition(value: &Value) -> Value {
1052 match value {
1053 Value::String(s) => {
1054 serde_json::json!({"var": s})
1056 }
1057 Value::Array(arr) => {
1058 if !arr.is_empty() {
1060 if let Some(op_str) = arr[0].as_str() {
1061 let is_comparison = matches!(
1063 op_str,
1064 "==" | "!=" | "===" | "!==" | "<" | ">" | "<=" | ">="
1065 );
1066
1067 if is_comparison && arr.len() >= 3 {
1068 let value_arg = arr[1].clone();
1072
1073 let col_arg = if let Value::String(col) = &arr[2] {
1075 serde_json::json!({"var": col})
1077 } else {
1078 Self::preprocess_table_condition(&arr[2])
1080 };
1081
1082 let mut obj = serde_json::Map::new();
1084 obj.insert(op_str.to_string(), Value::Array(vec![col_arg, value_arg]));
1085 return Value::Object(obj);
1086 }
1087
1088 let canonical_op = match op_str {
1090 "&&" => Some("and"),
1091 "||" => Some("or"),
1092 "and" | "or" | "!" | "not" | "if" => Some(op_str),
1093 _ => None,
1094 };
1095
1096 if let Some(op_name) = canonical_op {
1097 let args: Vec<Value> = arr[1..]
1099 .iter()
1100 .map(Self::preprocess_table_condition)
1101 .collect();
1102 let mut obj = serde_json::Map::new();
1103 obj.insert(op_name.to_string(), Value::Array(args));
1104 return Value::Object(obj);
1105 }
1106 }
1107 }
1108 Value::Array(arr.iter().map(Self::preprocess_table_condition).collect())
1110 }
1111 Value::Object(obj) => {
1112 let mut new_obj = serde_json::Map::new();
1114 for (key, val) in obj {
1115 if key == "$ref" || key == "ref" || key == "var" {
1117 new_obj.insert(key.clone(), val.clone());
1118 } else {
1119 new_obj.insert(key.clone(), Self::preprocess_table_condition(val));
1120 }
1121 }
1122 Value::Object(new_obj)
1123 }
1124 _ => value.clone(),
1125 }
1126 }
1127
1128 pub fn is_simple_ref(&self) -> bool {
1130 matches!(
1131 self,
1132 CompiledLogic::Ref(_, None) | CompiledLogic::Var(_, None)
1133 )
1134 }
1135
1136 pub fn referenced_vars(&self) -> Vec<String> {
1138 let mut vars = Vec::new();
1139 self.collect_vars(&mut vars);
1140 vars.sort();
1141 vars.dedup();
1142 vars
1143 }
1144
1145 fn flatten_and(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1147 let mut flattened = Vec::new();
1148 for item in items {
1149 match item {
1150 CompiledLogic::And(nested) => {
1151 flattened.extend(Self::flatten_and(nested));
1153 }
1154 _ => flattened.push(item),
1155 }
1156 }
1157 flattened
1158 }
1159
1160 fn flatten_or(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1162 let mut flattened = Vec::new();
1163 for item in items {
1164 match item {
1165 CompiledLogic::Or(nested) => {
1166 flattened.extend(Self::flatten_or(nested));
1168 }
1169 _ => flattened.push(item),
1170 }
1171 }
1172 flattened
1173 }
1174
1175 fn flatten_add(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1177 let mut flattened = Vec::new();
1178 for item in items {
1179 match item {
1180 CompiledLogic::Add(nested) => {
1181 flattened.extend(Self::flatten_add(nested));
1183 }
1184 _ => flattened.push(item),
1185 }
1186 }
1187 flattened
1188 }
1189
1190 fn flatten_multiply(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1192 let mut flattened = Vec::new();
1193 for item in items {
1194 match item {
1195 CompiledLogic::Multiply(nested) => {
1196 flattened.extend(Self::flatten_multiply(nested));
1198 }
1199 _ => flattened.push(item),
1200 }
1201 }
1202 flattened
1203 }
1204
1205 fn flatten_cat(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1208 let mut flattened = Vec::new();
1209 for item in items {
1210 match &item {
1211 CompiledLogic::Cat(nested) => {
1212 flattened.extend(Self::flatten_cat(nested.clone()));
1214 }
1215 _ => flattened.push(item),
1216 }
1217 }
1218 flattened
1219 }
1220
1221 pub fn has_forward_reference(&self) -> bool {
1224 let result = self.check_forward_reference();
1225 result
1226 }
1227
1228 fn check_forward_reference(&self) -> bool {
1229 match self {
1230 CompiledLogic::ValueAt(table, idx_logic, col_name) => {
1232 let has_fwd = idx_logic.contains_iteration_plus_positive();
1234 if has_fwd {
1235 return true;
1236 }
1237 let table_fwd = table.check_forward_reference();
1239 let idx_fwd = idx_logic.check_forward_reference();
1240 let col_fwd = col_name
1241 .as_ref()
1242 .map(|c| c.check_forward_reference())
1243 .unwrap_or(false);
1244 table_fwd || idx_fwd || col_fwd
1245 }
1246 CompiledLogic::Array(arr) => arr.iter().any(|item| item.check_forward_reference()),
1248 CompiledLogic::And(items)
1249 | CompiledLogic::Or(items)
1250 | CompiledLogic::Add(items)
1251 | CompiledLogic::Subtract(items)
1252 | CompiledLogic::Multiply(items)
1253 | CompiledLogic::Divide(items)
1254 | CompiledLogic::Merge(items)
1255 | CompiledLogic::Cat(items)
1256 | CompiledLogic::Max(items)
1257 | CompiledLogic::Min(items)
1258 | CompiledLogic::Concat(items)
1259 | CompiledLogic::Multiplies(items)
1260 | CompiledLogic::Divides(items) => {
1261 items.iter().any(|item| item.check_forward_reference())
1262 }
1263 CompiledLogic::Not(a)
1264 | CompiledLogic::Abs(a)
1265 | CompiledLogic::Length(a)
1266 | CompiledLogic::Len(a)
1267 | CompiledLogic::IsEmpty(a)
1268 | CompiledLogic::Year(a)
1269 | CompiledLogic::Month(a)
1270 | CompiledLogic::Day(a) => a.check_forward_reference(),
1271 CompiledLogic::Round(a, decimals)
1272 | CompiledLogic::RoundUp(a, decimals)
1273 | CompiledLogic::RoundDown(a, decimals)
1274 | CompiledLogic::Ceiling(a, decimals)
1275 | CompiledLogic::Floor(a, decimals)
1276 | CompiledLogic::Trunc(a, decimals)
1277 | CompiledLogic::DateFormat(a, decimals) => {
1278 a.check_forward_reference()
1279 || decimals
1280 .as_ref()
1281 .map_or(false, |d| d.check_forward_reference())
1282 }
1283 CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1284 a.check_forward_reference()
1285 || decimals
1286 .as_ref()
1287 .map_or(false, |d| d.check_forward_reference())
1288 || prefix
1289 .as_ref()
1290 .map_or(false, |p| p.check_forward_reference())
1291 || suffix
1292 .as_ref()
1293 .map_or(false, |s| s.check_forward_reference())
1294 || sep.as_ref().map_or(false, |s| s.check_forward_reference())
1295 }
1296 CompiledLogic::Mround(a, b) => {
1297 a.check_forward_reference() || b.check_forward_reference()
1298 }
1299 CompiledLogic::Return(_) => false, CompiledLogic::If(cond, then_val, else_val) => {
1301 cond.check_forward_reference()
1302 || then_val.check_forward_reference()
1303 || else_val.check_forward_reference()
1304 }
1305 CompiledLogic::Equal(a, b)
1306 | CompiledLogic::StrictEqual(a, b)
1307 | CompiledLogic::NotEqual(a, b)
1308 | CompiledLogic::StrictNotEqual(a, b)
1309 | CompiledLogic::LessThan(a, b)
1310 | CompiledLogic::LessThanOrEqual(a, b)
1311 | CompiledLogic::GreaterThan(a, b)
1312 | CompiledLogic::GreaterThanOrEqual(a, b)
1313 | CompiledLogic::Modulo(a, b)
1314 | CompiledLogic::Power(a, b)
1315 | CompiledLogic::Map(a, b)
1316 | CompiledLogic::Filter(a, b)
1317 | CompiledLogic::All(a, b)
1318 | CompiledLogic::Some(a, b)
1319 | CompiledLogic::None(a, b)
1320 | CompiledLogic::In(a, b)
1321 | CompiledLogic::Pow(a, b)
1322 | CompiledLogic::Xor(a, b)
1323 | CompiledLogic::IfNull(a, b)
1324 | CompiledLogic::Days(a, b)
1325 | CompiledLogic::SplitValue(a, b)
1326 | CompiledLogic::MaxAt(a, b)
1327 | CompiledLogic::RangeOptions(a, b) => {
1328 a.check_forward_reference() || b.check_forward_reference()
1329 }
1330 CompiledLogic::Reduce(a, b, c)
1331 | CompiledLogic::Mid(a, b, c)
1332 | CompiledLogic::Date(a, b, c)
1333 | CompiledLogic::DateDif(a, b, c)
1334 | CompiledLogic::MapOptions(a, b, c)
1335 | CompiledLogic::For(a, b, c) => {
1336 a.check_forward_reference()
1337 || b.check_forward_reference()
1338 || c.check_forward_reference()
1339 }
1340 CompiledLogic::Substr(s, start, len)
1341 | CompiledLogic::Search(s, start, len)
1342 | CompiledLogic::SplitText(s, start, len)
1343 | CompiledLogic::YearFrac(s, start, len) => {
1344 s.check_forward_reference()
1345 || start.check_forward_reference()
1346 || len
1347 .as_ref()
1348 .map(|l| l.check_forward_reference())
1349 .unwrap_or(false)
1350 }
1351 CompiledLogic::Left(a, opt) | CompiledLogic::Right(a, opt) => {
1352 a.check_forward_reference()
1353 || opt
1354 .as_ref()
1355 .map(|o| o.check_forward_reference())
1356 .unwrap_or(false)
1357 }
1358 CompiledLogic::Sum(a, opt1, opt2) => {
1359 a.check_forward_reference()
1360 || opt1
1361 .as_ref()
1362 .map(|o| o.check_forward_reference())
1363 .unwrap_or(false)
1364 || opt2
1365 .as_ref()
1366 .map(|o| o.check_forward_reference())
1367 .unwrap_or(false)
1368 }
1369 CompiledLogic::IndexAt(a, b, c, opt) => {
1370 a.check_forward_reference()
1371 || b.check_forward_reference()
1372 || c.check_forward_reference()
1373 || opt
1374 .as_ref()
1375 .map(|o| o.check_forward_reference())
1376 .unwrap_or(false)
1377 }
1378 CompiledLogic::Match(table, conds)
1379 | CompiledLogic::MatchRange(table, conds)
1380 | CompiledLogic::Choose(table, conds)
1381 | CompiledLogic::FindIndex(table, conds) => {
1382 table.check_forward_reference() || conds.iter().any(|c| c.check_forward_reference())
1383 }
1384 CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1385 table.check_forward_reference()
1386 || label.check_forward_reference()
1387 || value.check_forward_reference()
1388 || conds.iter().any(|c| c.check_forward_reference())
1389 }
1390 CompiledLogic::MissingSome(min, _) => min.check_forward_reference(),
1391 _ => false,
1392 }
1393 }
1394
1395 fn contains_iteration_plus_positive(&self) -> bool {
1397 match self {
1398 CompiledLogic::Add(items) => {
1399 let has_iteration = items
1401 .iter()
1402 .any(|item| item.referenced_vars().iter().any(|v| v == "$iteration"));
1403
1404 let has_positive = items.iter().any(|item| match item {
1405 CompiledLogic::Number(f) => *f > 0.0,
1406 _ => false,
1407 });
1408
1409 let result = has_iteration && has_positive;
1410 result
1411 }
1412 _ => false,
1413 }
1414 }
1415
1416 fn normalize_ref_path(path: &str) -> String {
1420 let mut normalized = path.to_string();
1421
1422 if normalized.starts_with("#/") {
1424 normalized = normalized[2..].to_string();
1425 } else if normalized.starts_with('/') {
1426 normalized = normalized[1..].to_string();
1427 }
1428
1429 normalized = normalized.replace('/', ".");
1431
1432 normalized = normalized.replace("properties.", "");
1434 normalized = normalized.replace(".properties.", ".");
1435
1436 while normalized.contains("..") {
1438 normalized = normalized.replace("..", ".");
1439 }
1440
1441 normalized = normalized.trim_matches('.').to_string();
1443
1444 normalized
1445 }
1446
1447 pub fn collect_vars(&self, vars: &mut Vec<String>) {
1448 match self {
1449 CompiledLogic::Var(name, default) => {
1450 vars.push(name.clone());
1451 if let Some(def) = default {
1452 def.collect_vars(vars);
1453 }
1454 }
1455 CompiledLogic::Ref(path, default) => {
1456 vars.push(Self::normalize_ref_path(path));
1458 if let Some(def) = default {
1459 def.collect_vars(vars);
1460 }
1461 }
1462 CompiledLogic::Array(arr) => {
1463 for item in arr {
1464 item.collect_vars(vars);
1465 }
1466 }
1467 CompiledLogic::And(items)
1468 | CompiledLogic::Or(items)
1469 | CompiledLogic::Add(items)
1470 | CompiledLogic::Subtract(items)
1471 | CompiledLogic::Multiply(items)
1472 | CompiledLogic::Divide(items)
1473 | CompiledLogic::Merge(items)
1474 | CompiledLogic::Cat(items)
1475 | CompiledLogic::Max(items)
1476 | CompiledLogic::Min(items)
1477 | CompiledLogic::Concat(items) => {
1478 for item in items {
1479 item.collect_vars(vars);
1480 }
1481 }
1482 CompiledLogic::Not(a)
1483 | CompiledLogic::Abs(a)
1484 | CompiledLogic::Length(a)
1485 | CompiledLogic::Len(a)
1486 | CompiledLogic::IsEmpty(a)
1487 | CompiledLogic::Year(a)
1488 | CompiledLogic::Month(a)
1489 | CompiledLogic::Day(a) => {
1490 a.collect_vars(vars);
1491 }
1492 CompiledLogic::Round(a, decimals)
1493 | CompiledLogic::RoundUp(a, decimals)
1494 | CompiledLogic::RoundDown(a, decimals)
1495 | CompiledLogic::Ceiling(a, decimals)
1496 | CompiledLogic::Floor(a, decimals)
1497 | CompiledLogic::Trunc(a, decimals)
1498 | CompiledLogic::DateFormat(a, decimals) => {
1499 a.collect_vars(vars);
1500 if let Some(d) = decimals {
1501 d.collect_vars(vars);
1502 }
1503 }
1504 CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1505 a.collect_vars(vars);
1506 if let Some(d) = decimals {
1507 d.collect_vars(vars);
1508 }
1509 if let Some(p) = prefix {
1510 p.collect_vars(vars);
1511 }
1512 if let Some(s) = suffix {
1513 s.collect_vars(vars);
1514 }
1515 if let Some(s) = sep {
1516 s.collect_vars(vars);
1517 }
1518 }
1519 CompiledLogic::Mround(a, b) => {
1520 a.collect_vars(vars);
1521 b.collect_vars(vars);
1522 }
1523 CompiledLogic::Return(_) => {} CompiledLogic::If(cond, then_val, else_val) => {
1525 cond.collect_vars(vars);
1526 then_val.collect_vars(vars);
1527 else_val.collect_vars(vars);
1528 }
1529 CompiledLogic::Equal(a, b)
1530 | CompiledLogic::StrictEqual(a, b)
1531 | CompiledLogic::NotEqual(a, b)
1532 | CompiledLogic::StrictNotEqual(a, b)
1533 | CompiledLogic::LessThan(a, b)
1534 | CompiledLogic::LessThanOrEqual(a, b)
1535 | CompiledLogic::GreaterThan(a, b)
1536 | CompiledLogic::GreaterThanOrEqual(a, b)
1537 | CompiledLogic::Modulo(a, b)
1538 | CompiledLogic::Power(a, b)
1539 | CompiledLogic::Map(a, b)
1540 | CompiledLogic::Filter(a, b)
1541 | CompiledLogic::All(a, b)
1542 | CompiledLogic::Some(a, b)
1543 | CompiledLogic::None(a, b)
1544 | CompiledLogic::In(a, b)
1545 | CompiledLogic::Pow(a, b)
1546 | CompiledLogic::Xor(a, b)
1547 | CompiledLogic::IfNull(a, b)
1548 | CompiledLogic::Days(a, b)
1549 | CompiledLogic::SplitValue(a, b)
1550 | CompiledLogic::MaxAt(a, b)
1551 | CompiledLogic::RangeOptions(a, b) => {
1552 a.collect_vars(vars);
1553 b.collect_vars(vars);
1554 }
1555 CompiledLogic::Reduce(a, b, c)
1556 | CompiledLogic::Mid(a, b, c)
1557 | CompiledLogic::Date(a, b, c)
1558 | CompiledLogic::DateDif(a, b, c)
1559 | CompiledLogic::MapOptions(a, b, c)
1560 | CompiledLogic::For(a, b, c) => {
1561 a.collect_vars(vars);
1562 b.collect_vars(vars);
1563 c.collect_vars(vars);
1564 }
1565 CompiledLogic::Substr(s, start, len)
1566 | CompiledLogic::Search(s, start, len)
1567 | CompiledLogic::SplitText(s, start, len)
1568 | CompiledLogic::YearFrac(s, start, len) => {
1569 s.collect_vars(vars);
1570 start.collect_vars(vars);
1571 if let Some(l) = len {
1572 l.collect_vars(vars);
1573 }
1574 }
1575 CompiledLogic::Left(a, opt)
1576 | CompiledLogic::Right(a, opt)
1577 | CompiledLogic::ValueAt(a, _, opt) => {
1578 a.collect_vars(vars);
1579 if let Some(o) = opt {
1580 o.collect_vars(vars);
1581 }
1582 }
1583 CompiledLogic::Sum(a, opt1, opt2) => {
1584 a.collect_vars(vars);
1585 if let Some(o) = opt1 {
1586 o.collect_vars(vars);
1587 }
1588 if let Some(o) = opt2 {
1589 o.collect_vars(vars);
1590 }
1591 }
1592 CompiledLogic::IndexAt(a, b, c, opt) => {
1593 a.collect_vars(vars);
1594 b.collect_vars(vars);
1595 c.collect_vars(vars);
1596 if let Some(o) = opt {
1597 o.collect_vars(vars);
1598 }
1599 }
1600 CompiledLogic::Match(table, conds)
1601 | CompiledLogic::MatchRange(table, conds)
1602 | CompiledLogic::Choose(table, conds)
1603 | CompiledLogic::FindIndex(table, conds) => {
1604 table.collect_vars(vars);
1605 for cond in conds {
1606 cond.collect_vars(vars);
1607 }
1608 }
1609 CompiledLogic::Multiplies(items) | CompiledLogic::Divides(items) => {
1610 for item in items {
1611 item.collect_vars(vars);
1612 }
1613 }
1614 CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1615 table.collect_vars(vars);
1616 label.collect_vars(vars);
1617 value.collect_vars(vars);
1618 for cond in conds {
1619 cond.collect_vars(vars);
1620 }
1621 }
1622 CompiledLogic::MissingSome(min, _) => {
1623 min.collect_vars(vars);
1624 }
1625 _ => {}
1626 }
1627 }
1628}
1629
1630pub struct CompiledLogicStore {
1636 next_id: u64,
1637 store: AHashMap<LogicId, CompiledLogic>,
1638 dependencies: AHashMap<LogicId, Vec<String>>,
1639}
1640
1641impl CompiledLogicStore {
1642 pub fn new() -> Self {
1643 Self {
1644 next_id: 0,
1645 store: AHashMap::default(),
1646 dependencies: AHashMap::default(),
1647 }
1648 }
1649
1650 pub fn compile(&mut self, logic: &Value) -> Result<LogicId, String> {
1656 let _global_id = super::compiled_logic_store::compile_logic_value(logic)?;
1658
1659 let compiled = super::compiled_logic_store::get_compiled_logic(_global_id)
1661 .ok_or_else(|| "Failed to retrieve compiled logic from global store".to_string())?;
1662
1663 let deps = compiled.referenced_vars();
1665
1666 let id = LogicId(self.next_id);
1668 self.next_id += 1;
1669
1670 self.store.insert(id, compiled);
1672 self.dependencies.insert(id, deps);
1673
1674 Ok(id)
1675 }
1676
1677 pub fn get(&self, id: &LogicId) -> Option<&CompiledLogic> {
1679 self.store.get(id)
1680 }
1681
1682 pub fn remove(&mut self, id: &LogicId) -> Option<CompiledLogic> {
1684 self.dependencies.remove(id);
1685 self.store.remove(id)
1686 }
1687
1688 pub fn get_dependencies(&self, id: &LogicId) -> Option<&[String]> {
1690 self.dependencies.get(id).map(|v| v.as_slice())
1691 }
1692}
1693
1694impl Default for CompiledLogicStore {
1695 fn default() -> Self {
1696 Self::new()
1697 }
1698}
1699
1700#[cfg(test)]
1701mod tests {
1702 use super::*;
1703 use serde_json::json;
1704
1705 fn is_ok(json_value: serde_json::Value) -> bool {
1706 CompiledLogic::compile(&json_value).is_ok()
1707 }
1708
1709 #[test]
1710 fn test_compile_literals() {
1711 assert!(matches!(CompiledLogic::compile(&json!(null)).unwrap(), CompiledLogic::Null));
1712 assert!(matches!(CompiledLogic::compile(&json!(true)).unwrap(), CompiledLogic::Bool(true)));
1713 assert!(matches!(CompiledLogic::compile(&json!(false)).unwrap(), CompiledLogic::Bool(false)));
1714 assert!(matches!(CompiledLogic::compile(&json!(42.5)).unwrap(), CompiledLogic::Number(n) if n == 42.5));
1715 assert!(matches!(CompiledLogic::compile(&json!("hello")).unwrap(), CompiledLogic::String(ref s) if s == "hello"));
1716 assert!(matches!(CompiledLogic::compile(&json!([1, 2, 3])).unwrap(), CompiledLogic::Array(_)));
1717 }
1718
1719 #[test]
1720 fn test_compile_variables() {
1721 assert!(is_ok(json!({"var": "a"})));
1722 assert!(is_ok(json!({"var": ["a", 1]})));
1723 assert!(!is_ok(json!({"var": {}}))); assert!(is_ok(json!({"$ref": "#/path"})));
1725 }
1726
1727 #[test]
1728 fn test_compile_logical() {
1729 assert!(is_ok(json!({"and": [true, false]})));
1730 assert!(is_ok(json!({"or": [true, false]})));
1731 assert!(is_ok(json!({"not": [true]})));
1732 assert!(is_ok(json!({"!": [true]})));
1733 assert!(is_ok(json!({"if": [true, 1, 0]})));
1734 assert!(is_ok(json!({"xor": [true, false]})));
1735 assert!(is_ok(json!({"ifnull": [{"var": "a"}, 0]})));
1736
1737 assert!(!is_ok(json!({"if": []})));
1739 assert!(!is_ok(json!({"and": true})));
1740 }
1741
1742 #[test]
1743 fn test_compile_comparison() {
1744 assert!(is_ok(json!({"==": [1, 1]})));
1745 assert!(is_ok(json!({"===": [1, 1]})));
1746 assert!(is_ok(json!({"!=": [1, 2]})));
1747 assert!(is_ok(json!({"!==": [1, 2]})));
1748 assert!(is_ok(json!({">": [2, 1]})));
1749 assert!(is_ok(json!({">=": [2, 2]})));
1750 assert!(is_ok(json!({"<": [1, 2]})));
1751 assert!(is_ok(json!({"<=": [2, 2]})));
1752
1753 assert!(!is_ok(json!({"==": 1})));
1755 }
1756
1757 #[test]
1758 fn test_compile_arithmetic() {
1759 assert!(is_ok(json!({"+": [1, 2]})));
1760 assert!(is_ok(json!({"-": [1, 2]})));
1761 assert!(is_ok(json!({"*": [1, 2]})));
1762 assert!(is_ok(json!({"/": [1, 2]})));
1763 assert!(is_ok(json!({"%": [1, 2]})));
1764 assert!(is_ok(json!({"pow": [2, 3]})));
1765
1766 assert!(!is_ok(json!({"-": "a"}))); }
1769
1770 #[test]
1771 fn test_compile_array_ops() {
1772 assert!(is_ok(json!({"map": [[1, 2], {"+": [{"var": ""}, 1]}]})));
1773 assert!(is_ok(json!({"filter": [[1, 2], {">": [{"var": ""}, 1]}]})));
1774 assert!(is_ok(json!({"reduce": [[1, 2], {"+": [{"var": "current"}, {"var": "accumulator"}]}, 0]})));
1775 assert!(is_ok(json!({"all": [[true, false], {"var": ""}]})));
1776 assert!(is_ok(json!({"some": [[true, false], {"var": ""}]})));
1777 assert!(is_ok(json!({"none": [[true, false], {"var": ""}]})));
1778 assert!(is_ok(json!({"merge": [[1], [2]]})));
1779 assert!(is_ok(json!({"in": [1, [1, 2, 3]]})));
1780 assert!(is_ok(json!({"sum": [{"var": "data"}, "a", 0]})));
1781 }
1782
1783 #[test]
1784 fn test_compile_string_ops() {
1785 assert!(is_ok(json!({"cat": ["a", "b"]})));
1786 assert!(is_ok(json!({"substr": ["hello", 0, 2]})));
1787 assert!(is_ok(json!({"search": ["a", "abc", 0]})));
1788 assert!(is_ok(json!({"SPLITTEXT": ["a,b", ",", 0]})));
1789 assert!(is_ok(json!({"left": ["abc", 2]})));
1790 assert!(is_ok(json!({"right": ["abc", 2]})));
1791
1792 assert!(!is_ok(json!({"substr": "hello"})));
1794 }
1795
1796 #[test]
1797 fn test_compile_math_ops() {
1798 assert!(is_ok(json!({"max": [1, 2, 3]})));
1799 assert!(is_ok(json!({"min": [1, 2, 3]})));
1800 assert!(is_ok(json!({"abs": -1})));
1801 assert!(is_ok(json!({"round": [1.234, 2]})));
1802 assert!(is_ok(json!({"roundup": [1.234, 2]})));
1803 assert!(is_ok(json!({"rounddown": [1.234, 2]})));
1804 assert!(is_ok(json!({"ceiling": [1.234, 1]})));
1805 assert!(is_ok(json!({"floor": [1.234, 1]})));
1806 assert!(is_ok(json!({"trunc": [1.234, 2]})));
1807 }
1808
1809 #[test]
1810 fn test_compile_date_ops() {
1811 assert!(is_ok(json!({"today": []})));
1812 assert!(is_ok(json!({"date": [2024, 12, 1]})));
1813 assert!(is_ok(json!({"days": ["2024-12-02", "2024-12-01"]})));
1814 assert!(is_ok(json!({"year": ["2024-12-01"]})));
1815 assert!(is_ok(json!({"month": ["2024-12-01"]})));
1816 assert!(is_ok(json!({"day": ["2024-12-01"]})));
1817 }
1818
1819 #[test]
1820 fn test_compile_table_ops() {
1821 assert!(is_ok(json!({"VALUEAT": [{"var": "t"}, 0, "a"]})));
1822 assert!(is_ok(json!({"INDEXAT": ["a", {"var": "t"}, "b", true]})));
1823 assert!(is_ok(json!({"MAXAT": [{"var": "t"}, "a"]})));
1824 assert!(is_ok(json!({"MATCH": [{"var": "t"}, "==", 1, "a"]})));
1825 assert!(is_ok(json!({"MATCHRANGE": [{"var": "t"}, "==", 1, "a"]})));
1826 assert!(is_ok(json!({"CHOOSE": [{"var": "t"}, "==", 1, "a"]})));
1827 assert!(is_ok(json!({"FINDINDEX": [{"var": "t"}, "==", 1, "a"]})));
1828 assert!(is_ok(json!({"MAPOPTIONS": [{"var": "t"}, "a", "b"]})));
1829 assert!(is_ok(json!({"MAPOPTIONSIF": [{"var": "t"}, "a", "b", [true, "==", "c"]]})));
1830
1831 assert!(!is_ok(json!({"VALUEAT": [[{"a": 1}], 0]})));
1833 assert!(!is_ok(json!({"MAPOPTIONSIF": [[], "a"]})));
1834 }
1835
1836 #[test]
1837 fn test_compile_util_ops() {
1838 assert!(is_ok(json!({"missing": ["a", "b"]})));
1839 assert!(is_ok(json!({"missing_some": [1, ["a", "b"]]})));
1840 assert!(is_ok(json!({"empty": []})));
1841 assert!(is_ok(json!({"return": 5})));
1842 assert!(is_ok(json!({"RANGEOPTIONS": [1, 5]})));
1843 }
1844
1845 #[test]
1846 fn test_compile_unknown() {
1847 assert!(!is_ok(json!({"UNKNOWN_OP": [1, 2]})));
1848 assert!(!is_ok(json!({"UNKNOWN_NON_ARRAY": 1})));
1849 }
1850}