1use crate::jsoneval::path_utils;
2use rapidhash::RapidHashMap;
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) => Ok(CompiledLogic::Number(n.as_f64().unwrap_or(0.0))),
182 Value::String(s) => Ok(CompiledLogic::String(s.clone())),
183 Value::Array(arr) => {
184 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
185 Ok(CompiledLogic::Array(compiled?))
186 }
187 Value::Object(obj) => {
188 if obj.is_empty() {
189 return Ok(CompiledLogic::Null);
190 }
191
192 let (op, args) = obj.iter().next().unwrap();
194
195 Self::compile_operator(op, args)
196 }
197 }
198 }
199
200 fn compile_operator(op: &str, args: &Value) -> Result<Self, String> {
201 match op {
202 "var" => {
204 if let Value::String(name) = args {
205 let normalized = path_utils::normalize_to_json_pointer(name).into_owned();
207 Ok(CompiledLogic::Var(normalized, None))
208 } else if let Value::Array(arr) = args {
209 if arr.is_empty() {
210 return Err("var requires at least one argument".to_string());
211 }
212 let name = arr[0].as_str().ok_or("var name must be a string")?;
213 let normalized = path_utils::normalize_to_json_pointer(name).into_owned();
215 let default = if arr.len() > 1 {
216 Some(Box::new(Self::compile(&arr[1])?))
217 } else {
218 None
219 };
220 Ok(CompiledLogic::Var(normalized, default))
221 } else {
222 Err("var requires string or array".to_string())
223 }
224 }
225 "$ref" | "ref" => {
226 if let Value::String(path) = args {
227 let normalized = path_utils::normalize_to_json_pointer(path).into_owned();
229 Ok(CompiledLogic::Ref(normalized, None))
230 } else if let Value::Array(arr) = args {
231 if arr.is_empty() {
232 return Err("$ref requires at least one argument".to_string());
233 }
234 let path = arr[0].as_str().ok_or("$ref path must be a string")?;
235 let normalized = path_utils::normalize_to_json_pointer(path).into_owned();
237 let default = if arr.len() > 1 {
238 Some(Box::new(Self::compile(&arr[1])?))
239 } else {
240 None
241 };
242 Ok(CompiledLogic::Ref(normalized, default))
243 } else {
244 Err("$ref requires string or array".to_string())
245 }
246 }
247
248 "and" => {
250 let arr = args.as_array().ok_or("and requires array")?;
251 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
252 let items = compiled?;
253 Ok(CompiledLogic::And(Self::flatten_and(items)))
255 }
256 "or" => {
257 let arr = args.as_array().ok_or("or requires array")?;
258 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
259 let items = compiled?;
260 Ok(CompiledLogic::Or(Self::flatten_or(items)))
262 }
263 "!" | "not" => {
264 let value_to_negate = if let Value::Array(arr) = args {
266 if arr.is_empty() {
267 return Err("! requires an argument".to_string());
268 }
269 &arr[0]
270 } else {
271 args
272 };
273
274 let inner = Self::compile(value_to_negate)?;
275 if let CompiledLogic::Not(inner_expr) = inner {
277 Ok(*inner_expr)
278 } else {
279 Ok(CompiledLogic::Not(Box::new(inner)))
280 }
281 }
282 "if" => {
283 let arr = args.as_array().ok_or("if requires array")?;
284 if arr.len() < 3 {
285 return Err("if requires at least 3 arguments".to_string());
286 }
287 Ok(CompiledLogic::If(
288 Box::new(Self::compile(&arr[0])?),
289 Box::new(Self::compile(&arr[1])?),
290 Box::new(Self::compile(&arr[2])?),
291 ))
292 }
293
294 "==" => Self::compile_binary(args, |a, b| CompiledLogic::Equal(a, b)),
296 "===" => Self::compile_binary(args, |a, b| CompiledLogic::StrictEqual(a, b)),
297 "!=" => Self::compile_binary(args, |a, b| CompiledLogic::NotEqual(a, b)),
298 "!==" => Self::compile_binary(args, |a, b| CompiledLogic::StrictNotEqual(a, b)),
299 "<" => Self::compile_binary(args, |a, b| CompiledLogic::LessThan(a, b)),
300 "<=" => Self::compile_binary(args, |a, b| CompiledLogic::LessThanOrEqual(a, b)),
301 ">" => Self::compile_binary(args, |a, b| CompiledLogic::GreaterThan(a, b)),
302 ">=" => Self::compile_binary(args, |a, b| CompiledLogic::GreaterThanOrEqual(a, b)),
303
304 "+" => {
306 let arr = args.as_array().ok_or("+ requires array")?;
307 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
308 let items = compiled?;
309 Ok(CompiledLogic::Add(Self::flatten_add(items)))
311 }
312 "-" => {
313 let arr = args.as_array().ok_or("- requires array")?;
314 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
315 Ok(CompiledLogic::Subtract(compiled?))
316 }
317 "*" => {
318 let arr = args.as_array().ok_or("* requires array")?;
319 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
320 let items = compiled?;
321 Ok(CompiledLogic::Multiply(Self::flatten_multiply(items)))
323 }
324 "/" => {
325 let arr = args.as_array().ok_or("/ requires array")?;
326 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
327 Ok(CompiledLogic::Divide(compiled?))
328 }
329 "%" => Self::compile_binary(args, |a, b| CompiledLogic::Modulo(a, b)),
330 "^" => Self::compile_binary(args, |a, b| CompiledLogic::Power(a, b)),
331
332 "map" => Self::compile_binary(args, |a, b| CompiledLogic::Map(a, b)),
334 "filter" => Self::compile_binary(args, |a, b| CompiledLogic::Filter(a, b)),
335 "reduce" => {
336 let arr = args.as_array().ok_or("reduce requires array")?;
337 if arr.len() < 3 {
338 return Err("reduce requires 3 arguments".to_string());
339 }
340 Ok(CompiledLogic::Reduce(
341 Box::new(Self::compile(&arr[0])?),
342 Box::new(Self::compile(&arr[1])?),
343 Box::new(Self::compile(&arr[2])?),
344 ))
345 }
346 "all" => Self::compile_binary(args, |a, b| CompiledLogic::All(a, b)),
347 "some" => Self::compile_binary(args, |a, b| CompiledLogic::Some(a, b)),
348 "none" => Self::compile_binary(args, |a, b| CompiledLogic::None(a, b)),
349 "merge" => {
350 let arr = args.as_array().ok_or("merge requires array")?;
351 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
352 Ok(CompiledLogic::Merge(compiled?))
353 }
354 "in" => Self::compile_binary(args, |a, b| CompiledLogic::In(a, b)),
355
356 "cat" => {
358 let arr = args.as_array().ok_or("cat requires array")?;
359 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
360 let items = compiled?;
361 Ok(CompiledLogic::Cat(Self::flatten_cat(items)))
363 }
364 "substr" => {
365 let arr = args.as_array().ok_or("substr requires array")?;
366 if arr.len() < 2 {
367 return Err("substr requires at least 2 arguments".to_string());
368 }
369 let length = if arr.len() > 2 {
370 Some(Box::new(Self::compile(&arr[2])?))
371 } else {
372 None
373 };
374 Ok(CompiledLogic::Substr(
375 Box::new(Self::compile(&arr[0])?),
376 Box::new(Self::compile(&arr[1])?),
377 length,
378 ))
379 }
380
381 "missing" => {
383 let keys = if let Value::Array(arr) = args {
384 arr.iter()
385 .map(|v| {
386 v.as_str()
387 .ok_or("missing key must be string")
388 .map(|s| s.to_string())
389 })
390 .collect::<Result<Vec<_>, _>>()?
391 } else if let Value::String(s) = args {
392 vec![s.clone()]
393 } else {
394 return Err("missing requires string or array".to_string());
395 };
396 Ok(CompiledLogic::Missing(keys))
397 }
398 "missing_some" => {
399 let arr = args.as_array().ok_or("missing_some requires array")?;
400 if arr.len() < 2 {
401 return Err("missing_some requires at least 2 arguments".to_string());
402 }
403 let minimum = Box::new(Self::compile(&arr[0])?);
404 let keys = if let Value::Array(key_arr) = &arr[1] {
405 key_arr
406 .iter()
407 .map(|v| {
408 v.as_str()
409 .ok_or("key must be string")
410 .map(|s| s.to_string())
411 })
412 .collect::<Result<Vec<_>, _>>()?
413 } else {
414 return Err("missing_some keys must be array".to_string());
415 };
416 Ok(CompiledLogic::MissingSome(minimum, keys))
417 }
418
419 "abs" => {
421 let arg = if let Value::Array(arr) = args {
422 if arr.is_empty() {
423 return Err("abs requires at least one argument".to_string());
424 }
425 &arr[0]
426 } else {
427 args
428 };
429 Ok(CompiledLogic::Abs(Box::new(Self::compile(arg)?)))
430 }
431 "max" => {
432 let arr = args.as_array().ok_or("max requires array")?;
433 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
434 Ok(CompiledLogic::Max(compiled?))
435 }
436 "min" => {
437 let arr = args.as_array().ok_or("min requires array")?;
438 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
439 Ok(CompiledLogic::Min(compiled?))
440 }
441 "pow" | "**" => Self::compile_binary(args, |a, b| CompiledLogic::Pow(a, b)),
442 "round" | "ROUND" => {
443 if let Value::Array(arr) = args {
444 let decimals = if arr.len() > 1 {
445 Some(Box::new(Self::compile(&arr[1])?))
446 } else {
447 None
448 };
449 Ok(CompiledLogic::Round(
450 Box::new(Self::compile(&arr[0])?),
451 decimals,
452 ))
453 } else {
454 Ok(CompiledLogic::Round(Box::new(Self::compile(args)?), None))
455 }
456 }
457 "roundup" | "ROUNDUP" => {
458 if let Value::Array(arr) = args {
459 let decimals = if arr.len() > 1 {
460 Some(Box::new(Self::compile(&arr[1])?))
461 } else {
462 None
463 };
464 Ok(CompiledLogic::RoundUp(
465 Box::new(Self::compile(&arr[0])?),
466 decimals,
467 ))
468 } else {
469 Ok(CompiledLogic::RoundUp(Box::new(Self::compile(args)?), None))
470 }
471 }
472 "rounddown" | "ROUNDDOWN" => {
473 if let Value::Array(arr) = args {
474 let decimals = if arr.len() > 1 {
475 Some(Box::new(Self::compile(&arr[1])?))
476 } else {
477 None
478 };
479 Ok(CompiledLogic::RoundDown(
480 Box::new(Self::compile(&arr[0])?),
481 decimals,
482 ))
483 } else {
484 Ok(CompiledLogic::RoundDown(
485 Box::new(Self::compile(args)?),
486 None,
487 ))
488 }
489 }
490 "ceiling" | "CEILING" => {
491 if let Value::Array(arr) = args {
492 let significance = if arr.len() > 1 {
493 Some(Box::new(Self::compile(&arr[1])?))
494 } else {
495 None
496 };
497 Ok(CompiledLogic::Ceiling(
498 Box::new(Self::compile(&arr[0])?),
499 significance,
500 ))
501 } else {
502 Ok(CompiledLogic::Ceiling(Box::new(Self::compile(args)?), None))
503 }
504 }
505 "floor" | "FLOOR" => {
506 if let Value::Array(arr) = args {
507 let significance = if arr.len() > 1 {
508 Some(Box::new(Self::compile(&arr[1])?))
509 } else {
510 None
511 };
512 Ok(CompiledLogic::Floor(
513 Box::new(Self::compile(&arr[0])?),
514 significance,
515 ))
516 } else {
517 Ok(CompiledLogic::Floor(Box::new(Self::compile(args)?), None))
518 }
519 }
520 "trunc" | "TRUNC" => {
521 if let Value::Array(arr) = args {
522 let decimals = if arr.len() > 1 {
523 Some(Box::new(Self::compile(&arr[1])?))
524 } else {
525 None
526 };
527 Ok(CompiledLogic::Trunc(
528 Box::new(Self::compile(&arr[0])?),
529 decimals,
530 ))
531 } else {
532 Ok(CompiledLogic::Trunc(Box::new(Self::compile(args)?), None))
533 }
534 }
535 "mround" | "MROUND" => Self::compile_binary(args, |a, b| CompiledLogic::Mround(a, b)),
536
537 "length" => {
539 let arg = if let Value::Array(arr) = args {
540 if arr.is_empty() {
541 return Err("length requires at least one argument".to_string());
542 }
543 &arr[0]
544 } else {
545 args
546 };
547 Ok(CompiledLogic::Length(Box::new(Self::compile(arg)?)))
548 }
549 "len" | "LEN" => {
550 let arg = if let Value::Array(arr) = args {
551 if arr.is_empty() {
552 return Err("len requires at least one argument".to_string());
553 }
554 &arr[0]
555 } else {
556 args
557 };
558 Ok(CompiledLogic::Len(Box::new(Self::compile(arg)?)))
559 }
560 "search" | "SEARCH" => {
561 let arr = args.as_array().ok_or("search requires array")?;
562 if arr.len() < 2 {
563 return Err("search requires at least 2 arguments".to_string());
564 }
565 let start_num = if arr.len() > 2 {
566 Some(Box::new(Self::compile(&arr[2])?))
567 } else {
568 None
569 };
570 Ok(CompiledLogic::Search(
571 Box::new(Self::compile(&arr[0])?),
572 Box::new(Self::compile(&arr[1])?),
573 start_num,
574 ))
575 }
576 "left" | "LEFT" => {
577 if let Value::Array(arr) = args {
578 let num_chars = if arr.len() > 1 {
579 Some(Box::new(Self::compile(&arr[1])?))
580 } else {
581 None
582 };
583 Ok(CompiledLogic::Left(
584 Box::new(Self::compile(&arr[0])?),
585 num_chars,
586 ))
587 } else {
588 Ok(CompiledLogic::Left(Box::new(Self::compile(args)?), None))
589 }
590 }
591 "right" | "RIGHT" => {
592 if let Value::Array(arr) = args {
593 let num_chars = if arr.len() > 1 {
594 Some(Box::new(Self::compile(&arr[1])?))
595 } else {
596 None
597 };
598 Ok(CompiledLogic::Right(
599 Box::new(Self::compile(&arr[0])?),
600 num_chars,
601 ))
602 } else {
603 Ok(CompiledLogic::Right(Box::new(Self::compile(args)?), None))
604 }
605 }
606 "mid" | "MID" => {
607 let arr = args.as_array().ok_or("mid requires array")?;
608 if arr.len() < 3 {
609 return Err("mid requires 3 arguments".to_string());
610 }
611 Ok(CompiledLogic::Mid(
612 Box::new(Self::compile(&arr[0])?),
613 Box::new(Self::compile(&arr[1])?),
614 Box::new(Self::compile(&arr[2])?),
615 ))
616 }
617 "splittext" | "SPLITTEXT" => {
618 let arr = args.as_array().ok_or("splittext requires array")?;
619 if arr.len() < 2 {
620 return Err("splittext requires at least 2 arguments".to_string());
621 }
622 let index = if arr.len() > 2 {
623 Some(Box::new(Self::compile(&arr[2])?))
624 } else {
625 None
626 };
627 Ok(CompiledLogic::SplitText(
628 Box::new(Self::compile(&arr[0])?),
629 Box::new(Self::compile(&arr[1])?),
630 index,
631 ))
632 }
633 "concat" | "CONCAT" => {
634 let arr = args.as_array().ok_or("concat requires array")?;
635 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
636 Ok(CompiledLogic::Concat(compiled?))
637 }
638 "splitvalue" | "SPLITVALUE" => {
639 Self::compile_binary(args, |a, b| CompiledLogic::SplitValue(a, b))
640 }
641 "stringformat" | "STRINGFORMAT" => {
642 let arr = args.as_array().ok_or("stringformat requires array")?;
643 if arr.is_empty() {
644 return Err("stringformat requires at least 1 argument".to_string());
645 }
646 let decimals = if arr.len() > 1 {
647 Some(Box::new(Self::compile(&arr[1])?))
648 } else {
649 None
650 };
651 let prefix = if arr.len() > 2 {
652 Some(Box::new(Self::compile(&arr[2])?))
653 } else {
654 None
655 };
656 let suffix = if arr.len() > 3 {
657 Some(Box::new(Self::compile(&arr[3])?))
658 } else {
659 None
660 };
661 let thousands_sep = if arr.len() > 4 {
662 Some(Box::new(Self::compile(&arr[4])?))
663 } else {
664 None
665 };
666 Ok(CompiledLogic::StringFormat(
667 Box::new(Self::compile(&arr[0])?),
668 decimals,
669 prefix,
670 suffix,
671 thousands_sep,
672 ))
673 }
674
675 "xor" => Self::compile_binary(args, |a, b| CompiledLogic::Xor(a, b)),
677 "ifnull" | "IFNULL" => Self::compile_binary(args, |a, b| CompiledLogic::IfNull(a, b)),
678 "isempty" | "ISEMPTY" => {
679 let arg = if let Value::Array(arr) = args {
680 if arr.is_empty() {
681 return Err("ISEMPTY requires at least one argument".to_string());
682 }
683 &arr[0]
684 } else {
685 args
686 };
687 Ok(CompiledLogic::IsEmpty(Box::new(Self::compile(arg)?)))
688 }
689 "empty" | "EMPTY" => Ok(CompiledLogic::Empty),
690
691 "today" | "TODAY" => Ok(CompiledLogic::Today),
693 "now" | "NOW" => Ok(CompiledLogic::Now),
694 "days" | "DAYS" => Self::compile_binary(args, |a, b| CompiledLogic::Days(a, b)),
695 "year" | "YEAR" => {
696 let arg = if let Value::Array(arr) = args {
697 if arr.is_empty() {
698 return Err("year requires at least one argument".to_string());
699 }
700 &arr[0]
701 } else {
702 args
703 };
704 Ok(CompiledLogic::Year(Box::new(Self::compile(arg)?)))
705 }
706 "month" | "MONTH" => {
707 let arg = if let Value::Array(arr) = args {
708 if arr.is_empty() {
709 return Err("month requires at least one argument".to_string());
710 }
711 &arr[0]
712 } else {
713 args
714 };
715 Ok(CompiledLogic::Month(Box::new(Self::compile(arg)?)))
716 }
717 "day" | "DAY" => {
718 let arg = if let Value::Array(arr) = args {
719 if arr.is_empty() {
720 return Err("day requires at least one argument".to_string());
721 }
722 &arr[0]
723 } else {
724 args
725 };
726 Ok(CompiledLogic::Day(Box::new(Self::compile(arg)?)))
727 }
728 "date" | "DATE" => {
729 let arr = args.as_array().ok_or("date requires array")?;
730 if arr.len() < 3 {
731 return Err("date requires 3 arguments".to_string());
732 }
733 Ok(CompiledLogic::Date(
734 Box::new(Self::compile(&arr[0])?),
735 Box::new(Self::compile(&arr[1])?),
736 Box::new(Self::compile(&arr[2])?),
737 ))
738 }
739 "dateformat" | "DATEFORMAT" => {
740 if let Value::Array(arr) = args {
741 if arr.is_empty() {
742 return Err("dateformat requires at least 1 argument".to_string());
743 }
744 let format = if arr.len() > 1 {
745 Some(Box::new(Self::compile(&arr[1])?))
746 } else {
747 None
748 };
749 Ok(CompiledLogic::DateFormat(
750 Box::new(Self::compile(&arr[0])?),
751 format,
752 ))
753 } else {
754 Ok(CompiledLogic::DateFormat(
755 Box::new(Self::compile(args)?),
756 None,
757 ))
758 }
759 }
760
761 "sum" | "SUM" => {
763 if let Value::Array(arr) = args {
764 if arr.is_empty() {
765 return Err("sum requires at least 1 argument".to_string());
766 }
767 let field = if arr.len() > 1 {
768 Some(Box::new(Self::compile(&arr[1])?))
769 } else {
770 None
771 };
772 let threshold = if arr.len() > 2 {
773 Some(Box::new(Self::compile(&arr[2])?))
774 } else {
775 None
776 };
777 Ok(CompiledLogic::Sum(
778 Box::new(Self::compile(&arr[0])?),
779 field,
780 threshold,
781 ))
782 } else {
783 Ok(CompiledLogic::Sum(
784 Box::new(Self::compile(args)?),
785 None,
786 None,
787 ))
788 }
789 }
790 "FOR" => {
791 let arr = args.as_array().ok_or("FOR requires array")?;
792 if arr.len() < 3 {
793 return Err("FOR requires 3 arguments: start, end, logic".to_string());
794 }
795 Ok(CompiledLogic::For(
796 Box::new(Self::compile(&arr[0])?),
797 Box::new(Self::compile(&arr[1])?),
798 Box::new(Self::compile(&arr[2])?),
799 ))
800 }
801
802 "valueat" | "VALUEAT" => {
804 let arr = args.as_array().ok_or("VALUEAT requires array")?;
805 if arr.len() < 2 {
806 return Err("VALUEAT requires at least 2 arguments".to_string());
807 }
808 let col_name = if arr.len() > 2 {
809 Some(Box::new(Self::compile(&arr[2])?))
810 } else {
811 None
812 };
813 Ok(CompiledLogic::ValueAt(
814 Box::new(Self::compile(&arr[0])?),
815 Box::new(Self::compile(&arr[1])?),
816 col_name,
817 ))
818 }
819 "maxat" | "MAXAT" => Self::compile_binary(args, |a, b| CompiledLogic::MaxAt(a, b)),
820 "indexat" | "INDEXAT" => {
821 let arr = args.as_array().ok_or("INDEXAT requires array")?;
822 if arr.len() < 3 {
823 return Err("INDEXAT requires at least 3 arguments".to_string());
824 }
825 let range = if arr.len() > 3 {
826 Some(Box::new(Self::compile(&arr[3])?))
827 } else {
828 None
829 };
830 Ok(CompiledLogic::IndexAt(
831 Box::new(Self::compile(&arr[0])?),
832 Box::new(Self::compile(&arr[1])?),
833 Box::new(Self::compile(&arr[2])?),
834 range,
835 ))
836 }
837 "match" | "MATCH" => {
838 let arr = args.as_array().ok_or("MATCH requires array")?;
839 if arr.is_empty() {
840 return Err("MATCH requires at least 1 argument".to_string());
841 }
842 let table = Box::new(Self::compile(&arr[0])?);
843 let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
844 Ok(CompiledLogic::Match(table, conditions?))
845 }
846 "matchrange" | "MATCHRANGE" => {
847 let arr = args.as_array().ok_or("MATCHRANGE requires array")?;
848 if arr.is_empty() {
849 return Err("MATCHRANGE requires at least 1 argument".to_string());
850 }
851 let table = Box::new(Self::compile(&arr[0])?);
852 let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
853 Ok(CompiledLogic::MatchRange(table, conditions?))
854 }
855 "choose" | "CHOOSE" => {
856 let arr = args.as_array().ok_or("CHOOSE requires array")?;
857 if arr.is_empty() {
858 return Err("CHOOSE requires at least 1 argument".to_string());
859 }
860 let table = Box::new(Self::compile(&arr[0])?);
861 let conditions: Result<Vec<_>, _> = arr[1..].iter().map(Self::compile).collect();
862 Ok(CompiledLogic::Choose(table, conditions?))
863 }
864 "findindex" | "FINDINDEX" => {
865 let arr = args.as_array().ok_or("FINDINDEX requires array")?;
866 if arr.len() < 2 {
867 return Err("FINDINDEX requires at least 2 arguments".to_string());
868 }
869 let table = Box::new(Self::compile(&arr[0])?);
870 let conditions: Result<Vec<_>, _> = arr[1..]
873 .iter()
874 .map(|cond| Self::compile(&Self::preprocess_table_condition(cond)))
875 .collect();
876 Ok(CompiledLogic::FindIndex(table, conditions?))
877 }
878
879 "MULTIPLIES" => {
881 let arr = args.as_array().ok_or("MULTIPLIES requires array")?;
882 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
883 Ok(CompiledLogic::Multiplies(compiled?))
884 }
885 "DIVIDES" => {
886 let arr = args.as_array().ok_or("DIVIDES requires array")?;
887 let compiled: Result<Vec<_>, _> = arr.iter().map(Self::compile).collect();
888 Ok(CompiledLogic::Divides(compiled?))
889 }
890
891 "YEARFRAC" => {
893 let arr = args.as_array().ok_or("YEARFRAC requires array")?;
894 if arr.len() < 2 {
895 return Err("YEARFRAC requires at least 2 arguments".to_string());
896 }
897 let basis = if arr.len() > 2 {
898 Some(Box::new(Self::compile(&arr[2])?))
899 } else {
900 None
901 };
902 Ok(CompiledLogic::YearFrac(
903 Box::new(Self::compile(&arr[0])?),
904 Box::new(Self::compile(&arr[1])?),
905 basis,
906 ))
907 }
908 "DATEDIF" => {
909 let arr = args.as_array().ok_or("DATEDIF requires array")?;
910 if arr.len() < 3 {
911 return Err("DATEDIF requires 3 arguments".to_string());
912 }
913 Ok(CompiledLogic::DateDif(
914 Box::new(Self::compile(&arr[0])?),
915 Box::new(Self::compile(&arr[1])?),
916 Box::new(Self::compile(&arr[2])?),
917 ))
918 }
919
920 "RANGEOPTIONS" => Self::compile_binary(args, |a, b| CompiledLogic::RangeOptions(a, b)),
922 "MAPOPTIONS" => {
923 let arr = args.as_array().ok_or("MAPOPTIONS requires array")?;
924 if arr.len() < 3 {
925 return Err("MAPOPTIONS requires 3 arguments".to_string());
926 }
927 Ok(CompiledLogic::MapOptions(
928 Box::new(Self::compile(&arr[0])?),
929 Box::new(Self::compile(&arr[1])?),
930 Box::new(Self::compile(&arr[2])?),
931 ))
932 }
933 "MAPOPTIONSIF" => {
934 let arr = args.as_array().ok_or("MAPOPTIONSIF requires array")?;
935 if arr.len() < 4 {
936 return Err("MAPOPTIONSIF requires at least 4 arguments".to_string());
937 }
938 let table = Box::new(Self::compile(&arr[0])?);
939 let field_label = Box::new(Self::compile(&arr[1])?);
940 let field_value = Box::new(Self::compile(&arr[2])?);
941
942 let condition_args = &arr[3..];
943 let mut conditions = Vec::new();
944
945 let mut i = 0;
946 while i < condition_args.len() {
947 let arg = &condition_args[i];
948
949 if let Some(arg_arr) = arg.as_array() {
951 if arg_arr.len() == 3 && arg_arr[1].is_string() {
952 let is_comparison = matches!(
953 arg_arr[1].as_str().unwrap(),
954 "==" | "!=" | "===" | "!==" | "<" | ">" | "<=" | ">="
955 );
956 if is_comparison {
957 let value = &arg_arr[0];
958 let operator = arg_arr[1].as_str().unwrap();
959 let field = &arg_arr[2];
960
961 let field_var = if let Value::String(f) = field {
962 serde_json::json!({"var": f})
963 } else {
964 field.clone()
965 };
966
967 let comparison = serde_json::json!({
968 operator: [value.clone(), field_var]
969 });
970
971 conditions.push(Self::compile(&comparison)?);
972 i += 1;
973 continue;
974 }
975 }
976 }
977
978 if i + 2 < condition_args.len() {
980 let value = &condition_args[i];
981 let operator = &condition_args[i + 1];
982 let field = &condition_args[i + 2];
983
984 if let Value::String(op) = operator {
985 let is_comparison = matches!(
986 op.as_str(),
987 "==" | "!=" | "===" | "!==" | "<" | ">" | "<=" | ">="
988 );
989
990 if is_comparison {
991 let field_var = if let Value::String(f) = field {
992 serde_json::json!({"var": f})
993 } else {
994 field.clone()
995 };
996
997 let comparison = serde_json::json!({
998 op: [value.clone(), field_var]
999 });
1000
1001 conditions.push(Self::compile(&comparison)?);
1002 i += 3;
1003 continue;
1004 }
1005 }
1006 }
1007
1008 conditions.push(Self::compile(&Self::preprocess_table_condition(
1010 &condition_args[i],
1011 ))?);
1012 i += 1;
1013 }
1014
1015 Ok(CompiledLogic::MapOptionsIf(
1016 table,
1017 field_label,
1018 field_value,
1019 conditions,
1020 ))
1021 }
1022 "return" => Ok(CompiledLogic::Return(Box::new(args.clone()))),
1023
1024 _ => Err(format!("Unknown operator: {}", op)),
1025 }
1026 }
1027
1028 fn compile_binary<F>(args: &Value, f: F) -> Result<Self, String>
1029 where
1030 F: FnOnce(Box<CompiledLogic>, Box<CompiledLogic>) -> CompiledLogic,
1031 {
1032 let arr = args.as_array().ok_or("Binary operator requires array")?;
1033 if arr.len() != 2 {
1034 return Err("Binary operator requires exactly 2 arguments".to_string());
1035 }
1036 Ok(f(
1037 Box::new(Self::compile(&arr[0])?),
1038 Box::new(Self::compile(&arr[1])?),
1039 ))
1040 }
1041
1042 fn preprocess_table_condition(value: &Value) -> Value {
1050 match value {
1051 Value::String(s) => {
1052 serde_json::json!({"var": s})
1054 }
1055 Value::Array(arr) => {
1056 if !arr.is_empty() {
1058 if let Some(op_str) = arr[0].as_str() {
1059 let is_comparison = matches!(
1061 op_str,
1062 "==" | "!=" | "===" | "!==" | "<" | ">" | "<=" | ">="
1063 );
1064
1065 if is_comparison && arr.len() >= 3 {
1066 let value_arg = arr[1].clone();
1070
1071 let col_arg = if let Value::String(col) = &arr[2] {
1073 serde_json::json!({"var": col})
1075 } else {
1076 Self::preprocess_table_condition(&arr[2])
1078 };
1079
1080 let mut obj = serde_json::Map::new();
1082 obj.insert(op_str.to_string(), Value::Array(vec![col_arg, value_arg]));
1083 return Value::Object(obj);
1084 }
1085
1086 let canonical_op = match op_str {
1088 "&&" => Some("and"),
1089 "||" => Some("or"),
1090 "and" | "or" | "!" | "not" | "if" => Some(op_str),
1091 _ => None,
1092 };
1093
1094 if let Some(op_name) = canonical_op {
1095 let args: Vec<Value> = arr[1..]
1097 .iter()
1098 .map(Self::preprocess_table_condition)
1099 .collect();
1100 let mut obj = serde_json::Map::new();
1101 obj.insert(op_name.to_string(), Value::Array(args));
1102 return Value::Object(obj);
1103 }
1104 }
1105 }
1106 Value::Array(arr.iter().map(Self::preprocess_table_condition).collect())
1108 }
1109 Value::Object(obj) => {
1110 let mut new_obj = serde_json::Map::new();
1112 for (key, val) in obj {
1113 if key == "$ref" || key == "ref" || key == "var" {
1115 new_obj.insert(key.clone(), val.clone());
1116 } else {
1117 new_obj.insert(key.clone(), Self::preprocess_table_condition(val));
1118 }
1119 }
1120 Value::Object(new_obj)
1121 }
1122 _ => value.clone(),
1123 }
1124 }
1125
1126 pub fn is_simple_ref(&self) -> bool {
1128 matches!(
1129 self,
1130 CompiledLogic::Ref(_, None) | CompiledLogic::Var(_, None)
1131 )
1132 }
1133
1134 pub fn referenced_vars(&self) -> Vec<String> {
1136 let mut vars = Vec::new();
1137 self.collect_vars(&mut vars);
1138 vars.sort();
1139 vars.dedup();
1140 vars
1141 }
1142
1143 fn flatten_and(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1145 let mut flattened = Vec::new();
1146 for item in items {
1147 match item {
1148 CompiledLogic::And(nested) => {
1149 flattened.extend(Self::flatten_and(nested));
1151 }
1152 _ => flattened.push(item),
1153 }
1154 }
1155 flattened
1156 }
1157
1158 fn flatten_or(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1160 let mut flattened = Vec::new();
1161 for item in items {
1162 match item {
1163 CompiledLogic::Or(nested) => {
1164 flattened.extend(Self::flatten_or(nested));
1166 }
1167 _ => flattened.push(item),
1168 }
1169 }
1170 flattened
1171 }
1172
1173 fn flatten_add(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1175 let mut flattened = Vec::new();
1176 for item in items {
1177 match item {
1178 CompiledLogic::Add(nested) => {
1179 flattened.extend(Self::flatten_add(nested));
1181 }
1182 _ => flattened.push(item),
1183 }
1184 }
1185 flattened
1186 }
1187
1188 fn flatten_multiply(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1190 let mut flattened = Vec::new();
1191 for item in items {
1192 match item {
1193 CompiledLogic::Multiply(nested) => {
1194 flattened.extend(Self::flatten_multiply(nested));
1196 }
1197 _ => flattened.push(item),
1198 }
1199 }
1200 flattened
1201 }
1202
1203 fn flatten_cat(items: Vec<CompiledLogic>) -> Vec<CompiledLogic> {
1206 let mut flattened = Vec::new();
1207 for item in items {
1208 match &item {
1209 CompiledLogic::Cat(nested) => {
1210 flattened.extend(Self::flatten_cat(nested.clone()));
1212 }
1213 _ => flattened.push(item),
1214 }
1215 }
1216 flattened
1217 }
1218
1219 pub fn has_forward_reference(&self) -> bool {
1222 let result = self.check_forward_reference();
1223 result
1224 }
1225
1226 fn check_forward_reference(&self) -> bool {
1227 match self {
1228 CompiledLogic::ValueAt(table, idx_logic, col_name) => {
1230 let has_fwd = idx_logic.contains_iteration_plus_positive();
1232 if has_fwd {
1233 return true;
1234 }
1235 let table_fwd = table.check_forward_reference();
1237 let idx_fwd = idx_logic.check_forward_reference();
1238 let col_fwd = col_name
1239 .as_ref()
1240 .map(|c| c.check_forward_reference())
1241 .unwrap_or(false);
1242 table_fwd || idx_fwd || col_fwd
1243 }
1244 CompiledLogic::Array(arr) => arr.iter().any(|item| item.check_forward_reference()),
1246 CompiledLogic::And(items)
1247 | CompiledLogic::Or(items)
1248 | CompiledLogic::Add(items)
1249 | CompiledLogic::Subtract(items)
1250 | CompiledLogic::Multiply(items)
1251 | CompiledLogic::Divide(items)
1252 | CompiledLogic::Merge(items)
1253 | CompiledLogic::Cat(items)
1254 | CompiledLogic::Max(items)
1255 | CompiledLogic::Min(items)
1256 | CompiledLogic::Concat(items)
1257 | CompiledLogic::Multiplies(items)
1258 | CompiledLogic::Divides(items) => {
1259 items.iter().any(|item| item.check_forward_reference())
1260 }
1261 CompiledLogic::Not(a)
1262 | CompiledLogic::Abs(a)
1263 | CompiledLogic::Length(a)
1264 | CompiledLogic::Len(a)
1265 | CompiledLogic::IsEmpty(a)
1266 | CompiledLogic::Year(a)
1267 | CompiledLogic::Month(a)
1268 | CompiledLogic::Day(a) => a.check_forward_reference(),
1269 CompiledLogic::Round(a, decimals)
1270 | CompiledLogic::RoundUp(a, decimals)
1271 | CompiledLogic::RoundDown(a, decimals)
1272 | CompiledLogic::Ceiling(a, decimals)
1273 | CompiledLogic::Floor(a, decimals)
1274 | CompiledLogic::Trunc(a, decimals)
1275 | CompiledLogic::DateFormat(a, decimals) => {
1276 a.check_forward_reference()
1277 || decimals
1278 .as_ref()
1279 .map_or(false, |d| d.check_forward_reference())
1280 }
1281 CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1282 a.check_forward_reference()
1283 || decimals
1284 .as_ref()
1285 .map_or(false, |d| d.check_forward_reference())
1286 || prefix
1287 .as_ref()
1288 .map_or(false, |p| p.check_forward_reference())
1289 || suffix
1290 .as_ref()
1291 .map_or(false, |s| s.check_forward_reference())
1292 || sep.as_ref().map_or(false, |s| s.check_forward_reference())
1293 }
1294 CompiledLogic::Mround(a, b) => {
1295 a.check_forward_reference() || b.check_forward_reference()
1296 }
1297 CompiledLogic::Return(_) => false, CompiledLogic::If(cond, then_val, else_val) => {
1299 cond.check_forward_reference()
1300 || then_val.check_forward_reference()
1301 || else_val.check_forward_reference()
1302 }
1303 CompiledLogic::Equal(a, b)
1304 | CompiledLogic::StrictEqual(a, b)
1305 | CompiledLogic::NotEqual(a, b)
1306 | CompiledLogic::StrictNotEqual(a, b)
1307 | CompiledLogic::LessThan(a, b)
1308 | CompiledLogic::LessThanOrEqual(a, b)
1309 | CompiledLogic::GreaterThan(a, b)
1310 | CompiledLogic::GreaterThanOrEqual(a, b)
1311 | CompiledLogic::Modulo(a, b)
1312 | CompiledLogic::Power(a, b)
1313 | CompiledLogic::Map(a, b)
1314 | CompiledLogic::Filter(a, b)
1315 | CompiledLogic::All(a, b)
1316 | CompiledLogic::Some(a, b)
1317 | CompiledLogic::None(a, b)
1318 | CompiledLogic::In(a, b)
1319 | CompiledLogic::Pow(a, b)
1320 | CompiledLogic::Xor(a, b)
1321 | CompiledLogic::IfNull(a, b)
1322 | CompiledLogic::Days(a, b)
1323 | CompiledLogic::SplitValue(a, b)
1324 | CompiledLogic::MaxAt(a, b)
1325 | CompiledLogic::RangeOptions(a, b) => {
1326 a.check_forward_reference() || b.check_forward_reference()
1327 }
1328 CompiledLogic::Reduce(a, b, c)
1329 | CompiledLogic::Mid(a, b, c)
1330 | CompiledLogic::Date(a, b, c)
1331 | CompiledLogic::DateDif(a, b, c)
1332 | CompiledLogic::MapOptions(a, b, c)
1333 | CompiledLogic::For(a, b, c) => {
1334 a.check_forward_reference()
1335 || b.check_forward_reference()
1336 || c.check_forward_reference()
1337 }
1338 CompiledLogic::Substr(s, start, len)
1339 | CompiledLogic::Search(s, start, len)
1340 | CompiledLogic::SplitText(s, start, len)
1341 | CompiledLogic::YearFrac(s, start, len) => {
1342 s.check_forward_reference()
1343 || start.check_forward_reference()
1344 || len
1345 .as_ref()
1346 .map(|l| l.check_forward_reference())
1347 .unwrap_or(false)
1348 }
1349 CompiledLogic::Left(a, opt) | CompiledLogic::Right(a, opt) => {
1350 a.check_forward_reference()
1351 || opt
1352 .as_ref()
1353 .map(|o| o.check_forward_reference())
1354 .unwrap_or(false)
1355 }
1356 CompiledLogic::Sum(a, opt1, opt2) => {
1357 a.check_forward_reference()
1358 || opt1
1359 .as_ref()
1360 .map(|o| o.check_forward_reference())
1361 .unwrap_or(false)
1362 || opt2
1363 .as_ref()
1364 .map(|o| o.check_forward_reference())
1365 .unwrap_or(false)
1366 }
1367 CompiledLogic::IndexAt(a, b, c, opt) => {
1368 a.check_forward_reference()
1369 || b.check_forward_reference()
1370 || c.check_forward_reference()
1371 || opt
1372 .as_ref()
1373 .map(|o| o.check_forward_reference())
1374 .unwrap_or(false)
1375 }
1376 CompiledLogic::Match(table, conds)
1377 | CompiledLogic::MatchRange(table, conds)
1378 | CompiledLogic::Choose(table, conds)
1379 | CompiledLogic::FindIndex(table, conds) => {
1380 table.check_forward_reference() || conds.iter().any(|c| c.check_forward_reference())
1381 }
1382 CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1383 table.check_forward_reference()
1384 || label.check_forward_reference()
1385 || value.check_forward_reference()
1386 || conds.iter().any(|c| c.check_forward_reference())
1387 }
1388 CompiledLogic::MissingSome(min, _) => min.check_forward_reference(),
1389 _ => false,
1390 }
1391 }
1392
1393 fn contains_iteration_plus_positive(&self) -> bool {
1395 match self {
1396 CompiledLogic::Add(items) => {
1397 let has_iteration = items
1399 .iter()
1400 .any(|item| item.referenced_vars().iter().any(|v| v == "$iteration"));
1401
1402 let has_positive = items.iter().any(|item| match item {
1403 CompiledLogic::Number(f) => *f > 0.0,
1404 _ => false,
1405 });
1406
1407 let result = has_iteration && has_positive;
1408 result
1409 }
1410 _ => false,
1411 }
1412 }
1413
1414 fn normalize_ref_path(path: &str) -> String {
1418 let mut normalized = path.to_string();
1419
1420 if normalized.starts_with("#/") {
1422 normalized = normalized[2..].to_string();
1423 } else if normalized.starts_with('/') {
1424 normalized = normalized[1..].to_string();
1425 }
1426
1427 normalized = normalized.replace('/', ".");
1429
1430 normalized = normalized.replace("properties.", "");
1432 normalized = normalized.replace(".properties.", ".");
1433
1434 while normalized.contains("..") {
1436 normalized = normalized.replace("..", ".");
1437 }
1438
1439 normalized = normalized.trim_matches('.').to_string();
1441
1442 normalized
1443 }
1444
1445 pub fn collect_vars(&self, vars: &mut Vec<String>) {
1446 match self {
1447 CompiledLogic::Var(name, default) => {
1448 vars.push(name.clone());
1449 if let Some(def) = default {
1450 def.collect_vars(vars);
1451 }
1452 }
1453 CompiledLogic::Ref(path, default) => {
1454 vars.push(Self::normalize_ref_path(path));
1456 if let Some(def) = default {
1457 def.collect_vars(vars);
1458 }
1459 }
1460 CompiledLogic::Array(arr) => {
1461 for item in arr {
1462 item.collect_vars(vars);
1463 }
1464 }
1465 CompiledLogic::And(items)
1466 | CompiledLogic::Or(items)
1467 | CompiledLogic::Add(items)
1468 | CompiledLogic::Subtract(items)
1469 | CompiledLogic::Multiply(items)
1470 | CompiledLogic::Divide(items)
1471 | CompiledLogic::Merge(items)
1472 | CompiledLogic::Cat(items)
1473 | CompiledLogic::Max(items)
1474 | CompiledLogic::Min(items)
1475 | CompiledLogic::Concat(items) => {
1476 for item in items {
1477 item.collect_vars(vars);
1478 }
1479 }
1480 CompiledLogic::Not(a)
1481 | CompiledLogic::Abs(a)
1482 | CompiledLogic::Length(a)
1483 | CompiledLogic::Len(a)
1484 | CompiledLogic::IsEmpty(a)
1485 | CompiledLogic::Year(a)
1486 | CompiledLogic::Month(a)
1487 | CompiledLogic::Day(a) => {
1488 a.collect_vars(vars);
1489 }
1490 CompiledLogic::Round(a, decimals)
1491 | CompiledLogic::RoundUp(a, decimals)
1492 | CompiledLogic::RoundDown(a, decimals)
1493 | CompiledLogic::Ceiling(a, decimals)
1494 | CompiledLogic::Floor(a, decimals)
1495 | CompiledLogic::Trunc(a, decimals)
1496 | CompiledLogic::DateFormat(a, decimals) => {
1497 a.collect_vars(vars);
1498 if let Some(d) = decimals {
1499 d.collect_vars(vars);
1500 }
1501 }
1502 CompiledLogic::StringFormat(a, decimals, prefix, suffix, sep) => {
1503 a.collect_vars(vars);
1504 if let Some(d) = decimals {
1505 d.collect_vars(vars);
1506 }
1507 if let Some(p) = prefix {
1508 p.collect_vars(vars);
1509 }
1510 if let Some(s) = suffix {
1511 s.collect_vars(vars);
1512 }
1513 if let Some(s) = sep {
1514 s.collect_vars(vars);
1515 }
1516 }
1517 CompiledLogic::Mround(a, b) => {
1518 a.collect_vars(vars);
1519 b.collect_vars(vars);
1520 }
1521 CompiledLogic::Return(_) => {} CompiledLogic::If(cond, then_val, else_val) => {
1523 cond.collect_vars(vars);
1524 then_val.collect_vars(vars);
1525 else_val.collect_vars(vars);
1526 }
1527 CompiledLogic::Equal(a, b)
1528 | CompiledLogic::StrictEqual(a, b)
1529 | CompiledLogic::NotEqual(a, b)
1530 | CompiledLogic::StrictNotEqual(a, b)
1531 | CompiledLogic::LessThan(a, b)
1532 | CompiledLogic::LessThanOrEqual(a, b)
1533 | CompiledLogic::GreaterThan(a, b)
1534 | CompiledLogic::GreaterThanOrEqual(a, b)
1535 | CompiledLogic::Modulo(a, b)
1536 | CompiledLogic::Power(a, b)
1537 | CompiledLogic::Map(a, b)
1538 | CompiledLogic::Filter(a, b)
1539 | CompiledLogic::All(a, b)
1540 | CompiledLogic::Some(a, b)
1541 | CompiledLogic::None(a, b)
1542 | CompiledLogic::In(a, b)
1543 | CompiledLogic::Pow(a, b)
1544 | CompiledLogic::Xor(a, b)
1545 | CompiledLogic::IfNull(a, b)
1546 | CompiledLogic::Days(a, b)
1547 | CompiledLogic::SplitValue(a, b)
1548 | CompiledLogic::MaxAt(a, b)
1549 | CompiledLogic::RangeOptions(a, b) => {
1550 a.collect_vars(vars);
1551 b.collect_vars(vars);
1552 }
1553 CompiledLogic::Reduce(a, b, c)
1554 | CompiledLogic::Mid(a, b, c)
1555 | CompiledLogic::Date(a, b, c)
1556 | CompiledLogic::DateDif(a, b, c)
1557 | CompiledLogic::MapOptions(a, b, c)
1558 | CompiledLogic::For(a, b, c) => {
1559 a.collect_vars(vars);
1560 b.collect_vars(vars);
1561 c.collect_vars(vars);
1562 }
1563 CompiledLogic::Substr(s, start, len)
1564 | CompiledLogic::Search(s, start, len)
1565 | CompiledLogic::SplitText(s, start, len)
1566 | CompiledLogic::YearFrac(s, start, len) => {
1567 s.collect_vars(vars);
1568 start.collect_vars(vars);
1569 if let Some(l) = len {
1570 l.collect_vars(vars);
1571 }
1572 }
1573 CompiledLogic::Left(a, opt)
1574 | CompiledLogic::Right(a, opt)
1575 | CompiledLogic::ValueAt(a, _, opt) => {
1576 a.collect_vars(vars);
1577 if let Some(o) = opt {
1578 o.collect_vars(vars);
1579 }
1580 }
1581 CompiledLogic::Sum(a, opt1, opt2) => {
1582 a.collect_vars(vars);
1583 if let Some(o) = opt1 {
1584 o.collect_vars(vars);
1585 }
1586 if let Some(o) = opt2 {
1587 o.collect_vars(vars);
1588 }
1589 }
1590 CompiledLogic::IndexAt(a, b, c, opt) => {
1591 a.collect_vars(vars);
1592 b.collect_vars(vars);
1593 c.collect_vars(vars);
1594 if let Some(o) = opt {
1595 o.collect_vars(vars);
1596 }
1597 }
1598 CompiledLogic::Match(table, conds)
1599 | CompiledLogic::MatchRange(table, conds)
1600 | CompiledLogic::Choose(table, conds)
1601 | CompiledLogic::FindIndex(table, conds) => {
1602 table.collect_vars(vars);
1603 for cond in conds {
1604 cond.collect_vars(vars);
1605 }
1606 }
1607 CompiledLogic::Multiplies(items) | CompiledLogic::Divides(items) => {
1608 for item in items {
1609 item.collect_vars(vars);
1610 }
1611 }
1612 CompiledLogic::MapOptionsIf(table, label, value, conds) => {
1613 table.collect_vars(vars);
1614 label.collect_vars(vars);
1615 value.collect_vars(vars);
1616 for cond in conds {
1617 cond.collect_vars(vars);
1618 }
1619 }
1620 CompiledLogic::MissingSome(min, _) => {
1621 min.collect_vars(vars);
1622 }
1623 _ => {}
1624 }
1625 }
1626}
1627
1628pub struct CompiledLogicStore {
1634 next_id: u64,
1635 store: RapidHashMap<LogicId, CompiledLogic>,
1636 dependencies: RapidHashMap<LogicId, Vec<String>>,
1637}
1638
1639impl CompiledLogicStore {
1640 pub fn new() -> Self {
1641 Self {
1642 next_id: 0,
1643 store: RapidHashMap::default(),
1644 dependencies: RapidHashMap::default(),
1645 }
1646 }
1647
1648 pub fn compile(&mut self, logic: &Value) -> Result<LogicId, String> {
1654 let _global_id = super::compiled_logic_store::compile_logic_value(logic)?;
1656
1657 let compiled = super::compiled_logic_store::get_compiled_logic(_global_id)
1659 .ok_or_else(|| "Failed to retrieve compiled logic from global store".to_string())?;
1660
1661 let deps = compiled.referenced_vars();
1663
1664 let id = LogicId(self.next_id);
1666 self.next_id += 1;
1667
1668 self.store.insert(id, compiled);
1670 self.dependencies.insert(id, deps);
1671
1672 Ok(id)
1673 }
1674
1675 pub fn get(&self, id: &LogicId) -> Option<&CompiledLogic> {
1677 self.store.get(id)
1678 }
1679
1680 pub fn remove(&mut self, id: &LogicId) -> Option<CompiledLogic> {
1682 self.dependencies.remove(id);
1683 self.store.remove(id)
1684 }
1685
1686 pub fn get_dependencies(&self, id: &LogicId) -> Option<&[String]> {
1688 self.dependencies.get(id).map(|v| v.as_slice())
1689 }
1690}
1691
1692impl Default for CompiledLogicStore {
1693 fn default() -> Self {
1694 Self::new()
1695 }
1696}
1697
1698#[cfg(test)]
1699mod tests {
1700 use super::*;
1701 use serde_json::json;
1702
1703 fn is_ok(json_value: serde_json::Value) -> bool {
1704 CompiledLogic::compile(&json_value).is_ok()
1705 }
1706
1707 #[test]
1708 fn test_compile_literals() {
1709 assert!(matches!(
1710 CompiledLogic::compile(&json!(null)).unwrap(),
1711 CompiledLogic::Null
1712 ));
1713 assert!(matches!(
1714 CompiledLogic::compile(&json!(true)).unwrap(),
1715 CompiledLogic::Bool(true)
1716 ));
1717 assert!(matches!(
1718 CompiledLogic::compile(&json!(false)).unwrap(),
1719 CompiledLogic::Bool(false)
1720 ));
1721 assert!(
1722 matches!(CompiledLogic::compile(&json!(42.5)).unwrap(), CompiledLogic::Number(n) if n == 42.5)
1723 );
1724 assert!(
1725 matches!(CompiledLogic::compile(&json!("hello")).unwrap(), CompiledLogic::String(ref s) if s == "hello")
1726 );
1727 assert!(matches!(
1728 CompiledLogic::compile(&json!([1, 2, 3])).unwrap(),
1729 CompiledLogic::Array(_)
1730 ));
1731 }
1732
1733 #[test]
1734 fn test_compile_variables() {
1735 assert!(is_ok(json!({"var": "a"})));
1736 assert!(is_ok(json!({"var": ["a", 1]})));
1737 assert!(!is_ok(json!({"var": {}}))); assert!(is_ok(json!({"$ref": "#/path"})));
1739 }
1740
1741 #[test]
1742 fn test_compile_logical() {
1743 assert!(is_ok(json!({"and": [true, false]})));
1744 assert!(is_ok(json!({"or": [true, false]})));
1745 assert!(is_ok(json!({"not": [true]})));
1746 assert!(is_ok(json!({"!": [true]})));
1747 assert!(is_ok(json!({"if": [true, 1, 0]})));
1748 assert!(is_ok(json!({"xor": [true, false]})));
1749 assert!(is_ok(json!({"ifnull": [{"var": "a"}, 0]})));
1750
1751 assert!(!is_ok(json!({"if": []})));
1753 assert!(!is_ok(json!({"and": true})));
1754 }
1755
1756 #[test]
1757 fn test_compile_comparison() {
1758 assert!(is_ok(json!({"==": [1, 1]})));
1759 assert!(is_ok(json!({"===": [1, 1]})));
1760 assert!(is_ok(json!({"!=": [1, 2]})));
1761 assert!(is_ok(json!({"!==": [1, 2]})));
1762 assert!(is_ok(json!({">": [2, 1]})));
1763 assert!(is_ok(json!({">=": [2, 2]})));
1764 assert!(is_ok(json!({"<": [1, 2]})));
1765 assert!(is_ok(json!({"<=": [2, 2]})));
1766
1767 assert!(!is_ok(json!({"==": 1})));
1769 }
1770
1771 #[test]
1772 fn test_compile_arithmetic() {
1773 assert!(is_ok(json!({"+": [1, 2]})));
1774 assert!(is_ok(json!({"-": [1, 2]})));
1775 assert!(is_ok(json!({"*": [1, 2]})));
1776 assert!(is_ok(json!({"/": [1, 2]})));
1777 assert!(is_ok(json!({"%": [1, 2]})));
1778 assert!(is_ok(json!({"pow": [2, 3]})));
1779
1780 assert!(!is_ok(json!({"-": "a"}))); }
1783
1784 #[test]
1785 fn test_compile_array_ops() {
1786 assert!(is_ok(json!({"map": [[1, 2], {"+": [{"var": ""}, 1]}]})));
1787 assert!(is_ok(json!({"filter": [[1, 2], {">": [{"var": ""}, 1]}]})));
1788 assert!(is_ok(
1789 json!({"reduce": [[1, 2], {"+": [{"var": "current"}, {"var": "accumulator"}]}, 0]})
1790 ));
1791 assert!(is_ok(json!({"all": [[true, false], {"var": ""}]})));
1792 assert!(is_ok(json!({"some": [[true, false], {"var": ""}]})));
1793 assert!(is_ok(json!({"none": [[true, false], {"var": ""}]})));
1794 assert!(is_ok(json!({"merge": [[1], [2]]})));
1795 assert!(is_ok(json!({"in": [1, [1, 2, 3]]})));
1796 assert!(is_ok(json!({"sum": [{"var": "data"}, "a", 0]})));
1797 }
1798
1799 #[test]
1800 fn test_compile_string_ops() {
1801 assert!(is_ok(json!({"cat": ["a", "b"]})));
1802 assert!(is_ok(json!({"substr": ["hello", 0, 2]})));
1803 assert!(is_ok(json!({"search": ["a", "abc", 0]})));
1804 assert!(is_ok(json!({"SPLITTEXT": ["a,b", ",", 0]})));
1805 assert!(is_ok(json!({"left": ["abc", 2]})));
1806 assert!(is_ok(json!({"right": ["abc", 2]})));
1807
1808 assert!(!is_ok(json!({"substr": "hello"})));
1810 }
1811
1812 #[test]
1813 fn test_compile_math_ops() {
1814 assert!(is_ok(json!({"max": [1, 2, 3]})));
1815 assert!(is_ok(json!({"min": [1, 2, 3]})));
1816 assert!(is_ok(json!({"abs": -1})));
1817 assert!(is_ok(json!({"round": [1.234, 2]})));
1818 assert!(is_ok(json!({"roundup": [1.234, 2]})));
1819 assert!(is_ok(json!({"rounddown": [1.234, 2]})));
1820 assert!(is_ok(json!({"ceiling": [1.234, 1]})));
1821 assert!(is_ok(json!({"floor": [1.234, 1]})));
1822 assert!(is_ok(json!({"trunc": [1.234, 2]})));
1823 }
1824
1825 #[test]
1826 fn test_compile_date_ops() {
1827 assert!(is_ok(json!({"today": []})));
1828 assert!(is_ok(json!({"date": [2024, 12, 1]})));
1829 assert!(is_ok(json!({"days": ["2024-12-02", "2024-12-01"]})));
1830 assert!(is_ok(json!({"year": ["2024-12-01"]})));
1831 assert!(is_ok(json!({"month": ["2024-12-01"]})));
1832 assert!(is_ok(json!({"day": ["2024-12-01"]})));
1833 }
1834
1835 #[test]
1836 fn test_compile_table_ops() {
1837 assert!(is_ok(json!({"VALUEAT": [{"var": "t"}, 0, "a"]})));
1838 assert!(is_ok(json!({"INDEXAT": ["a", {"var": "t"}, "b", true]})));
1839 assert!(is_ok(json!({"MAXAT": [{"var": "t"}, "a"]})));
1840 assert!(is_ok(json!({"MATCH": [{"var": "t"}, "==", 1, "a"]})));
1841 assert!(is_ok(json!({"MATCHRANGE": [{"var": "t"}, "==", 1, "a"]})));
1842 assert!(is_ok(json!({"CHOOSE": [{"var": "t"}, "==", 1, "a"]})));
1843 assert!(is_ok(json!({"FINDINDEX": [{"var": "t"}, "==", 1, "a"]})));
1844 assert!(is_ok(json!({"MAPOPTIONS": [{"var": "t"}, "a", "b"]})));
1845 assert!(is_ok(
1846 json!({"MAPOPTIONSIF": [{"var": "t"}, "a", "b", [true, "==", "c"]]})
1847 ));
1848
1849 assert!(!is_ok(json!({"VALUEAT": [[{"a": 1}], 0]})));
1851 assert!(!is_ok(json!({"MAPOPTIONSIF": [[], "a"]})));
1852 }
1853
1854 #[test]
1855 fn test_compile_util_ops() {
1856 assert!(is_ok(json!({"missing": ["a", "b"]})));
1857 assert!(is_ok(json!({"missing_some": [1, ["a", "b"]]})));
1858 assert!(is_ok(json!({"empty": []})));
1859 assert!(is_ok(json!({"return": 5})));
1860 assert!(is_ok(json!({"RANGEOPTIONS": [1, 5]})));
1861 }
1862
1863 #[test]
1864 fn test_compile_unknown() {
1865 assert!(!is_ok(json!({"UNKNOWN_OP": [1, 2]})));
1866 assert!(!is_ok(json!({"UNKNOWN_NON_ARRAY": 1})));
1867 }
1868}