1use crate::functions::defs::{
2 CompositeFunction, FunctionDefinition, FunctionSignature, StaticFunction,
3};
4use std::rc::Rc;
5use strum_macros::{Display, EnumIter, EnumString, IntoStaticStr};
6
7#[derive(Debug, PartialEq, Eq, Hash, Display, EnumString, EnumIter, IntoStaticStr, Clone, Copy)]
8#[strum(serialize_all = "camelCase")]
9pub enum InternalFunction {
10 Len,
12 Contains,
13 Flatten,
14 Merge,
15 MergeDeep,
16
17 Upper,
19 Lower,
20 Trim,
21 StartsWith,
22 EndsWith,
23 Matches,
24 Extract,
25 FuzzyMatch,
26 Split,
27
28 Abs,
30 Sum,
31 Avg,
32 Min,
33 Max,
34 Rand,
35 Median,
36 Mode,
37 Floor,
38 Ceil,
39 Round,
40 Trunc,
41
42 IsNumeric,
44 String,
45 Number,
46 Bool,
47 Type,
48
49 Keys,
51 Values,
52
53 #[strum(serialize = "d")]
54 Date,
55}
56
57impl From<&InternalFunction> for Rc<dyn FunctionDefinition> {
58 fn from(value: &InternalFunction) -> Self {
59 use crate::variable::VariableType as VT;
60 use InternalFunction as IF;
61
62 let s: Rc<dyn FunctionDefinition> = match value {
63 IF::Len => Rc::new(CompositeFunction {
64 implementation: Rc::new(imp::len),
65 signatures: vec![
66 FunctionSignature::single(VT::String, VT::Number),
67 FunctionSignature::single(VT::Any.array(), VT::Number),
68 ],
69 }),
70
71 IF::Contains => Rc::new(CompositeFunction {
72 implementation: Rc::new(imp::contains),
73 signatures: vec![
74 FunctionSignature {
75 parameters: vec![VT::String, VT::String],
76 return_type: VT::Bool,
77 },
78 FunctionSignature {
79 parameters: vec![VT::Any.array(), VT::Any],
80 return_type: VT::Bool,
81 },
82 ],
83 }),
84
85 IF::Flatten => Rc::new(StaticFunction {
86 implementation: Rc::new(imp::flatten),
87 signature: FunctionSignature::single(VT::Any.array(), VT::Any.array()),
88 }),
89
90 IF::Merge => Rc::new(CompositeFunction {
91 implementation: Rc::new(imp::merge),
92 signatures: vec![
93 FunctionSignature::single(VT::Any.array(), VT::Any.array()),
94 FunctionSignature::single(
95 VT::Object(Default::default()).array(),
96 VT::Object(Default::default()),
97 ),
98 ],
99 }),
100
101 IF::MergeDeep => Rc::new(StaticFunction {
102 implementation: Rc::new(imp::merge_deep),
103 signature: FunctionSignature::single(
104 VT::Object(Default::default()).array(),
105 VT::Object(Default::default()),
106 ),
107 }),
108
109 IF::Upper => Rc::new(StaticFunction {
110 implementation: Rc::new(imp::upper),
111 signature: FunctionSignature::single(VT::String, VT::String),
112 }),
113
114 IF::Lower => Rc::new(StaticFunction {
115 implementation: Rc::new(imp::lower),
116 signature: FunctionSignature::single(VT::String, VT::String),
117 }),
118
119 IF::Trim => Rc::new(StaticFunction {
120 implementation: Rc::new(imp::trim),
121 signature: FunctionSignature::single(VT::String, VT::String),
122 }),
123
124 IF::StartsWith => Rc::new(StaticFunction {
125 implementation: Rc::new(imp::starts_with),
126 signature: FunctionSignature {
127 parameters: vec![VT::String, VT::String],
128 return_type: VT::Bool,
129 },
130 }),
131
132 IF::EndsWith => Rc::new(StaticFunction {
133 implementation: Rc::new(imp::ends_with),
134 signature: FunctionSignature {
135 parameters: vec![VT::String, VT::String],
136 return_type: VT::Bool,
137 },
138 }),
139
140 IF::Matches => Rc::new(StaticFunction {
141 implementation: Rc::new(imp::matches),
142 signature: FunctionSignature {
143 parameters: vec![VT::String, VT::String],
144 return_type: VT::Bool,
145 },
146 }),
147
148 IF::Extract => Rc::new(StaticFunction {
149 implementation: Rc::new(imp::extract),
150 signature: FunctionSignature {
151 parameters: vec![VT::String, VT::String],
152 return_type: VT::String.array(),
153 },
154 }),
155
156 IF::Split => Rc::new(StaticFunction {
157 implementation: Rc::new(imp::split),
158 signature: FunctionSignature {
159 parameters: vec![VT::String, VT::String],
160 return_type: VT::String.array(),
161 },
162 }),
163
164 IF::FuzzyMatch => Rc::new(CompositeFunction {
165 implementation: Rc::new(imp::fuzzy_match),
166 signatures: vec![
167 FunctionSignature {
168 parameters: vec![VT::String, VT::String],
169 return_type: VT::Number,
170 },
171 FunctionSignature {
172 parameters: vec![VT::String.array(), VT::String],
173 return_type: VT::Number.array(),
174 },
175 ],
176 }),
177
178 IF::Abs => Rc::new(StaticFunction {
179 implementation: Rc::new(imp::abs),
180 signature: FunctionSignature::single(VT::Number, VT::Number),
181 }),
182
183 IF::Rand => Rc::new(StaticFunction {
184 implementation: Rc::new(imp::rand),
185 signature: FunctionSignature::single(VT::Number, VT::Number),
186 }),
187
188 IF::Floor => Rc::new(StaticFunction {
189 implementation: Rc::new(imp::floor),
190 signature: FunctionSignature::single(VT::Number, VT::Number),
191 }),
192
193 IF::Ceil => Rc::new(StaticFunction {
194 implementation: Rc::new(imp::ceil),
195 signature: FunctionSignature::single(VT::Number, VT::Number),
196 }),
197
198 IF::Round => Rc::new(CompositeFunction {
199 implementation: Rc::new(imp::round),
200 signatures: vec![
201 FunctionSignature {
202 parameters: vec![VT::Number],
203 return_type: VT::Number,
204 },
205 FunctionSignature {
206 parameters: vec![VT::Number, VT::Number],
207 return_type: VT::Number,
208 },
209 ],
210 }),
211
212 IF::Trunc => Rc::new(CompositeFunction {
213 implementation: Rc::new(imp::trunc),
214 signatures: vec![
215 FunctionSignature {
216 parameters: vec![VT::Number],
217 return_type: VT::Number,
218 },
219 FunctionSignature {
220 parameters: vec![VT::Number, VT::Number],
221 return_type: VT::Number,
222 },
223 ],
224 }),
225
226 IF::Sum => Rc::new(StaticFunction {
227 implementation: Rc::new(imp::sum),
228 signature: FunctionSignature::single(VT::Number.array(), VT::Number),
229 }),
230
231 IF::Avg => Rc::new(StaticFunction {
232 implementation: Rc::new(imp::avg),
233 signature: FunctionSignature::single(VT::Number.array(), VT::Number),
234 }),
235
236 IF::Min => Rc::new(CompositeFunction {
237 implementation: Rc::new(imp::min),
238 signatures: vec![
239 FunctionSignature::single(VT::Number.array(), VT::Number),
240 FunctionSignature::single(VT::Date.array(), VT::Date),
241 ],
242 }),
243
244 IF::Max => Rc::new(CompositeFunction {
245 implementation: Rc::new(imp::max),
246 signatures: vec![
247 FunctionSignature::single(VT::Number.array(), VT::Number),
248 FunctionSignature::single(VT::Date.array(), VT::Date),
249 ],
250 }),
251
252 IF::Median => Rc::new(StaticFunction {
253 implementation: Rc::new(imp::median),
254 signature: FunctionSignature::single(VT::Number.array(), VT::Number),
255 }),
256
257 IF::Mode => Rc::new(StaticFunction {
258 implementation: Rc::new(imp::mode),
259 signature: FunctionSignature::single(VT::Number.array(), VT::Number),
260 }),
261
262 IF::Type => Rc::new(StaticFunction {
263 implementation: Rc::new(imp::to_type),
264 signature: FunctionSignature::single(VT::Any, VT::String),
265 }),
266
267 IF::String => Rc::new(StaticFunction {
268 implementation: Rc::new(imp::to_string),
269 signature: FunctionSignature::single(VT::Any, VT::String),
270 }),
271
272 IF::Bool => Rc::new(StaticFunction {
273 implementation: Rc::new(imp::to_bool),
274 signature: FunctionSignature::single(VT::Any, VT::Bool),
275 }),
276
277 IF::IsNumeric => Rc::new(StaticFunction {
278 implementation: Rc::new(imp::is_numeric),
279 signature: FunctionSignature::single(VT::Any, VT::Bool),
280 }),
281
282 IF::Number => Rc::new(StaticFunction {
283 implementation: Rc::new(imp::to_number),
284 signature: FunctionSignature::single(VT::Any, VT::Number),
285 }),
286
287 IF::Keys => Rc::new(CompositeFunction {
288 implementation: Rc::new(imp::keys),
289 signatures: vec![
290 FunctionSignature::single(VT::Object(Default::default()), VT::String.array()),
291 FunctionSignature::single(VT::Any.array(), VT::Number.array()),
292 ],
293 }),
294
295 IF::Values => Rc::new(StaticFunction {
296 implementation: Rc::new(imp::values),
297 signature: FunctionSignature::single(
298 VT::Object(Default::default()),
299 VT::Any.array(),
300 ),
301 }),
302
303 IF::Date => Rc::new(CompositeFunction {
304 implementation: Rc::new(imp::date),
305 signatures: vec![
306 FunctionSignature {
307 parameters: vec![],
308 return_type: VT::Date,
309 },
310 FunctionSignature {
311 parameters: vec![VT::Any],
312 return_type: VT::Date,
313 },
314 FunctionSignature {
315 parameters: vec![VT::Any, VT::String],
316 return_type: VT::Date,
317 },
318 ],
319 }),
320 };
321
322 s
323 }
324}
325
326pub(crate) mod imp {
327 use crate::functions::arguments::Arguments;
328 use crate::vm::date::DynamicVariableExt;
329 use crate::vm::VmDate;
330 use crate::{Variable as V, Variable};
331 use anyhow::{anyhow, Context};
332 use chrono_tz::Tz;
333 #[cfg(not(feature = "regex-lite"))]
334 use regex::Regex;
335 #[cfg(feature = "regex-lite")]
336 use regex_lite::Regex;
337 use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
338 use rust_decimal::{Decimal, RoundingStrategy};
339 use rust_decimal_macros::dec;
340 use std::collections::BTreeMap;
341 use std::rc::Rc;
342 use std::str::FromStr;
343
344 fn __internal_number_array(args: &Arguments, pos: usize) -> anyhow::Result<Vec<Decimal>> {
345 let a = args.array(pos)?;
346 let arr = a.borrow();
347
348 arr.iter()
349 .map(|v| v.as_number())
350 .collect::<Option<Vec<_>>>()
351 .context("Expected a number array")
352 }
353
354 enum Either<A, B> {
355 Left(A),
356 Right(B),
357 }
358
359 fn __internal_number_or_date_array(
360 args: &Arguments,
361 pos: usize,
362 ) -> anyhow::Result<Either<Vec<Decimal>, Vec<VmDate>>> {
363 let a = args.array(pos)?;
364 let arr = a.borrow();
365
366 let is_number = arr.first().map(|v| v.as_number()).flatten().is_some();
367 if is_number {
368 Ok(Either::Left(
369 arr.iter()
370 .map(|v| v.as_number())
371 .collect::<Option<Vec<_>>>()
372 .context("Expected a number array")?,
373 ))
374 } else {
375 Ok(Either::Right(
376 arr.iter()
377 .map(|v| match v {
378 Variable::Dynamic(d) => d.as_date().cloned(),
379 _ => None,
380 })
381 .collect::<Option<Vec<_>>>()
382 .context("Expected a number array")?,
383 ))
384 }
385 }
386
387 pub fn starts_with(args: Arguments) -> anyhow::Result<V> {
388 let a = args.str(0)?;
389 let b = args.str(1)?;
390
391 Ok(V::Bool(a.starts_with(b)))
392 }
393
394 pub fn ends_with(args: Arguments) -> anyhow::Result<V> {
395 let a = args.str(0)?;
396 let b = args.str(1)?;
397
398 Ok(V::Bool(a.ends_with(b)))
399 }
400
401 pub fn matches(args: Arguments) -> anyhow::Result<V> {
402 let a = args.str(0)?;
403 let b = args.str(1)?;
404
405 let regex = Regex::new(b.as_ref()).context("Invalid regular expression")?;
406
407 Ok(V::Bool(regex.is_match(a.as_ref())))
408 }
409
410 pub fn upper(args: Arguments) -> anyhow::Result<V> {
411 let a = args.str(0)?;
412 Ok(V::String(a.to_uppercase().into()))
413 }
414
415 pub fn lower(args: Arguments) -> anyhow::Result<V> {
416 let a = args.str(0)?;
417 Ok(V::String(a.to_lowercase().into()))
418 }
419
420 pub fn trim(args: Arguments) -> anyhow::Result<V> {
421 let a = args.str(0)?;
422 Ok(V::String(a.trim().into()))
423 }
424
425 pub fn extract(args: Arguments) -> anyhow::Result<V> {
426 let a = args.str(0)?;
427 let b = args.str(1)?;
428
429 let regex = Regex::new(b.as_ref()).context("Invalid regular expression")?;
430
431 let captures = regex
432 .captures(a.as_ref())
433 .map(|capture| {
434 capture
435 .iter()
436 .map(|c| c.map(|c| c.as_str()))
437 .filter_map(|c| c)
438 .map(|s| V::String((s).into()))
439 .collect()
440 })
441 .unwrap_or_default();
442
443 Ok(V::from_array(captures))
444 }
445
446 pub fn split(args: Arguments) -> anyhow::Result<V> {
447 let a = args.str(0)?;
448 let b = args.str(1)?;
449
450 let arr = Vec::from_iter(
451 a.split(b)
452 .into_iter()
453 .map(|s| V::String(s.to_string().into())),
454 );
455
456 Ok(V::from_array(arr))
457 }
458
459 pub fn flatten(args: Arguments) -> anyhow::Result<V> {
460 let a = args.array(0)?;
461
462 let arr = a.borrow();
463 let mut flat_arr = Vec::with_capacity(arr.len());
464 arr.iter().for_each(|v| match v {
465 V::Array(b) => {
466 let arr = b.borrow();
467 arr.iter().for_each(|v| flat_arr.push(v.clone()))
468 }
469 _ => flat_arr.push(v.clone()),
470 });
471
472 Ok(V::from_array(flat_arr))
473 }
474
475 pub fn merge(args: Arguments) -> anyhow::Result<V> {
476 let a = args.array(0)?;
477 let arr = a.borrow();
478
479 let Some(first) = arr.iter().find(|item| !matches!(item, V::Null)) else {
480 return Ok(V::empty_object());
481 };
482
483 let capacity = arr
484 .iter()
485 .map(|item| match item {
486 V::Object(obj) => obj.borrow().len(),
487 V::Array(arr) => arr.borrow().len(),
488 _ => 0,
489 })
490 .sum();
491
492 match first {
493 V::Array(_) => {
494 let mut merged = Vec::with_capacity(capacity);
495
496 for item in arr.iter() {
497 match item {
498 V::Array(inner) => {
499 let inner = inner.borrow();
500 merged.extend(inner.iter().cloned());
501 }
502 V::Null => {}
503 _ => return Err(anyhow!("Expected array of arrays")),
504 }
505 }
506
507 Ok(V::from_array(merged))
508 }
509 V::Object(_) => {
510 let mut merged = zen_types::variable::VariableMap::with_capacity(capacity);
511 for item in arr.iter() {
512 match item {
513 V::Object(obj) => {
514 let obj = obj.borrow();
515 for (key, value) in obj.iter() {
516 merged.insert(key.clone(), value.clone());
517 }
518 }
519 V::Null => {}
520 _ => return Err(anyhow!("Expected array of objects")),
521 }
522 }
523
524 Ok(V::from_object(merged))
525 }
526 other => Err(anyhow!(
527 "merge expects an array of arrays or objects, got {}",
528 other.type_name()
529 )),
530 }
531 }
532
533 pub fn merge_deep(args: Arguments) -> anyhow::Result<V> {
534 let a = args.array(0)?;
535 let arr = a.borrow();
536
537 let mut result = V::empty_object();
538 for item in arr.iter() {
539 match item {
540 V::Object(_) => {
541 result = deep_merge_variables(&result, item);
542 }
543 V::Null => {}
544 _ => return Err(anyhow!("Expected array of objects")),
545 }
546 }
547
548 Ok(result)
549 }
550
551 fn deep_merge_variables(base: &V, patch: &V) -> V {
552 match (base, patch) {
553 (V::Object(a), V::Object(b)) => {
554 let a = a.borrow();
555 let b = b.borrow();
556 let mut merged = zen_types::variable::VariableMap::with_capacity(a.len() + b.len());
557
558 for (key, value) in a.iter() {
559 merged.insert(key.clone(), value.clone());
560 }
561
562 for (key, value) in b.iter() {
563 let entry = merged
564 .get(key)
565 .map(|existing| deep_merge_variables(existing, value))
566 .unwrap_or_else(|| value.clone());
567 merged.insert(key.clone(), entry);
568 }
569
570 V::from_object(merged)
571 }
572 (V::Array(a), V::Array(b)) => {
573 let a = a.borrow();
574 let b = b.borrow();
575 let mut merged = Vec::with_capacity(a.len() + b.len());
576 merged.extend(a.iter().cloned());
577 merged.extend(b.iter().cloned());
578 V::from_array(merged)
579 }
580 (_, patch) => patch.clone(),
581 }
582 }
583
584 pub fn abs(args: Arguments) -> anyhow::Result<V> {
585 let a = args.number(0)?;
586 Ok(V::Number(a.abs()))
587 }
588
589 pub fn ceil(args: Arguments) -> anyhow::Result<V> {
590 let a = args.number(0)?;
591 Ok(V::Number(a.ceil()))
592 }
593
594 pub fn floor(args: Arguments) -> anyhow::Result<V> {
595 let a = args.number(0)?;
596 Ok(V::Number(a.floor()))
597 }
598
599 pub fn round(args: Arguments) -> anyhow::Result<V> {
600 let a = args.number(0)?;
601 let dp = args
602 .onumber(1)?
603 .map(|v| v.to_u32().context("Invalid number of decimal places"))
604 .transpose()?
605 .unwrap_or(0);
606
607 Ok(V::Number(a.round_dp_with_strategy(
608 dp,
609 RoundingStrategy::MidpointAwayFromZero,
610 )))
611 }
612
613 pub fn trunc(args: Arguments) -> anyhow::Result<V> {
614 let a = args.number(0)?;
615 let dp = args
616 .onumber(1)?
617 .map(|v| v.to_u32().context("Invalid number of decimal places"))
618 .transpose()?
619 .unwrap_or(0);
620
621 Ok(V::Number(a.trunc_with_scale(dp)))
622 }
623
624 pub fn rand(args: Arguments) -> anyhow::Result<V> {
625 let a = args.number(0)?;
626 let upper_range = a.round().to_i64().context("Invalid upper range")?;
627
628 let random_number = fastrand::i64(0..=upper_range);
629 Ok(V::Number(Decimal::from(random_number)))
630 }
631
632 pub fn min(args: Arguments) -> anyhow::Result<V> {
633 let a = __internal_number_or_date_array(&args, 0)?;
634
635 match a {
636 Either::Left(arr) => {
637 let max = arr.into_iter().min().context("Empty array")?;
638 Ok(V::Number(Decimal::from(max)))
639 }
640 Either::Right(arr) => {
641 let max = arr.into_iter().min().context("Empty array")?;
642 Ok(V::Dynamic(Rc::new(max)))
643 }
644 }
645 }
646
647 pub fn max(args: Arguments) -> anyhow::Result<V> {
648 let a = __internal_number_or_date_array(&args, 0)?;
649
650 match a {
651 Either::Left(arr) => {
652 let max = arr.into_iter().max().context("Empty array")?;
653 Ok(V::Number(Decimal::from(max)))
654 }
655 Either::Right(arr) => {
656 let max = arr.into_iter().max().context("Empty array")?;
657 Ok(V::Dynamic(Rc::new(max)))
658 }
659 }
660 }
661
662 pub fn avg(args: Arguments) -> anyhow::Result<V> {
663 let a = __internal_number_array(&args, 0)?;
664 let sum = a
665 .iter()
666 .try_fold(Decimal::ZERO, |acc, x| acc.checked_add(*x))
667 .context("Number overflow")?;
668
669 Ok(V::Number(Decimal::from(
670 sum.checked_div(Decimal::from(a.len()))
671 .context("Empty array")?,
672 )))
673 }
674
675 pub fn sum(args: Arguments) -> anyhow::Result<V> {
676 let a = __internal_number_array(&args, 0)?;
677 let sum = a
678 .iter()
679 .try_fold(Decimal::ZERO, |acc, v| acc.checked_add(*v))
680 .context("Number overflow")?;
681
682 Ok(V::Number(Decimal::from(sum)))
683 }
684
685 pub fn median(args: Arguments) -> anyhow::Result<V> {
686 let mut a = __internal_number_array(&args, 0)?;
687 a.sort();
688
689 let center = a.len() / 2;
690 if a.len() % 2 == 1 {
691 let center_num = a.get(center).context("Index out of bounds")?;
692 Ok(V::Number(*center_num))
693 } else {
694 let center_left = a.get(center - 1).context("Index out of bounds")?;
695 let center_right = a.get(center).context("Index out of bounds")?;
696
697 let median = center_left
698 .checked_add(*center_right)
699 .context("Number overflow")?
700 / dec!(2);
701 Ok(V::Number(median))
702 }
703 }
704
705 pub fn mode(args: Arguments) -> anyhow::Result<V> {
706 let a = __internal_number_array(&args, 0)?;
707 let mut counts = BTreeMap::new();
708 for num in a {
709 *counts.entry(num).or_insert(0) += 1;
710 }
711
712 let most_common = counts
713 .into_iter()
714 .max_by_key(|&(_, count)| count)
715 .map(|(num, _)| num)
716 .context("Empty array")?;
717
718 Ok(V::Number(most_common))
719 }
720
721 pub fn to_type(args: Arguments) -> anyhow::Result<V> {
722 let a = args.var(0)?;
723 Ok(V::String(a.type_name().into()))
724 }
725
726 pub fn to_bool(args: Arguments) -> anyhow::Result<V> {
727 let a = args.var(0)?;
728 let val = match a {
729 V::Null => false,
730 V::Bool(v) => *v,
731 V::Number(n) => !n.is_zero(),
732 V::Array(_) | V::Object(_) | V::Dynamic(_) => true,
733 V::String(s) => match s.as_str().trim() {
734 "true" => true,
735 "false" => false,
736 _ => s.is_empty(),
737 },
738 };
739
740 Ok(V::Bool(val))
741 }
742
743 pub fn to_string(args: Arguments) -> anyhow::Result<V> {
744 let a = args.var(0)?;
745 let val = match a {
746 V::Null => "null".into(),
747 V::Bool(v) => v.to_string().into(),
748 V::Number(n) => n.to_string().into(),
749 V::String(s) => s.clone(),
750 _ => return Err(anyhow!("Cannot convert type {} to string", a.type_name())),
751 };
752
753 Ok(V::String(val))
754 }
755
756 pub fn to_number(args: Arguments) -> anyhow::Result<V> {
757 let a = args.var(0)?;
758 let val = match a {
759 V::Number(n) => *n,
760 V::String(str) => {
761 let s = str.as_str().trim();
762 Decimal::from_str_exact(s)
763 .or_else(|_| Decimal::from_scientific(s))
764 .context("Invalid number")?
765 }
766 V::Bool(b) => match *b {
767 true => Decimal::ONE,
768 false => Decimal::ZERO,
769 },
770 _ => return Err(anyhow!("Cannot convert type {} to number", a.type_name())),
771 };
772
773 Ok(V::Number(val))
774 }
775
776 pub fn is_numeric(args: Arguments) -> anyhow::Result<V> {
777 let a = args.var(0)?;
778 let is_ok = match a {
779 V::Number(_) => true,
780 V::String(str) => {
781 let s = str.as_str().trim();
782 Decimal::from_str_exact(s)
783 .or_else(|_| Decimal::from_scientific(s))
784 .is_ok()
785 }
786 _ => false,
787 };
788
789 Ok(V::Bool(is_ok))
790 }
791
792 pub fn len(args: Arguments) -> anyhow::Result<V> {
793 let a = args.var(0)?;
794 let len = match a {
795 V::String(s) => s.len(),
796 V::Array(s) => {
797 let arr = s.borrow();
798 arr.len()
799 }
800 _ => {
801 return Err(anyhow!("Cannot determine len of type {}", a.type_name()));
802 }
803 };
804
805 Ok(V::Number(len.into()))
806 }
807
808 pub fn contains(args: Arguments) -> anyhow::Result<V> {
809 let a = args.var(0)?;
810 let b = args.var(1)?;
811
812 let val = match (a, b) {
813 (V::String(a), V::String(b)) => a.contains(b.as_str()),
814 (V::Array(a), _) => {
815 let arr = a.borrow();
816
817 arr.iter().any(|a| match (a, b) {
818 (V::Number(a), V::Number(b)) => a == b,
819 (V::String(a), V::String(b)) => a == b,
820 (V::Bool(a), V::Bool(b)) => a == b,
821 (V::Null, V::Null) => true,
822 _ => false,
823 })
824 }
825 _ => {
826 return Err(anyhow!(
827 "Cannot determine contains for type {} and {}",
828 a.type_name(),
829 b.type_name()
830 ));
831 }
832 };
833
834 Ok(V::Bool(val))
835 }
836
837 pub fn fuzzy_match(args: Arguments) -> anyhow::Result<V> {
838 let a = args.var(0)?;
839 let b = args.str(1)?;
840
841 let val = match a {
842 V::String(a) => {
843 let sim = strsim::normalized_damerau_levenshtein(a.as_ref(), b.as_ref());
844 V::Number(Decimal::from_f64(sim).unwrap_or(dec!(0)))
846 }
847 V::Array(_a) => {
848 let a = _a.borrow();
849 let mut sims = Vec::with_capacity(a.len());
850 for v in a.iter() {
851 let s = v.as_str().context("Expected string array")?;
852
853 let sim = Decimal::from_f64(strsim::normalized_damerau_levenshtein(
854 s.as_ref(),
855 b.as_ref(),
856 ))
857 .unwrap_or(dec!(0));
858 sims.push(V::Number(sim));
859 }
860
861 V::from_array(sims)
862 }
863 _ => return Err(anyhow!("Fuzzy match not available for type")),
864 };
865
866 Ok(val)
867 }
868
869 pub fn keys(args: Arguments) -> anyhow::Result<V> {
870 let a = args.var(0)?;
871 let var = match a {
872 V::Array(a) => {
873 let arr = a.borrow();
874 let indices = arr
875 .iter()
876 .enumerate()
877 .map(|(index, _)| V::Number(index.into()))
878 .collect();
879
880 V::from_array(indices)
881 }
882 V::Object(a) => {
883 let obj = a.borrow();
884 let keys = obj
885 .iter()
886 .map(|(key, _)| V::String((key.as_str()).into()))
887 .collect();
888
889 V::from_array(keys)
890 }
891 _ => {
892 return Err(anyhow!("Cannot determine keys of type {}", a.type_name()));
893 }
894 };
895
896 Ok(var)
897 }
898
899 pub fn values(args: Arguments) -> anyhow::Result<V> {
900 let a = args.object(0)?;
901 let obj = a.borrow();
902 let values: Vec<_> = obj.values().cloned().collect();
903
904 Ok(V::from_array(values))
905 }
906
907 pub fn date(args: Arguments) -> anyhow::Result<V> {
908 let provided = args.ovar(0);
909 let tz = args
910 .ostr(1)?
911 .map(|v| Tz::from_str(v).context("Invalid timezone"))
912 .transpose()?;
913
914 let date_time = match provided {
915 Some(v) => VmDate::new(v.clone(), tz),
916 None => VmDate::now(),
917 };
918
919 Ok(V::Dynamic(Rc::new(date_time)))
920 }
921}