Skip to main content

jpx_core/extensions/
array.rs

1//! Array manipulation functions.
2
3use std::collections::HashSet;
4
5use serde_json::{Number, Value};
6
7use crate::ast::Ast;
8use crate::functions::{Function, custom_error};
9use crate::interpreter::{SearchResult, interpret};
10use crate::registry::register_if_enabled;
11use crate::{Context, Runtime, arg, defn, get_expref_id};
12
13/// Helper to extract an expref AST from a function argument.
14fn get_expref_ast<'a>(value: &Value, ctx: &'a Context<'_>) -> Option<&'a Ast> {
15    get_expref_id(value).and_then(|id| ctx.get_expref(id))
16}
17
18/// Convert a Value to a string key for grouping/indexing.
19fn value_to_key(value: &Value) -> String {
20    match value {
21        Value::String(s) => s.clone(),
22        Value::Number(n) => n.to_string(),
23        Value::Bool(b) => b.to_string(),
24        Value::Null => "null".to_string(),
25        _ => serde_json::to_string(value).unwrap_or_default(),
26    }
27}
28
29/// Extract a key from an item using either an expref or a string field name.
30/// Returns `None` if the item is not an object (for string field mode) or the
31/// field is missing and we should skip the item.
32fn extract_key(
33    item: &Value,
34    second_arg: &Value,
35    ctx: &mut Context<'_>,
36) -> Result<Option<String>, crate::error::JmespathError> {
37    if let Some(ast) = get_expref_ast(second_arg, ctx) {
38        let ast = ast.clone();
39        let key_val = interpret(item, &ast, ctx)?;
40        Ok(Some(value_to_key(&key_val)))
41    } else if let Some(field_name) = second_arg.as_str() {
42        if let Some(obj) = item.as_object() {
43            if let Some(field_value) = obj.get(field_name) {
44                Ok(Some(value_to_key(field_value)))
45            } else {
46                Ok(None) // field missing
47            }
48        } else {
49            Ok(None) // not an object
50        }
51    } else {
52        Err(custom_error(ctx, "Expected expref or string field name"))
53    }
54}
55
56/// Register only the array functions that are in the enabled set.
57pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
58    register_if_enabled(runtime, "unique", enabled, Box::new(UniqueFn::new()));
59    register_if_enabled(runtime, "zip", enabled, Box::new(ZipFn::new()));
60    register_if_enabled(runtime, "chunk", enabled, Box::new(ChunkFn::new()));
61    register_if_enabled(runtime, "take", enabled, Box::new(TakeFn::new()));
62    register_if_enabled(runtime, "drop", enabled, Box::new(DropFn::new()));
63    register_if_enabled(
64        runtime,
65        "flatten_deep",
66        enabled,
67        Box::new(FlattenDeepFn::new()),
68    );
69    register_if_enabled(runtime, "flatten", enabled, Box::new(FlattenFn::new()));
70    register_if_enabled(runtime, "compact", enabled, Box::new(CompactFn::new()));
71    register_if_enabled(runtime, "range", enabled, Box::new(RangeFn::new()));
72    register_if_enabled(runtime, "index_at", enabled, Box::new(IndexAtFn::new()));
73    register_if_enabled(runtime, "includes", enabled, Box::new(IncludesFn::new()));
74    register_if_enabled(runtime, "find_index", enabled, Box::new(FindIndexFn::new()));
75    register_if_enabled(runtime, "first", enabled, Box::new(FirstFn::new()));
76    register_if_enabled(runtime, "last", enabled, Box::new(LastFn::new()));
77    register_if_enabled(runtime, "group_by", enabled, Box::new(GroupByFn::new()));
78    register_if_enabled(runtime, "index_by", enabled, Box::new(IndexByFn::new()));
79    register_if_enabled(runtime, "nth", enabled, Box::new(NthFn::new()));
80    register_if_enabled(
81        runtime,
82        "interleave",
83        enabled,
84        Box::new(InterleaveFn::new()),
85    );
86    register_if_enabled(runtime, "rotate", enabled, Box::new(RotateFn::new()));
87    register_if_enabled(runtime, "partition", enabled, Box::new(PartitionFn::new()));
88    register_if_enabled(
89        runtime,
90        "difference",
91        enabled,
92        Box::new(DifferenceFn::new()),
93    );
94    register_if_enabled(
95        runtime,
96        "intersection",
97        enabled,
98        Box::new(IntersectionFn::new()),
99    );
100    register_if_enabled(runtime, "union", enabled, Box::new(UnionFn::new()));
101    register_if_enabled(
102        runtime,
103        "frequencies",
104        enabled,
105        Box::new(FrequenciesFn::new()),
106    );
107    register_if_enabled(runtime, "cartesian", enabled, Box::new(CartesianFn::new()));
108    register_if_enabled(runtime, "initial", enabled, Box::new(InitialFn::new()));
109    // Alias for initial (Clojure-style name)
110    register_if_enabled(runtime, "butlast", enabled, Box::new(InitialFn::new()));
111    // Clojure-inspired functions
112    register_if_enabled(runtime, "interpose", enabled, Box::new(InterposeFn::new()));
113    register_if_enabled(runtime, "zipmap", enabled, Box::new(ZipmapFn::new()));
114    register_if_enabled(
115        runtime,
116        "partition_by",
117        enabled,
118        Box::new(PartitionByFn::new()),
119    );
120    register_if_enabled(runtime, "dedupe", enabled, Box::new(DedupeFn::new()));
121    register_if_enabled(runtime, "tail", enabled, Box::new(TailFn::new()));
122    register_if_enabled(runtime, "without", enabled, Box::new(WithoutFn::new()));
123    register_if_enabled(runtime, "xor", enabled, Box::new(XorFn::new()));
124    register_if_enabled(runtime, "fill", enabled, Box::new(FillFn::new()));
125    register_if_enabled(runtime, "pull_at", enabled, Box::new(PullAtFn::new()));
126    register_if_enabled(runtime, "window", enabled, Box::new(WindowFn::new()));
127    register_if_enabled(
128        runtime,
129        "combinations",
130        enabled,
131        Box::new(CombinationsFn::new()),
132    );
133    register_if_enabled(runtime, "transpose", enabled, Box::new(TransposeFn::new()));
134    register_if_enabled(runtime, "pairwise", enabled, Box::new(PairwiseFn::new()));
135    // Alias for window (sliding_window is a common name)
136    register_if_enabled(
137        runtime,
138        "sliding_window",
139        enabled,
140        Box::new(WindowFn::new()),
141    );
142    // jq-parity functions
143    register_if_enabled(
144        runtime,
145        "indices_array",
146        enabled,
147        Box::new(IndicesArrayFn::new()),
148    );
149    register_if_enabled(
150        runtime,
151        "inside_array",
152        enabled,
153        Box::new(InsideArrayFn::new()),
154    );
155    register_if_enabled(runtime, "bsearch", enabled, Box::new(BsearchFn::new()));
156    // Clojure-inspired functions (Phase 3)
157    register_if_enabled(
158        runtime,
159        "repeat_array",
160        enabled,
161        Box::new(RepeatArrayFn::new()),
162    );
163    register_if_enabled(runtime, "cycle", enabled, Box::new(CycleFn::new()));
164    register_if_enabled(runtime, "lag", enabled, Box::new(LagFn::new()));
165    register_if_enabled(runtime, "lead", enabled, Box::new(LeadFn::new()));
166}
167
168// =============================================================================
169// unique(array) -> array
170// =============================================================================
171
172defn!(UniqueFn, vec![arg!(array)], None);
173
174impl Function for UniqueFn {
175    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
176        self.signature.validate(args, ctx)?;
177
178        let arr = args[0]
179            .as_array()
180            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
181
182        let mut seen = HashSet::new();
183        let mut result = Vec::new();
184
185        for item in arr {
186            let key = serde_json::to_string(item).unwrap_or_default();
187            if seen.insert(key) {
188                result.push(item.clone());
189            }
190        }
191
192        Ok(Value::Array(result))
193    }
194}
195
196// =============================================================================
197// zip(array1, array2) -> array of pairs
198// =============================================================================
199
200defn!(ZipFn, vec![arg!(array), arg!(array)], None);
201
202impl Function for ZipFn {
203    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
204        self.signature.validate(args, ctx)?;
205
206        let arr1 = args[0]
207            .as_array()
208            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
209
210        let arr2 = args[1]
211            .as_array()
212            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
213
214        let result: Vec<Value> = arr1
215            .iter()
216            .zip(arr2.iter())
217            .map(|(a, b)| Value::Array(vec![a.clone(), b.clone()]))
218            .collect();
219
220        Ok(Value::Array(result))
221    }
222}
223
224// =============================================================================
225// chunk(array, size) -> array of arrays
226// =============================================================================
227
228defn!(ChunkFn, vec![arg!(array), arg!(number)], None);
229
230impl Function for ChunkFn {
231    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
232        self.signature.validate(args, ctx)?;
233
234        let arr = args[0]
235            .as_array()
236            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
237
238        let size = args[1]
239            .as_f64()
240            .map(|n| n as usize)
241            .ok_or_else(|| custom_error(ctx, "Expected positive number for size"))?;
242
243        if size == 0 {
244            return Ok(Value::Array(vec![]));
245        }
246
247        let chunks: Vec<Value> = arr
248            .chunks(size)
249            .map(|chunk| Value::Array(chunk.to_vec()))
250            .collect();
251
252        Ok(Value::Array(chunks))
253    }
254}
255
256// =============================================================================
257// take(array, n) -> array (first n elements)
258// =============================================================================
259
260defn!(TakeFn, vec![arg!(array), arg!(number)], None);
261
262impl Function for TakeFn {
263    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
264        self.signature.validate(args, ctx)?;
265
266        let arr = args[0]
267            .as_array()
268            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
269
270        let n = args[1]
271            .as_f64()
272            .map(|n| n as usize)
273            .ok_or_else(|| custom_error(ctx, "Expected positive number"))?;
274
275        let result: Vec<Value> = arr.iter().take(n).cloned().collect();
276
277        Ok(Value::Array(result))
278    }
279}
280
281// =============================================================================
282// drop(array, n) -> array (skip first n elements)
283// =============================================================================
284
285defn!(DropFn, vec![arg!(array), arg!(number)], None);
286
287impl Function for DropFn {
288    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
289        self.signature.validate(args, ctx)?;
290
291        let arr = args[0]
292            .as_array()
293            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
294
295        let n = args[1]
296            .as_f64()
297            .map(|n| n as usize)
298            .ok_or_else(|| custom_error(ctx, "Expected positive number"))?;
299
300        let result: Vec<Value> = arr.iter().skip(n).cloned().collect();
301
302        Ok(Value::Array(result))
303    }
304}
305
306// =============================================================================
307// flatten_deep(array) -> array (recursively flatten)
308// =============================================================================
309
310defn!(FlattenDeepFn, vec![arg!(array)], None);
311
312fn flatten_recursive(arr: &[Value]) -> Vec<Value> {
313    let mut result = Vec::new();
314    for item in arr {
315        if let Some(inner) = item.as_array() {
316            result.extend(flatten_recursive(inner));
317        } else {
318            result.push(item.clone());
319        }
320    }
321    result
322}
323
324impl Function for FlattenDeepFn {
325    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
326        self.signature.validate(args, ctx)?;
327
328        let arr = args[0]
329            .as_array()
330            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
331
332        Ok(Value::Array(flatten_recursive(arr)))
333    }
334}
335
336// =============================================================================
337// flatten(array) -> array (single-level flatten)
338// =============================================================================
339
340defn!(FlattenFn, vec![arg!(array)], None);
341
342impl Function for FlattenFn {
343    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
344        self.signature.validate(args, ctx)?;
345
346        let arr = args[0]
347            .as_array()
348            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
349
350        let mut result = Vec::new();
351        for item in arr {
352            if let Some(inner) = item.as_array() {
353                result.extend(inner.iter().cloned());
354            } else {
355                result.push(item.clone());
356            }
357        }
358
359        Ok(Value::Array(result))
360    }
361}
362
363// =============================================================================
364// compact(array) -> array (remove null/false values)
365// =============================================================================
366
367defn!(CompactFn, vec![arg!(array)], None);
368
369impl Function for CompactFn {
370    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
371        self.signature.validate(args, ctx)?;
372
373        let arr = args[0]
374            .as_array()
375            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
376
377        let result: Vec<Value> = arr
378            .iter()
379            .filter(|v| !v.is_null() && !matches!(v, Value::Bool(false)))
380            .cloned()
381            .collect();
382
383        Ok(Value::Array(result))
384    }
385}
386
387// =============================================================================
388// range(start, end, step?) -> array
389// =============================================================================
390
391defn!(
392    RangeFn,
393    vec![arg!(number), arg!(number)],
394    Some(arg!(number))
395);
396
397impl Function for RangeFn {
398    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
399        self.signature.validate(args, ctx)?;
400
401        let start = args[0]
402            .as_f64()
403            .map(|n| n as i64)
404            .ok_or_else(|| custom_error(ctx, "Expected start number"))?;
405
406        let end = args[1]
407            .as_f64()
408            .map(|n| n as i64)
409            .ok_or_else(|| custom_error(ctx, "Expected end number"))?;
410
411        let step = if args.len() > 2 {
412            args[2]
413                .as_f64()
414                .map(|n| n as i64)
415                .ok_or_else(|| custom_error(ctx, "Expected step number"))?
416        } else {
417            1
418        };
419
420        if step == 0 {
421            return Err(custom_error(ctx, "Step cannot be zero"));
422        }
423
424        let mut result = Vec::new();
425        let mut current = start;
426
427        const MAX_RANGE: usize = 10000;
428
429        if step > 0 {
430            while current < end && result.len() < MAX_RANGE {
431                result.push(Value::Number(Number::from(current)));
432                current += step;
433            }
434        } else {
435            while current > end && result.len() < MAX_RANGE {
436                result.push(Value::Number(Number::from(current)));
437                current += step;
438            }
439        }
440
441        Ok(Value::Array(result))
442    }
443}
444
445// =============================================================================
446// index_at(array, index) -> element (supports negative index)
447// =============================================================================
448
449defn!(IndexAtFn, vec![arg!(array), arg!(number)], None);
450
451impl Function for IndexAtFn {
452    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
453        self.signature.validate(args, ctx)?;
454
455        let arr = args[0]
456            .as_array()
457            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
458
459        let index = args[1]
460            .as_f64()
461            .map(|n| n as i64)
462            .ok_or_else(|| custom_error(ctx, "Expected number for index"))?;
463
464        let len = arr.len() as i64;
465        let actual_index = if index < 0 {
466            (len + index) as usize
467        } else {
468            index as usize
469        };
470
471        if actual_index < arr.len() {
472            Ok(arr[actual_index].clone())
473        } else {
474            Ok(Value::Null)
475        }
476    }
477}
478
479// =============================================================================
480// includes(array, value) -> boolean
481// =============================================================================
482
483defn!(IncludesFn, vec![arg!(array), arg!(any)], None);
484
485impl Function for IncludesFn {
486    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
487        self.signature.validate(args, ctx)?;
488
489        let arr = args[0]
490            .as_array()
491            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
492
493        let search_key = serde_json::to_string(&args[1]).unwrap_or_default();
494
495        let found = arr.iter().any(|item| {
496            let item_key = serde_json::to_string(item).unwrap_or_default();
497            item_key == search_key
498        });
499
500        Ok(Value::Bool(found))
501    }
502}
503
504// =============================================================================
505// find_index(array, value) -> number (-1 if not found)
506// =============================================================================
507
508defn!(FindIndexFn, vec![arg!(array), arg!(any)], None);
509
510impl Function for FindIndexFn {
511    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
512        self.signature.validate(args, ctx)?;
513
514        let arr = args[0]
515            .as_array()
516            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
517
518        let search_key = serde_json::to_string(&args[1]).unwrap_or_default();
519
520        let index = arr
521            .iter()
522            .position(|item| {
523                let item_key = serde_json::to_string(item).unwrap_or_default();
524                item_key == search_key
525            })
526            .map(|i| i as i64)
527            .unwrap_or(-1);
528
529        Ok(Value::Number(Number::from(index)))
530    }
531}
532
533// =============================================================================
534// first(array) -> any (first element or null)
535// =============================================================================
536
537defn!(FirstFn, vec![arg!(array)], None);
538
539impl Function for FirstFn {
540    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
541        self.signature.validate(args, ctx)?;
542
543        let arr = args[0]
544            .as_array()
545            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
546
547        Ok(arr.first().cloned().unwrap_or(Value::Null))
548    }
549}
550
551// =============================================================================
552// last(array) -> any (last element or null)
553// =============================================================================
554
555defn!(LastFn, vec![arg!(array)], None);
556
557impl Function for LastFn {
558    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
559        self.signature.validate(args, ctx)?;
560
561        let arr = args[0]
562            .as_array()
563            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
564
565        Ok(arr.last().cloned().unwrap_or(Value::Null))
566    }
567}
568
569// =============================================================================
570// group_by(array, &expr | field_name) -> object
571// =============================================================================
572
573defn!(GroupByFn, vec![arg!(array), arg!(expref | string)], None);
574
575impl Function for GroupByFn {
576    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
577        self.signature.validate(args, ctx)?;
578
579        let arr = args[0]
580            .as_array()
581            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
582
583        let mut group_keys: Vec<String> = Vec::new();
584        let mut group_map: std::collections::HashMap<String, Vec<Value>> =
585            std::collections::HashMap::new();
586
587        for item in arr {
588            let key = match extract_key(item, &args[1], ctx)? {
589                Some(k) => k,
590                None => "null".to_string(),
591            };
592            if !group_map.contains_key(&key) {
593                group_keys.push(key.clone());
594            }
595            group_map.entry(key).or_default().push(item.clone());
596        }
597
598        let mut result = serde_json::Map::new();
599        for key in group_keys {
600            if let Some(items) = group_map.remove(&key) {
601                result.insert(key, Value::Array(items));
602            }
603        }
604
605        Ok(Value::Object(result))
606    }
607}
608
609// =============================================================================
610// index_by(array, &expr | field_name) -> object (last value wins for duplicates)
611// =============================================================================
612
613defn!(IndexByFn, vec![arg!(array), arg!(expref | string)], None);
614
615impl Function for IndexByFn {
616    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
617        self.signature.validate(args, ctx)?;
618
619        let arr = args[0]
620            .as_array()
621            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
622
623        let mut result = serde_json::Map::new();
624
625        for item in arr {
626            let key = match extract_key(item, &args[1], ctx)? {
627                Some(k) => k,
628                None => continue, // skip items without the key
629            };
630            // Last value wins for duplicate keys
631            result.insert(key, item.clone());
632        }
633
634        Ok(Value::Object(result))
635    }
636}
637
638// =============================================================================
639// nth(array, n) -> array (every nth element)
640// =============================================================================
641
642defn!(NthFn, vec![arg!(array), arg!(number)], None);
643
644impl Function for NthFn {
645    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
646        self.signature.validate(args, ctx)?;
647
648        let arr = args[0]
649            .as_array()
650            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
651
652        let n = args[1]
653            .as_f64()
654            .ok_or_else(|| custom_error(ctx, "Expected number argument"))? as usize;
655
656        if n == 0 {
657            return Ok(Value::Null);
658        }
659
660        let result: Vec<Value> = arr.iter().step_by(n).cloned().collect();
661        Ok(Value::Array(result))
662    }
663}
664
665// =============================================================================
666// interleave(array1, array2) -> array (alternate elements)
667// =============================================================================
668
669defn!(InterleaveFn, vec![arg!(array), arg!(array)], None);
670
671impl Function for InterleaveFn {
672    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
673        self.signature.validate(args, ctx)?;
674
675        let arr1 = args[0]
676            .as_array()
677            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
678
679        let arr2 = args[1]
680            .as_array()
681            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
682
683        let mut result = Vec::with_capacity(arr1.len() + arr2.len());
684        let mut iter1 = arr1.iter();
685        let mut iter2 = arr2.iter();
686
687        loop {
688            match (iter1.next(), iter2.next()) {
689                (Some(a), Some(b)) => {
690                    result.push(a.clone());
691                    result.push(b.clone());
692                }
693                (Some(a), None) => {
694                    result.push(a.clone());
695                    result.extend(iter1.cloned());
696                    break;
697                }
698                (None, Some(b)) => {
699                    result.push(b.clone());
700                    result.extend(iter2.cloned());
701                    break;
702                }
703                (None, None) => break,
704            }
705        }
706
707        Ok(Value::Array(result))
708    }
709}
710
711// =============================================================================
712// rotate(array, n) -> array (rotate elements by n positions)
713// =============================================================================
714
715defn!(RotateFn, vec![arg!(array), arg!(number)], None);
716
717impl Function for RotateFn {
718    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
719        self.signature.validate(args, ctx)?;
720
721        let arr = args[0]
722            .as_array()
723            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
724
725        if arr.is_empty() {
726            return Ok(Value::Array(vec![]));
727        }
728
729        let n = args[1]
730            .as_f64()
731            .ok_or_else(|| custom_error(ctx, "Expected number argument"))? as i64;
732
733        let len = arr.len() as i64;
734        let rotation = ((n % len) + len) % len;
735        let rotation = rotation as usize;
736
737        let mut result = Vec::with_capacity(arr.len());
738        result.extend(arr[rotation..].iter().cloned());
739        result.extend(arr[..rotation].iter().cloned());
740
741        Ok(Value::Array(result))
742    }
743}
744
745// =============================================================================
746// partition(array, n) -> array (split into n equal parts)
747// =============================================================================
748
749defn!(PartitionFn, vec![arg!(array), arg!(number)], None);
750
751impl Function for PartitionFn {
752    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
753        self.signature.validate(args, ctx)?;
754
755        let arr = args[0]
756            .as_array()
757            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
758
759        let n = args[1]
760            .as_f64()
761            .ok_or_else(|| custom_error(ctx, "Expected number argument"))? as usize;
762
763        if n == 0 {
764            return Ok(Value::Null);
765        }
766
767        let len = arr.len();
768        let base_size = len / n;
769        let remainder = len % n;
770
771        let mut result = Vec::with_capacity(n);
772        let mut start = 0;
773
774        for i in 0..n {
775            let size = base_size + if i < remainder { 1 } else { 0 };
776            if size > 0 {
777                result.push(Value::Array(arr[start..start + size].to_vec()));
778            } else {
779                result.push(Value::Array(vec![]));
780            }
781            start += size;
782        }
783
784        Ok(Value::Array(result))
785    }
786}
787
788// =============================================================================
789// difference(arr1, arr2) -> array (set difference)
790// =============================================================================
791
792defn!(DifferenceFn, vec![arg!(array), arg!(array)], None);
793
794impl Function for DifferenceFn {
795    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
796        self.signature.validate(args, ctx)?;
797
798        let arr1 = args[0]
799            .as_array()
800            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
801
802        let arr2 = args[1]
803            .as_array()
804            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
805
806        let set2: HashSet<String> = arr2
807            .iter()
808            .map(|v| serde_json::to_string(v).unwrap_or_default())
809            .collect();
810
811        let result: Vec<Value> = arr1
812            .iter()
813            .filter(|v| {
814                let key = serde_json::to_string(*v).unwrap_or_default();
815                !set2.contains(&key)
816            })
817            .cloned()
818            .collect();
819
820        Ok(Value::Array(result))
821    }
822}
823
824// =============================================================================
825// intersection(arr1, arr2) -> array (set intersection)
826// =============================================================================
827
828defn!(IntersectionFn, vec![arg!(array), arg!(array)], None);
829
830impl Function for IntersectionFn {
831    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
832        self.signature.validate(args, ctx)?;
833
834        let arr1 = args[0]
835            .as_array()
836            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
837
838        let arr2 = args[1]
839            .as_array()
840            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
841
842        let set2: HashSet<String> = arr2
843            .iter()
844            .map(|v| serde_json::to_string(v).unwrap_or_default())
845            .collect();
846
847        let mut seen: HashSet<String> = HashSet::new();
848        let result: Vec<Value> = arr1
849            .iter()
850            .filter(|v| {
851                let key = serde_json::to_string(*v).unwrap_or_default();
852                set2.contains(&key) && seen.insert(key)
853            })
854            .cloned()
855            .collect();
856
857        Ok(Value::Array(result))
858    }
859}
860
861// =============================================================================
862// union(arr1, arr2) -> array (set union)
863// =============================================================================
864
865defn!(UnionFn, vec![arg!(array), arg!(array)], None);
866
867impl Function for UnionFn {
868    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
869        self.signature.validate(args, ctx)?;
870
871        let arr1 = args[0]
872            .as_array()
873            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
874
875        let arr2 = args[1]
876            .as_array()
877            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
878
879        let mut seen: HashSet<String> = HashSet::new();
880        let mut result: Vec<Value> = Vec::new();
881
882        for item in arr1.iter().chain(arr2.iter()) {
883            let key = serde_json::to_string(item).unwrap_or_default();
884            if seen.insert(key) {
885                result.push(item.clone());
886            }
887        }
888
889        Ok(Value::Array(result))
890    }
891}
892
893// =============================================================================
894// frequencies(array) -> object (count occurrences)
895// =============================================================================
896
897defn!(FrequenciesFn, vec![arg!(array)], None);
898
899impl Function for FrequenciesFn {
900    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
901        self.signature.validate(args, ctx)?;
902
903        let arr = args[0]
904            .as_array()
905            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
906
907        let mut counts: std::collections::HashMap<String, i64> = std::collections::HashMap::new();
908
909        for item in arr {
910            let key = match item {
911                Value::String(s) => s.clone(),
912                Value::Number(n) => n.to_string(),
913                Value::Bool(b) => b.to_string(),
914                Value::Null => "null".to_string(),
915                _ => serde_json::to_string(item).unwrap_or_else(|_| "null".to_string()),
916            };
917            *counts.entry(key).or_insert(0) += 1;
918        }
919
920        let mut result = serde_json::Map::new();
921        // Use BTreeMap for sorted output
922        let sorted: std::collections::BTreeMap<String, i64> = counts.into_iter().collect();
923        for (k, v) in sorted {
924            result.insert(k, Value::Number(Number::from(v)));
925        }
926
927        Ok(Value::Object(result))
928    }
929}
930
931// `mode` is owned by the math category (extensions/math.rs); functions.toml
932// lists it under `math`.
933
934// =============================================================================
935// cartesian(arr1, arr2) -> array (cartesian product of 2 arrays)
936// cartesian(array_of_arrays) -> array (cartesian product of N arrays, jq parity)
937// =============================================================================
938
939defn!(CartesianFn, vec![arg!(array)], Some(arg!(array)));
940
941impl Function for CartesianFn {
942    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
943        self.signature.validate(args, ctx)?;
944
945        let first = args[0]
946            .as_array()
947            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
948
949        // Two modes:
950        // 1. cartesian(arr1, arr2) - two separate arrays (original behavior)
951        // 2. cartesian(array_of_arrays) - single array containing arrays (jq-style)
952
953        if args.len() == 2 {
954            // Original two-argument mode
955            let arr2 = args[1]
956                .as_array()
957                .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
958
959            let mut result = Vec::with_capacity(first.len() * arr2.len());
960            for a in first {
961                for b in arr2 {
962                    result.push(Value::Array(vec![a.clone(), b.clone()]));
963                }
964            }
965            Ok(Value::Array(result))
966        } else {
967            // Single argument mode - check if it's an array of arrays
968            // If all elements are arrays, do N-way cartesian product
969            let arrays: Vec<&Vec<Value>> =
970                first.iter().filter_map(|item| item.as_array()).collect();
971
972            if arrays.len() != first.len() || arrays.is_empty() {
973                // Not all elements are arrays, or empty - return empty
974                return Ok(Value::Array(vec![]));
975            }
976
977            // N-way cartesian product
978            let result = cartesian_product_n(&arrays);
979            Ok(Value::Array(result))
980        }
981    }
982}
983
984/// Compute N-way cartesian product of arrays.
985fn cartesian_product_n(arrays: &[&Vec<Value>]) -> Vec<Value> {
986    if arrays.is_empty() {
987        return vec![];
988    }
989
990    if arrays.len() == 1 {
991        // Single array - each element becomes a 1-element tuple
992        return arrays[0]
993            .iter()
994            .map(|item| Value::Array(vec![item.clone()]))
995            .collect();
996    }
997
998    // Calculate total size for pre-allocation
999    let total_size: usize = arrays.iter().map(|a| a.len()).product();
1000    if total_size == 0 {
1001        return vec![];
1002    }
1003
1004    let mut result = Vec::with_capacity(total_size);
1005
1006    // Iterative cartesian product using indices
1007    let mut indices = vec![0usize; arrays.len()];
1008
1009    loop {
1010        // Build current combination
1011        let combo: Vec<Value> = indices
1012            .iter()
1013            .enumerate()
1014            .map(|(arr_idx, &elem_idx)| arrays[arr_idx][elem_idx].clone())
1015            .collect();
1016        result.push(Value::Array(combo));
1017
1018        // Increment indices (like counting in mixed radix)
1019        let mut carry = true;
1020        for i in (0..arrays.len()).rev() {
1021            if carry {
1022                indices[i] += 1;
1023                if indices[i] >= arrays[i].len() {
1024                    indices[i] = 0;
1025                } else {
1026                    carry = false;
1027                }
1028            }
1029        }
1030
1031        // If we carried all the way through, we're done
1032        if carry {
1033            break;
1034        }
1035    }
1036
1037    result
1038}
1039
1040// =============================================================================
1041// initial(array) -> array (all elements except the last)
1042// =============================================================================
1043
1044defn!(InitialFn, vec![arg!(array)], None);
1045
1046impl Function for InitialFn {
1047    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1048        self.signature.validate(args, ctx)?;
1049
1050        let arr = args[0]
1051            .as_array()
1052            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1053
1054        if arr.is_empty() {
1055            return Ok(Value::Array(vec![]));
1056        }
1057
1058        let result: Vec<Value> = arr[..arr.len() - 1].to_vec();
1059        Ok(Value::Array(result))
1060    }
1061}
1062
1063// =============================================================================
1064// interpose(array, separator) -> array (insert separator between elements)
1065// =============================================================================
1066
1067defn!(InterposeFn, vec![arg!(array), arg!(any)], None);
1068
1069impl Function for InterposeFn {
1070    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1071        self.signature.validate(args, ctx)?;
1072
1073        let arr = args[0]
1074            .as_array()
1075            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1076
1077        let separator = args[1].clone();
1078
1079        if arr.is_empty() {
1080            return Ok(Value::Array(vec![]));
1081        }
1082
1083        if arr.len() == 1 {
1084            return Ok(Value::Array(arr.clone()));
1085        }
1086
1087        let mut result = Vec::with_capacity(arr.len() * 2 - 1);
1088        for (i, item) in arr.iter().enumerate() {
1089            if i > 0 {
1090                result.push(separator.clone());
1091            }
1092            result.push(item.clone());
1093        }
1094
1095        Ok(Value::Array(result))
1096    }
1097}
1098
1099// =============================================================================
1100// zipmap(keys, values) -> object (create object from parallel arrays)
1101// =============================================================================
1102
1103defn!(ZipmapFn, vec![arg!(array), arg!(array)], None);
1104
1105impl Function for ZipmapFn {
1106    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1107        self.signature.validate(args, ctx)?;
1108
1109        let keys = args[0]
1110            .as_array()
1111            .ok_or_else(|| custom_error(ctx, "Expected array argument for keys"))?;
1112
1113        let values = args[1]
1114            .as_array()
1115            .ok_or_else(|| custom_error(ctx, "Expected array argument for values"))?;
1116
1117        let len = keys.len().min(values.len());
1118        let mut result = serde_json::Map::new();
1119
1120        for i in 0..len {
1121            let key = keys[i]
1122                .as_str()
1123                .ok_or_else(|| custom_error(ctx, "Keys must be strings"))?;
1124            result.insert(key.to_string(), values[i].clone());
1125        }
1126
1127        Ok(Value::Object(result))
1128    }
1129}
1130
1131// =============================================================================
1132// partition_by(array, &expr | field_name) -> array (split when value changes)
1133// =============================================================================
1134
1135defn!(
1136    PartitionByFn,
1137    vec![arg!(array), arg!(expref | string)],
1138    None
1139);
1140
1141impl Function for PartitionByFn {
1142    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1143        self.signature.validate(args, ctx)?;
1144
1145        let arr = args[0]
1146            .as_array()
1147            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1148
1149        if arr.is_empty() {
1150            return Ok(Value::Array(vec![]));
1151        }
1152
1153        let is_expref = get_expref_ast(&args[1], ctx).is_some();
1154
1155        let mut result: Vec<Value> = Vec::new();
1156        let mut current_partition: Vec<Value> = Vec::new();
1157        let mut last_key: Option<String> = None;
1158
1159        for item in arr {
1160            let key = if is_expref {
1161                // expref mode: evaluate the expression against each item
1162                match extract_key(item, &args[1], ctx)? {
1163                    Some(k) => k,
1164                    None => "null".to_string(),
1165                }
1166            } else if let Some(field_name) = args[1].as_str() {
1167                // string field mode: extract field, or serialize the item for primitives
1168                if let Some(obj) = item.as_object() {
1169                    if let Some(field_value) = obj.get(field_name) {
1170                        serde_json::to_string(field_value).unwrap_or_default()
1171                    } else {
1172                        "null".to_string()
1173                    }
1174                } else {
1175                    serde_json::to_string(item).unwrap_or_default()
1176                }
1177            } else {
1178                return Err(custom_error(ctx, "Expected expref or string field name"));
1179            };
1180
1181            match &last_key {
1182                Some(prev_key) if *prev_key == key => {
1183                    current_partition.push(item.clone());
1184                }
1185                _ => {
1186                    if !current_partition.is_empty() {
1187                        result.push(Value::Array(current_partition));
1188                    }
1189                    current_partition = vec![item.clone()];
1190                    last_key = Some(key);
1191                }
1192            }
1193        }
1194
1195        // Don't forget the last partition
1196        if !current_partition.is_empty() {
1197            result.push(Value::Array(current_partition));
1198        }
1199
1200        Ok(Value::Array(result))
1201    }
1202}
1203
1204// =============================================================================
1205// dedupe(array) -> array (remove consecutive duplicates)
1206// =============================================================================
1207
1208defn!(DedupeFn, vec![arg!(array)], None);
1209
1210impl Function for DedupeFn {
1211    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1212        self.signature.validate(args, ctx)?;
1213
1214        let arr = args[0]
1215            .as_array()
1216            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1217
1218        if arr.is_empty() {
1219            return Ok(Value::Array(vec![]));
1220        }
1221
1222        let mut result: Vec<Value> = Vec::with_capacity(arr.len());
1223        let mut last_key: Option<String> = None;
1224
1225        for item in arr {
1226            let key = serde_json::to_string(item).unwrap_or_default();
1227            match &last_key {
1228                Some(prev_key) if *prev_key == key => {
1229                    // Skip consecutive duplicate
1230                }
1231                _ => {
1232                    result.push(item.clone());
1233                    last_key = Some(key);
1234                }
1235            }
1236        }
1237
1238        Ok(Value::Array(result))
1239    }
1240}
1241
1242// =============================================================================
1243// tail(array) -> array (all elements except the first)
1244// =============================================================================
1245
1246defn!(TailFn, vec![arg!(array)], None);
1247
1248impl Function for TailFn {
1249    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1250        self.signature.validate(args, ctx)?;
1251
1252        let arr = args[0]
1253            .as_array()
1254            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1255
1256        if arr.is_empty() {
1257            return Ok(Value::Array(vec![]));
1258        }
1259
1260        let result: Vec<Value> = arr[1..].to_vec();
1261        Ok(Value::Array(result))
1262    }
1263}
1264
1265// =============================================================================
1266// without(array, values_array) -> array (remove specified values)
1267// =============================================================================
1268
1269defn!(WithoutFn, vec![arg!(array), arg!(array)], None);
1270
1271impl Function for WithoutFn {
1272    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1273        self.signature.validate(args, ctx)?;
1274
1275        let arr = args[0]
1276            .as_array()
1277            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1278
1279        let exclude = args[1]
1280            .as_array()
1281            .ok_or_else(|| custom_error(ctx, "Expected array argument for values to exclude"))?;
1282
1283        // Create a set of serialized values to exclude for efficient lookup
1284        let exclude_set: HashSet<String> = exclude
1285            .iter()
1286            .map(|v| serde_json::to_string(v).unwrap_or_default())
1287            .collect();
1288
1289        let result: Vec<Value> = arr
1290            .iter()
1291            .filter(|item| {
1292                let key = serde_json::to_string(*item).unwrap_or_default();
1293                !exclude_set.contains(&key)
1294            })
1295            .cloned()
1296            .collect();
1297
1298        Ok(Value::Array(result))
1299    }
1300}
1301
1302// =============================================================================
1303// xor(array1, array2) -> array (symmetric difference)
1304// =============================================================================
1305
1306defn!(XorFn, vec![arg!(array), arg!(array)], None);
1307
1308impl Function for XorFn {
1309    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1310        self.signature.validate(args, ctx)?;
1311
1312        let arr1 = args[0]
1313            .as_array()
1314            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1315
1316        let arr2 = args[1]
1317            .as_array()
1318            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1319
1320        // Create sets of serialized values
1321        let set1: HashSet<String> = arr1
1322            .iter()
1323            .map(|v| serde_json::to_string(v).unwrap_or_default())
1324            .collect();
1325
1326        let set2: HashSet<String> = arr2
1327            .iter()
1328            .map(|v| serde_json::to_string(v).unwrap_or_default())
1329            .collect();
1330
1331        let mut result = Vec::new();
1332
1333        // Add elements from arr1 that are not in arr2
1334        for item in arr1 {
1335            let key = serde_json::to_string(item).unwrap_or_default();
1336            if !set2.contains(&key) {
1337                result.push(item.clone());
1338            }
1339        }
1340
1341        // Add elements from arr2 that are not in arr1
1342        for item in arr2 {
1343            let key = serde_json::to_string(item).unwrap_or_default();
1344            if !set1.contains(&key) {
1345                result.push(item.clone());
1346            }
1347        }
1348
1349        Ok(Value::Array(result))
1350    }
1351}
1352
1353// =============================================================================
1354// window(array, size, step?) -> array (sliding window)
1355// =============================================================================
1356
1357defn!(
1358    WindowFn,
1359    vec![arg!(array), arg!(number)],
1360    Some(arg!(number))
1361);
1362
1363impl Function for WindowFn {
1364    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1365        self.signature.validate(args, ctx)?;
1366
1367        let arr = args[0]
1368            .as_array()
1369            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1370
1371        let size = args[1]
1372            .as_f64()
1373            .ok_or_else(|| custom_error(ctx, "Expected number for window size"))?
1374            as usize;
1375
1376        if size == 0 {
1377            return Ok(Value::Array(vec![]));
1378        }
1379
1380        // Default step is 1
1381        let step = if args.len() > 2 {
1382            args[2]
1383                .as_f64()
1384                .ok_or_else(|| custom_error(ctx, "Expected number for step"))? as usize
1385        } else {
1386            1
1387        };
1388
1389        if step == 0 {
1390            return Err(custom_error(ctx, "Step cannot be zero"));
1391        }
1392
1393        let len = arr.len();
1394        if len < size {
1395            return Ok(Value::Array(vec![]));
1396        }
1397
1398        let mut result = Vec::new();
1399        let mut i = 0;
1400
1401        while i + size <= len {
1402            let window: Vec<Value> = arr[i..i + size].to_vec();
1403            result.push(Value::Array(window));
1404            i += step;
1405        }
1406
1407        Ok(Value::Array(result))
1408    }
1409}
1410
1411// =============================================================================
1412// combinations(array, k) -> array (k-combinations of array)
1413// =============================================================================
1414
1415defn!(CombinationsFn, vec![arg!(array), arg!(number)], None);
1416
1417fn generate_combinations(arr: &[Value], k: usize) -> Vec<Vec<Value>> {
1418    if k == 0 {
1419        return vec![vec![]];
1420    }
1421    if arr.len() < k {
1422        return vec![];
1423    }
1424
1425    let mut result = Vec::new();
1426
1427    // Include first element in combination
1428    let first = arr[0].clone();
1429    let rest = &arr[1..];
1430    for mut combo in generate_combinations(rest, k - 1) {
1431        let mut new_combo = vec![first.clone()];
1432        new_combo.append(&mut combo);
1433        result.push(new_combo);
1434    }
1435
1436    // Exclude first element
1437    result.extend(generate_combinations(rest, k));
1438
1439    result
1440}
1441
1442impl Function for CombinationsFn {
1443    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1444        self.signature.validate(args, ctx)?;
1445
1446        let arr = args[0]
1447            .as_array()
1448            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1449
1450        let k = args[1]
1451            .as_f64()
1452            .ok_or_else(|| custom_error(ctx, "Expected number for k"))? as usize;
1453
1454        // Limit to prevent excessive computation
1455        const MAX_COMBINATIONS: usize = 10000;
1456
1457        // Quick check: if C(n, k) would be too large, return error
1458        let n = arr.len();
1459        if n > 20 && k > 3 && k < n - 3 {
1460            return Err(custom_error(ctx, "Combination size too large"));
1461        }
1462
1463        let combinations = generate_combinations(arr, k);
1464
1465        if combinations.len() > MAX_COMBINATIONS {
1466            return Err(custom_error(ctx, "Too many combinations generated"));
1467        }
1468
1469        let result: Vec<Value> = combinations.into_iter().map(Value::Array).collect();
1470
1471        Ok(Value::Array(result))
1472    }
1473}
1474
1475// =============================================================================
1476// fill(array, value, start?, end?) -> array (fill range with value)
1477// =============================================================================
1478
1479defn!(FillFn, vec![arg!(array), arg!(any)], Some(arg!(number)));
1480
1481impl Function for FillFn {
1482    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1483        self.signature.validate(args, ctx)?;
1484
1485        let arr = args[0]
1486            .as_array()
1487            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1488
1489        let fill_value = args[1].clone();
1490
1491        let len = arr.len();
1492        if len == 0 {
1493            return Ok(Value::Array(vec![]));
1494        }
1495
1496        // Default start is 0, default end is array length
1497        let start = if args.len() > 2 {
1498            let s = args[2]
1499                .as_f64()
1500                .ok_or_else(|| custom_error(ctx, "Expected number for start index"))?
1501                as i64;
1502            // Handle negative indices
1503            if s < 0 {
1504                (len as i64 + s).max(0) as usize
1505            } else {
1506                (s as usize).min(len)
1507            }
1508        } else {
1509            0
1510        };
1511
1512        let end = if args.len() > 3 {
1513            let e = args[3]
1514                .as_f64()
1515                .ok_or_else(|| custom_error(ctx, "Expected number for end index"))?
1516                as i64;
1517            // Handle negative indices
1518            if e < 0 {
1519                (len as i64 + e).max(0) as usize
1520            } else {
1521                (e as usize).min(len)
1522            }
1523        } else {
1524            len
1525        };
1526
1527        let mut result: Vec<Value> = arr.clone();
1528
1529        for item in result.iter_mut().take(end.min(len)).skip(start) {
1530            *item = fill_value.clone();
1531        }
1532
1533        Ok(Value::Array(result))
1534    }
1535}
1536
1537// =============================================================================
1538// pull_at(array, indices_array) -> array (get elements at specified indices)
1539// =============================================================================
1540
1541defn!(PullAtFn, vec![arg!(array), arg!(array)], None);
1542
1543impl Function for PullAtFn {
1544    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1545        self.signature.validate(args, ctx)?;
1546
1547        let arr = args[0]
1548            .as_array()
1549            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1550
1551        let indices = args[1]
1552            .as_array()
1553            .ok_or_else(|| custom_error(ctx, "Expected array of indices"))?;
1554
1555        let len = arr.len();
1556        let mut result = Vec::new();
1557
1558        for idx_var in indices {
1559            let idx = idx_var
1560                .as_f64()
1561                .ok_or_else(|| custom_error(ctx, "Expected number in indices array"))?
1562                as i64;
1563
1564            // Handle negative indices
1565            let actual_idx = if idx < 0 {
1566                (len as i64 + idx).max(0) as usize
1567            } else {
1568                idx as usize
1569            };
1570
1571            if actual_idx < len {
1572                result.push(arr[actual_idx].clone());
1573            }
1574        }
1575
1576        Ok(Value::Array(result))
1577    }
1578}
1579
1580// =============================================================================
1581// transpose(array) -> array
1582// =============================================================================
1583
1584defn!(TransposeFn, vec![arg!(array)], None);
1585
1586impl Function for TransposeFn {
1587    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1588        self.signature.validate(args, ctx)?;
1589
1590        let arr = args[0]
1591            .as_array()
1592            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1593
1594        if arr.is_empty() {
1595            return Ok(Value::Array(vec![]));
1596        }
1597
1598        // Get all inner arrays and find the minimum length
1599        let mut inner_arrays: Vec<&Vec<Value>> = Vec::new();
1600        let mut min_len = usize::MAX;
1601
1602        for item in arr {
1603            if let Some(inner) = item.as_array() {
1604                min_len = min_len.min(inner.len());
1605                inner_arrays.push(inner);
1606            } else {
1607                // If any element is not an array, return empty
1608                return Ok(Value::Array(vec![]));
1609            }
1610        }
1611
1612        if inner_arrays.is_empty() || min_len == 0 {
1613            return Ok(Value::Array(vec![]));
1614        }
1615
1616        // Transpose: create new arrays where each contains the i-th element from each inner array
1617        let mut result = Vec::with_capacity(min_len);
1618        for i in 0..min_len {
1619            let mut row = Vec::with_capacity(inner_arrays.len());
1620            for inner in &inner_arrays {
1621                row.push(inner[i].clone());
1622            }
1623            result.push(Value::Array(row));
1624        }
1625
1626        Ok(Value::Array(result))
1627    }
1628}
1629
1630// =============================================================================
1631// pairwise(array) -> array
1632// =============================================================================
1633
1634defn!(PairwiseFn, vec![arg!(array)], None);
1635
1636impl Function for PairwiseFn {
1637    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1638        self.signature.validate(args, ctx)?;
1639
1640        let arr = args[0]
1641            .as_array()
1642            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1643
1644        if arr.len() < 2 {
1645            return Ok(Value::Array(vec![]));
1646        }
1647
1648        let mut result = Vec::with_capacity(arr.len() - 1);
1649        for i in 0..arr.len() - 1 {
1650            let pair = vec![arr[i].clone(), arr[i + 1].clone()];
1651            result.push(Value::Array(pair));
1652        }
1653
1654        Ok(Value::Array(result))
1655    }
1656}
1657
1658// =============================================================================
1659// indices_array(array, value) -> array of indices
1660// =============================================================================
1661
1662defn!(IndicesArrayFn, vec![arg!(array), arg!(any)], None);
1663
1664impl Function for IndicesArrayFn {
1665    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1666        self.signature.validate(args, ctx)?;
1667
1668        let arr = args[0]
1669            .as_array()
1670            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1671
1672        let search_key = serde_json::to_string(&args[1]).unwrap_or_default();
1673
1674        let mut indices: Vec<Value> = Vec::new();
1675        for (i, item) in arr.iter().enumerate() {
1676            let item_key = serde_json::to_string(item).unwrap_or_default();
1677            if item_key == search_key {
1678                indices.push(Value::Number(Number::from(i as i64)));
1679            }
1680        }
1681
1682        Ok(Value::Array(indices))
1683    }
1684}
1685
1686// =============================================================================
1687// inside_array(needle, haystack) -> boolean
1688// =============================================================================
1689
1690defn!(InsideArrayFn, vec![arg!(array), arg!(array)], None);
1691
1692impl Function for InsideArrayFn {
1693    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1694        self.signature.validate(args, ctx)?;
1695
1696        let needle = args[0]
1697            .as_array()
1698            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1699
1700        let haystack = args[1]
1701            .as_array()
1702            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1703
1704        // Build a set of serialized haystack values for efficient lookup
1705        let haystack_set: HashSet<String> = haystack
1706            .iter()
1707            .map(|item| serde_json::to_string(item).unwrap_or_default())
1708            .collect();
1709
1710        // Check if all needle elements are in the haystack
1711        let result = needle.iter().all(|item| {
1712            let item_key = serde_json::to_string(item).unwrap_or_default();
1713            haystack_set.contains(&item_key)
1714        });
1715
1716        Ok(Value::Bool(result))
1717    }
1718}
1719
1720// =============================================================================
1721// bsearch(sorted_array, value) -> number
1722// =============================================================================
1723
1724defn!(BsearchFn, vec![arg!(array), arg!(any)], None);
1725
1726impl Function for BsearchFn {
1727    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1728        self.signature.validate(args, ctx)?;
1729
1730        let arr = args[0]
1731            .as_array()
1732            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1733
1734        let target = &args[1];
1735
1736        if arr.is_empty() {
1737            return Ok(Value::Number(Number::from(-1)));
1738        }
1739
1740        // Helper to compare two Value values
1741        // Returns Ordering based on type-aware comparison
1742        fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
1743            match (a, b) {
1744                // Numbers: compare numerically
1745                (Value::Number(n1), Value::Number(n2)) => {
1746                    let f1 = n1.as_f64().unwrap_or(0.0);
1747                    let f2 = n2.as_f64().unwrap_or(0.0);
1748                    f1.partial_cmp(&f2).unwrap_or(std::cmp::Ordering::Equal)
1749                }
1750                // Strings: compare lexicographically
1751                (Value::String(s1), Value::String(s2)) => s1.cmp(s2),
1752                // Booleans: false < true
1753                (Value::Bool(b1), Value::Bool(b2)) => b1.cmp(b2),
1754                // Different types or complex types: fall back to JSON string comparison
1755                _ => {
1756                    let s1 = serde_json::to_string(a).unwrap_or_default();
1757                    let s2 = serde_json::to_string(b).unwrap_or_default();
1758                    s1.cmp(&s2)
1759                }
1760            }
1761        }
1762
1763        let mut left = 0i64;
1764        let mut right = arr.len() as i64 - 1;
1765
1766        while left <= right {
1767            let mid = left + (right - left) / 2;
1768
1769            match compare_values(&arr[mid as usize], target) {
1770                std::cmp::Ordering::Equal => {
1771                    return Ok(Value::Number(Number::from(mid)));
1772                }
1773                std::cmp::Ordering::Less => {
1774                    left = mid + 1;
1775                }
1776                std::cmp::Ordering::Greater => {
1777                    right = mid - 1;
1778                }
1779            }
1780        }
1781
1782        // Not found - return -(insertion_point) - 1
1783        // At this point, left is the insertion point
1784        let result = -(left) - 1;
1785        Ok(Value::Number(Number::from(result)))
1786    }
1787}
1788
1789// =============================================================================
1790// repeat_array(value, n) -> array (create array with value repeated n times)
1791// =============================================================================
1792
1793defn!(RepeatArrayFn, vec![arg!(any), arg!(number)], None);
1794
1795impl Function for RepeatArrayFn {
1796    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1797        self.signature.validate(args, ctx)?;
1798
1799        let value = &args[0];
1800        let n = args[1]
1801            .as_f64()
1802            .ok_or_else(|| custom_error(ctx, "Expected number for count argument"))?
1803            as i64;
1804
1805        if n < 0 {
1806            return Err(custom_error(ctx, "Count must be non-negative"));
1807        }
1808
1809        let result: Vec<Value> = (0..n).map(|_| value.clone()).collect();
1810        Ok(Value::Array(result))
1811    }
1812}
1813
1814// =============================================================================
1815// cycle(array, n) -> array (cycle through array n times)
1816// =============================================================================
1817
1818defn!(CycleFn, vec![arg!(array), arg!(number)], None);
1819
1820impl Function for CycleFn {
1821    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1822        self.signature.validate(args, ctx)?;
1823
1824        let arr = args[0]
1825            .as_array()
1826            .ok_or_else(|| custom_error(ctx, "Expected array argument"))?;
1827
1828        let n = args[1]
1829            .as_f64()
1830            .ok_or_else(|| custom_error(ctx, "Expected number for count argument"))?
1831            as i64;
1832
1833        if n < 0 {
1834            return Err(custom_error(ctx, "Count must be non-negative"));
1835        }
1836
1837        if arr.is_empty() || n == 0 {
1838            return Ok(Value::Array(vec![]));
1839        }
1840
1841        let mut result: Vec<Value> = Vec::with_capacity(arr.len() * n as usize);
1842        for _ in 0..n {
1843            result.extend(arr.iter().cloned());
1844        }
1845        Ok(Value::Array(result))
1846    }
1847}
1848
1849// =============================================================================
1850// lag(array, n) -> array
1851// =============================================================================
1852
1853defn!(LagFn, vec![arg!(array), arg!(number)], None);
1854
1855impl Function for LagFn {
1856    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1857        self.signature.validate(args, ctx)?;
1858        let arr = args[0].as_array().unwrap();
1859        let n = args[1].as_f64().unwrap() as usize;
1860        let len = arr.len();
1861        let mut result = Vec::with_capacity(len);
1862        for _ in 0..n.min(len) {
1863            result.push(Value::Null);
1864        }
1865        if n < len {
1866            result.extend_from_slice(&arr[..len - n]);
1867        }
1868        Ok(Value::Array(result))
1869    }
1870}
1871
1872// =============================================================================
1873// lead(array, n) -> array
1874// =============================================================================
1875
1876defn!(LeadFn, vec![arg!(array), arg!(number)], None);
1877
1878impl Function for LeadFn {
1879    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1880        self.signature.validate(args, ctx)?;
1881        let arr = args[0].as_array().unwrap();
1882        let n = args[1].as_f64().unwrap() as usize;
1883        let len = arr.len();
1884        let mut result = Vec::with_capacity(len);
1885        if n < len {
1886            result.extend_from_slice(&arr[n..]);
1887        }
1888        let remaining = len.saturating_sub(result.len());
1889        for _ in 0..remaining {
1890            result.push(Value::Null);
1891        }
1892        Ok(Value::Array(result))
1893    }
1894}
1895
1896#[cfg(test)]
1897mod tests {
1898    use crate::Runtime;
1899    use serde_json::json;
1900
1901    fn setup_runtime() -> Runtime {
1902        Runtime::builder()
1903            .with_standard()
1904            .with_all_extensions()
1905            .build()
1906    }
1907
1908    #[test]
1909    fn test_unique() {
1910        let runtime = setup_runtime();
1911        let expr = runtime.compile("unique(@)").unwrap();
1912        let data = json!([1, 2, 1]);
1913        let result = expr.search(&data).unwrap();
1914        let arr = result.as_array().unwrap();
1915        assert_eq!(arr.len(), 2);
1916    }
1917
1918    #[test]
1919    fn test_first() {
1920        let runtime = setup_runtime();
1921        let expr = runtime.compile("first(@)").unwrap();
1922        let data = json!([1, 2]);
1923        let result = expr.search(&data).unwrap();
1924        assert_eq!(result.as_f64().unwrap() as i64, 1);
1925    }
1926
1927    #[test]
1928    fn test_last() {
1929        let runtime = setup_runtime();
1930        let expr = runtime.compile("last(@)").unwrap();
1931        let data = json!([1, 2]);
1932        let result = expr.search(&data).unwrap();
1933        assert_eq!(result.as_f64().unwrap() as i64, 2);
1934    }
1935
1936    #[test]
1937    fn test_range() {
1938        let runtime = setup_runtime();
1939        let expr = runtime.compile("range(`0`, `5`)").unwrap();
1940        let data = json!(null);
1941        let result = expr.search(&data).unwrap();
1942        let arr = result.as_array().unwrap();
1943        assert_eq!(arr.len(), 5);
1944    }
1945
1946    #[test]
1947    fn test_butlast() {
1948        // butlast is an alias for initial
1949        let runtime = setup_runtime();
1950        let expr = runtime.compile("butlast(@)").unwrap();
1951        let data = json!([1, 2, 3]);
1952        let result = expr.search(&data).unwrap();
1953        let arr = result.as_array().unwrap();
1954        assert_eq!(arr.len(), 2);
1955        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
1956        assert_eq!(arr[1].as_f64().unwrap() as i64, 2);
1957    }
1958
1959    #[test]
1960    fn test_interpose() {
1961        let runtime = setup_runtime();
1962        let expr = runtime.compile("interpose(@, `0`)").unwrap();
1963        let data = json!([1, 2, 3]);
1964        let result = expr.search(&data).unwrap();
1965        let arr = result.as_array().unwrap();
1966        assert_eq!(arr.len(), 5);
1967        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
1968        assert_eq!(arr[1].as_f64().unwrap() as i64, 0);
1969        assert_eq!(arr[2].as_f64().unwrap() as i64, 2);
1970        assert_eq!(arr[3].as_f64().unwrap() as i64, 0);
1971        assert_eq!(arr[4].as_f64().unwrap() as i64, 3);
1972    }
1973
1974    #[test]
1975    fn test_interpose_empty() {
1976        let runtime = setup_runtime();
1977        let expr = runtime.compile("interpose(@, `0`)").unwrap();
1978        let data = json!([]);
1979        let result = expr.search(&data).unwrap();
1980        let arr = result.as_array().unwrap();
1981        assert_eq!(arr.len(), 0);
1982    }
1983
1984    #[test]
1985    fn test_interpose_single() {
1986        let runtime = setup_runtime();
1987        let expr = runtime.compile("interpose(@, `0`)").unwrap();
1988        let data = json!([1]);
1989        let result = expr.search(&data).unwrap();
1990        let arr = result.as_array().unwrap();
1991        assert_eq!(arr.len(), 1);
1992        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
1993    }
1994
1995    #[test]
1996    fn test_interpose_string_separator() {
1997        let runtime = setup_runtime();
1998        let expr = runtime.compile("interpose(@, `\"-\"`)").unwrap();
1999        let data = json!(["a", "b", "c"]);
2000        let result = expr.search(&data).unwrap();
2001        let arr = result.as_array().unwrap();
2002        assert_eq!(arr.len(), 5);
2003        assert_eq!(arr[0].as_str().unwrap(), "a");
2004        assert_eq!(arr[1].as_str().unwrap(), "-");
2005        assert_eq!(arr[2].as_str().unwrap(), "b");
2006        assert_eq!(arr[3].as_str().unwrap(), "-");
2007        assert_eq!(arr[4].as_str().unwrap(), "c");
2008    }
2009
2010    #[test]
2011    fn test_zipmap() {
2012        let runtime = setup_runtime();
2013        let expr = runtime
2014            .compile("zipmap(`[\"a\", \"b\", \"c\"]`, `[1, 2, 3]`)")
2015            .unwrap();
2016        let data = json!(null);
2017        let result = expr.search(&data).unwrap();
2018        let obj = result.as_object().unwrap();
2019        assert_eq!(obj.len(), 3);
2020        assert_eq!(obj.get("a").unwrap().as_f64().unwrap() as i64, 1);
2021        assert_eq!(obj.get("b").unwrap().as_f64().unwrap() as i64, 2);
2022        assert_eq!(obj.get("c").unwrap().as_f64().unwrap() as i64, 3);
2023    }
2024
2025    #[test]
2026    fn test_zipmap_unequal_lengths() {
2027        let runtime = setup_runtime();
2028        // Keys shorter than values
2029        let expr = runtime
2030            .compile("zipmap(`[\"x\", \"y\"]`, `[10, 20, 30]`)")
2031            .unwrap();
2032        let data = json!(null);
2033        let result = expr.search(&data).unwrap();
2034        let obj = result.as_object().unwrap();
2035        assert_eq!(obj.len(), 2);
2036        assert_eq!(obj.get("x").unwrap().as_f64().unwrap() as i64, 10);
2037        assert_eq!(obj.get("y").unwrap().as_f64().unwrap() as i64, 20);
2038    }
2039
2040    #[test]
2041    fn test_zipmap_empty() {
2042        let runtime = setup_runtime();
2043        let expr = runtime.compile("zipmap(`[]`, `[]`)").unwrap();
2044        let data = json!(null);
2045        let result = expr.search(&data).unwrap();
2046        let obj = result.as_object().unwrap();
2047        assert_eq!(obj.len(), 0);
2048    }
2049
2050    #[test]
2051    fn test_partition_by() {
2052        let runtime = setup_runtime();
2053        // Test with objects - partition by "type" field
2054        let expr = runtime.compile(r#"partition_by(@, `"type"`)"#).unwrap();
2055        let data: serde_json::Value = serde_json::from_str(
2056            r#"[{"type": "a", "v": 1}, {"type": "a", "v": 2}, {"type": "b", "v": 3}, {"type": "a", "v": 4}]"#,
2057        )
2058        .unwrap();
2059        let result = expr.search(&data).unwrap();
2060        let arr = result.as_array().unwrap();
2061        assert_eq!(arr.len(), 3);
2062
2063        let partition1 = arr[0].as_array().unwrap();
2064        assert_eq!(partition1.len(), 2); // Two "a" items
2065
2066        let partition2 = arr[1].as_array().unwrap();
2067        assert_eq!(partition2.len(), 1); // One "b" item
2068
2069        let partition3 = arr[2].as_array().unwrap();
2070        assert_eq!(partition3.len(), 1); // One more "a" item
2071    }
2072
2073    #[test]
2074    fn test_partition_by_primitives() {
2075        let runtime = setup_runtime();
2076        // For primitives, use any field name - it will use the value itself
2077        let expr = runtime.compile(r#"partition_by(@, `"_"`)"#).unwrap();
2078        let data = json!([1, 1, 2, 2, 1, 1]);
2079        let result = expr.search(&data).unwrap();
2080        let arr = result.as_array().unwrap();
2081        assert_eq!(arr.len(), 3);
2082
2083        let partition1 = arr[0].as_array().unwrap();
2084        assert_eq!(partition1.len(), 2);
2085        assert_eq!(partition1[0].as_f64().unwrap() as i64, 1);
2086
2087        let partition2 = arr[1].as_array().unwrap();
2088        assert_eq!(partition2.len(), 2);
2089        assert_eq!(partition2[0].as_f64().unwrap() as i64, 2);
2090
2091        let partition3 = arr[2].as_array().unwrap();
2092        assert_eq!(partition3.len(), 2);
2093        assert_eq!(partition3[0].as_f64().unwrap() as i64, 1);
2094    }
2095
2096    #[test]
2097    fn test_partition_by_empty() {
2098        let runtime = setup_runtime();
2099        let expr = runtime.compile(r#"partition_by(@, `"type"`)"#).unwrap();
2100        let data = json!([]);
2101        let result = expr.search(&data).unwrap();
2102        let arr = result.as_array().unwrap();
2103        assert_eq!(arr.len(), 0);
2104    }
2105
2106    #[test]
2107    fn test_partition_by_single() {
2108        let runtime = setup_runtime();
2109        let expr = runtime.compile(r#"partition_by(@, `"type"`)"#).unwrap();
2110        let data: serde_json::Value = serde_json::from_str(r#"[{"type": "a"}]"#).unwrap();
2111        let result = expr.search(&data).unwrap();
2112        let arr = result.as_array().unwrap();
2113        assert_eq!(arr.len(), 1);
2114        let partition1 = arr[0].as_array().unwrap();
2115        assert_eq!(partition1.len(), 1);
2116    }
2117
2118    #[test]
2119    fn test_dedupe() {
2120        let runtime = setup_runtime();
2121        let expr = runtime.compile("dedupe(@)").unwrap();
2122        let data = json!([1, 1, 2, 2, 1, 1]);
2123        let result = expr.search(&data).unwrap();
2124        let arr = result.as_array().unwrap();
2125        assert_eq!(arr.len(), 3);
2126        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
2127        assert_eq!(arr[1].as_f64().unwrap() as i64, 2);
2128        assert_eq!(arr[2].as_f64().unwrap() as i64, 1);
2129    }
2130
2131    #[test]
2132    fn test_dedupe_empty() {
2133        let runtime = setup_runtime();
2134        let expr = runtime.compile("dedupe(@)").unwrap();
2135        let data = json!([]);
2136        let result = expr.search(&data).unwrap();
2137        let arr = result.as_array().unwrap();
2138        assert_eq!(arr.len(), 0);
2139    }
2140
2141    #[test]
2142    fn test_dedupe_no_consecutive() {
2143        let runtime = setup_runtime();
2144        let expr = runtime.compile("dedupe(@)").unwrap();
2145        let data = json!([1, 2, 3]);
2146        let result = expr.search(&data).unwrap();
2147        let arr = result.as_array().unwrap();
2148        assert_eq!(arr.len(), 3);
2149    }
2150
2151    #[test]
2152    fn test_dedupe_strings() {
2153        let runtime = setup_runtime();
2154        let expr = runtime.compile("dedupe(@)").unwrap();
2155        let data = json!(["a", "a", "b", "a"]);
2156        let result = expr.search(&data).unwrap();
2157        let arr = result.as_array().unwrap();
2158        assert_eq!(arr.len(), 3);
2159        assert_eq!(arr[0].as_str().unwrap(), "a");
2160        assert_eq!(arr[1].as_str().unwrap(), "b");
2161        assert_eq!(arr[2].as_str().unwrap(), "a");
2162    }
2163
2164    #[test]
2165    fn test_dedupe_objects() {
2166        let runtime = setup_runtime();
2167        let expr = runtime.compile("dedupe(@)").unwrap();
2168        let data: serde_json::Value =
2169            serde_json::from_str(r#"[{"x": 1}, {"x": 1}, {"x": 2}, {"x": 1}]"#).unwrap();
2170        let result = expr.search(&data).unwrap();
2171        let arr = result.as_array().unwrap();
2172        assert_eq!(arr.len(), 3);
2173    }
2174
2175    #[test]
2176    fn test_dedupe_all_same() {
2177        let runtime = setup_runtime();
2178        let expr = runtime.compile("dedupe(@)").unwrap();
2179        let data = json!([1, 1, 1]);
2180        let result = expr.search(&data).unwrap();
2181        let arr = result.as_array().unwrap();
2182        assert_eq!(arr.len(), 1);
2183        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
2184    }
2185
2186    #[test]
2187    fn test_zipmap_duplicate_keys() {
2188        // Later key should overwrite earlier one
2189        let runtime = setup_runtime();
2190        let expr = runtime
2191            .compile("zipmap(`[\"a\", \"b\", \"a\"]`, `[1, 2, 3]`)")
2192            .unwrap();
2193        let data = json!(null);
2194        let result = expr.search(&data).unwrap();
2195        let obj = result.as_object().unwrap();
2196        assert_eq!(obj.len(), 2);
2197        assert_eq!(obj.get("a").unwrap().as_f64().unwrap() as i64, 3); // Last value wins
2198        assert_eq!(obj.get("b").unwrap().as_f64().unwrap() as i64, 2);
2199    }
2200
2201    #[test]
2202    fn test_zipmap_values_longer() {
2203        let runtime = setup_runtime();
2204        let expr = runtime.compile("zipmap(`[\"a\"]`, `[1, 2, 3]`)").unwrap();
2205        let data = json!(null);
2206        let result = expr.search(&data).unwrap();
2207        let obj = result.as_object().unwrap();
2208        assert_eq!(obj.len(), 1);
2209        assert_eq!(obj.get("a").unwrap().as_f64().unwrap() as i64, 1);
2210    }
2211
2212    #[test]
2213    fn test_partition_by_missing_field() {
2214        let runtime = setup_runtime();
2215        let expr = runtime.compile(r#"partition_by(@, `"type"`)"#).unwrap();
2216        // Some objects have type, some don't
2217        let data: serde_json::Value =
2218            serde_json::from_str(r#"[{"type": "a"}, {"name": "no-type"}, {"type": "a"}]"#).unwrap();
2219        let result = expr.search(&data).unwrap();
2220        let arr = result.as_array().unwrap();
2221        // Should partition into 3: [type:a], [no type -> null], [type:a]
2222        assert_eq!(arr.len(), 3);
2223    }
2224
2225    #[test]
2226    fn test_partition_by_all_same() {
2227        let runtime = setup_runtime();
2228        let expr = runtime.compile(r#"partition_by(@, `"type"`)"#).unwrap();
2229        let data: serde_json::Value =
2230            serde_json::from_str(r#"[{"type": "a"}, {"type": "a"}, {"type": "a"}]"#).unwrap();
2231        let result = expr.search(&data).unwrap();
2232        let arr = result.as_array().unwrap();
2233        assert_eq!(arr.len(), 1);
2234        let partition = arr[0].as_array().unwrap();
2235        assert_eq!(partition.len(), 3);
2236    }
2237
2238    #[test]
2239    fn test_interpose_null_separator() {
2240        let runtime = setup_runtime();
2241        let expr = runtime.compile("interpose(@, `null`)").unwrap();
2242        let data = json!([1, 2]);
2243        let result = expr.search(&data).unwrap();
2244        let arr = result.as_array().unwrap();
2245        assert_eq!(arr.len(), 3);
2246        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
2247        assert!(arr[1].is_null());
2248        assert_eq!(arr[2].as_f64().unwrap() as i64, 2);
2249    }
2250
2251    #[test]
2252    fn test_interpose_object_separator() {
2253        let runtime = setup_runtime();
2254        let expr = runtime.compile(r#"interpose(@, `{"sep": true}`)"#).unwrap();
2255        let data = json!([1, 2]);
2256        let result = expr.search(&data).unwrap();
2257        let arr = result.as_array().unwrap();
2258        assert_eq!(arr.len(), 3);
2259        assert!(arr[1].as_object().is_some());
2260    }
2261
2262    // =========================================================================
2263    // zip tests
2264    // =========================================================================
2265
2266    #[test]
2267    fn test_zip_basic() {
2268        let runtime = setup_runtime();
2269        let data: serde_json::Value =
2270            serde_json::from_str(r#"{"a": [1, 2, 3], "b": ["x", "y", "z"]}"#).unwrap();
2271        let expr = runtime.compile("zip(a, b)").unwrap();
2272        let result = expr.search(&data).unwrap();
2273        let arr = result.as_array().unwrap();
2274        assert_eq!(arr.len(), 3);
2275        assert_eq!(arr[0].as_array().unwrap()[0].as_f64().unwrap() as i64, 1);
2276        assert_eq!(arr[0].as_array().unwrap()[1].as_str().unwrap(), "x");
2277    }
2278
2279    #[test]
2280    fn test_zip_unequal_lengths() {
2281        let runtime = setup_runtime();
2282        let data: serde_json::Value =
2283            serde_json::from_str(r#"{"a": [1, 2], "b": ["x", "y", "z"]}"#).unwrap();
2284        let expr = runtime.compile("zip(a, b)").unwrap();
2285        let result = expr.search(&data).unwrap();
2286        let arr = result.as_array().unwrap();
2287        // Stops at shorter array
2288        assert_eq!(arr.len(), 2);
2289    }
2290
2291    #[test]
2292    fn test_zip_empty_array() {
2293        let runtime = setup_runtime();
2294        let data: serde_json::Value = serde_json::from_str(r#"{"a": [], "b": [1, 2, 3]}"#).unwrap();
2295        let expr = runtime.compile("zip(a, b)").unwrap();
2296        let result = expr.search(&data).unwrap();
2297        let arr = result.as_array().unwrap();
2298        assert_eq!(arr.len(), 0);
2299    }
2300
2301    #[test]
2302    fn test_zip_with_objects() {
2303        let runtime = setup_runtime();
2304        let data: serde_json::Value =
2305            serde_json::from_str(r#"{"names": ["Alice", "Bob"], "scores": [95, 87]}"#).unwrap();
2306        let expr = runtime.compile("zip(names, scores)").unwrap();
2307        let result = expr.search(&data).unwrap();
2308        let arr = result.as_array().unwrap();
2309        assert_eq!(arr.len(), 2);
2310        assert_eq!(arr[0].as_array().unwrap()[0].as_str().unwrap(), "Alice");
2311        assert_eq!(arr[0].as_array().unwrap()[1].as_f64().unwrap() as i64, 95);
2312    }
2313
2314    // =========================================================================
2315    // chunk tests
2316    // =========================================================================
2317
2318    #[test]
2319    fn test_chunk_basic() {
2320        let runtime = setup_runtime();
2321        let data = json!([1, 2, 3, 4, 5]);
2322        let expr = runtime.compile("chunk(@, `2`)").unwrap();
2323        let result = expr.search(&data).unwrap();
2324        let arr = result.as_array().unwrap();
2325        assert_eq!(arr.len(), 3); // [1,2], [3,4], [5]
2326        assert_eq!(arr[0].as_array().unwrap().len(), 2);
2327        assert_eq!(arr[2].as_array().unwrap().len(), 1);
2328    }
2329
2330    #[test]
2331    fn test_chunk_exact_fit() {
2332        let runtime = setup_runtime();
2333        let data = json!([1, 2, 3, 4, 5, 6]);
2334        let expr = runtime.compile("chunk(@, `3`)").unwrap();
2335        let result = expr.search(&data).unwrap();
2336        let arr = result.as_array().unwrap();
2337        assert_eq!(arr.len(), 2);
2338        assert_eq!(arr[0].as_array().unwrap().len(), 3);
2339        assert_eq!(arr[1].as_array().unwrap().len(), 3);
2340    }
2341
2342    #[test]
2343    fn test_chunk_size_larger_than_array() {
2344        let runtime = setup_runtime();
2345        let data = json!([1, 2, 3]);
2346        let expr = runtime.compile("chunk(@, `10`)").unwrap();
2347        let result = expr.search(&data).unwrap();
2348        let arr = result.as_array().unwrap();
2349        assert_eq!(arr.len(), 1);
2350        assert_eq!(arr[0].as_array().unwrap().len(), 3);
2351    }
2352
2353    #[test]
2354    fn test_chunk_size_one() {
2355        let runtime = setup_runtime();
2356        let data = json!([1, 2, 3]);
2357        let expr = runtime.compile("chunk(@, `1`)").unwrap();
2358        let result = expr.search(&data).unwrap();
2359        let arr = result.as_array().unwrap();
2360        assert_eq!(arr.len(), 3);
2361    }
2362
2363    #[test]
2364    fn test_chunk_and_process_pipeline() {
2365        let runtime = setup_runtime();
2366        let data = json!([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2367        let expr = runtime.compile("chunk(@, `3`)").unwrap();
2368        let result = expr.search(&data).unwrap();
2369        let arr = result.as_array().unwrap();
2370        // [1,2,3], [4,5,6], [7,8,9], [10]
2371        assert_eq!(arr.len(), 4);
2372    }
2373
2374    // =========================================================================
2375    // take tests
2376    // =========================================================================
2377
2378    #[test]
2379    fn test_take_basic() {
2380        let runtime = setup_runtime();
2381        let data = json!([1, 2, 3, 4, 5]);
2382        let expr = runtime.compile("take(@, `3`)").unwrap();
2383        let result = expr.search(&data).unwrap();
2384        let arr = result.as_array().unwrap();
2385        assert_eq!(arr.len(), 3);
2386        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
2387        assert_eq!(arr[2].as_f64().unwrap() as i64, 3);
2388    }
2389
2390    #[test]
2391    fn test_take_more_than_length() {
2392        let runtime = setup_runtime();
2393        let data = json!([1, 2]);
2394        let expr = runtime.compile("take(@, `10`)").unwrap();
2395        let result = expr.search(&data).unwrap();
2396        let arr = result.as_array().unwrap();
2397        assert_eq!(arr.len(), 2);
2398    }
2399
2400    #[test]
2401    fn test_take_zero() {
2402        let runtime = setup_runtime();
2403        let data = json!([1, 2, 3]);
2404        let expr = runtime.compile("take(@, `0`)").unwrap();
2405        let result = expr.search(&data).unwrap();
2406        let arr = result.as_array().unwrap();
2407        assert_eq!(arr.len(), 0);
2408    }
2409
2410    // =========================================================================
2411    // drop tests
2412    // =========================================================================
2413
2414    #[test]
2415    fn test_drop_basic() {
2416        let runtime = setup_runtime();
2417        let data = json!([1, 2, 3, 4, 5]);
2418        let expr = runtime.compile("drop(@, `2`)").unwrap();
2419        let result = expr.search(&data).unwrap();
2420        let arr = result.as_array().unwrap();
2421        assert_eq!(arr.len(), 3);
2422        assert_eq!(arr[0].as_f64().unwrap() as i64, 3);
2423    }
2424
2425    #[test]
2426    fn test_drop_more_than_length() {
2427        let runtime = setup_runtime();
2428        let data = json!([1, 2]);
2429        let expr = runtime.compile("drop(@, `10`)").unwrap();
2430        let result = expr.search(&data).unwrap();
2431        let arr = result.as_array().unwrap();
2432        assert_eq!(arr.len(), 0);
2433    }
2434
2435    #[test]
2436    fn test_drop_zero() {
2437        let runtime = setup_runtime();
2438        let data = json!([1, 2, 3]);
2439        let expr = runtime.compile("drop(@, `0`)").unwrap();
2440        let result = expr.search(&data).unwrap();
2441        let arr = result.as_array().unwrap();
2442        assert_eq!(arr.len(), 3);
2443    }
2444
2445    // =========================================================================
2446    // flatten_deep tests
2447    // =========================================================================
2448
2449    #[test]
2450    fn test_flatten_deep_basic() {
2451        let runtime = setup_runtime();
2452        let data = json!([[1, 2], [3, 4]]);
2453        let expr = runtime.compile("flatten_deep(@)").unwrap();
2454        let result = expr.search(&data).unwrap();
2455        let arr = result.as_array().unwrap();
2456        assert_eq!(arr.len(), 4);
2457    }
2458
2459    #[test]
2460    fn test_flatten_deep_nested() {
2461        let runtime = setup_runtime();
2462        let data = json!([1, [2, [3, [4, [5]]]]]);
2463        let expr = runtime.compile("flatten_deep(@)").unwrap();
2464        let result = expr.search(&data).unwrap();
2465        let arr = result.as_array().unwrap();
2466        assert_eq!(arr.len(), 5);
2467        assert_eq!(arr[4].as_f64().unwrap() as i64, 5);
2468    }
2469
2470    #[test]
2471    fn test_flatten_deep_already_flat() {
2472        let runtime = setup_runtime();
2473        let data = json!([1, 2, 3]);
2474        let expr = runtime.compile("flatten_deep(@)").unwrap();
2475        let result = expr.search(&data).unwrap();
2476        let arr = result.as_array().unwrap();
2477        assert_eq!(arr.len(), 3);
2478    }
2479
2480    #[test]
2481    fn test_flatten_deep_mixed() {
2482        let runtime = setup_runtime();
2483        let data = json!([1, [2, 3], [[4]], [[[5, 6]]]]);
2484        let expr = runtime.compile("flatten_deep(@)").unwrap();
2485        let result = expr.search(&data).unwrap();
2486        let arr = result.as_array().unwrap();
2487        assert_eq!(arr.len(), 6);
2488    }
2489
2490    // =========================================================================
2491    // compact tests
2492    // =========================================================================
2493
2494    #[test]
2495    fn test_compact_basic() {
2496        let runtime = setup_runtime();
2497        let data = json!([1, null, 2, false, 3]);
2498        let expr = runtime.compile("compact(@)").unwrap();
2499        let result = expr.search(&data).unwrap();
2500        let arr = result.as_array().unwrap();
2501        assert_eq!(arr.len(), 3);
2502    }
2503
2504    #[test]
2505    fn test_compact_keeps_zero_and_empty_string() {
2506        let runtime = setup_runtime();
2507        let data = json!([0, "", null, true]);
2508        let expr = runtime.compile("compact(@)").unwrap();
2509        let result = expr.search(&data).unwrap();
2510        let arr = result.as_array().unwrap();
2511        assert_eq!(arr.len(), 3); // 0, "", true
2512    }
2513
2514    #[test]
2515    fn test_compact_all_falsy() {
2516        let runtime = setup_runtime();
2517        let data = json!([null, false, null]);
2518        let expr = runtime.compile("compact(@)").unwrap();
2519        let result = expr.search(&data).unwrap();
2520        let arr = result.as_array().unwrap();
2521        assert_eq!(arr.len(), 0);
2522    }
2523
2524    // =========================================================================
2525    // index_at tests
2526    // =========================================================================
2527
2528    #[test]
2529    fn test_index_at_positive() {
2530        let runtime = setup_runtime();
2531        let data = json!(["a", "b", "c", "d"]);
2532        let expr = runtime.compile("index_at(@, `2`)").unwrap();
2533        let result = expr.search(&data).unwrap();
2534        assert_eq!(result.as_str().unwrap(), "c");
2535    }
2536
2537    #[test]
2538    fn test_index_at_negative() {
2539        let runtime = setup_runtime();
2540        let data = json!(["a", "b", "c", "d"]);
2541        let expr = runtime.compile("index_at(@, `-1`)").unwrap();
2542        let result = expr.search(&data).unwrap();
2543        assert_eq!(result.as_str().unwrap(), "d");
2544    }
2545
2546    #[test]
2547    fn test_index_at_negative_second() {
2548        let runtime = setup_runtime();
2549        let data = json!(["a", "b", "c", "d"]);
2550        let expr = runtime.compile("index_at(@, `-2`)").unwrap();
2551        let result = expr.search(&data).unwrap();
2552        assert_eq!(result.as_str().unwrap(), "c");
2553    }
2554
2555    #[test]
2556    fn test_index_at_out_of_bounds() {
2557        let runtime = setup_runtime();
2558        let data = json!(["a", "b", "c"]);
2559        let expr = runtime.compile("index_at(@, `10`)").unwrap();
2560        let result = expr.search(&data).unwrap();
2561        assert!(result.is_null());
2562    }
2563
2564    // =========================================================================
2565    // includes tests
2566    // =========================================================================
2567
2568    #[test]
2569    fn test_includes_number() {
2570        let runtime = setup_runtime();
2571        let data = json!([1, 2, 3, 4, 5]);
2572        let expr = runtime.compile("includes(@, `3`)").unwrap();
2573        let result = expr.search(&data).unwrap();
2574        assert!(result.as_bool().unwrap());
2575    }
2576
2577    #[test]
2578    fn test_includes_not_found() {
2579        let runtime = setup_runtime();
2580        let data = json!([1, 2, 3]);
2581        let expr = runtime.compile("includes(@, `10`)").unwrap();
2582        let result = expr.search(&data).unwrap();
2583        assert!(!result.as_bool().unwrap());
2584    }
2585
2586    #[test]
2587    fn test_includes_string() {
2588        let runtime = setup_runtime();
2589        let data = json!(["apple", "banana", "cherry"]);
2590        let expr = runtime.compile(r#"includes(@, `"banana"`)"#).unwrap();
2591        let result = expr.search(&data).unwrap();
2592        assert!(result.as_bool().unwrap());
2593    }
2594
2595    #[test]
2596    fn test_includes_object() {
2597        let runtime = setup_runtime();
2598        let data = json!([{"a": 1}, {"b": 2}]);
2599        let expr = runtime.compile(r#"includes(@, `{"a": 1}`)"#).unwrap();
2600        let result = expr.search(&data).unwrap();
2601        assert!(result.as_bool().unwrap());
2602    }
2603
2604    // =========================================================================
2605    // find_index tests
2606    // =========================================================================
2607
2608    #[test]
2609    fn test_find_index_found() {
2610        let runtime = setup_runtime();
2611        let data = json!(["a", "b", "c", "d"]);
2612        let expr = runtime.compile(r#"find_index(@, `"c"`)"#).unwrap();
2613        let result = expr.search(&data).unwrap();
2614        assert_eq!(result.as_f64().unwrap() as i64, 2);
2615    }
2616
2617    #[test]
2618    fn test_find_index_not_found() {
2619        let runtime = setup_runtime();
2620        let data = json!(["a", "b", "c"]);
2621        let expr = runtime.compile(r#"find_index(@, `"z"`)"#).unwrap();
2622        let result = expr.search(&data).unwrap();
2623        assert_eq!(result.as_f64().unwrap() as i64, -1);
2624    }
2625
2626    // =========================================================================
2627    // group_by tests
2628    // =========================================================================
2629
2630    #[test]
2631    fn test_group_by_basic() {
2632        let runtime = setup_runtime();
2633        let data: serde_json::Value = serde_json::from_str(
2634            r#"[{"type": "a", "v": 1}, {"type": "b", "v": 2}, {"type": "a", "v": 3}]"#,
2635        )
2636        .unwrap();
2637        let expr = runtime.compile(r#"group_by(@, `"type"`)"#).unwrap();
2638        let result = expr.search(&data).unwrap();
2639        let obj = result.as_object().unwrap();
2640        assert_eq!(obj.get("a").unwrap().as_array().unwrap().len(), 2);
2641        assert_eq!(obj.get("b").unwrap().as_array().unwrap().len(), 1);
2642    }
2643
2644    // =========================================================================
2645    // index_by tests
2646    // =========================================================================
2647
2648    #[test]
2649    fn test_index_by_basic() {
2650        let runtime = setup_runtime();
2651        let data: serde_json::Value =
2652            serde_json::from_str(r#"[{"id": 1, "name": "alice"}, {"id": 2, "name": "bob"}]"#)
2653                .unwrap();
2654        let expr = runtime.compile(r#"index_by(@, `"id"`)"#).unwrap();
2655        let result = expr.search(&data).unwrap();
2656        let obj = result.as_object().unwrap();
2657        assert_eq!(obj.len(), 2);
2658        // Keys are string versions of the id
2659        let alice = obj.get("1").unwrap().as_object().unwrap();
2660        assert_eq!(alice.get("name").unwrap().as_str().unwrap(), "alice");
2661        let bob = obj.get("2").unwrap().as_object().unwrap();
2662        assert_eq!(bob.get("name").unwrap().as_str().unwrap(), "bob");
2663    }
2664
2665    #[test]
2666    fn test_index_by_string_key() {
2667        let runtime = setup_runtime();
2668        let data: serde_json::Value = serde_json::from_str(
2669            r#"[{"code": "US", "name": "United States"}, {"code": "UK", "name": "United Kingdom"}]"#,
2670        )
2671        .unwrap();
2672        let expr = runtime.compile(r#"index_by(@, `"code"`)"#).unwrap();
2673        let result = expr.search(&data).unwrap();
2674        let obj = result.as_object().unwrap();
2675        assert_eq!(obj.len(), 2);
2676        let us = obj.get("US").unwrap().as_object().unwrap();
2677        assert_eq!(us.get("name").unwrap().as_str().unwrap(), "United States");
2678    }
2679
2680    #[test]
2681    fn test_index_by_duplicate_keys() {
2682        // Last value wins for duplicate keys
2683        let runtime = setup_runtime();
2684        let data: serde_json::Value = serde_json::from_str(
2685            r#"[{"type": "a", "v": 1}, {"type": "a", "v": 2}, {"type": "a", "v": 3}]"#,
2686        )
2687        .unwrap();
2688        let expr = runtime.compile(r#"index_by(@, `"type"`)"#).unwrap();
2689        let result = expr.search(&data).unwrap();
2690        let obj = result.as_object().unwrap();
2691        assert_eq!(obj.len(), 1);
2692        // Last value (v=3) wins
2693        let a = obj.get("a").unwrap().as_object().unwrap();
2694        assert_eq!(a.get("v").unwrap().as_f64().unwrap() as i64, 3);
2695    }
2696
2697    #[test]
2698    fn test_index_by_missing_key() {
2699        // Items without the key field are skipped
2700        let runtime = setup_runtime();
2701        let data: serde_json::Value = serde_json::from_str(
2702            r#"[{"id": 1, "name": "alice"}, {"name": "bob"}, {"id": 3, "name": "charlie"}]"#,
2703        )
2704        .unwrap();
2705        let expr = runtime.compile(r#"index_by(@, `"id"`)"#).unwrap();
2706        let result = expr.search(&data).unwrap();
2707        let obj = result.as_object().unwrap();
2708        assert_eq!(obj.len(), 2);
2709        assert!(obj.contains_key("1"));
2710        assert!(obj.contains_key("3"));
2711        assert!(!obj.contains_key("2")); // bob was skipped
2712    }
2713
2714    #[test]
2715    fn test_index_by_empty_array() {
2716        let runtime = setup_runtime();
2717        let data = json!([]);
2718        let expr = runtime.compile(r#"index_by(@, `"id"`)"#).unwrap();
2719        let result = expr.search(&data).unwrap();
2720        let obj = result.as_object().unwrap();
2721        assert!(obj.is_empty());
2722    }
2723
2724    // =========================================================================
2725    // set operations tests
2726    // =========================================================================
2727
2728    #[test]
2729    fn test_difference() {
2730        let runtime = setup_runtime();
2731        let data: serde_json::Value =
2732            serde_json::from_str(r#"{"a": [1, 2, 3, 4], "b": [2, 4]}"#).unwrap();
2733        let expr = runtime.compile("difference(a, b)").unwrap();
2734        let result = expr.search(&data).unwrap();
2735        let arr = result.as_array().unwrap();
2736        assert_eq!(arr.len(), 2); // 1, 3
2737    }
2738
2739    #[test]
2740    fn test_intersection() {
2741        let runtime = setup_runtime();
2742        let data: serde_json::Value =
2743            serde_json::from_str(r#"{"a": [1, 2, 3], "b": [2, 3, 4]}"#).unwrap();
2744        let expr = runtime.compile("intersection(a, b)").unwrap();
2745        let result = expr.search(&data).unwrap();
2746        let arr = result.as_array().unwrap();
2747        assert_eq!(arr.len(), 2); // 2, 3
2748    }
2749
2750    #[test]
2751    fn test_union() {
2752        let runtime = setup_runtime();
2753        let data: serde_json::Value =
2754            serde_json::from_str(r#"{"a": [1, 2], "b": [2, 3]}"#).unwrap();
2755        let expr = runtime.compile("union(a, b)").unwrap();
2756        let result = expr.search(&data).unwrap();
2757        let arr = result.as_array().unwrap();
2758        assert_eq!(arr.len(), 3); // 1, 2, 3
2759    }
2760
2761    // =========================================================================
2762    // frequencies tests
2763    // =========================================================================
2764
2765    #[test]
2766    fn test_frequencies_basic() {
2767        let runtime = setup_runtime();
2768        let data = json!(["a", "b", "a", "c", "a", "b"]);
2769        let expr = runtime.compile("frequencies(@)").unwrap();
2770        let result = expr.search(&data).unwrap();
2771        let obj = result.as_object().unwrap();
2772        assert_eq!(obj.get("a").unwrap().as_f64().unwrap() as i64, 3);
2773        assert_eq!(obj.get("b").unwrap().as_f64().unwrap() as i64, 2);
2774        assert_eq!(obj.get("c").unwrap().as_f64().unwrap() as i64, 1);
2775    }
2776
2777    #[test]
2778    fn test_frequencies_numbers() {
2779        let runtime = setup_runtime();
2780        let data = json!([1, 2, 1, 1, 2, 3]);
2781        let expr = runtime.compile("frequencies(@)").unwrap();
2782        let result = expr.search(&data).unwrap();
2783        let obj = result.as_object().unwrap();
2784        assert_eq!(obj.get("1").unwrap().as_f64().unwrap() as i64, 3);
2785        assert_eq!(obj.get("2").unwrap().as_f64().unwrap() as i64, 2);
2786    }
2787
2788    // =========================================================================
2789    // mode tests
2790    // =========================================================================
2791
2792    #[test]
2793    fn test_mode_basic() {
2794        let runtime = setup_runtime();
2795        let data = json!([1, 2, 2, 3, 2, 4]);
2796        let expr = runtime.compile("mode(@)").unwrap();
2797        let result = expr.search(&data).unwrap();
2798        assert_eq!(result.as_f64().unwrap() as i64, 2);
2799    }
2800
2801    #[test]
2802    fn test_mode_empty() {
2803        let runtime = setup_runtime();
2804        let data = json!([]);
2805        let expr = runtime.compile("mode(@)").unwrap();
2806        let result = expr.search(&data).unwrap();
2807        assert!(result.is_null());
2808    }
2809
2810    // =========================================================================
2811    // cartesian tests
2812    // =========================================================================
2813
2814    #[test]
2815    fn test_cartesian_basic() {
2816        let runtime = setup_runtime();
2817        let data: serde_json::Value =
2818            serde_json::from_str(r#"{"a": [1, 2], "b": ["x", "y"]}"#).unwrap();
2819        let expr = runtime.compile("cartesian(a, b)").unwrap();
2820        let result = expr.search(&data).unwrap();
2821        let arr = result.as_array().unwrap();
2822        assert_eq!(arr.len(), 4); // [1,x], [1,y], [2,x], [2,y]
2823    }
2824
2825    #[test]
2826    fn test_cartesian_empty() {
2827        let runtime = setup_runtime();
2828        let data: serde_json::Value = serde_json::from_str(r#"{"a": [], "b": [1, 2]}"#).unwrap();
2829        let expr = runtime.compile("cartesian(a, b)").unwrap();
2830        let result = expr.search(&data).unwrap();
2831        let arr = result.as_array().unwrap();
2832        assert_eq!(arr.len(), 0);
2833    }
2834
2835    #[test]
2836    fn test_cartesian_n_way_two_arrays() {
2837        let runtime = setup_runtime();
2838        let data = json!([[1, 2], ["a", "b"]]);
2839        let expr = runtime.compile("cartesian(@)").unwrap();
2840        let result = expr.search(&data).unwrap();
2841        let arr = result.as_array().unwrap();
2842        assert_eq!(arr.len(), 4); // [1,a], [1,b], [2,a], [2,b]
2843    }
2844
2845    #[test]
2846    fn test_cartesian_n_way_three_arrays() {
2847        let runtime = setup_runtime();
2848        let data = json!([[1, 2], ["a", "b"], [true, false]]);
2849        let expr = runtime.compile("cartesian(@)").unwrap();
2850        let result = expr.search(&data).unwrap();
2851        let arr = result.as_array().unwrap();
2852        assert_eq!(arr.len(), 8); // 2 * 2 * 2 = 8 combinations
2853    }
2854
2855    #[test]
2856    fn test_cartesian_n_way_single_array() {
2857        let runtime = setup_runtime();
2858        let data = json!([[1, 2, 3]]);
2859        let expr = runtime.compile("cartesian(@)").unwrap();
2860        let result = expr.search(&data).unwrap();
2861        let arr = result.as_array().unwrap();
2862        assert_eq!(arr.len(), 3); // [1], [2], [3]
2863    }
2864
2865    #[test]
2866    fn test_cartesian_n_way_empty() {
2867        let runtime = setup_runtime();
2868        let data = json!([]);
2869        let expr = runtime.compile("cartesian(@)").unwrap();
2870        let result = expr.search(&data).unwrap();
2871        let arr = result.as_array().unwrap();
2872        assert_eq!(arr.len(), 0);
2873    }
2874
2875    // =========================================================================
2876    // Edge cases
2877    // =========================================================================
2878
2879    #[test]
2880    fn test_first_empty_array() {
2881        let runtime = setup_runtime();
2882        let data = json!([]);
2883        let expr = runtime.compile("first(@)").unwrap();
2884        let result = expr.search(&data).unwrap();
2885        assert!(result.is_null());
2886    }
2887
2888    #[test]
2889    fn test_last_empty_array() {
2890        let runtime = setup_runtime();
2891        let data = json!([]);
2892        let expr = runtime.compile("last(@)").unwrap();
2893        let result = expr.search(&data).unwrap();
2894        assert!(result.is_null());
2895    }
2896
2897    #[test]
2898    fn test_unique_preserves_order() {
2899        let runtime = setup_runtime();
2900        let data = json!(["c", "a", "b", "a", "c"]);
2901        let expr = runtime.compile("unique(@)").unwrap();
2902        let result = expr.search(&data).unwrap();
2903        let arr = result.as_array().unwrap();
2904        assert_eq!(arr.len(), 3);
2905        assert_eq!(arr[0].as_str().unwrap(), "c");
2906        assert_eq!(arr[1].as_str().unwrap(), "a");
2907        assert_eq!(arr[2].as_str().unwrap(), "b");
2908    }
2909
2910    #[test]
2911    fn test_unique_different_types() {
2912        let runtime = setup_runtime();
2913        let data = json!([1, "1", 1, "1"]);
2914        let expr = runtime.compile("unique(@)").unwrap();
2915        let result = expr.search(&data).unwrap();
2916        let arr = result.as_array().unwrap();
2917        assert_eq!(arr.len(), 2); // 1 and "1" are different
2918    }
2919
2920    #[test]
2921    fn test_range_with_step() {
2922        let runtime = setup_runtime();
2923        let data = json!(null);
2924        let expr = runtime.compile("range(`1`, `10`, `2`)").unwrap();
2925        let result = expr.search(&data).unwrap();
2926        let arr = result.as_array().unwrap();
2927        assert_eq!(arr.len(), 5); // 1, 3, 5, 7, 9
2928        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
2929        assert_eq!(arr[4].as_f64().unwrap() as i64, 9);
2930    }
2931
2932    #[test]
2933    fn test_range_descending() {
2934        let runtime = setup_runtime();
2935        let data = json!(null);
2936        let expr = runtime.compile("range(`5`, `0`, `-1`)").unwrap();
2937        let result = expr.search(&data).unwrap();
2938        let arr = result.as_array().unwrap();
2939        assert_eq!(arr.len(), 5); // 5, 4, 3, 2, 1
2940        assert_eq!(arr[0].as_f64().unwrap() as i64, 5);
2941        assert_eq!(arr[4].as_f64().unwrap() as i64, 1);
2942    }
2943
2944    // =========================================================================
2945    // Pipeline patterns with arrays
2946    // =========================================================================
2947
2948    #[test]
2949    fn test_pipeline_unique_sort() {
2950        let runtime = setup_runtime();
2951        let data = json!(["redis", "database", "redis", "nosql", "database"]);
2952        let expr = runtime.compile("unique(@) | sort(@)").unwrap();
2953        let result = expr.search(&data).unwrap();
2954        let arr = result.as_array().unwrap();
2955        assert_eq!(arr.len(), 3);
2956        assert_eq!(arr[0].as_str().unwrap(), "database");
2957        assert_eq!(arr[1].as_str().unwrap(), "nosql");
2958        assert_eq!(arr[2].as_str().unwrap(), "redis");
2959    }
2960
2961    #[test]
2962    fn test_pipeline_filter_take() {
2963        let runtime = setup_runtime();
2964        let data = json!([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2965        let expr = runtime.compile("[?@ > `3`] | take(@, `3`)").unwrap();
2966        let result = expr.search(&data).unwrap();
2967        let arr = result.as_array().unwrap();
2968        assert_eq!(arr.len(), 3);
2969        assert_eq!(arr[0].as_f64().unwrap() as i64, 4);
2970        assert_eq!(arr[1].as_f64().unwrap() as i64, 5);
2971        assert_eq!(arr[2].as_f64().unwrap() as i64, 6);
2972    }
2973
2974    #[test]
2975    fn test_pipeline_flatten_unique() {
2976        let runtime = setup_runtime();
2977        let data = json!([[1, 2], [2, 3], [3, 4]]);
2978        let expr = runtime.compile("flatten_deep(@) | unique(@)").unwrap();
2979        let result = expr.search(&data).unwrap();
2980        let arr = result.as_array().unwrap();
2981        assert_eq!(arr.len(), 4); // 1, 2, 3, 4
2982    }
2983
2984    #[test]
2985    fn test_large_array_processing() {
2986        let runtime = setup_runtime();
2987        // Create array with 1000 elements
2988        let items: Vec<i32> = (1..=1000).collect();
2989        let json_str = serde_json::to_string(&items).unwrap();
2990        let data: serde_json::Value = serde_json::from_str(&json_str).unwrap();
2991
2992        let expr = runtime.compile("length(@)").unwrap();
2993        let result = expr.search(&data).unwrap();
2994        assert_eq!(result.as_f64().unwrap() as i64, 1000);
2995    }
2996
2997    #[test]
2998    fn test_transpose_basic() {
2999        let runtime = setup_runtime();
3000        let data = json!([[1, 2, 3], [4, 5, 6]]);
3001        let expr = runtime.compile("transpose(@)").unwrap();
3002        let result = expr.search(&data).unwrap();
3003        let arr = result.as_array().unwrap();
3004        assert_eq!(arr.len(), 3);
3005        // First column: [1, 4]
3006        let col0 = arr[0].as_array().unwrap();
3007        assert_eq!(col0[0].as_f64().unwrap() as i64, 1);
3008        assert_eq!(col0[1].as_f64().unwrap() as i64, 4);
3009        // Second column: [2, 5]
3010        let col1 = arr[1].as_array().unwrap();
3011        assert_eq!(col1[0].as_f64().unwrap() as i64, 2);
3012        assert_eq!(col1[1].as_f64().unwrap() as i64, 5);
3013    }
3014
3015    #[test]
3016    fn test_transpose_empty() {
3017        let runtime = setup_runtime();
3018        let data = json!([]);
3019        let expr = runtime.compile("transpose(@)").unwrap();
3020        let result = expr.search(&data).unwrap();
3021        let arr = result.as_array().unwrap();
3022        assert_eq!(arr.len(), 0);
3023    }
3024
3025    #[test]
3026    fn test_transpose_unequal_rows() {
3027        let runtime = setup_runtime();
3028        let data = json!([[1, 2], [3, 4, 5], [6, 7]]);
3029        let expr = runtime.compile("transpose(@)").unwrap();
3030        let result = expr.search(&data).unwrap();
3031        let arr = result.as_array().unwrap();
3032        // Should use minimum length (2)
3033        assert_eq!(arr.len(), 2);
3034    }
3035
3036    #[test]
3037    fn test_pairwise_basic() {
3038        let runtime = setup_runtime();
3039        let data = json!([1, 2, 3, 4]);
3040        let expr = runtime.compile("pairwise(@)").unwrap();
3041        let result = expr.search(&data).unwrap();
3042        let arr = result.as_array().unwrap();
3043        assert_eq!(arr.len(), 3);
3044        // First pair: [1, 2]
3045        let pair0 = arr[0].as_array().unwrap();
3046        assert_eq!(pair0[0].as_f64().unwrap() as i64, 1);
3047        assert_eq!(pair0[1].as_f64().unwrap() as i64, 2);
3048        // Second pair: [2, 3]
3049        let pair1 = arr[1].as_array().unwrap();
3050        assert_eq!(pair1[0].as_f64().unwrap() as i64, 2);
3051        assert_eq!(pair1[1].as_f64().unwrap() as i64, 3);
3052    }
3053
3054    #[test]
3055    fn test_pairwise_short_array() {
3056        let runtime = setup_runtime();
3057        let data = json!([1]);
3058        let expr = runtime.compile("pairwise(@)").unwrap();
3059        let result = expr.search(&data).unwrap();
3060        let arr = result.as_array().unwrap();
3061        assert_eq!(arr.len(), 0);
3062    }
3063
3064    #[test]
3065    fn test_sliding_window_alias() {
3066        let runtime = setup_runtime();
3067        let data = json!([1, 2, 3, 4, 5]);
3068        let expr = runtime.compile("sliding_window(@, `3`)").unwrap();
3069        let result = expr.search(&data).unwrap();
3070        let arr = result.as_array().unwrap();
3071        assert_eq!(arr.len(), 3);
3072        // First window: [1, 2, 3]
3073        let win0 = arr[0].as_array().unwrap();
3074        assert_eq!(win0.len(), 3);
3075        assert_eq!(win0[0].as_f64().unwrap() as i64, 1);
3076    }
3077
3078    // indices_array tests
3079    #[test]
3080    fn test_indices_array_found() {
3081        let runtime = setup_runtime();
3082        let data = json!([1, 2, 3, 2, 4, 2]);
3083        let expr = runtime.compile("indices_array(@, `2`)").unwrap();
3084        let result = expr.search(&data).unwrap();
3085        let arr = result.as_array().unwrap();
3086        assert_eq!(arr.len(), 3);
3087        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
3088        assert_eq!(arr[1].as_f64().unwrap() as i64, 3);
3089        assert_eq!(arr[2].as_f64().unwrap() as i64, 5);
3090    }
3091
3092    #[test]
3093    fn test_indices_array_not_found() {
3094        let runtime = setup_runtime();
3095        let data = json!([1, 2, 3]);
3096        let expr = runtime.compile("indices_array(@, `5`)").unwrap();
3097        let result = expr.search(&data).unwrap();
3098        let arr = result.as_array().unwrap();
3099        assert_eq!(arr.len(), 0);
3100    }
3101
3102    #[test]
3103    fn test_indices_array_strings() {
3104        let runtime = setup_runtime();
3105        let data = json!(["a", "b", "a", "c", "a"]);
3106        let expr = runtime.compile(r#"indices_array(@, `"a"`)"#).unwrap();
3107        let result = expr.search(&data).unwrap();
3108        let arr = result.as_array().unwrap();
3109        assert_eq!(arr.len(), 3);
3110        assert_eq!(arr[0].as_f64().unwrap() as i64, 0);
3111        assert_eq!(arr[1].as_f64().unwrap() as i64, 2);
3112        assert_eq!(arr[2].as_f64().unwrap() as i64, 4);
3113    }
3114
3115    // inside_array tests
3116    #[test]
3117    fn test_inside_array_true() {
3118        let runtime = setup_runtime();
3119        let data: serde_json::Value =
3120            serde_json::from_str(r#"{"a": [1, 2], "b": [1, 2, 3, 4]}"#).unwrap();
3121        let expr = runtime.compile("inside_array(a, b)").unwrap();
3122        let result = expr.search(&data).unwrap();
3123        assert!(result.as_bool().unwrap());
3124    }
3125
3126    #[test]
3127    fn test_inside_array_false() {
3128        let runtime = setup_runtime();
3129        let data: serde_json::Value =
3130            serde_json::from_str(r#"{"a": [1, 5], "b": [1, 2, 3, 4]}"#).unwrap();
3131        let expr = runtime.compile("inside_array(a, b)").unwrap();
3132        let result = expr.search(&data).unwrap();
3133        assert!(!result.as_bool().unwrap());
3134    }
3135
3136    #[test]
3137    fn test_inside_array_empty() {
3138        let runtime = setup_runtime();
3139        let data: serde_json::Value = serde_json::from_str(r#"{"a": [], "b": [1, 2, 3]}"#).unwrap();
3140        let expr = runtime.compile("inside_array(a, b)").unwrap();
3141        let result = expr.search(&data).unwrap();
3142        assert!(result.as_bool().unwrap());
3143    }
3144
3145    // bsearch tests
3146    #[test]
3147    fn test_bsearch_found() {
3148        let runtime = setup_runtime();
3149        let data = json!([1, 3, 5, 7, 9]);
3150        let expr = runtime.compile("bsearch(@, `5`)").unwrap();
3151        let result = expr.search(&data).unwrap();
3152        assert_eq!(result.as_f64().unwrap() as i64, 2);
3153    }
3154
3155    #[test]
3156    fn test_bsearch_not_found_middle() {
3157        let runtime = setup_runtime();
3158        let data = json!([1, 3, 5, 7, 9]);
3159        let expr = runtime.compile("bsearch(@, `4`)").unwrap();
3160        let result = expr.search(&data).unwrap();
3161        assert_eq!(result.as_f64().unwrap() as i64, -3);
3162    }
3163
3164    #[test]
3165    fn test_bsearch_not_found_start() {
3166        let runtime = setup_runtime();
3167        let data = json!([1, 3, 5, 7, 9]);
3168        let expr = runtime.compile("bsearch(@, `0`)").unwrap();
3169        let result = expr.search(&data).unwrap();
3170        assert_eq!(result.as_f64().unwrap() as i64, -1);
3171    }
3172
3173    #[test]
3174    fn test_bsearch_not_found_end() {
3175        let runtime = setup_runtime();
3176        let data = json!([1, 3, 5, 7, 9]);
3177        let expr = runtime.compile("bsearch(@, `10`)").unwrap();
3178        let result = expr.search(&data).unwrap();
3179        assert_eq!(result.as_f64().unwrap() as i64, -6);
3180    }
3181
3182    #[test]
3183    fn test_bsearch_empty_array() {
3184        let runtime = setup_runtime();
3185        let data = json!([]);
3186        let expr = runtime.compile("bsearch(@, `5`)").unwrap();
3187        let result = expr.search(&data).unwrap();
3188        assert_eq!(result.as_f64().unwrap() as i64, -1);
3189    }
3190
3191    // repeat_array tests
3192    #[test]
3193    fn test_repeat_array_basic() {
3194        let runtime = setup_runtime();
3195        let data = json!(null);
3196        let expr = runtime.compile("repeat_array(`1`, `3`)").unwrap();
3197        let result = expr.search(&data).unwrap();
3198        let arr = result.as_array().unwrap();
3199        assert_eq!(arr.len(), 3);
3200        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
3201        assert_eq!(arr[1].as_f64().unwrap() as i64, 1);
3202        assert_eq!(arr[2].as_f64().unwrap() as i64, 1);
3203    }
3204
3205    #[test]
3206    fn test_repeat_array_string() {
3207        let runtime = setup_runtime();
3208        let data = json!(null);
3209        let expr = runtime.compile(r#"repeat_array(`"x"`, `4`)"#).unwrap();
3210        let result = expr.search(&data).unwrap();
3211        let arr = result.as_array().unwrap();
3212        assert_eq!(arr.len(), 4);
3213        assert_eq!(arr[0].as_str().unwrap(), "x");
3214        assert_eq!(arr[3].as_str().unwrap(), "x");
3215    }
3216
3217    #[test]
3218    fn test_repeat_array_zero() {
3219        let runtime = setup_runtime();
3220        let data = json!(null);
3221        let expr = runtime.compile("repeat_array(`1`, `0`)").unwrap();
3222        let result = expr.search(&data).unwrap();
3223        let arr = result.as_array().unwrap();
3224        assert_eq!(arr.len(), 0);
3225    }
3226
3227    #[test]
3228    fn test_repeat_array_object() {
3229        let runtime = setup_runtime();
3230        let data = json!(null);
3231        let expr = runtime.compile(r#"repeat_array(`{"a": 1}`, `2`)"#).unwrap();
3232        let result = expr.search(&data).unwrap();
3233        let arr = result.as_array().unwrap();
3234        assert_eq!(arr.len(), 2);
3235        assert_eq!(
3236            arr[0]
3237                .as_object()
3238                .unwrap()
3239                .get("a")
3240                .unwrap()
3241                .as_f64()
3242                .unwrap() as i64,
3243            1
3244        );
3245    }
3246
3247    // cycle tests
3248    #[test]
3249    fn test_cycle_basic() {
3250        let runtime = setup_runtime();
3251        let data = json!([1, 2, 3]);
3252        let expr = runtime.compile("cycle(@, `2`)").unwrap();
3253        let result = expr.search(&data).unwrap();
3254        let arr = result.as_array().unwrap();
3255        assert_eq!(arr.len(), 6);
3256        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
3257        assert_eq!(arr[1].as_f64().unwrap() as i64, 2);
3258        assert_eq!(arr[2].as_f64().unwrap() as i64, 3);
3259        assert_eq!(arr[3].as_f64().unwrap() as i64, 1);
3260        assert_eq!(arr[4].as_f64().unwrap() as i64, 2);
3261        assert_eq!(arr[5].as_f64().unwrap() as i64, 3);
3262    }
3263
3264    #[test]
3265    fn test_cycle_strings() {
3266        let runtime = setup_runtime();
3267        let data = json!(["a", "b"]);
3268        let expr = runtime.compile("cycle(@, `3`)").unwrap();
3269        let result = expr.search(&data).unwrap();
3270        let arr = result.as_array().unwrap();
3271        assert_eq!(arr.len(), 6);
3272        assert_eq!(arr[0].as_str().unwrap(), "a");
3273        assert_eq!(arr[1].as_str().unwrap(), "b");
3274        assert_eq!(arr[2].as_str().unwrap(), "a");
3275    }
3276
3277    #[test]
3278    fn test_cycle_zero() {
3279        let runtime = setup_runtime();
3280        let data = json!([1, 2, 3]);
3281        let expr = runtime.compile("cycle(@, `0`)").unwrap();
3282        let result = expr.search(&data).unwrap();
3283        let arr = result.as_array().unwrap();
3284        assert_eq!(arr.len(), 0);
3285    }
3286
3287    #[test]
3288    fn test_cycle_empty_array() {
3289        let runtime = setup_runtime();
3290        let data = json!([]);
3291        let expr = runtime.compile("cycle(@, `5`)").unwrap();
3292        let result = expr.search(&data).unwrap();
3293        let arr = result.as_array().unwrap();
3294        assert_eq!(arr.len(), 0);
3295    }
3296
3297    #[test]
3298    fn test_cycle_once() {
3299        let runtime = setup_runtime();
3300        let data = json!([1, 2]);
3301        let expr = runtime.compile("cycle(@, `1`)").unwrap();
3302        let result = expr.search(&data).unwrap();
3303        let arr = result.as_array().unwrap();
3304        assert_eq!(arr.len(), 2);
3305        assert_eq!(arr[0].as_f64().unwrap() as i64, 1);
3306        assert_eq!(arr[1].as_f64().unwrap() as i64, 2);
3307    }
3308
3309    #[test]
3310    fn test_lag_by_one() {
3311        let runtime = setup_runtime();
3312        let data = json!([1, 2, 3]);
3313        let expr = runtime.compile("lag(@, `1`)").unwrap();
3314        let result = expr.search(&data).unwrap();
3315        assert_eq!(result, json!([null, 1, 2]));
3316    }
3317
3318    #[test]
3319    fn test_lag_by_two() {
3320        let runtime = setup_runtime();
3321        let data = json!([1, 2, 3]);
3322        let expr = runtime.compile("lag(@, `2`)").unwrap();
3323        let result = expr.search(&data).unwrap();
3324        assert_eq!(result, json!([null, null, 1]));
3325    }
3326
3327    #[test]
3328    fn test_lag_by_zero() {
3329        let runtime = setup_runtime();
3330        let data = json!([1, 2, 3]);
3331        let expr = runtime.compile("lag(@, `0`)").unwrap();
3332        let result = expr.search(&data).unwrap();
3333        assert_eq!(result, json!([1, 2, 3]));
3334    }
3335
3336    #[test]
3337    fn test_lag_exceeds_length() {
3338        let runtime = setup_runtime();
3339        let data = json!([1, 2, 3]);
3340        let expr = runtime.compile("lag(@, `5`)").unwrap();
3341        let result = expr.search(&data).unwrap();
3342        assert_eq!(result, json!([null, null, null]));
3343    }
3344
3345    #[test]
3346    fn test_lag_empty_array() {
3347        let runtime = setup_runtime();
3348        let data = json!([]);
3349        let expr = runtime.compile("lag(@, `1`)").unwrap();
3350        let result = expr.search(&data).unwrap();
3351        assert_eq!(result, json!([]));
3352    }
3353
3354    #[test]
3355    fn test_lead_by_one() {
3356        let runtime = setup_runtime();
3357        let data = json!([1, 2, 3]);
3358        let expr = runtime.compile("lead(@, `1`)").unwrap();
3359        let result = expr.search(&data).unwrap();
3360        assert_eq!(result, json!([2, 3, null]));
3361    }
3362
3363    #[test]
3364    fn test_lead_by_two() {
3365        let runtime = setup_runtime();
3366        let data = json!([1, 2, 3]);
3367        let expr = runtime.compile("lead(@, `2`)").unwrap();
3368        let result = expr.search(&data).unwrap();
3369        assert_eq!(result, json!([3, null, null]));
3370    }
3371
3372    #[test]
3373    fn test_lead_by_zero() {
3374        let runtime = setup_runtime();
3375        let data = json!([1, 2, 3]);
3376        let expr = runtime.compile("lead(@, `0`)").unwrap();
3377        let result = expr.search(&data).unwrap();
3378        assert_eq!(result, json!([1, 2, 3]));
3379    }
3380
3381    #[test]
3382    fn test_lead_exceeds_length() {
3383        let runtime = setup_runtime();
3384        let data = json!([1, 2, 3]);
3385        let expr = runtime.compile("lead(@, `5`)").unwrap();
3386        let result = expr.search(&data).unwrap();
3387        assert_eq!(result, json!([null, null, null]));
3388    }
3389
3390    #[test]
3391    fn test_lead_empty_array() {
3392        let runtime = setup_runtime();
3393        let data = json!([]);
3394        let expr = runtime.compile("lead(@, `1`)").unwrap();
3395        let result = expr.search(&data).unwrap();
3396        assert_eq!(result, json!([]));
3397    }
3398}