Skip to main content

zen_expression/functions/
internal.rs

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    // General
11    Len,
12    Contains,
13    Flatten,
14    Merge,
15    MergeDeep,
16
17    // String
18    Upper,
19    Lower,
20    Trim,
21    StartsWith,
22    EndsWith,
23    Matches,
24    Extract,
25    FuzzyMatch,
26    Split,
27
28    // Math
29    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    // Type
43    IsNumeric,
44    String,
45    Number,
46    Bool,
47    Type,
48
49    // Map
50    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                // This is okay, as NDL will return [0, 1]
845                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}