jsonata_rs/evaluator/
functions.rs

1use base64::Engine;
2use chrono::{TimeZone, Utc};
3use hashbrown::{DefaultHashBuilder, HashMap};
4use rand::Rng;
5use regress::{Range, Regex};
6use std::borrow::{Borrow, Cow};
7use std::collections::HashSet;
8use std::time::{SystemTime, UNIX_EPOCH};
9use uuid::Uuid;
10
11use crate::datetime::{format_custom_date, parse_custom_format, parse_timezone_offset};
12use crate::evaluator::RegexLiteral;
13use crate::parser::expressions::check_balanced_brackets;
14
15use bumpalo::collections::CollectIn;
16use bumpalo::collections::String as BumpString;
17use bumpalo::collections::Vec as BumpVec;
18use bumpalo::Bump;
19
20use crate::{Error, Result};
21
22use super::frame::Frame;
23use super::value::serialize::{DumpFormatter, PrettyFormatter, Serializer};
24use super::value::{ArrayFlags, Value};
25use super::Evaluator;
26
27macro_rules! min_args {
28    ($context:ident, $args:ident, $min:literal) => {
29        if $args.len() < $min {
30            return Err(Error::T0410ArgumentNotValid(
31                $context.char_index,
32                $min,
33                $context.name.to_string(),
34            ));
35        }
36    };
37}
38
39macro_rules! max_args {
40    ($context:ident, $args:ident, $max:literal) => {
41        if $args.len() > $max {
42            return Err(Error::T0410ArgumentNotValid(
43                $context.char_index,
44                $max,
45                $context.name.to_string(),
46            ));
47        }
48    };
49}
50
51macro_rules! bad_arg {
52    ($context:ident, $index:literal) => {
53        return Err(Error::T0410ArgumentNotValid(
54            $context.char_index,
55            $index,
56            $context.name.to_string(),
57        ))
58    };
59}
60
61macro_rules! assert_arg {
62    ($condition: expr, $context:ident, $index:literal) => {
63        if !($condition) {
64            bad_arg!($context, $index);
65        }
66    };
67}
68
69macro_rules! assert_array_of_type {
70    ($condition:expr, $context:ident, $index:literal, $t:literal) => {
71        if !($condition) {
72            return Err(Error::T0412ArgumentMustBeArrayOfType(
73                $context.char_index,
74                $index,
75                $context.name.to_string(),
76                $t.to_string(),
77            ));
78        };
79    };
80}
81
82#[derive(Clone)]
83pub struct FunctionContext<'a, 'e> {
84    pub name: &'a str,
85    pub char_index: usize,
86    pub input: &'a Value<'a>,
87    pub frame: Frame<'a>,
88    pub evaluator: &'e Evaluator<'a>,
89    pub arena: &'a Bump,
90}
91
92#[allow(clippy::needless_lifetimes)]
93impl<'a, 'e> FunctionContext<'a, 'e> {
94    pub fn evaluate_function(
95        &self,
96        proc: &'a Value<'a>,
97        args: &[&'a Value<'a>],
98    ) -> Result<&'a Value<'a>> {
99        self.evaluator
100            .apply_function(self.char_index, self.input, proc, args, &self.frame)
101    }
102
103    pub fn trampoline_evaluate_value(&self, value: &'a Value<'a>) -> Result<&'a Value<'a>> {
104        self.evaluator
105            .trampoline_evaluate_value(value, self.input, &self.frame)
106    }
107}
108
109/// Extend the given values with value.
110///
111/// If the value is a single value then, append the value as is.
112/// If the value is an array, extends values with the value's members.
113pub fn fn_append_internal<'a>(values: &mut BumpVec<&'a Value<'a>>, value: &'a Value<'a>) {
114    if value.is_undefined() {
115        return;
116    }
117
118    match value {
119        Value::Array(a, _) => values.extend_from_slice(a),
120        Value::Range(_) => values.extend(value.members()),
121        _ => values.push(value),
122    }
123}
124
125pub fn fn_append<'a>(
126    context: FunctionContext<'a, '_>,
127    args: &[&'a Value<'a>],
128) -> Result<&'a Value<'a>> {
129    let arg1 = args.first().copied().unwrap_or_else(Value::undefined);
130    let arg2 = args.get(1).copied().unwrap_or_else(Value::undefined);
131
132    if arg1.is_undefined() {
133        return Ok(arg2);
134    }
135
136    if arg2.is_undefined() {
137        return Ok(arg1);
138    }
139
140    let arg1_len = if arg1.is_array() { arg1.len() } else { 1 };
141    let arg2_len = if arg2.is_array() { arg2.len() } else { 1 };
142
143    let result = Value::array_with_capacity(
144        context.arena,
145        arg1_len + arg2_len,
146        if arg1.is_array() {
147            arg1.get_flags()
148        } else {
149            ArrayFlags::SEQUENCE
150        },
151    );
152
153    if arg1.is_array() {
154        arg1.members().for_each(|m| result.push(m));
155    } else {
156        result.push(arg1);
157    }
158
159    if arg2.is_array() {
160        arg2.members().for_each(|m| result.push(m));
161    } else {
162        result.push(arg2)
163    }
164
165    Ok(result)
166}
167
168pub fn fn_boolean<'a>(
169    context: FunctionContext<'a, '_>,
170    args: &[&'a Value<'a>],
171) -> Result<&'a Value<'a>> {
172    max_args!(context, args, 1);
173
174    let arg = args.first().copied().unwrap_or_else(Value::undefined);
175    Ok(match arg {
176        Value::Undefined => Value::undefined(),
177        Value::Null => Value::bool(false),
178        Value::Bool(b) => Value::bool(*b),
179        Value::Number(n) => {
180            arg.is_valid_number()?;
181            Value::bool(*n != 0.0)
182        }
183        Value::String(ref str) => Value::bool(!str.is_empty()),
184        Value::Object(ref obj) => Value::bool(!obj.is_empty()),
185        Value::Array { .. } => match arg.len() {
186            0 => Value::bool(false),
187            1 => fn_boolean(context.clone(), &[arg.get_member(0)])?,
188            _ => {
189                for item in arg.members() {
190                    if fn_boolean(context.clone(), &[item])?.as_bool() {
191                        return Ok(Value::bool(true));
192                    }
193                }
194                Value::bool(false)
195            }
196        },
197        Value::Regex(_) => Value::bool(true),
198        Value::Lambda { .. } | Value::NativeFn { .. } | Value::Transformer { .. } => {
199            Value::bool(false)
200        }
201        Value::Range(ref range) => Value::bool(!range.is_empty()),
202    })
203}
204
205pub fn fn_map<'a>(
206    context: FunctionContext<'a, '_>,
207    args: &[&'a Value<'a>],
208) -> Result<&'a Value<'a>> {
209    let arr = args.first().copied().unwrap_or_else(Value::undefined);
210    let func = args.get(1).copied().unwrap_or_else(Value::undefined);
211
212    if arr.is_undefined() {
213        return Ok(Value::undefined());
214    }
215
216    let arr = Value::wrap_in_array_if_needed(context.arena, arr, ArrayFlags::empty());
217
218    assert_arg!(func.is_function(), context, 2);
219
220    let result = Value::array(context.arena, ArrayFlags::SEQUENCE);
221
222    for (index, item) in arr.members().enumerate() {
223        let mut args = Vec::new();
224        let arity = func.arity();
225
226        args.push(item);
227        if arity >= 2 {
228            args.push(Value::number(context.arena, index as f64));
229        }
230        if arity >= 3 {
231            args.push(arr);
232        }
233
234        let mapped = context.trampoline_evaluate_value(context.evaluate_function(func, &args)?)?;
235
236        if !mapped.is_undefined() {
237            result.push(mapped);
238        }
239    }
240
241    Ok(result)
242}
243
244pub fn fn_filter<'a>(
245    context: FunctionContext<'a, '_>,
246    args: &[&'a Value<'a>],
247) -> Result<&'a Value<'a>> {
248    let arr = args.first().copied().unwrap_or_else(Value::undefined);
249    let func = args.get(1).copied().unwrap_or_else(Value::undefined);
250
251    if arr.is_undefined() {
252        return Ok(Value::undefined());
253    }
254
255    let arr = Value::wrap_in_array_if_needed(context.arena, arr, ArrayFlags::empty());
256
257    assert_arg!(func.is_function(), context, 2);
258
259    let result = Value::array(context.arena, ArrayFlags::SEQUENCE);
260
261    for (index, item) in arr.members().enumerate() {
262        let mut args = Vec::new();
263        let arity = func.arity();
264
265        args.push(item);
266        if arity >= 2 {
267            args.push(Value::number(context.arena, index as f64));
268        }
269        if arity >= 3 {
270            args.push(arr);
271        }
272
273        let include = context.evaluate_function(func, &args)?;
274
275        if include.is_truthy() {
276            result.push(item);
277        }
278    }
279
280    Ok(result)
281}
282
283pub fn fn_each<'a>(
284    context: FunctionContext<'a, '_>,
285    args: &[&'a Value<'a>],
286) -> Result<&'a Value<'a>> {
287    let (obj, func) = if args.len() == 1 {
288        let obj_arg = if context.input.is_array() && context.input.has_flags(ArrayFlags::WRAPPED) {
289            &context.input[0]
290        } else {
291            context.input
292        };
293
294        (obj_arg, args[0])
295    } else {
296        (args[0], args[1])
297    };
298
299    if obj.is_undefined() {
300        return Ok(Value::undefined());
301    }
302
303    assert_arg!(obj.is_object(), context, 1);
304    assert_arg!(func.is_function(), context, 2);
305
306    let result = Value::array(context.arena, ArrayFlags::SEQUENCE);
307
308    for (key, value) in obj.entries() {
309        let key = Value::string(context.arena, key);
310
311        let mapped = context.evaluate_function(func, &[value, key])?;
312        if !mapped.is_undefined() {
313            result.push(mapped);
314        }
315    }
316
317    Ok(result)
318}
319
320pub fn fn_keys<'a>(
321    context: FunctionContext<'a, '_>,
322    args: &[&'a Value<'a>],
323) -> Result<&'a Value<'a>> {
324    let obj = if args.is_empty() {
325        if context.input.is_array() && context.input.has_flags(ArrayFlags::WRAPPED) {
326            &context.input[0]
327        } else {
328            context.input
329        }
330    } else {
331        args[0]
332    };
333
334    if obj.is_undefined() {
335        return Ok(Value::undefined());
336    }
337
338    let mut keys = Vec::new();
339
340    if obj.is_array() && obj.members().all(|member| member.is_object()) {
341        for sub_object in obj.members() {
342            for (key, _) in sub_object.entries() {
343                // deduplicating keys from multiple objects
344                if !keys.iter().any(|item| item == key) {
345                    keys.push(key.to_string());
346                }
347            }
348        }
349    } else if obj.is_object() {
350        for (key, _) in obj.entries() {
351            keys.push(key.to_string());
352        }
353    }
354
355    let result = Value::array(context.arena, ArrayFlags::SEQUENCE);
356    for key in keys {
357        result.push(Value::string(context.arena, &key));
358    }
359
360    Ok(result)
361}
362
363pub fn fn_merge<'a>(
364    context: FunctionContext<'a, '_>,
365    args: &[&'a Value<'a>],
366) -> Result<&'a Value<'a>> {
367    let mut array_of_objects = if args.is_empty() {
368        if context.input.is_array() && context.input.has_flags(ArrayFlags::WRAPPED) {
369            &context.input[0]
370        } else {
371            context.input
372        }
373    } else {
374        args[0]
375    };
376
377    if array_of_objects.is_undefined() {
378        return Ok(Value::undefined());
379    }
380
381    if array_of_objects.is_object() {
382        array_of_objects =
383            Value::wrap_in_array(context.arena, array_of_objects, ArrayFlags::empty());
384    }
385
386    assert_arg!(
387        array_of_objects.is_array() && array_of_objects.members().all(|member| member.is_object()),
388        context,
389        1
390    );
391
392    let result = Value::object(context.arena);
393
394    for obj in array_of_objects.members() {
395        for (key, value) in obj.entries() {
396            result.insert(key, value);
397        }
398    }
399
400    Ok(result)
401}
402
403pub fn fn_string<'a>(
404    context: FunctionContext<'a, '_>,
405    args: &[&'a Value<'a>],
406) -> Result<&'a Value<'a>> {
407    max_args!(context, args, 2);
408
409    let input = if args.is_empty() {
410        if context.input.is_array() && context.input.has_flags(ArrayFlags::WRAPPED) {
411            &context.input[0]
412        } else {
413            context.input
414        }
415    } else {
416        args.first().copied().unwrap_or_else(Value::undefined)
417    };
418
419    if input.is_undefined() {
420        return Ok(Value::undefined());
421    }
422
423    let pretty = args.get(1).copied().unwrap_or_else(Value::undefined);
424    assert_arg!(pretty.is_undefined() || pretty.is_bool(), context, 2);
425
426    if input.is_string() {
427        Ok(input)
428    } else if input.is_function() {
429        Ok(Value::string(context.arena, ""))
430    } else if input.is_number() && !input.is_finite() {
431        Err(Error::D3001StringNotFinite(context.char_index))
432    } else if *pretty == true {
433        let serializer = Serializer::new(PrettyFormatter::default(), true);
434        let output = serializer.serialize(input)?;
435        Ok(Value::string(context.arena, &output))
436    } else {
437        let serializer = Serializer::new(DumpFormatter, true);
438        let output = serializer.serialize(input)?;
439        Ok(Value::string(context.arena, &output))
440    }
441}
442
443pub fn fn_substring_before<'a>(
444    context: FunctionContext<'a, '_>,
445    args: &[&'a Value<'a>],
446) -> Result<&'a Value<'a>> {
447    let string = args.first().copied().unwrap_or_else(Value::undefined);
448
449    let chars = args.get(1).copied().unwrap_or_else(Value::undefined);
450
451    if !string.is_string() {
452        return Ok(Value::undefined());
453    }
454
455    if !chars.is_string() {
456        return Err(Error::D3010EmptyPattern(context.char_index));
457    }
458
459    let string: &str = &string.as_str();
460    let chars: &str = &chars.as_str();
461
462    if let Some(index) = string.find(chars) {
463        Ok(Value::string(context.arena, &string[..index]))
464    } else {
465        Ok(Value::string(context.arena, string))
466    }
467}
468
469pub fn fn_substring_after<'a>(
470    context: FunctionContext<'a, '_>,
471    args: &[&'a Value<'a>],
472) -> Result<&'a Value<'a>> {
473    let string = args.first().copied().unwrap_or_else(Value::undefined);
474
475    let chars = args.get(1).copied().unwrap_or_else(Value::undefined);
476
477    if !string.is_string() {
478        return Ok(Value::undefined());
479    }
480
481    if !chars.is_string() {
482        return Err(Error::D3010EmptyPattern(context.char_index));
483    }
484
485    let string: &str = &string.as_str();
486    let chars: &str = &chars.as_str();
487
488    if let Some(index) = string.find(chars) {
489        let after_index = index + chars.len();
490        Ok(Value::string(context.arena, &string[after_index..]))
491    } else {
492        // Return the original string if 'chars' is not found
493        Ok(Value::string(context.arena, string))
494    }
495}
496
497pub fn fn_not<'a>(
498    _context: FunctionContext<'a, '_>,
499    args: &[&'a Value<'a>],
500) -> Result<&'a Value<'a>> {
501    let arg = args.first().copied().unwrap_or_else(Value::undefined);
502
503    Ok(if arg.is_undefined() {
504        Value::undefined()
505    } else {
506        Value::bool(!arg.is_truthy())
507    })
508}
509
510pub fn fn_lowercase<'a>(
511    context: FunctionContext<'a, '_>,
512    args: &[&'a Value<'a>],
513) -> Result<&'a Value<'a>> {
514    let arg = args.first().copied().unwrap_or_else(Value::undefined);
515
516    Ok(if !arg.is_string() {
517        Value::undefined()
518    } else {
519        Value::string(context.arena, &arg.as_str().to_lowercase())
520    })
521}
522
523pub fn fn_uppercase<'a>(
524    context: FunctionContext<'a, '_>,
525    args: &[&'a Value<'a>],
526) -> Result<&'a Value<'a>> {
527    let arg = args.first().copied().unwrap_or_else(Value::undefined);
528
529    if !arg.is_string() {
530        Ok(Value::undefined())
531    } else {
532        Ok(Value::string(context.arena, &arg.as_str().to_uppercase()))
533    }
534}
535
536pub fn fn_trim<'a>(
537    context: FunctionContext<'a, '_>,
538    args: &[&'a Value<'a>],
539) -> Result<&'a Value<'a>> {
540    let arg = args.first().copied().unwrap_or_else(Value::undefined);
541
542    if !arg.is_string() {
543        Ok(Value::undefined())
544    } else {
545        let orginal = arg.as_str();
546        let mut words = orginal.split_whitespace();
547        let trimmed = match words.next() {
548            None => String::new(),
549            Some(first_word) => {
550                // estimate lower bound of capacity needed
551                let (lower, _) = words.size_hint();
552                let mut result = String::with_capacity(lower);
553                result.push_str(first_word);
554                for word in words {
555                    result.push(' ');
556                    result.push_str(word);
557                }
558                result
559            }
560        };
561        Ok(Value::string(context.arena, &trimmed))
562    }
563}
564
565pub fn fn_substring<'a>(
566    context: FunctionContext<'a, '_>,
567    args: &[&'a Value<'a>],
568) -> Result<&'a Value<'a>> {
569    let string = args.first().copied().unwrap_or_else(Value::undefined);
570    let start = args.get(1).copied().unwrap_or_else(Value::undefined);
571    let length = args.get(2).copied().unwrap_or_else(Value::undefined);
572
573    if string.is_undefined() {
574        return Ok(Value::undefined());
575    }
576
577    assert_arg!(string.is_string(), context, 1);
578    assert_arg!(start.is_number(), context, 2);
579
580    let string = string.as_str();
581
582    // Scan the string chars for the actual number of characters.
583    // NOTE: Chars are not grapheme clusters, so for some inputs like "नमस्ते" we will get 6
584    //       as it will include the diacritics.
585    //       See: https://doc.rust-lang.org/nightly/book/ch08-02-strings.html
586    let len = string.chars().count() as isize;
587    let mut start = start.as_isize();
588
589    // If start is negative and runs off the front of the string
590    if len + start < 0 {
591        start = 0;
592    }
593
594    // If start is negative, count from the end of the string
595    let start = if start < 0 { len + start } else { start };
596
597    if length.is_undefined() {
598        Ok(Value::string(context.arena, &string[start as usize..]))
599    } else {
600        assert_arg!(length.is_number(), context, 3);
601
602        let length = length.as_isize();
603        if length < 0 {
604            Ok(Value::string(context.arena, ""))
605        } else {
606            let end = if start >= 0 {
607                (start + length) as usize
608            } else {
609                (len + start + length) as usize
610            };
611
612            let substring = string
613                .chars()
614                .skip(start as usize)
615                .take(end - start as usize)
616                .collect::<String>();
617
618            Ok(Value::string(context.arena, &substring))
619        }
620    }
621}
622
623pub fn fn_contains<'a>(
624    context: FunctionContext<'a, '_>,
625    args: &[&'a Value<'a>],
626) -> Result<&'a Value<'a>> {
627    let str_value = args.first().copied().unwrap_or_else(Value::undefined);
628    let token_value = args.get(1).copied().unwrap_or_else(Value::undefined);
629
630    if str_value.is_undefined() {
631        return Ok(Value::undefined());
632    }
633
634    assert_arg!(str_value.is_string(), context, 1);
635
636    let str_value = str_value.as_str();
637
638    // Check if token_value is a regex or string
639    let contains_result = match token_value {
640        Value::Regex(ref regex_literal) => {
641            let regex = regex_literal.get_regex();
642            regex.find_iter(&str_value).next().is_some()
643        }
644        Value::String(_) => {
645            let token_value = token_value.as_str();
646            str_value.contains(&token_value.to_string())
647        }
648        _ => {
649            return Err(Error::T0410ArgumentNotValid(
650                context.char_index,
651                2,
652                context.name.to_string(),
653            ));
654        }
655    };
656
657    Ok(Value::bool(contains_result))
658}
659
660pub fn fn_replace<'a>(
661    context: FunctionContext<'a, '_>,
662    args: &[&'a Value<'a>],
663) -> Result<&'a Value<'a>> {
664    let str_value = args.first().copied().unwrap_or_else(Value::undefined);
665    let pattern_value = args.get(1).copied().unwrap_or_else(Value::undefined);
666    let replacement_value = args.get(2).copied().unwrap_or_else(Value::undefined);
667    let limit_value = args.get(3).copied().unwrap_or_else(Value::undefined);
668
669    if str_value.is_undefined() {
670        return Ok(Value::undefined());
671    }
672
673    if pattern_value.is_string() && pattern_value.as_str().is_empty() {
674        return Err(Error::D3010EmptyPattern(context.char_index));
675    }
676
677    assert_arg!(str_value.is_string(), context, 1);
678
679    let str_value = str_value.as_str();
680    let limit_value = if limit_value.is_undefined() {
681        None
682    } else {
683        assert_arg!(limit_value.is_number(), context, 4);
684        if limit_value.as_isize().is_negative() {
685            return Err(Error::D3011NegativeLimit(context.char_index));
686        }
687        Some(limit_value.as_isize() as usize)
688    };
689
690    // Check if pattern_value is a Regex or String and handle appropriately
691    let regex = match pattern_value {
692        Value::Regex(ref regex_literal) => regex_literal.get_regex(),
693        Value::String(ref pattern_str) => {
694            assert_arg!(replacement_value.is_string(), context, 3);
695            let replacement_str = replacement_value.as_str();
696
697            let replaced_string = if let Some(limit) = limit_value {
698                str_value.replacen(&pattern_str.to_string(), &replacement_str, limit)
699            } else {
700                str_value.replace(&pattern_str.to_string(), &replacement_str)
701            };
702
703            return Ok(Value::string(context.arena, &replaced_string));
704        }
705        _ => bad_arg!(context, 2),
706    };
707
708    let mut result = String::new();
709    let mut last_end = 0;
710
711    for (replacements, m) in regex.find_iter(&str_value).enumerate() {
712        if m.range().is_empty() {
713            return Err(Error::D1004ZeroLengthMatch(context.char_index));
714        }
715
716        if let Some(limit) = limit_value {
717            if replacements >= limit {
718                break;
719            }
720        }
721
722        result.push_str(&str_value[last_end..m.start()]);
723
724        let match_str = &str_value[m.start()..m.end()];
725
726        // Process replacement based on the replacement_value type
727        let replacement_text = match replacement_value {
728            Value::NativeFn { func, .. } => {
729                let match_list = evaluate_match(context.arena, regex, match_str, None);
730
731                let func_result = func(context.clone(), &[match_list])?;
732
733                if let Value::String(ref s) = func_result {
734                    s.as_str().to_string()
735                } else {
736                    return Err(Error::D3012InvalidReplacementType(context.char_index));
737                }
738            }
739
740            func @ Value::Lambda { .. } => {
741                let match_list = evaluate_match(context.arena, regex, match_str, None);
742
743                let args = &[match_list];
744
745                let func_result =
746                    context.trampoline_evaluate_value(context.evaluate_function(func, args)?)?;
747
748                match func_result {
749                    Value::String(ref s) => s.as_str().to_string(),
750                    _ => return Err(Error::D3012InvalidReplacementType(context.char_index)),
751                }
752            }
753
754            Value::String(replacement_str) => {
755                evaluate_replacement_string(replacement_str.as_str(), &str_value, &m)
756            }
757
758            _ => bad_arg!(context, 3),
759        };
760
761        result.push_str(&replacement_text);
762        last_end = m.end();
763    }
764
765    result.push_str(&str_value[last_end..]);
766
767    Ok(Value::string(context.arena, &result))
768}
769
770/// Parse and evaluate a replacement string.
771///
772/// Parsing the string is context-dependent because of an odd jsonata behavior:
773/// - if $NM is a valid match group number, it is replaced with the match.
774/// - if $NM is not valid, it is replaced with the match for $M followed by a literal 'N'.
775///
776/// This is why the `Match` object is needed.
777///
778/// # Parameters
779/// - `replacement_str`: The replacement string to parse and evaluate.
780/// - `str_value`: The complete original string, the first argument to `$replace`.
781/// - `m`: The `Match` object for the current match which is being replaced.
782fn evaluate_replacement_string(
783    replacement_str: &str,
784    str_value: &str,
785    m: &regress::Match,
786) -> String {
787    #[derive(Debug)]
788    enum S {
789        Literal,
790        Dollar,
791        Group(u32),
792        End,
793    }
794
795    let mut state = S::Literal;
796    let mut acc = String::new();
797
798    let groups: Vec<Option<Range>> = m.groups().collect();
799    let mut chars = replacement_str.chars();
800
801    loop {
802        let c = chars.next();
803        match (&state, c) {
804            (S::Literal, Some('$')) => {
805                state = S::Dollar;
806            }
807            (S::Literal, Some(c)) => {
808                acc.push(c);
809            }
810            (S::Dollar, Some('$')) => {
811                acc.push('$');
812                state = S::Literal;
813            }
814
815            // Start parsing a group number
816            (S::Dollar, Some(c)) if c.is_numeric() => {
817                let digit = c
818                    .to_digit(10)
819                    .expect("numeric char failed to parse as digit");
820                state = S::Group(digit);
821            }
822
823            // `$` followed by something other than a group number
824            // (including end of string) is treated as a literal `$`
825            (S::Dollar, c) => {
826                acc.push('$');
827                c.inspect(|c| acc.push(*c));
828                state = S::Literal;
829            }
830
831            // Still parsing a group number
832            (S::Group(so_far), Some(c)) if c.is_numeric() => {
833                let digit = c
834                    .to_digit(10)
835                    .expect("numeric char failed to parse as digit");
836
837                let next = so_far * 10 + digit;
838                let groups_len = groups.len() as u32;
839
840                // A bizarre behavior of the jsonata reference implementation is that in $NM if NM is not a
841                // valid group number, it will use $N and treat M as a literal. This is not documented behavior and
842                // feels like a bug, but our test cases cover it in several ways.
843                if next >= groups_len {
844                    if let Some(match_range) = groups.get(*so_far as usize).and_then(|x| x.as_ref())
845                    {
846                        str_value
847                            .get(match_range.start..match_range.end)
848                            .inspect(|s| acc.push_str(s));
849                    } else {
850                        // The capture group did not match.
851                    }
852
853                    acc.push(c);
854
855                    state = S::Literal
856                } else {
857                    state = S::Group(next);
858                }
859            }
860
861            // The group number is complete, so we can now process it
862            (S::Group(index), c) => {
863                if let Some(match_range) = groups.get(*index as usize).and_then(|x| x.as_ref()) {
864                    str_value
865                        .get(match_range.start..match_range.end)
866                        .inspect(|s| acc.push_str(s));
867                } else {
868                    // The capture group did not match.
869                }
870
871                if let Some(c) = c {
872                    if c == '$' {
873                        state = S::Dollar;
874                    } else {
875                        acc.push(c);
876                        state = S::Literal;
877                    }
878                } else {
879                    state = S::End;
880                }
881            }
882            (S::Literal, None) => {
883                state = S::End;
884            }
885
886            (S::End, _) => {
887                break;
888            }
889        }
890    }
891    acc
892}
893
894pub fn fn_split<'a>(
895    context: FunctionContext<'a, '_>,
896    args: &[&'a Value<'a>],
897) -> Result<&'a Value<'a>> {
898    let str_value = args.first().copied().unwrap_or_else(Value::undefined);
899    let separator_value = args.get(1).copied().unwrap_or_else(Value::undefined);
900    let limit_value = args.get(2).copied().unwrap_or_else(Value::undefined);
901
902    if str_value.is_undefined() {
903        return Ok(Value::undefined());
904    }
905
906    assert_arg!(str_value.is_string(), context, 1);
907
908    let str_value = str_value.as_str();
909    let separator_is_regex = match separator_value {
910        Value::Regex(_) => true,
911        Value::String(_) => false,
912        _ => {
913            return Err(Error::T0410ArgumentNotValid(
914                context.char_index,
915                2,
916                context.name.to_string(),
917            ));
918        }
919    };
920
921    // Handle optional limit
922    let limit = if limit_value.is_undefined() {
923        None
924    } else {
925        assert_arg!(limit_value.is_number(), context, 3);
926        if limit_value.as_f64() < 0.0 {
927            return Err(Error::D3020NegativeLimit(context.char_index));
928        }
929        Some(limit_value.as_f64() as usize)
930    };
931
932    let substrings: Vec<String> = if separator_is_regex {
933        // Regex-based split using find_iter to find matches
934        let regex = match separator_value {
935            Value::Regex(ref regex_literal) => regex_literal.get_regex(),
936            _ => unreachable!(),
937        };
938
939        let mut results = Vec::new();
940        let mut last_end = 0;
941        let effective_limit = limit.unwrap_or(usize::MAX);
942
943        for m in regex.find_iter(&str_value) {
944            if results.len() >= effective_limit {
945                break;
946            }
947
948            if m.start() > last_end {
949                let substring = str_value[last_end..m.start()].to_string();
950                results.push(substring);
951            }
952
953            last_end = m.end();
954        }
955
956        if results.len() < effective_limit {
957            let remaining = str_value[last_end..].to_string();
958            results.push(remaining);
959        }
960        results
961    } else {
962        // Convert separator_value to &str
963        let separator_str = separator_value.as_str().to_string();
964        let separator_str = separator_str.as_str();
965        if separator_str.is_empty() {
966            // Split into individual characters, collecting directly into a Vec<String>
967            if let Some(limit) = limit {
968                str_value
969                    .chars()
970                    .take(limit)
971                    .map(|c| c.to_string())
972                    .collect()
973            } else {
974                str_value.chars().map(|c| c.to_string()).collect()
975            }
976        } else if let Some(limit) = limit {
977            str_value
978                .split(separator_str)
979                .take(limit)
980                .map(|s| s.to_string())
981                .collect()
982        } else {
983            str_value
984                .split(separator_str)
985                .map(|s| s.to_string())
986                .collect()
987        }
988    };
989
990    let result = Value::array_with_capacity(context.arena, substrings.len(), ArrayFlags::empty());
991    for substring in &substrings {
992        result.push(Value::string(context.arena, substring));
993    }
994    Ok(result)
995}
996
997pub fn fn_abs<'a>(
998    context: FunctionContext<'a, '_>,
999    args: &[&'a Value<'a>],
1000) -> Result<&'a Value<'a>> {
1001    let arg = args.first().copied().unwrap_or_else(Value::undefined);
1002
1003    if arg.is_undefined() {
1004        return Ok(Value::undefined());
1005    }
1006
1007    assert_arg!(arg.is_number(), context, 1);
1008
1009    Ok(Value::number(context.arena, arg.as_f64().abs()))
1010}
1011
1012pub fn fn_floor<'a>(
1013    context: FunctionContext<'a, '_>,
1014    args: &[&'a Value<'a>],
1015) -> Result<&'a Value<'a>> {
1016    let arg = args.first().copied().unwrap_or_else(Value::undefined);
1017
1018    if arg.is_undefined() {
1019        return Ok(Value::undefined());
1020    }
1021
1022    assert_arg!(arg.is_number(), context, 1);
1023
1024    Ok(Value::number(context.arena, arg.as_f64().floor()))
1025}
1026
1027pub fn fn_ceil<'a>(
1028    context: FunctionContext<'a, '_>,
1029    args: &[&'a Value<'a>],
1030) -> Result<&'a Value<'a>> {
1031    let arg = args.first().copied().unwrap_or_else(Value::undefined);
1032
1033    if arg.is_undefined() {
1034        return Ok(Value::undefined());
1035    }
1036
1037    assert_arg!(arg.is_number(), context, 1);
1038
1039    Ok(Value::number(context.arena, arg.as_f64().ceil()))
1040}
1041
1042pub fn fn_lookup_internal<'a>(
1043    context: FunctionContext<'a, '_>,
1044    input: &'a Value<'a>,
1045    key: &str,
1046) -> &'a Value<'a> {
1047    match input {
1048        Value::Array { .. } => {
1049            let result = Value::array(context.arena, ArrayFlags::SEQUENCE);
1050
1051            for input in input.members() {
1052                let res = fn_lookup_internal(context.clone(), input, key);
1053                match res {
1054                    Value::Undefined => {}
1055                    Value::Array { .. } => {
1056                        res.members().for_each(|item| result.push(item));
1057                    }
1058                    _ => result.push(res),
1059                };
1060            }
1061
1062            result
1063        }
1064        Value::Object(..) => input.get_entry(key),
1065        _ => Value::undefined(),
1066    }
1067}
1068
1069pub fn fn_lookup<'a>(
1070    context: FunctionContext<'a, '_>,
1071    args: &[&'a Value<'a>],
1072) -> Result<&'a Value<'a>> {
1073    let input = args.first().copied().unwrap_or_else(Value::undefined);
1074    let key = args.get(1).copied().unwrap_or_else(Value::undefined);
1075    assert_arg!(key.is_string(), context, 2);
1076    Ok(fn_lookup_internal(context.clone(), input, &key.as_str()))
1077}
1078
1079pub fn fn_count<'a>(
1080    context: FunctionContext<'a, '_>,
1081    args: &[&'a Value<'a>],
1082) -> Result<&'a Value<'a>> {
1083    max_args!(context, args, 1);
1084
1085    let count = match args.first() {
1086        Some(Value::Array(a, _)) => a.len() as f64,
1087        Some(Value::Undefined) => 0.0,
1088        Some(_) => 1.0,
1089        None => 0.0,
1090    };
1091
1092    Ok(Value::number(context.arena, count))
1093}
1094
1095pub fn fn_max<'a>(
1096    context: FunctionContext<'a, '_>,
1097    args: &[&'a Value<'a>],
1098) -> Result<&'a Value<'a>> {
1099    max_args!(context, args, 1);
1100
1101    let arg = args.first().copied().unwrap_or_else(Value::undefined);
1102
1103    // $max(undefined) and $max([]) return undefined
1104    if arg.is_undefined() || (arg.is_array() && arg.is_empty()) {
1105        return Ok(Value::undefined());
1106    }
1107
1108    let arr = Value::wrap_in_array_if_needed(context.arena, arg, ArrayFlags::empty());
1109
1110    let mut max = f64::MIN;
1111
1112    for member in arr.members() {
1113        assert_array_of_type!(member.is_number(), context, 1, "number");
1114        max = f64::max(max, member.as_f64());
1115    }
1116    Ok(Value::number(context.arena, max))
1117}
1118
1119pub fn fn_min<'a>(
1120    context: FunctionContext<'a, '_>,
1121    args: &[&'a Value<'a>],
1122) -> Result<&'a Value<'a>> {
1123    max_args!(context, args, 1);
1124
1125    let arg = args.first().copied().unwrap_or_else(Value::undefined);
1126
1127    // $min(undefined) and $min([]) return undefined
1128    if arg.is_undefined() || (arg.is_array() && arg.is_empty()) {
1129        return Ok(Value::undefined());
1130    }
1131
1132    let arr = Value::wrap_in_array_if_needed(context.arena, arg, ArrayFlags::empty());
1133
1134    let mut min = f64::MAX;
1135
1136    for member in arr.members() {
1137        assert_array_of_type!(member.is_number(), context, 1, "number");
1138        min = f64::min(min, member.as_f64());
1139    }
1140    Ok(Value::number(context.arena, min))
1141}
1142
1143pub fn fn_sum<'a>(
1144    context: FunctionContext<'a, '_>,
1145    args: &[&'a Value<'a>],
1146) -> Result<&'a Value<'a>> {
1147    max_args!(context, args, 1);
1148
1149    let arg = args.first().copied().unwrap_or_else(Value::undefined);
1150
1151    // $sum(undefined) returns undefined
1152    if arg.is_undefined() {
1153        return Ok(Value::undefined());
1154    }
1155
1156    let arr = Value::wrap_in_array_if_needed(context.arena, arg, ArrayFlags::empty());
1157
1158    let mut sum = 0.0;
1159
1160    for member in arr.members() {
1161        assert_array_of_type!(member.is_number(), context, 1, "number");
1162        sum += member.as_f64();
1163    }
1164    Ok(Value::number(context.arena, sum))
1165}
1166
1167pub fn fn_number<'a>(
1168    context: FunctionContext<'a, '_>,
1169    args: &[&'a Value<'a>],
1170) -> Result<&'a Value<'a>> {
1171    max_args!(context, args, 1);
1172
1173    let arg = args.first().copied().unwrap_or_else(Value::undefined);
1174
1175    match arg {
1176        Value::Undefined => Ok(Value::undefined()),
1177        Value::Number(..) => Ok(arg),
1178        Value::Bool(true) => Ok(Value::number(context.arena, 1)),
1179        Value::Bool(false) => Ok(Value::number(context.arena, 0)),
1180        Value::String(s) => {
1181            let result: f64 = s
1182                .parse()
1183                .map_err(|_e| Error::D3030NonNumericCast(context.char_index, arg.to_string()))?;
1184
1185            if !result.is_nan() && !result.is_infinite() {
1186                Ok(Value::number(context.arena, result))
1187            } else {
1188                Ok(Value::undefined())
1189            }
1190        }
1191        _ => bad_arg!(context, 1),
1192    }
1193}
1194
1195pub fn fn_random<'a>(
1196    context: FunctionContext<'a, '_>,
1197    args: &[&'a Value<'a>],
1198) -> Result<&'a Value<'a>> {
1199    max_args!(context, args, 0);
1200
1201    let v: f32 = rand::thread_rng().gen();
1202    Ok(Value::number(context.arena, v))
1203}
1204
1205pub fn fn_now<'a>(
1206    context: FunctionContext<'a, '_>,
1207    args: &[&'a Value<'a>],
1208) -> Result<&'a Value<'a>> {
1209    max_args!(context, args, 2);
1210
1211    let now = Utc::now();
1212
1213    let (picture, timezone) = match args {
1214        [picture, timezone] => (picture.as_str(), timezone.as_str()),
1215        [picture] => (picture.as_str(), Cow::Borrowed("")),
1216        [] => (Cow::Borrowed(""), Cow::Borrowed("")),
1217        _ => return Ok(Value::string(context.arena, "")),
1218    };
1219
1220    if picture.is_empty() && timezone.is_empty() {
1221        return Ok(Value::string(
1222            context.arena,
1223            &now.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
1224        ));
1225    }
1226
1227    let adjusted_time = if !timezone.is_empty() {
1228        parse_timezone_offset(&timezone)
1229            .map(|offset| now.with_timezone(&offset))
1230            .ok_or_else(|| Error::T0410ArgumentNotValid(2, 1, context.name.to_string()))?
1231    } else {
1232        now.into()
1233    };
1234
1235    // If a valid picture is provided, format the time accordingly
1236    if !picture.is_empty() {
1237        // Handle the Result<String, Error> from format_custom_date
1238        let formatted_date = format_custom_date(&adjusted_time, &picture)?;
1239        return Ok(Value::string(context.arena, &formatted_date));
1240    }
1241
1242    // Return an empty string if the picture is empty but a valid timezone is provided
1243    Ok(Value::string(context.arena, ""))
1244}
1245
1246pub fn fn_exists<'a>(
1247    context: FunctionContext<'a, '_>,
1248    args: &[&'a Value<'a>],
1249) -> Result<&'a Value<'a>> {
1250    min_args!(context, args, 1);
1251    max_args!(context, args, 1);
1252
1253    let arg = args.first().copied().unwrap_or_else(Value::undefined);
1254
1255    match arg {
1256        Value::Undefined => Ok(Value::bool(false)),
1257        _ => Ok(Value::bool(true)),
1258    }
1259}
1260
1261pub fn from_millis<'a>(
1262    context: FunctionContext<'a, '_>,
1263    args: &[&'a Value<'a>],
1264) -> Result<&'a Value<'a>> {
1265    let arr = args.first().copied().unwrap_or_else(Value::undefined);
1266
1267    if arr.is_undefined() {
1268        return Ok(Value::undefined());
1269    }
1270
1271    max_args!(context, args, 3);
1272    assert_arg!(args[0].is_number(), context, 1);
1273
1274    let millis = args[0].as_f64() as i64;
1275
1276    let Some(timestamp) = Utc.timestamp_millis_opt(millis).single() else {
1277        bad_arg!(context, 1);
1278    };
1279
1280    let (picture, timezone) = match args {
1281        [_, picture, timezone] if picture.is_undefined() => {
1282            assert_arg!(timezone.is_string(), context, 3);
1283            (Cow::Borrowed(""), timezone.as_str())
1284        }
1285        [_, picture, timezone] => {
1286            assert_arg!(picture.is_string(), context, 2);
1287            assert_arg!(timezone.is_string(), context, 3);
1288            (picture.as_str(), timezone.as_str())
1289        }
1290        [_, picture] => {
1291            assert_arg!(picture.is_string(), context, 2);
1292            (picture.as_str(), Cow::Borrowed(""))
1293        }
1294        _ => (Cow::Borrowed(""), Cow::Borrowed("")),
1295    };
1296
1297    // Handle default case: ISO 8601 format in UTC
1298    if picture.is_empty() && timezone.is_empty() {
1299        return Ok(Value::string(
1300            context.arena,
1301            &timestamp.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
1302        ));
1303    }
1304
1305    // Check for balanced brackets in the picture string
1306    if let Err(err) = check_balanced_brackets(&picture) {
1307        return Err(Error::D3135PictureStringNoClosingBracketError(err));
1308    }
1309
1310    let adjusted_time = if !timezone.is_empty() {
1311        parse_timezone_offset(&timezone)
1312            .map(|offset| timestamp.with_timezone(&offset))
1313            .ok_or_else(|| Error::T0410ArgumentNotValid(0, 1, context.name.to_string()))?
1314    } else {
1315        timestamp.into()
1316    };
1317
1318    // If a picture is provided, format the timestamp accordingly
1319    if !picture.is_empty() {
1320        // Call format_custom_date and handle its result
1321        let formatted_result = format_custom_date(&adjusted_time, &picture)?;
1322
1323        return Ok(Value::string(context.arena, &formatted_result));
1324    }
1325
1326    // Return ISO 8601 if only timezone is provided
1327    Ok(Value::string(
1328        context.arena,
1329        &adjusted_time.to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
1330    ))
1331}
1332
1333pub fn fn_millis<'a>(
1334    context: FunctionContext<'a, '_>,
1335    args: &[&'a Value<'a>],
1336) -> Result<&'a Value<'a>> {
1337    max_args!(context, args, 0);
1338
1339    let timestamp = SystemTime::now()
1340        .duration_since(UNIX_EPOCH)
1341        .expect("Time went backwards");
1342
1343    Ok(Value::number_from_u128(
1344        context.arena,
1345        timestamp.as_millis(),
1346    )?)
1347}
1348
1349pub fn fn_uuid<'a>(
1350    context: FunctionContext<'a, '_>,
1351    args: &[&'a Value<'a>],
1352) -> Result<&'a Value<'a>> {
1353    max_args!(context, args, 0);
1354
1355    Ok(Value::string(
1356        context.arena,
1357        Uuid::new_v4().to_string().as_str(),
1358    ))
1359}
1360
1361pub fn to_millis<'a>(
1362    context: FunctionContext<'a, '_>,
1363    args: &[&'a Value<'a>],
1364) -> Result<&'a Value<'a>> {
1365    let arr: &Value<'a> = args.first().copied().unwrap_or_else(Value::undefined);
1366
1367    if arr.is_undefined() {
1368        return Ok(Value::undefined());
1369    }
1370
1371    max_args!(context, args, 2);
1372    assert_arg!(args[0].is_string(), context, 1);
1373
1374    // Extract the timestamp string
1375    let timestamp_str = args[0].as_str();
1376    if timestamp_str.is_empty() {
1377        return Ok(Value::undefined());
1378    }
1379
1380    // Extract the optional picture string
1381    let picture = match args {
1382        [_, picture] if picture.is_undefined() => Cow::Borrowed(""),
1383        [_, picture] => {
1384            assert_arg!(picture.is_string(), context, 2);
1385            picture.as_str()
1386        }
1387        _ => Cow::Borrowed(""),
1388    };
1389
1390    // Handle different formats using a match handler function
1391    match parse_custom_format(&timestamp_str, &picture) {
1392        Some(millis) => Ok(Value::number(context.arena, millis as f64)),
1393        None => Ok(Value::undefined()),
1394    }
1395}
1396
1397pub fn fn_zip<'a>(
1398    context: FunctionContext<'a, '_>,
1399    args: &[&'a Value<'a>],
1400) -> Result<&'a Value<'a>> {
1401    // Check for null or undefined values in the arguments
1402    if args.iter().any(|arg| arg.is_null() || arg.is_undefined()) {
1403        return Ok(Value::array(context.arena, ArrayFlags::empty()));
1404    }
1405
1406    let arrays: Vec<&bumpalo::collections::Vec<'a, &'a Value<'a>>> = args
1407        .iter()
1408        .filter_map(|arg| match *arg {
1409            Value::Array(arr, _) => Some(arr),
1410            _ => None,
1411        })
1412        .collect();
1413
1414    if arrays.is_empty() {
1415        let result: bumpalo::collections::Vec<&Value<'a>> =
1416            args.iter().copied().collect_in(context.arena);
1417
1418        let outer_array =
1419            Value::array_from(context.arena, result, ArrayFlags::empty()) as &Value<'a>;
1420
1421        let outer_array_alloc: bumpalo::collections::Vec<&Value<'a>> =
1422            bumpalo::vec![in context.arena; outer_array];
1423
1424        return Ok(Value::array_from(
1425            context.arena,
1426            outer_array_alloc,
1427            ArrayFlags::empty(),
1428        ));
1429    }
1430
1431    let min_length = arrays.iter().map(|arr| arr.len()).min().unwrap_or(0);
1432    let mut iterators: Vec<_> = arrays
1433        .iter()
1434        .map(|arr| arr.iter().take(min_length))
1435        .collect();
1436
1437    // Use an iterator of zipping all the array iterators and collect the result in bumpalo
1438    let result: bumpalo::collections::Vec<&Value<'a>> = std::iter::repeat(())
1439        .take(min_length)
1440        .map(|_| {
1441            let zipped: bumpalo::collections::Vec<&Value<'a>> = iterators
1442                .iter_mut()
1443                .map(|it| *it.next().unwrap()) // Dereference here to get `&Value<'a>`
1444                .collect_in(context.arena);
1445
1446            // Allocate the zipped tuple as a new array in the bumpalo arena
1447            context
1448                .arena
1449                .alloc(Value::Array(zipped, ArrayFlags::empty())) as &Value<'a>
1450        })
1451        .collect_in(context.arena);
1452
1453    // Return the final result array created from the zipped arrays
1454    Ok(Value::array_from(
1455        context.arena,
1456        result,
1457        ArrayFlags::empty(),
1458    ))
1459}
1460
1461pub fn single<'a>(
1462    context: FunctionContext<'a, '_>,
1463    args: &[&'a Value<'a>],
1464) -> Result<&'a Value<'a>> {
1465    max_args!(context, args, 2);
1466
1467    let arr: &Value<'a> = args.first().copied().unwrap_or_else(Value::undefined);
1468    if arr.is_undefined() {
1469        return Ok(Value::undefined());
1470    }
1471
1472    let func = args
1473        .get(1)
1474        .filter(|f| f.is_function())
1475        .copied()
1476        .unwrap_or_else(|| {
1477            // Default function that always returns true
1478            context
1479                .arena
1480                .alloc(Value::nativefn(context.arena, "default_true", 1, |_, _| {
1481                    Ok(&Value::Bool(true))
1482                }))
1483        });
1484
1485    if !arr.is_array() {
1486        let res = context.evaluate_function(func, &[arr])?;
1487        return if res.as_bool() {
1488            Ok(arr)
1489        } else {
1490            Err(Error::D3139Error(
1491                "No value matched the predicate.".to_string(),
1492            ))
1493        };
1494    }
1495
1496    if let Value::Array(elements, _) = arr {
1497        let mut result: Option<&'a Value<'a>> = None;
1498
1499        for (index, entry) in elements.iter().enumerate() {
1500            let res = context.evaluate_function(
1501                func,
1502                &[entry, Value::number(context.arena, index as f64), arr],
1503            )?;
1504
1505            if res.as_bool() {
1506                if result.is_some() {
1507                    return Err(Error::D3138Error(format!(
1508                        "More than one value matched the predicate at index {}",
1509                        index
1510                    )));
1511                } else {
1512                    result = Some(entry);
1513                }
1514            }
1515        }
1516
1517        result.ok_or_else(|| Error::D3139Error("No values matched the predicate.".to_string()))
1518    } else {
1519        Err(Error::T0410ArgumentNotValid(0, 2, context.name.to_string()))
1520    }
1521}
1522
1523pub fn fn_assert<'a>(
1524    context: FunctionContext<'a, '_>,
1525    args: &[&'a Value<'a>],
1526) -> Result<&'a Value<'a>> {
1527    let condition = args.first().copied().unwrap_or_else(Value::undefined);
1528    let message = args.get(1).copied().unwrap_or_else(Value::undefined);
1529
1530    assert_arg!(condition.is_bool(), context, 1);
1531
1532    if let Value::Bool(false) = condition {
1533        Err(Error::D3141Assert(if message.is_string() {
1534            message.as_str().to_string()
1535        } else {
1536            "$assert() statement failed".to_string()
1537        }))
1538    } else {
1539        Ok(Value::undefined())
1540    }
1541}
1542
1543pub fn fn_error<'a>(
1544    context: FunctionContext<'a, '_>,
1545    args: &[&'a Value<'a>],
1546) -> Result<&'a Value<'a>> {
1547    let message = args.first().copied().unwrap_or_else(Value::undefined);
1548
1549    assert_arg!(message.is_undefined() || message.is_string(), context, 1);
1550
1551    Err(Error::D3137Error(if message.is_string() {
1552        message.as_str().to_string()
1553    } else {
1554        "$error() function evaluated".to_string()
1555    }))
1556}
1557
1558pub fn fn_length<'a>(
1559    context: FunctionContext<'a, '_>,
1560    args: &[&'a Value<'a>],
1561) -> Result<&'a Value<'a>> {
1562    max_args!(context, args, 1);
1563
1564    let arg1 = args.first().copied().unwrap_or_else(Value::undefined);
1565
1566    if arg1.is_undefined() {
1567        return Ok(Value::undefined());
1568    }
1569
1570    assert_arg!(arg1.is_string(), context, 1);
1571
1572    Ok(Value::number(
1573        context.arena,
1574        arg1.as_str().chars().count() as f64,
1575    ))
1576}
1577
1578pub fn fn_sqrt<'a>(
1579    context: FunctionContext<'a, '_>,
1580    args: &[&'a Value<'a>],
1581) -> Result<&'a Value<'a>> {
1582    max_args!(context, args, 1);
1583    let arg1 = args.first().copied().unwrap_or_else(Value::undefined);
1584
1585    if arg1.is_undefined() {
1586        return Ok(Value::undefined());
1587    }
1588
1589    assert_arg!(arg1.is_number(), context, 1);
1590
1591    let n = arg1.as_f64();
1592    if n.is_sign_negative() {
1593        Err(Error::D3060SqrtNegative(context.char_index, n.to_string()))
1594    } else {
1595        Ok(Value::number(context.arena, n.sqrt()))
1596    }
1597}
1598
1599pub fn fn_power<'a>(
1600    context: FunctionContext<'a, '_>,
1601    args: &[&'a Value<'a>],
1602) -> Result<&'a Value<'a>> {
1603    max_args!(context, args, 2);
1604
1605    let number = args.first().copied().unwrap_or_else(Value::undefined);
1606    let exp = args.get(1).copied().unwrap_or_else(Value::undefined);
1607
1608    if number.is_undefined() {
1609        return Ok(Value::undefined());
1610    }
1611
1612    assert_arg!(number.is_number(), context, 1);
1613    assert_arg!(exp.is_number(), context, 2);
1614
1615    let result = number.as_f64().powf(exp.as_f64());
1616
1617    if !result.is_finite() {
1618        Err(Error::D3061PowUnrepresentable(
1619            context.char_index,
1620            number.to_string(),
1621            exp.to_string(),
1622        ))
1623    } else {
1624        Ok(Value::number(context.arena, result))
1625    }
1626}
1627
1628pub fn fn_reverse<'a>(
1629    context: FunctionContext<'a, '_>,
1630    args: &[&'a Value<'a>],
1631) -> Result<&'a Value<'a>> {
1632    max_args!(context, args, 1);
1633
1634    let arr = args.first().copied().unwrap_or_else(Value::undefined);
1635
1636    if arr.is_undefined() {
1637        return Ok(Value::undefined());
1638    }
1639
1640    assert_arg!(arr.is_array(), context, 1);
1641
1642    let result = Value::array_with_capacity(context.arena, arr.len(), ArrayFlags::empty());
1643    arr.members().rev().for_each(|member| result.push(member));
1644    Ok(result)
1645}
1646
1647#[allow(clippy::mutable_key_type)]
1648pub fn fn_distinct<'a>(
1649    context: FunctionContext<'a, '_>,
1650    args: &[&'a Value<'a>],
1651) -> Result<&'a Value<'a>> {
1652    max_args!(context, args, 1);
1653
1654    let arr = args.first().copied().unwrap_or_else(Value::undefined);
1655    if !arr.is_array() || arr.len() <= 1 {
1656        return Ok(arr);
1657    }
1658
1659    let result = Value::array_with_capacity(context.arena, arr.len(), ArrayFlags::empty());
1660    let mut set = HashSet::new();
1661    for member in arr.members() {
1662        if set.contains(member) {
1663            continue;
1664        }
1665        set.insert(member);
1666        result.push(member);
1667    }
1668
1669    Ok(result)
1670}
1671
1672pub fn fn_join<'a>(
1673    context: FunctionContext<'a, '_>,
1674    args: &[&'a Value<'a>],
1675) -> Result<&'a Value<'a>> {
1676    max_args!(context, args, 2);
1677    let strings = args.first().copied().unwrap_or_else(Value::undefined);
1678
1679    if strings.is_undefined() {
1680        return Ok(Value::undefined());
1681    }
1682
1683    if strings.is_string() {
1684        return Ok(strings);
1685    }
1686
1687    assert_array_of_type!(strings.is_array(), context, 1, "string");
1688
1689    let separator = args.get(1).copied().unwrap_or_else(Value::undefined);
1690    assert_arg!(
1691        separator.is_undefined() || separator.is_string(),
1692        context,
1693        2
1694    );
1695
1696    let separator = if separator.is_string() {
1697        separator.as_str()
1698    } else {
1699        "".into()
1700    };
1701
1702    let mut result = String::with_capacity(1024);
1703    for (index, member) in strings.members().enumerate() {
1704        assert_array_of_type!(member.is_string(), context, 1, "string");
1705        result.push_str(member.as_str().borrow());
1706        if index != strings.len() - 1 {
1707            result.push_str(&separator);
1708        }
1709    }
1710
1711    Ok(Value::string(context.arena, &result))
1712}
1713
1714pub fn fn_sort<'a, 'e>(
1715    context: FunctionContext<'a, 'e>,
1716    args: &[&'a Value<'a>],
1717) -> Result<&'a Value<'a>> {
1718    max_args!(context, args, 2);
1719
1720    let arr = args.first().copied().unwrap_or_else(Value::undefined);
1721
1722    if arr.is_undefined() {
1723        return Ok(Value::undefined());
1724    }
1725
1726    if !arr.is_array() || arr.len() <= 1 {
1727        return Ok(Value::wrap_in_array_if_needed(
1728            context.arena,
1729            arr,
1730            ArrayFlags::empty(),
1731        ));
1732    }
1733
1734    // TODO: This is all a bit inefficient, copying Vecs of references around, but
1735    // at least it's just references.
1736
1737    let unsorted = arr.members().collect::<Vec<&'a Value<'a>>>();
1738    let sorted = if args.get(1).is_none() {
1739        merge_sort(
1740            unsorted,
1741            &|a: &'a Value<'a>, b: &'a Value<'a>| match (a, b) {
1742                (Value::Number(a), Value::Number(b)) => Ok(a > b),
1743                (Value::String(a), Value::String(b)) => Ok(a > b),
1744                _ => Err(Error::D3070InvalidDefaultSort(context.char_index)),
1745            },
1746        )?
1747    } else {
1748        let comparator = args.get(1).copied().unwrap_or_else(Value::undefined);
1749        assert_arg!(comparator.is_function(), context, 2);
1750        merge_sort(unsorted, &|a: &'a Value<'a>, b: &'a Value<'a>| {
1751            let result = context.evaluate_function(comparator, &[a, b])?;
1752            Ok(result.is_truthy())
1753        })?
1754    };
1755
1756    let result = Value::array_with_capacity(context.arena, sorted.len(), arr.get_flags());
1757    sorted.iter().for_each(|member| result.push(member));
1758
1759    Ok(result)
1760}
1761
1762pub fn merge_sort<'a, F>(items: Vec<&'a Value<'a>>, comp: &F) -> Result<Vec<&'a Value<'a>>>
1763where
1764    F: Fn(&'a Value<'a>, &'a Value<'a>) -> Result<bool>,
1765{
1766    fn merge_iter<'a, F>(
1767        result: &mut Vec<&'a Value<'a>>,
1768        left: &[&'a Value<'a>],
1769        right: &[&'a Value<'a>],
1770        comp: &F,
1771    ) -> Result<()>
1772    where
1773        F: Fn(&'a Value<'a>, &'a Value<'a>) -> Result<bool>,
1774    {
1775        if left.is_empty() {
1776            result.extend(right);
1777            Ok(())
1778        } else if right.is_empty() {
1779            result.extend(left);
1780            Ok(())
1781        } else if comp(left[0], right[0])? {
1782            result.push(right[0]);
1783            merge_iter(result, left, &right[1..], comp)
1784        } else {
1785            result.push(left[0]);
1786            merge_iter(result, &left[1..], right, comp)
1787        }
1788    }
1789
1790    fn merge<'a, F>(
1791        left: &[&'a Value<'a>],
1792        right: &[&'a Value<'a>],
1793        comp: &F,
1794    ) -> Result<Vec<&'a Value<'a>>>
1795    where
1796        F: Fn(&'a Value<'a>, &'a Value<'a>) -> Result<bool>,
1797    {
1798        let mut merged = Vec::with_capacity(left.len() + right.len());
1799        merge_iter(&mut merged, left, right, comp)?;
1800        Ok(merged)
1801    }
1802
1803    if items.len() <= 1 {
1804        return Ok(items);
1805    }
1806    let middle = (items.len() as f64 / 2.0).floor() as usize;
1807    let (left, right) = items.split_at(middle);
1808    let left = merge_sort(left.to_vec(), comp)?;
1809    let right = merge_sort(right.to_vec(), comp)?;
1810    merge(&left, &right, comp)
1811}
1812
1813pub fn fn_base64_encode<'a>(
1814    context: FunctionContext<'a, '_>,
1815    args: &[&'a Value<'a>],
1816) -> Result<&'a Value<'a>> {
1817    max_args!(context, args, 1);
1818    let arg = args.first().copied().unwrap_or_else(Value::undefined);
1819    if arg.is_undefined() {
1820        return Ok(Value::undefined());
1821    }
1822    assert_arg!(arg.is_string(), context, 1);
1823
1824    let base64 = base64::engine::general_purpose::STANDARD;
1825
1826    let encoded = base64.encode(arg.as_str().as_bytes());
1827
1828    Ok(Value::string(context.arena, &encoded))
1829}
1830
1831pub fn fn_base64_decode<'a>(
1832    context: FunctionContext<'a, '_>,
1833    args: &[&'a Value<'a>],
1834) -> Result<&'a Value<'a>> {
1835    max_args!(context, args, 1);
1836    let arg = args.first().copied().unwrap_or_else(Value::undefined);
1837    if arg.is_undefined() {
1838        return Ok(Value::undefined());
1839    }
1840    assert_arg!(arg.is_string(), context, 1);
1841
1842    let base64 = base64::engine::general_purpose::STANDARD;
1843
1844    let decoded = base64.decode(arg.as_str().as_bytes());
1845    let data = decoded.map_err(|e| Error::D3137Error(e.to_string()))?;
1846    let decoded = String::from_utf8(data).map_err(|e| Error::D3137Error(e.to_string()))?;
1847
1848    Ok(Value::string(context.arena, &decoded))
1849}
1850
1851pub fn fn_round<'a>(
1852    context: FunctionContext<'a, '_>,
1853    args: &[&'a Value<'a>],
1854) -> Result<&'a Value<'a>> {
1855    max_args!(context, args, 2);
1856    let number = &args[0];
1857    if number.is_undefined() {
1858        return Ok(Value::undefined());
1859    }
1860    assert_arg!(number.is_number(), context, 1);
1861
1862    let precision = if let Some(precision) = args.get(1) {
1863        assert_arg!(precision.is_integer(), context, 2);
1864        precision.as_isize()
1865    } else {
1866        0
1867    };
1868
1869    let num = multiply_by_pow10(number.as_f64(), precision)?;
1870    let num = num.round_ties_even();
1871    let num = multiply_by_pow10(num, -precision)?;
1872
1873    Ok(Value::number(context.arena, num))
1874}
1875
1876fn is_array_of_strings(value: &Value) -> bool {
1877    if let Value::Array(elements, _) = value {
1878        elements.iter().all(|v| v.is_string())
1879    } else {
1880        false
1881    }
1882}
1883
1884pub fn fn_reduce<'a>(
1885    context: FunctionContext<'a, '_>,
1886    args: &[&'a Value<'a>],
1887) -> Result<&'a Value<'a>> {
1888    max_args!(context, args, 3);
1889
1890    if args.len() < 2 {
1891        return Err(Error::T0410ArgumentNotValid(0, 2, context.name.to_string()));
1892    }
1893
1894    let original_value = args[0];
1895    let func = args[1];
1896    let init = args.get(2).copied();
1897
1898    if func.is_function() && func.arity() < 2 {
1899        return Err(Error::D3050SecondArguement(context.name.to_string()));
1900    }
1901
1902    if !original_value.is_array() {
1903        if original_value.is_number() {
1904            return Ok(original_value);
1905        }
1906
1907        if original_value.is_string() {
1908            return Ok(original_value);
1909        }
1910
1911        return Ok(Value::undefined());
1912    }
1913
1914    let (elements, _extra_field) = match original_value {
1915        Value::Array(elems, extra) => (elems, extra),
1916        _ => return Err(Error::D3050SecondArguement(context.name.to_string())),
1917    };
1918
1919    if elements.is_empty() {
1920        return Ok(init.unwrap_or_else(|| Value::undefined()));
1921    }
1922
1923    if !func.is_function() {
1924        return Err(Error::T0410ArgumentNotValid(1, 1, context.name.to_string()));
1925    }
1926
1927    let mut accumulator = init.unwrap_or_else(|| elements[0]);
1928
1929    let has_init_value = init.is_some();
1930    let is_non_single_array_of_strings = is_array_of_strings(original_value) && elements.len() > 1;
1931
1932    let start_index = if has_init_value || is_non_single_array_of_strings {
1933        0
1934    } else {
1935        1
1936    };
1937
1938    for (index, value) in elements[start_index..].iter().enumerate() {
1939        let index_value = Value::number(context.arena, index as f64);
1940
1941        let result =
1942            context.evaluate_function(func, &[accumulator, value, index_value, original_value]);
1943
1944        match result {
1945            Ok(new_accumulator) => {
1946                // If the result is a thunk, let's evaluate it so it's ready for the next iteration
1947                accumulator = context.trampoline_evaluate_value(new_accumulator)?;
1948            }
1949            Err(_) => {
1950                return Err(Error::T0410ArgumentNotValid(1, 1, context.name.to_string()));
1951            }
1952        }
1953    }
1954
1955    Ok(accumulator)
1956}
1957
1958// We need to do this multiplication by powers of 10 in a string to avoid
1959// floating point precision errors which will affect the rounding algorithm
1960fn multiply_by_pow10(num: f64, pow: isize) -> Result<f64> {
1961    let num_str = format!("{}e{}", num, pow);
1962    num_str
1963        .parse::<f64>()
1964        .map_err(|e| Error::D3137Error(e.to_string()))
1965}
1966
1967pub fn fn_pad<'a>(
1968    context: FunctionContext<'a, '_>,
1969    args: &[&'a Value<'a>],
1970) -> Result<&'a Value<'a>> {
1971    let str_value = args.first().copied().unwrap_or_else(Value::undefined);
1972    if !str_value.is_string() {
1973        return Ok(Value::undefined());
1974    }
1975
1976    let width_value = args.get(1).copied().unwrap_or_else(Value::undefined);
1977    if !width_value.is_number() {
1978        return Ok(Value::undefined());
1979    }
1980
1981    let str_to_pad = str_value.as_str(); // as_str returns Cow<'_, str>
1982
1983    let width_i64 = width_value.as_f64().round() as i64;
1984    let width = width_i64.unsigned_abs() as usize;
1985    let is_right_padding = width_i64 > 0; // Positive width means right padding
1986
1987    let pad_char = args
1988        .get(2)
1989        .map(|v| v.as_str())
1990        .filter(|c| !c.is_empty())
1991        .unwrap_or(Cow::Borrowed(" "));
1992
1993    let pad_length = width.saturating_sub(str_to_pad.chars().count());
1994
1995    // Early return if no padding is needed
1996    if pad_length == 0 {
1997        return Ok(Value::string(context.arena, &str_to_pad));
1998    }
1999
2000    let padding = pad_char
2001        .chars()
2002        .cycle()
2003        .take(pad_length)
2004        .collect::<String>();
2005
2006    // Depending on whether it's right or left padding, append or prepend the padding
2007    let result = if is_right_padding {
2008        format!("{}{}", str_to_pad, padding)
2009    } else {
2010        format!("{}{}", padding, str_to_pad)
2011    };
2012
2013    Ok(Value::string(context.arena, &result))
2014}
2015
2016pub fn fn_match<'a>(
2017    context: FunctionContext<'a, '_>,
2018    args: &[&'a Value<'a>],
2019) -> Result<&'a Value<'a>> {
2020    let value_to_validate = match args.first().copied() {
2021        Some(val) if !val.is_undefined() => val,
2022        _ => return Ok(Value::undefined()),
2023    };
2024    assert_arg!(value_to_validate.is_string(), context, 1);
2025
2026    let pattern_value = match args.get(1).copied() {
2027        Some(val) => val,
2028        _ => return Err(Error::D3010EmptyPattern(context.char_index)),
2029    };
2030
2031    let regex_literal = match pattern_value {
2032        Value::Regex(ref regex_literal) => regex_literal,
2033        Value::String(ref s) => {
2034            let regex = RegexLiteral::new(s.as_str(), false, false)
2035                .map_err(|_| Error::D3010EmptyPattern(context.char_index))?;
2036            &*context.arena.alloc(regex)
2037        }
2038        _ => return Err(Error::D3010EmptyPattern(context.char_index)),
2039    };
2040
2041    let limit = args.get(2).and_then(|val| {
2042        if val.is_number() {
2043            Some(val.as_f64() as usize)
2044        } else {
2045            None
2046        }
2047    });
2048
2049    Ok(evaluate_match(
2050        context.arena,
2051        regex_literal.get_regex(),
2052        &value_to_validate.as_str(),
2053        limit,
2054    ))
2055}
2056
2057/// An inner helper which evaluates the `$match` function.
2058///
2059/// The return value is a Value::Array which looks like:
2060///
2061/// [
2062///   {
2063///     "match": "ab",
2064///     "index": 0,
2065///     "groups": ["b"]
2066///   },
2067///   {
2068///     "match": "abb",
2069///     "index": 2,
2070///     "groups": ["bb"]
2071///   },
2072///   {
2073///     "match": "abb",
2074///     "index": 5,
2075///     "groups": ["bb" ]
2076///   }
2077/// ]
2078fn evaluate_match<'a>(
2079    arena: &'a Bump,
2080    regex: &Regex,
2081    input_str: &str,
2082    limit: Option<usize>,
2083) -> &'a Value<'a> {
2084    let limit = limit.unwrap_or(usize::MAX);
2085
2086    let key_match = BumpString::from_str_in("match", arena);
2087    let key_index = BumpString::from_str_in("index", arena);
2088    let key_groups = BumpString::from_str_in("groups", arena);
2089
2090    let mut matches: bumpalo::collections::Vec<&Value<'a>> =
2091        bumpalo::collections::Vec::new_in(arena);
2092
2093    for (i, m) in regex.find_iter(input_str).enumerate() {
2094        if i >= limit {
2095            break;
2096        }
2097
2098        let matched_text = &input_str[m.start()..m.end()];
2099        let match_str = arena.alloc(Value::string(arena, matched_text));
2100
2101        let index_val = arena.alloc(Value::number(arena, i as f64));
2102
2103        // Extract capture groups as values
2104        let capture_groups = m
2105            .groups()
2106            .filter_map(|group| group.map(|range| &input_str[range.start..range.end]))
2107            .map(|s| BumpString::from_str_in(s, arena))
2108            .map(|s| &*arena.alloc(Value::String(s)))
2109            // Skip the first group which is the entire match
2110            .skip(1);
2111
2112        let group_vec = BumpVec::from_iter_in(capture_groups, arena);
2113
2114        let groups_val = arena.alloc(Value::Array(group_vec, ArrayFlags::empty()));
2115
2116        let mut match_obj: HashMap<BumpString, &Value<'a>, DefaultHashBuilder, &Bump> =
2117            HashMap::with_capacity_and_hasher_in(3, DefaultHashBuilder::default(), arena);
2118        match_obj.insert(key_match.clone(), match_str);
2119        match_obj.insert(key_index.clone(), index_val);
2120        match_obj.insert(key_groups.clone(), groups_val);
2121
2122        matches.push(arena.alloc(Value::Object(match_obj)));
2123    }
2124
2125    arena.alloc(Value::Array(matches, ArrayFlags::empty()))
2126}