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