Skip to main content

jpx_core/extensions/
expression.rs

1//! Expression functions.
2//!
3//! Higher-order functions that use expression references (exprefs) to apply
4//! transformations, filters, and reductions over arrays and objects.
5
6use std::collections::HashSet;
7
8use serde_json::{Map, Number, Value};
9
10use crate::ast::Ast;
11use crate::functions::{Function, custom_error, number_value};
12use crate::interpreter::{SearchResult, interpret};
13use crate::registry::register_if_enabled;
14use crate::value_ext::ValueExt;
15use crate::{Context, Runtime, arg, defn, get_expref_id};
16
17/// Helper to extract an expref AST from a function argument.
18fn get_expref_ast<'a>(value: &Value, ctx: &'a Context<'_>) -> Option<&'a Ast> {
19    get_expref_id(value).and_then(|id| ctx.get_expref(id))
20}
21
22/// Convert a Value to a string key for grouping/deduplication.
23fn value_to_string(value: &Value) -> String {
24    match value {
25        Value::String(s) => s.clone(),
26        Value::Number(n) => n.to_string(),
27        Value::Bool(b) => b.to_string(),
28        Value::Null => "null".to_string(),
29        _ => serde_json::to_string(value).unwrap_or_default(),
30    }
31}
32
33/// Compare two values for sorting purposes.
34fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
35    use std::cmp::Ordering;
36    match (a, b) {
37        (Value::Number(an), Value::Number(bn)) => {
38            let a_f = an.as_f64().unwrap_or(0.0);
39            let b_f = bn.as_f64().unwrap_or(0.0);
40            a_f.partial_cmp(&b_f).unwrap_or(Ordering::Equal)
41        }
42        (Value::String(a_s), Value::String(b_s)) => a_s.cmp(b_s),
43        (Value::Null, Value::Null) => Ordering::Equal,
44        (Value::Null, _) => Ordering::Less,
45        (_, Value::Null) => Ordering::Greater,
46        _ => Ordering::Equal,
47    }
48}
49
50/// Register only the expression functions that are in the enabled set.
51pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
52    register_if_enabled(runtime, "map_expr", enabled, Box::new(MapExprFn::new()));
53    register_if_enabled(
54        runtime,
55        "filter_expr",
56        enabled,
57        Box::new(FilterExprFn::new()),
58    );
59    register_if_enabled(runtime, "any_expr", enabled, Box::new(AnyExprFn::new()));
60    register_if_enabled(runtime, "all_expr", enabled, Box::new(AllExprFn::new()));
61    register_if_enabled(runtime, "find_expr", enabled, Box::new(FindExprFn::new()));
62    register_if_enabled(
63        runtime,
64        "find_index_expr",
65        enabled,
66        Box::new(FindIndexExprFn::new()),
67    );
68    register_if_enabled(runtime, "count_expr", enabled, Box::new(CountExprFn::new()));
69    register_if_enabled(
70        runtime,
71        "sort_by_expr",
72        enabled,
73        Box::new(SortByExprFn::new()),
74    );
75    register_if_enabled(
76        runtime,
77        "group_by_expr",
78        enabled,
79        Box::new(GroupByExprFn::new()),
80    );
81    register_if_enabled(
82        runtime,
83        "partition_expr",
84        enabled,
85        Box::new(PartitionExprFn::new()),
86    );
87    register_if_enabled(
88        runtime,
89        "min_by_expr",
90        enabled,
91        Box::new(MinByExprFn::new()),
92    );
93    register_if_enabled(
94        runtime,
95        "max_by_expr",
96        enabled,
97        Box::new(MaxByExprFn::new()),
98    );
99    register_if_enabled(
100        runtime,
101        "unique_by_expr",
102        enabled,
103        Box::new(UniqueByExprFn::new()),
104    );
105    register_if_enabled(
106        runtime,
107        "flat_map_expr",
108        enabled,
109        Box::new(FlatMapExprFn::new()),
110    );
111
112    // Clojure-style alias for flat_map_expr
113    register_if_enabled(runtime, "mapcat", enabled, Box::new(FlatMapExprFn::new()));
114
115    // Lodash-style aliases
116    register_if_enabled(runtime, "some", enabled, Box::new(AnyExprFn::new()));
117    register_if_enabled(runtime, "every", enabled, Box::new(AllExprFn::new()));
118    register_if_enabled(runtime, "reject", enabled, Box::new(RejectFn::new()));
119    register_if_enabled(runtime, "map_keys", enabled, Box::new(MapKeysFn::new()));
120    register_if_enabled(runtime, "map_values", enabled, Box::new(MapValuesFn::new()));
121    register_if_enabled(runtime, "order_by", enabled, Box::new(OrderByFn::new()));
122    register_if_enabled(
123        runtime,
124        "reduce_expr",
125        enabled,
126        Box::new(ReduceExprFn::new()),
127    );
128    register_if_enabled(runtime, "scan_expr", enabled, Box::new(ScanExprFn::new()));
129    // Alias for reduce_expr (lodash-style)
130    register_if_enabled(runtime, "fold", enabled, Box::new(ReduceExprFn::new()));
131    // Clojure-style alias for scan_expr
132    register_if_enabled(runtime, "reductions", enabled, Box::new(ScanExprFn::new()));
133    // none - opposite of any_expr/some
134    register_if_enabled(runtime, "none", enabled, Box::new(NoneFn::new()));
135    register_if_enabled(runtime, "count_by", enabled, Box::new(CountByFn::new()));
136
137    // Partial application functions
138    register_if_enabled(runtime, "partial", enabled, Box::new(PartialFn::new()));
139    register_if_enabled(runtime, "apply", enabled, Box::new(ApplyFn::new()));
140
141    // Functional array operations
142    register_if_enabled(runtime, "take_while", enabled, Box::new(TakeWhileFn::new()));
143    register_if_enabled(runtime, "drop_while", enabled, Box::new(DropWhileFn::new()));
144    register_if_enabled(runtime, "zip_with", enabled, Box::new(ZipWithFn::new()));
145
146    // Recursive transformation
147    register_if_enabled(runtime, "walk", enabled, Box::new(WalkFn::new()));
148
149    // Recursive descent (jq parity)
150    register_if_enabled(runtime, "recurse", enabled, Box::new(RecurseFn::new()));
151    register_if_enabled(
152        runtime,
153        "recurse_with",
154        enabled,
155        Box::new(RecurseWithFn::new()),
156    );
157
158    // Loop functions (jq parity)
159    register_if_enabled(runtime, "while_expr", enabled, Box::new(WhileExprFn::new()));
160    register_if_enabled(runtime, "until_expr", enabled, Box::new(UntilExprFn::new()));
161}
162
163// =============================================================================
164// map_expr(expr, array) -> array
165// =============================================================================
166
167defn!(MapExprFn, vec![arg!(expref), arg!(array)], None);
168
169impl Function for MapExprFn {
170    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
171        self.signature.validate(args, ctx)?;
172
173        let ast = get_expref_ast(&args[0], ctx)
174            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
175            .clone();
176        let arr = args[1].as_array().unwrap();
177
178        let mut results = Vec::with_capacity(arr.len());
179        for item in arr {
180            results.push(interpret(item, &ast, ctx)?);
181        }
182
183        Ok(Value::Array(results))
184    }
185}
186
187// =============================================================================
188// filter_expr(expr, array) -> array
189// =============================================================================
190
191defn!(FilterExprFn, vec![arg!(expref), arg!(array)], None);
192
193impl Function for FilterExprFn {
194    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
195        self.signature.validate(args, ctx)?;
196
197        let ast = get_expref_ast(&args[0], ctx)
198            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
199            .clone();
200        let arr = args[1].as_array().unwrap();
201
202        let mut results = Vec::new();
203        for item in arr {
204            let result = interpret(item, &ast, ctx)?;
205            if result.is_truthy() {
206                results.push(item.clone());
207            }
208        }
209
210        Ok(Value::Array(results))
211    }
212}
213
214// =============================================================================
215// any_expr(expr, array) -> bool
216// =============================================================================
217
218defn!(AnyExprFn, vec![arg!(expref), arg!(array)], None);
219
220impl Function for AnyExprFn {
221    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
222        self.signature.validate(args, ctx)?;
223
224        let ast = get_expref_ast(&args[0], ctx)
225            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
226            .clone();
227        let arr = args[1].as_array().unwrap();
228
229        for item in arr {
230            let result = interpret(item, &ast, ctx)?;
231            if result.is_truthy() {
232                return Ok(Value::Bool(true));
233            }
234        }
235
236        Ok(Value::Bool(false))
237    }
238}
239
240// =============================================================================
241// none(expr, array) -> bool (opposite of any_expr/some)
242// =============================================================================
243
244defn!(NoneFn, vec![arg!(expref), arg!(array)], None);
245
246impl Function for NoneFn {
247    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
248        self.signature.validate(args, ctx)?;
249
250        let ast = get_expref_ast(&args[0], ctx)
251            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
252            .clone();
253        let arr = args[1].as_array().unwrap();
254
255        // Empty array returns true (vacuously, no elements satisfy the predicate)
256        if arr.is_empty() {
257            return Ok(Value::Bool(true));
258        }
259
260        for item in arr {
261            let result = interpret(item, &ast, ctx)?;
262            if result.is_truthy() {
263                return Ok(Value::Bool(false));
264            }
265        }
266
267        Ok(Value::Bool(true))
268    }
269}
270
271// =============================================================================
272// all_expr(expr, array) -> bool
273// =============================================================================
274
275defn!(AllExprFn, vec![arg!(expref), arg!(array)], None);
276
277impl Function for AllExprFn {
278    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
279        self.signature.validate(args, ctx)?;
280
281        let ast = get_expref_ast(&args[0], ctx)
282            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
283            .clone();
284        let arr = args[1].as_array().unwrap();
285
286        // Empty array returns true (vacuous truth)
287        if arr.is_empty() {
288            return Ok(Value::Bool(true));
289        }
290
291        for item in arr {
292            let result = interpret(item, &ast, ctx)?;
293            if !result.is_truthy() {
294                return Ok(Value::Bool(false));
295            }
296        }
297
298        Ok(Value::Bool(true))
299    }
300}
301
302// =============================================================================
303// find_expr(expr, array) -> element | null
304// =============================================================================
305
306defn!(FindExprFn, vec![arg!(expref), arg!(array)], None);
307
308impl Function for FindExprFn {
309    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
310        self.signature.validate(args, ctx)?;
311
312        let ast = get_expref_ast(&args[0], ctx)
313            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
314            .clone();
315        let arr = args[1].as_array().unwrap();
316
317        for item in arr {
318            let result = interpret(item, &ast, ctx)?;
319            if result.is_truthy() {
320                return Ok(item.clone());
321            }
322        }
323
324        Ok(Value::Null)
325    }
326}
327
328// =============================================================================
329// find_index_expr(expr, array) -> number | null
330// =============================================================================
331
332defn!(FindIndexExprFn, vec![arg!(expref), arg!(array)], None);
333
334impl Function for FindIndexExprFn {
335    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
336        self.signature.validate(args, ctx)?;
337
338        let ast = get_expref_ast(&args[0], ctx)
339            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
340            .clone();
341        let arr = args[1].as_array().unwrap();
342
343        for (i, item) in arr.iter().enumerate() {
344            let result = interpret(item, &ast, ctx)?;
345            if result.is_truthy() {
346                return Ok(number_value(i as f64));
347            }
348        }
349
350        Ok(number_value(-1.0))
351    }
352}
353
354// =============================================================================
355// count_expr(expr, array) -> number
356// =============================================================================
357
358defn!(CountExprFn, vec![arg!(expref), arg!(array)], None);
359
360impl Function for CountExprFn {
361    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
362        self.signature.validate(args, ctx)?;
363
364        let ast = get_expref_ast(&args[0], ctx)
365            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
366            .clone();
367        let arr = args[1].as_array().unwrap();
368
369        let mut count = 0i64;
370        for item in arr {
371            let result = interpret(item, &ast, ctx)?;
372            if result.is_truthy() {
373                count += 1;
374            }
375        }
376
377        Ok(Value::Number(Number::from(count)))
378    }
379}
380
381// =============================================================================
382// sort_by_expr(expr, array) -> array
383// =============================================================================
384
385defn!(SortByExprFn, vec![arg!(expref), arg!(array)], None);
386
387impl Function for SortByExprFn {
388    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
389        self.signature.validate(args, ctx)?;
390
391        let ast = get_expref_ast(&args[0], ctx)
392            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
393            .clone();
394        let arr = args[1].as_array().unwrap();
395
396        if arr.is_empty() {
397            return Ok(Value::Array(vec![]));
398        }
399
400        // Compute sort keys for each element
401        let mut keyed: Vec<(Value, Value)> = Vec::with_capacity(arr.len());
402        for item in arr {
403            let key = interpret(item, &ast, ctx)?;
404            keyed.push((item.clone(), key));
405        }
406
407        // Sort by key
408        keyed.sort_by(|a, b| compare_values(&a.1, &b.1));
409
410        let results: Vec<Value> = keyed.into_iter().map(|(item, _)| item).collect();
411        Ok(Value::Array(results))
412    }
413}
414
415// =============================================================================
416// group_by_expr(expr, array) -> object
417// =============================================================================
418
419defn!(GroupByExprFn, vec![arg!(expref), arg!(array)], None);
420
421impl Function for GroupByExprFn {
422    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
423        self.signature.validate(args, ctx)?;
424
425        let ast = get_expref_ast(&args[0], ctx)
426            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
427            .clone();
428        let arr = args[1].as_array().unwrap();
429
430        // Use an index-based approach to preserve insertion order
431        let mut group_keys: Vec<String> = Vec::new();
432        let mut group_map: std::collections::HashMap<String, Vec<Value>> =
433            std::collections::HashMap::new();
434
435        for item in arr {
436            let key_val = interpret(item, &ast, ctx)?;
437            let key = value_to_string(&key_val);
438            if !group_map.contains_key(&key) {
439                group_keys.push(key.clone());
440            }
441            group_map.entry(key).or_default().push(item.clone());
442        }
443
444        let mut result = Map::new();
445        for key in group_keys {
446            if let Some(items) = group_map.remove(&key) {
447                result.insert(key, Value::Array(items));
448            }
449        }
450
451        Ok(Value::Object(result))
452    }
453}
454
455// =============================================================================
456// count_by(expr, array) -> object
457// =============================================================================
458
459defn!(CountByFn, vec![arg!(expref), arg!(array)], None);
460
461impl Function for CountByFn {
462    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
463        self.signature.validate(args, ctx)?;
464
465        let ast = get_expref_ast(&args[0], ctx)
466            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
467            .clone();
468        let arr = args[1].as_array().unwrap();
469
470        let mut count_keys: Vec<String> = Vec::new();
471        let mut counts: std::collections::HashMap<String, i64> = std::collections::HashMap::new();
472
473        for item in arr {
474            let key_val = interpret(item, &ast, ctx)?;
475            let key = value_to_string(&key_val);
476            if !counts.contains_key(&key) {
477                count_keys.push(key.clone());
478            }
479            *counts.entry(key).or_insert(0) += 1;
480        }
481
482        let mut result = Map::new();
483        for key in count_keys {
484            if let Some(&count) = counts.get(&key) {
485                result.insert(key, Value::Number(Number::from(count)));
486            }
487        }
488
489        Ok(Value::Object(result))
490    }
491}
492
493// =============================================================================
494// partition_expr(expr, array) -> [matches, non_matches]
495// =============================================================================
496
497defn!(PartitionExprFn, vec![arg!(expref), arg!(array)], None);
498
499impl Function for PartitionExprFn {
500    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
501        self.signature.validate(args, ctx)?;
502
503        let ast = get_expref_ast(&args[0], ctx)
504            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
505            .clone();
506        let arr = args[1].as_array().unwrap();
507
508        let mut matches = Vec::new();
509        let mut non_matches = Vec::new();
510
511        for item in arr {
512            let result = interpret(item, &ast, ctx)?;
513            if result.is_truthy() {
514                matches.push(item.clone());
515            } else {
516                non_matches.push(item.clone());
517            }
518        }
519
520        Ok(Value::Array(vec![
521            Value::Array(matches),
522            Value::Array(non_matches),
523        ]))
524    }
525}
526
527// =============================================================================
528// min_by_expr(expr, array) -> element | null
529// =============================================================================
530
531defn!(MinByExprFn, vec![arg!(expref), arg!(array)], None);
532
533impl Function for MinByExprFn {
534    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
535        self.signature.validate(args, ctx)?;
536
537        let ast = get_expref_ast(&args[0], ctx)
538            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
539            .clone();
540        let arr = args[1].as_array().unwrap();
541
542        if arr.is_empty() {
543            return Ok(Value::Null);
544        }
545
546        let mut min_item = arr[0].clone();
547        let mut min_key = interpret(&arr[0], &ast, ctx)?;
548
549        for item in arr.iter().skip(1) {
550            let key = interpret(item, &ast, ctx)?;
551            if compare_values(&key, &min_key) == std::cmp::Ordering::Less {
552                min_item = item.clone();
553                min_key = key;
554            }
555        }
556
557        Ok(min_item)
558    }
559}
560
561// =============================================================================
562// max_by_expr(expr, array) -> element | null
563// =============================================================================
564
565defn!(MaxByExprFn, vec![arg!(expref), arg!(array)], None);
566
567impl Function for MaxByExprFn {
568    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
569        self.signature.validate(args, ctx)?;
570
571        let ast = get_expref_ast(&args[0], ctx)
572            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
573            .clone();
574        let arr = args[1].as_array().unwrap();
575
576        if arr.is_empty() {
577            return Ok(Value::Null);
578        }
579
580        let mut max_item = arr[0].clone();
581        let mut max_key = interpret(&arr[0], &ast, ctx)?;
582
583        for item in arr.iter().skip(1) {
584            let key = interpret(item, &ast, ctx)?;
585            if compare_values(&key, &max_key) == std::cmp::Ordering::Greater {
586                max_item = item.clone();
587                max_key = key;
588            }
589        }
590
591        Ok(max_item)
592    }
593}
594
595// =============================================================================
596// unique_by_expr(expr, array) -> array
597// =============================================================================
598
599defn!(UniqueByExprFn, vec![arg!(expref), arg!(array)], None);
600
601impl Function for UniqueByExprFn {
602    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
603        self.signature.validate(args, ctx)?;
604
605        let ast = get_expref_ast(&args[0], ctx)
606            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
607            .clone();
608        let arr = args[1].as_array().unwrap();
609
610        let mut seen: HashSet<String> = HashSet::new();
611        let mut results = Vec::new();
612
613        for item in arr {
614            let key_val = interpret(item, &ast, ctx)?;
615            let key = value_to_string(&key_val);
616            if seen.insert(key) {
617                results.push(item.clone());
618            }
619        }
620
621        Ok(Value::Array(results))
622    }
623}
624
625// =============================================================================
626// flat_map_expr(expr, array) -> array
627// =============================================================================
628
629defn!(FlatMapExprFn, vec![arg!(expref), arg!(array)], None);
630
631impl Function for FlatMapExprFn {
632    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
633        self.signature.validate(args, ctx)?;
634
635        let ast = get_expref_ast(&args[0], ctx)
636            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
637            .clone();
638        let arr = args[1].as_array().unwrap();
639
640        let mut results = Vec::new();
641        for item in arr {
642            let result = interpret(item, &ast, ctx)?;
643            match result {
644                Value::Array(inner) => {
645                    results.extend(inner);
646                }
647                Value::Null => {
648                    // Skip nulls
649                }
650                _ => {
651                    results.push(result);
652                }
653            }
654        }
655
656        Ok(Value::Array(results))
657    }
658}
659
660// =============================================================================
661// reject(expr, array) -> array (inverse of filter_expr)
662// =============================================================================
663
664defn!(RejectFn, vec![arg!(expref), arg!(array)], None);
665
666impl Function for RejectFn {
667    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
668        self.signature.validate(args, ctx)?;
669
670        let ast = get_expref_ast(&args[0], ctx)
671            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
672            .clone();
673        let arr = args[1].as_array().unwrap();
674
675        let mut results = Vec::new();
676        for item in arr {
677            let result = interpret(item, &ast, ctx)?;
678            // Keep items where expression is falsy (inverse of filter)
679            if !result.is_truthy() {
680                results.push(item.clone());
681            }
682        }
683
684        Ok(Value::Array(results))
685    }
686}
687
688// =============================================================================
689// map_keys(expr, object) -> object
690// =============================================================================
691
692defn!(MapKeysFn, vec![arg!(expref), arg!(object)], None);
693
694impl Function for MapKeysFn {
695    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
696        self.signature.validate(args, ctx)?;
697
698        let ast = get_expref_ast(&args[0], ctx)
699            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
700            .clone();
701        let obj = args[1].as_object().unwrap();
702
703        let mut result = Map::new();
704        for (key, value) in obj.iter() {
705            // Apply expression to the key
706            let key_val = Value::String(key.clone());
707            let new_key_val = interpret(&key_val, &ast, ctx)?;
708
709            let new_key_str = match &new_key_val {
710                Value::String(s) => s.clone(),
711                Value::Number(n) => n.to_string(),
712                _ => key.clone(), // Keep original if result is not a string/number
713            };
714
715            result.insert(new_key_str, value.clone());
716        }
717
718        Ok(Value::Object(result))
719    }
720}
721
722// =============================================================================
723// map_values(expr, object) -> object
724// =============================================================================
725
726defn!(MapValuesFn, vec![arg!(expref), arg!(object)], None);
727
728impl Function for MapValuesFn {
729    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
730        self.signature.validate(args, ctx)?;
731
732        let ast = get_expref_ast(&args[0], ctx)
733            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
734            .clone();
735        let obj = args[1].as_object().unwrap();
736
737        let mut result = Map::new();
738        for (key, value) in obj.iter() {
739            let new_value = interpret(value, &ast, ctx)?;
740            result.insert(key.clone(), new_value);
741        }
742
743        Ok(Value::Object(result))
744    }
745}
746
747// =============================================================================
748// order_by(array, criteria) -> array
749// =============================================================================
750
751defn!(OrderByFn, vec![arg!(array), arg!(array)], None);
752
753impl Function for OrderByFn {
754    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
755        self.signature.validate(args, ctx)?;
756
757        let arr = args[0].as_array().unwrap();
758        let criteria = args[1].as_array().unwrap();
759
760        if arr.is_empty() {
761            return Ok(Value::Array(vec![]));
762        }
763
764        // Parse criteria: each element should be [field, direction]
765        let mut sort_specs: Vec<(String, bool)> = Vec::new(); // (field, ascending)
766        for criterion in criteria {
767            let crit_arr = criterion.as_array().ok_or_else(|| {
768                custom_error(ctx, "Each criterion must be an array [field, direction]")
769            })?;
770
771            if crit_arr.len() < 2 {
772                return Err(custom_error(
773                    ctx,
774                    "Each criterion must have [field, direction]",
775                ));
776            }
777
778            let field = crit_arr[0]
779                .as_str()
780                .ok_or_else(|| custom_error(ctx, "Field name must be a string"))?;
781
782            let direction = crit_arr[1]
783                .as_str()
784                .ok_or_else(|| custom_error(ctx, "Direction must be 'asc' or 'desc'"))?;
785
786            let ascending = match direction.to_lowercase().as_str() {
787                "asc" | "ascending" => true,
788                "desc" | "descending" => false,
789                _ => {
790                    return Err(custom_error(ctx, "Direction must be 'asc' or 'desc'"));
791                }
792            };
793
794            sort_specs.push((field.to_string(), ascending));
795        }
796
797        // Clone and sort the array
798        let mut result: Vec<Value> = arr.clone();
799        result.sort_by(|a, b| {
800            for (field, ascending) in &sort_specs {
801                let a_val = a
802                    .as_object()
803                    .and_then(|o| o.get(field.as_str()))
804                    .unwrap_or(&Value::Null);
805                let b_val = b
806                    .as_object()
807                    .and_then(|o| o.get(field.as_str()))
808                    .unwrap_or(&Value::Null);
809
810                let cmp = compare_values(a_val, b_val);
811                if cmp != std::cmp::Ordering::Equal {
812                    return if *ascending { cmp } else { cmp.reverse() };
813                }
814            }
815            std::cmp::Ordering::Equal
816        });
817
818        Ok(Value::Array(result))
819    }
820}
821
822// =============================================================================
823// reduce_expr(expr, array, initial) -> any
824// =============================================================================
825
826defn!(
827    ReduceExprFn,
828    vec![arg!(expref), arg!(array), arg!(any)],
829    None
830);
831
832impl Function for ReduceExprFn {
833    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
834        self.signature.validate(args, ctx)?;
835
836        let ast = get_expref_ast(&args[0], ctx)
837            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
838            .clone();
839        let arr = args[1].as_array().unwrap();
840        let initial = args[2].clone();
841
842        if arr.is_empty() {
843            return Ok(initial);
844        }
845
846        let mut accumulator = initial;
847
848        for (idx, item) in arr.iter().enumerate() {
849            // Create context object with accumulator, current, and index
850            let mut context_map = Map::new();
851            context_map.insert("accumulator".to_string(), accumulator.clone());
852            context_map.insert("current".to_string(), item.clone());
853            context_map.insert("index".to_string(), Value::Number(Number::from(idx as i64)));
854            let context_val = Value::Object(context_map);
855
856            accumulator = interpret(&context_val, &ast, ctx)?;
857        }
858
859        Ok(accumulator)
860    }
861}
862
863// =============================================================================
864// scan_expr(expr, array, initial) -> array
865// =============================================================================
866
867defn!(ScanExprFn, vec![arg!(expref), arg!(array), arg!(any)], None);
868
869impl Function for ScanExprFn {
870    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
871        self.signature.validate(args, ctx)?;
872
873        let ast = get_expref_ast(&args[0], ctx)
874            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
875            .clone();
876        let arr = args[1].as_array().unwrap();
877        let initial = args[2].clone();
878
879        if arr.is_empty() {
880            return Ok(Value::Array(vec![]));
881        }
882
883        let mut accumulator = initial;
884        let mut results: Vec<Value> = Vec::with_capacity(arr.len());
885
886        for (idx, item) in arr.iter().enumerate() {
887            // Create context object with accumulator, current, and index
888            let mut context_map = Map::new();
889            context_map.insert("accumulator".to_string(), accumulator.clone());
890            context_map.insert("current".to_string(), item.clone());
891            context_map.insert("index".to_string(), Value::Number(Number::from(idx as i64)));
892            let context_val = Value::Object(context_map);
893
894            accumulator = interpret(&context_val, &ast, ctx)?;
895            results.push(accumulator.clone());
896        }
897
898        Ok(Value::Array(results))
899    }
900}
901
902// =============================================================================
903// partial(fn_name, ...args) -> partial object
904// =============================================================================
905
906defn!(PartialFn, vec![arg!(string)], Some(arg!(any)));
907
908impl Function for PartialFn {
909    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
910        self.signature.validate(args, ctx)?;
911
912        let fn_name = args[0].as_str().ok_or_else(|| {
913            custom_error(
914                ctx,
915                "partial() first argument must be a function name string",
916            )
917        })?;
918
919        // Collect the pre-filled arguments
920        let prefilled_args: Vec<Value> = args[1..].to_vec();
921
922        // Create the partial object
923        let mut partial_obj = Map::new();
924        partial_obj.insert("__partial__".to_string(), Value::Bool(true));
925        partial_obj.insert("fn".to_string(), Value::String(fn_name.to_string()));
926        partial_obj.insert("args".to_string(), Value::Array(prefilled_args));
927
928        Ok(Value::Object(partial_obj))
929    }
930}
931
932// =============================================================================
933// apply(partial_or_fn, ...args) -> result
934// =============================================================================
935
936defn!(ApplyFn, vec![arg!(any)], Some(arg!(any)));
937
938impl Function for ApplyFn {
939    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
940        self.signature.validate(args, ctx)?;
941
942        let first_arg = &args[0];
943        let additional_args = &args[1..];
944
945        // Check if it's a partial object
946        if let Some(obj) = first_arg.as_object()
947            && obj.get("__partial__").and_then(|v| v.as_bool()) == Some(true)
948        {
949            // It's a partial - extract fn name and pre-filled args
950            let fn_name = obj
951                .get("fn")
952                .and_then(|v| v.as_str())
953                .ok_or_else(|| custom_error(ctx, "Invalid partial object: missing 'fn' field"))?;
954
955            let prefilled = obj
956                .get("args")
957                .and_then(|v| v.as_array())
958                .ok_or_else(|| custom_error(ctx, "Invalid partial object: missing 'args' field"))?;
959
960            return invoke_function(fn_name, prefilled, additional_args, ctx);
961        }
962
963        // If it's a string, treat as function name
964        if let Some(fn_name) = first_arg.as_str() {
965            return invoke_function(fn_name, &[], additional_args, ctx);
966        }
967
968        Err(custom_error(
969            ctx,
970            "apply() first argument must be a partial object or function name string",
971        ))
972    }
973}
974
975/// Helper to invoke a function by name with pre-filled and additional arguments.
976fn invoke_function(
977    fn_name: &str,
978    prefilled: &[Value],
979    additional: &[Value],
980    ctx: &mut Context<'_>,
981) -> SearchResult {
982    // Build the argument list for the expression
983    let mut all_args_json: Vec<String> = Vec::new();
984
985    // Add pre-filled args as literals
986    for a in prefilled {
987        all_args_json.push(format!("`{}`", serde_json::to_string(a).unwrap()));
988    }
989
990    // Add additional args as literals
991    for a in additional {
992        all_args_json.push(format!("`{}`", serde_json::to_string(a).unwrap()));
993    }
994
995    // Build and execute the expression
996    let expr_str = format!("{}({})", fn_name, all_args_json.join(", "));
997
998    let compiled = ctx.runtime.compile(&expr_str).map_err(|_| {
999        custom_error(
1000            ctx,
1001            &format!("Failed to compile function call '{}'", expr_str),
1002        )
1003    })?;
1004
1005    compiled
1006        .search(&Value::Null)
1007        .map_err(|_| custom_error(ctx, &format!("Failed to execute '{}'", fn_name)))
1008}
1009
1010// =============================================================================
1011// take_while(expr, array) -> array
1012// =============================================================================
1013
1014defn!(TakeWhileFn, vec![arg!(expref), arg!(array)], None);
1015
1016impl Function for TakeWhileFn {
1017    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1018        self.signature.validate(args, ctx)?;
1019
1020        let ast = get_expref_ast(&args[0], ctx)
1021            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1022            .clone();
1023        let arr = args[1].as_array().unwrap();
1024
1025        let mut results = Vec::new();
1026        for item in arr {
1027            let result = interpret(item, &ast, ctx)?;
1028            if result.is_truthy() {
1029                results.push(item.clone());
1030            } else {
1031                break;
1032            }
1033        }
1034
1035        Ok(Value::Array(results))
1036    }
1037}
1038
1039// =============================================================================
1040// drop_while(expr, array) -> array
1041// =============================================================================
1042
1043defn!(DropWhileFn, vec![arg!(expref), arg!(array)], None);
1044
1045impl Function for DropWhileFn {
1046    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1047        self.signature.validate(args, ctx)?;
1048
1049        let ast = get_expref_ast(&args[0], ctx)
1050            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1051            .clone();
1052        let arr = args[1].as_array().unwrap();
1053
1054        let mut dropping = true;
1055        let mut results = Vec::new();
1056        for item in arr {
1057            if dropping {
1058                let result = interpret(item, &ast, ctx)?;
1059                if !result.is_truthy() {
1060                    dropping = false;
1061                    results.push(item.clone());
1062                }
1063            } else {
1064                results.push(item.clone());
1065            }
1066        }
1067
1068        Ok(Value::Array(results))
1069    }
1070}
1071
1072// =============================================================================
1073// zip_with(expr, array1, array2) -> array
1074// =============================================================================
1075
1076defn!(
1077    ZipWithFn,
1078    vec![arg!(expref), arg!(array), arg!(array)],
1079    None
1080);
1081
1082impl Function for ZipWithFn {
1083    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1084        self.signature.validate(args, ctx)?;
1085
1086        let ast = get_expref_ast(&args[0], ctx)
1087            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1088            .clone();
1089        let arr1 = args[1].as_array().unwrap();
1090        let arr2 = args[2].as_array().unwrap();
1091
1092        let min_len = arr1.len().min(arr2.len());
1093        let mut results = Vec::with_capacity(min_len);
1094
1095        for i in 0..min_len {
1096            // Create a pair array [element1, element2] as input to the expression
1097            let pair = Value::Array(vec![arr1[i].clone(), arr2[i].clone()]);
1098            let result = interpret(&pair, &ast, ctx)?;
1099            results.push(result);
1100        }
1101
1102        Ok(Value::Array(results))
1103    }
1104}
1105
1106// =============================================================================
1107// walk(expr, value) -> value (recursive transformation)
1108// =============================================================================
1109
1110defn!(WalkFn, vec![arg!(expref), arg!(any)], None);
1111
1112/// Recursively walk a value, applying the expression bottom-up.
1113fn walk_value(value: &Value, ast: &Ast, ctx: &mut Context<'_>) -> SearchResult {
1114    match value {
1115        Value::Array(arr) => {
1116            // First, recursively walk all elements
1117            let walked_elements: Result<Vec<Value>, _> =
1118                arr.iter().map(|elem| walk_value(elem, ast, ctx)).collect();
1119            let new_array = Value::Array(walked_elements?);
1120            // Then apply the expression to the array itself
1121            interpret(&new_array, ast, ctx)
1122        }
1123        Value::Object(obj) => {
1124            // First, recursively walk all values
1125            let mut walked_obj = Map::new();
1126            for (k, v) in obj.iter() {
1127                walked_obj.insert(k.clone(), walk_value(v, ast, ctx)?);
1128            }
1129            let new_object = Value::Object(walked_obj);
1130            // Then apply the expression to the object itself
1131            interpret(&new_object, ast, ctx)
1132        }
1133        // For scalars (string, number, bool, null), just apply the expression
1134        _ => interpret(value, ast, ctx),
1135    }
1136}
1137
1138impl Function for WalkFn {
1139    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1140        self.signature.validate(args, ctx)?;
1141
1142        let ast = get_expref_ast(&args[0], ctx)
1143            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1144            .clone();
1145
1146        walk_value(&args[1], &ast, ctx)
1147    }
1148}
1149
1150// =============================================================================
1151// recurse(value) -> array (collect all nested values, jq parity)
1152// =============================================================================
1153
1154defn!(RecurseFn, vec![arg!(any)], None);
1155
1156/// Helper to collect all values recursively.
1157fn collect_recursive(value: &Value, results: &mut Vec<Value>) {
1158    results.push(value.clone());
1159    match value {
1160        Value::Array(arr) => {
1161            for elem in arr {
1162                collect_recursive(elem, results);
1163            }
1164        }
1165        Value::Object(obj) => {
1166            for (_, v) in obj.iter() {
1167                collect_recursive(v, results);
1168            }
1169        }
1170        _ => {}
1171    }
1172}
1173
1174impl Function for RecurseFn {
1175    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1176        self.signature.validate(args, ctx)?;
1177
1178        let mut results = Vec::new();
1179        collect_recursive(&args[0], &mut results);
1180
1181        Ok(Value::Array(results))
1182    }
1183}
1184
1185// =============================================================================
1186// recurse_with(value, expr) -> array (recursive descent with filter, jq parity)
1187// =============================================================================
1188
1189defn!(RecurseWithFn, vec![arg!(any), arg!(expref)], None);
1190
1191impl Function for RecurseWithFn {
1192    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1193        self.signature.validate(args, ctx)?;
1194
1195        let ast = get_expref_ast(&args[1], ctx)
1196            .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1197            .clone();
1198
1199        let mut results = Vec::new();
1200        let mut queue = vec![args[0].clone()];
1201        let max_iterations = 10000; // Safety limit
1202        let mut iterations = 0;
1203
1204        while let Some(current) = queue.pop() {
1205            if iterations >= max_iterations {
1206                return Err(custom_error(
1207                    ctx,
1208                    "recurse_with exceeded maximum iterations",
1209                ));
1210            }
1211            iterations += 1;
1212
1213            // Skip null values
1214            if current.is_null() {
1215                continue;
1216            }
1217
1218            results.push(current.clone());
1219
1220            // Apply expression to get next values
1221            let next = interpret(&current, &ast, ctx)?;
1222
1223            match next {
1224                Value::Null => {}
1225                Value::Array(arr) => {
1226                    // Add non-null elements to queue (in reverse to maintain order)
1227                    for elem in arr.into_iter().rev() {
1228                        if !elem.is_null() {
1229                            queue.push(elem);
1230                        }
1231                    }
1232                }
1233                _ => {
1234                    queue.push(next);
1235                }
1236            }
1237        }
1238
1239        Ok(Value::Array(results))
1240    }
1241}
1242
1243// =============================================================================
1244// while_expr(init, condition, update) -> value
1245// =============================================================================
1246
1247defn!(
1248    WhileExprFn,
1249    vec![arg!(any), arg!(expref), arg!(expref)],
1250    None
1251);
1252
1253impl Function for WhileExprFn {
1254    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1255        self.signature.validate(args, ctx)?;
1256
1257        let cond_ast = get_expref_ast(&args[1], ctx)
1258            .ok_or_else(|| custom_error(ctx, "Expected expref for condition"))?
1259            .clone();
1260        let update_ast = get_expref_ast(&args[2], ctx)
1261            .ok_or_else(|| custom_error(ctx, "Expected expref for update"))?
1262            .clone();
1263
1264        let mut current = args[0].clone();
1265        let max_iterations = 100000; // Safety limit
1266        let mut iterations = 0;
1267
1268        loop {
1269            if iterations >= max_iterations {
1270                return Err(custom_error(ctx, "while_expr exceeded maximum iterations"));
1271            }
1272            iterations += 1;
1273
1274            // Check condition
1275            let cond_result = interpret(&current, &cond_ast, ctx)?;
1276            if !cond_result.is_truthy() {
1277                break;
1278            }
1279
1280            // Apply update
1281            current = interpret(&current, &update_ast, ctx)?;
1282        }
1283
1284        Ok(current)
1285    }
1286}
1287
1288// =============================================================================
1289// until_expr(init, condition, update) -> value
1290// =============================================================================
1291
1292defn!(
1293    UntilExprFn,
1294    vec![arg!(any), arg!(expref), arg!(expref)],
1295    None
1296);
1297
1298impl Function for UntilExprFn {
1299    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1300        self.signature.validate(args, ctx)?;
1301
1302        let cond_ast = get_expref_ast(&args[1], ctx)
1303            .ok_or_else(|| custom_error(ctx, "Expected expref for condition"))?
1304            .clone();
1305        let update_ast = get_expref_ast(&args[2], ctx)
1306            .ok_or_else(|| custom_error(ctx, "Expected expref for update"))?
1307            .clone();
1308
1309        let mut current = args[0].clone();
1310        let max_iterations = 100000; // Safety limit
1311        let mut iterations = 0;
1312
1313        loop {
1314            if iterations >= max_iterations {
1315                return Err(custom_error(ctx, "until_expr exceeded maximum iterations"));
1316            }
1317            iterations += 1;
1318
1319            // Check condition (stop when true, opposite of while)
1320            let cond_result = interpret(&current, &cond_ast, ctx)?;
1321            if cond_result.is_truthy() {
1322                break;
1323            }
1324
1325            // Apply update
1326            current = interpret(&current, &update_ast, ctx)?;
1327        }
1328
1329        Ok(current)
1330    }
1331}