Skip to main content

linkrs_query/executor/expression/functions/builtin/
container.rs

1//! Implementation of container operation functions
2//!
3//! Provide functions for operating on lists and maps, including head, last, tail, size, range, and keys.
4
5use crate::executor::expression::ExpressionError;
6use linkrs_core::value::list::List;
7use linkrs_core::value::NullType;
8use linkrs_core::Value;
9use std::collections::BTreeSet;
10
11/// Container function enumeration
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ContainerFunction {
14    Head,
15    Last,
16    Tail,
17    Size,
18    Range,
19    Keys,
20    ReverseList,
21    ToSet,
22    ListContains,
23    ListAppend,
24    ListPrepend,
25    ListFilter,
26    ListTransform,
27    ListConcat,
28    ListSort,
29    ListSlice,
30    ListToString,
31    ListDistinct,
32    ListUnique,
33    ListExtract,
34    ListAny,
35    ListAll,
36    ListSingle,
37    ListReduce,
38    StructPack,
39    StructExtract,
40    MapCreation,
41    MapExtract,
42    ElementAt,
43    Cardinality,
44    MapKeys,
45    MapValues,
46}
47
48impl ContainerFunction {
49    /// Obtain the function name
50    pub fn name(&self) -> &str {
51        match self {
52            Self::Head => "head",
53            Self::Last => "last",
54            Self::Tail => "tail",
55            Self::Size => "size",
56            Self::Range => "range",
57            Self::Keys => "keys",
58            Self::ReverseList => "reverse",
59            Self::ToSet => "toset",
60            Self::ListContains => "list_contains",
61            Self::ListAppend => "list_append",
62            Self::ListPrepend => "list_prepend",
63            Self::ListFilter => "list_filter",
64            Self::ListTransform => "list_transform",
65            Self::ListConcat => "list_concat",
66            Self::ListSort => "list_sort",
67            Self::ListSlice => "list_slice",
68            Self::ListToString => "list_to_string",
69            Self::ListDistinct => "list_distinct",
70            Self::ListUnique => "list_unique",
71            Self::ListExtract => "list_extract",
72            Self::ListAny => "list_any",
73            Self::ListAll => "list_all",
74            Self::ListSingle => "list_single",
75            Self::ListReduce => "list_reduce",
76            Self::StructPack => "struct_pack",
77            Self::StructExtract => "struct_extract",
78            Self::MapCreation => "map",
79            Self::MapExtract => "map_extract",
80            Self::ElementAt => "element_at",
81            Self::Cardinality => "cardinality",
82            Self::MapKeys => "map_keys",
83            Self::MapValues => "map_values",
84        }
85    }
86
87    /// Determine the number of parameters
88    pub fn arity(&self) -> usize {
89        match self {
90            Self::Head => 1,
91            Self::Last => 1,
92            Self::Tail => 1,
93            Self::Size => 1,
94            Self::Range => 2,
95            Self::Keys => 1,
96            Self::ReverseList => 1,
97            Self::ToSet => 1,
98            Self::ListContains => 2,
99            Self::ListAppend => 2,
100            Self::ListPrepend => 2,
101            Self::ListFilter => 2,
102            Self::ListTransform => 2,
103            Self::ListConcat => 2,
104            Self::ListSort => 1,
105            Self::ListSlice => 3,
106            Self::ListToString => 2,
107            Self::ListDistinct => 1,
108            Self::ListUnique => 1,
109            Self::ListExtract => 2,
110            Self::ListAny => 2,
111            Self::ListAll => 2,
112            Self::ListSingle => 2,
113            Self::ListReduce => 3,
114            Self::StructPack => 0,
115            Self::StructExtract => 2,
116            Self::MapCreation => 0,
117            Self::MapExtract => 2,
118            Self::ElementAt => 2,
119            Self::Cardinality => 1,
120            Self::MapKeys => 1,
121            Self::MapValues => 1,
122        }
123    }
124
125    /// Is it a function with variable parameters?
126    pub fn is_variadic(&self) -> bool {
127        matches!(
128            self,
129            Self::Range | Self::ListConcat | Self::StructPack | Self::MapCreation
130        )
131    }
132
133    /// Obtain the function description
134    pub fn description(&self) -> &str {
135        match self {
136            Self::Head => "Get the first element of the list",
137            Self::Last => "Get the last element of the list",
138            Self::Tail => "Get all elements of the list except the first one",
139            Self::Size => "Get the size of a string, list, map, or set",
140            Self::Range => "Generate a list of integer ranges",
141            Self::Keys => "Get all the keys for vertices, edges, or mappings.",
142            Self::ReverseList => "inversion list",
143            Self::ToSet => "Convert the list to a set.",
144            Self::ListContains => "Check if list contains a value",
145            Self::ListAppend => "Append element to list",
146            Self::ListPrepend => "Prepend element to list",
147            Self::ListFilter => "Filter list elements by predicate",
148            Self::ListTransform => "Transform list elements by function",
149            Self::ListConcat => "Concatenate multiple lists into one",
150            Self::ListSort => "Sort list elements",
151            Self::ListSlice => "Extract a slice from a list",
152            Self::ListToString => "Convert a list to a string with delimiter",
153            Self::ListDistinct => "Remove duplicate elements from list",
154            Self::ListUnique => "Remove duplicate elements from list",
155            Self::ListExtract => "Extract element from list at specified index",
156            Self::ListAny => "Check if any element in mask list is true",
157            Self::ListAll => "Check if all elements in mask list are true",
158            Self::ListSingle => "Check if exactly one element in mask list is true",
159            Self::ListReduce => "Reduce list to a single value using accumulator",
160            Self::StructPack => "Pack values into a struct",
161            Self::StructExtract => "Extract field from struct",
162            Self::MapCreation => "Create a map from key-value pairs",
163            Self::MapExtract => "Extract value from map by key",
164            Self::ElementAt => "Get element at index from list or map",
165            Self::Cardinality => "Get number of elements in map or set",
166            Self::MapKeys => "Get all keys from a map",
167            Self::MapValues => "Get all values from a map",
168        }
169    }
170
171    pub fn execute(&self, args: &[Value]) -> Result<Value, ExpressionError> {
172        if !self.is_variadic() && args.len() != self.arity() {
173            return Err(ExpressionError::invalid_arity(
174                self.name(),
175                self.arity(),
176                args.len(),
177            ));
178        }
179        match self {
180            Self::Head => execute_head(args),
181            Self::Last => execute_last(args),
182            Self::Tail => execute_tail(args),
183            Self::Size => execute_size(args),
184            Self::Range => execute_range(args),
185            Self::Keys => execute_keys(args),
186            Self::ReverseList => execute_reverse_list(args),
187            Self::ToSet => execute_toset(args),
188            Self::ListContains => execute_list_contains(args),
189            Self::ListAppend => execute_list_append(args),
190            Self::ListPrepend => execute_list_prepend(args),
191            Self::ListFilter => execute_list_filter(args),
192            Self::ListTransform => execute_list_transform(args),
193            Self::ListConcat => execute_list_concat(args),
194            Self::ListSort => execute_list_sort(args),
195            Self::ListSlice => execute_list_slice(args),
196            Self::ListToString => execute_list_to_string(args),
197            Self::ListDistinct => execute_list_distinct(args),
198            Self::ListUnique => execute_list_distinct(args),
199            Self::ListExtract => execute_list_extract(args),
200            Self::ListAny => execute_list_any(args),
201            Self::ListAll => execute_list_all(args),
202            Self::ListSingle => execute_list_single(args),
203            Self::ListReduce => execute_list_reduce(args),
204            Self::StructPack => execute_struct_pack(args),
205            Self::StructExtract => execute_struct_extract(args),
206            Self::MapCreation => execute_map_creation(args),
207            Self::MapExtract => execute_map_extract(args),
208            Self::ElementAt => execute_element_at(args),
209            Self::Cardinality => execute_cardinality(args),
210            Self::MapKeys => execute_map_keys(args),
211            Self::MapValues => execute_map_values(args),
212        }
213    }
214}
215
216fn execute_head(args: &[Value]) -> Result<Value, ExpressionError> {
217    match &args[0] {
218        Value::List(list) => Ok(list
219            .values
220            .first()
221            .cloned()
222            .unwrap_or(Value::Null(NullType::Null))),
223        Value::Null(_) => Ok(Value::Null(NullType::Null)),
224        _ => Err(ExpressionError::type_error("head requires a list type")),
225    }
226}
227
228fn execute_last(args: &[Value]) -> Result<Value, ExpressionError> {
229    match &args[0] {
230        Value::List(list) => Ok(list
231            .values
232            .last()
233            .cloned()
234            .unwrap_or(Value::Null(NullType::Null))),
235        Value::Null(_) => Ok(Value::Null(NullType::Null)),
236        _ => Err(ExpressionError::type_error("last requires a list type")),
237    }
238}
239
240fn execute_tail(args: &[Value]) -> Result<Value, ExpressionError> {
241    match &args[0] {
242        Value::List(list) => {
243            if list.values.is_empty() {
244                Ok(Value::list(List { values: vec![] }))
245            } else {
246                Ok(Value::list(List {
247                    values: list.values[1..].to_vec(),
248                }))
249            }
250        }
251        Value::Null(_) => Ok(Value::Null(NullType::Null)),
252        _ => Err(ExpressionError::type_error("tail requires a list type")),
253    }
254}
255
256fn execute_size(args: &[Value]) -> Result<Value, ExpressionError> {
257    match &args[0] {
258        Value::String(s) => Ok(Value::BigInt(s.chars().count() as i64)),
259        Value::List(list) => Ok(Value::BigInt(list.values.len() as i64)),
260        Value::Map(map) => Ok(Value::BigInt(map.len() as i64)),
261        Value::Set(set) => Ok(Value::BigInt(set.len() as i64)),
262        Value::Json(j) => {
263            if let Ok(v) = j.to_value() {
264                match v {
265                    serde_json::Value::Object(m) => Ok(Value::BigInt(m.len() as i64)),
266                    serde_json::Value::Array(a) => Ok(Value::BigInt(a.len() as i64)),
267                    _ => Ok(Value::BigInt(1)),
268                }
269            } else {
270                Ok(Value::Null(NullType::Null))
271            }
272        }
273        Value::JsonB(j) => match j.as_value() {
274            serde_json::Value::Object(m) => Ok(Value::BigInt(m.len() as i64)),
275            serde_json::Value::Array(a) => Ok(Value::BigInt(a.len() as i64)),
276            _ => Ok(Value::BigInt(1)),
277        },
278        Value::Null(_) => Ok(Value::Null(NullType::Null)),
279        _ => Err(ExpressionError::type_error(
280            "size requires string, list, map, set, or json type",
281        )),
282    }
283}
284
285fn execute_range(args: &[Value]) -> Result<Value, ExpressionError> {
286    if args.len() < 2 || args.len() > 3 {
287        return Err(ExpressionError::type_error(
288            "range requires 2 or 3 arguments",
289        ));
290    }
291    let to_i64 = |value: &Value| -> Option<i64> {
292        match value {
293            Value::SmallInt(i) => Some(*i as i64),
294            Value::Int(i) => Some(*i as i64),
295            Value::BigInt(i) => Some(*i),
296            _ => None,
297        }
298    };
299    let start = match to_i64(&args[0]) {
300        Some(v) => v,
301        None if args[0].is_null() => return Ok(Value::Null(NullType::Null)),
302        None => {
303            return Err(ExpressionError::type_error(
304                "range requires integer arguments",
305            ))
306        }
307    };
308    let end = match to_i64(&args[1]) {
309        Some(v) => v,
310        None if args[1].is_null() => return Ok(Value::Null(NullType::Null)),
311        None => {
312            return Err(ExpressionError::type_error(
313                "The range function takes integer arguments",
314            ))
315        }
316    };
317    let step = if args.len() > 2 {
318        match to_i64(&args[2]) {
319            Some(v) => v,
320            None if args[2].is_null() => return Ok(Value::Null(NullType::Null)),
321            None => return Err(ExpressionError::type_error("range step must be an integer")),
322        }
323    } else {
324        1
325    };
326
327    if step == 0 {
328        return Err(ExpressionError::new(
329            crate::executor::expression::ExpressionErrorType::InvalidOperation,
330            "range step cannot be 0".to_string(),
331        ));
332    }
333
334    let mut result = Vec::new();
335    if step > 0 {
336        let mut i = start;
337        while i <= end {
338            result.push(Value::BigInt(i));
339            i += step;
340        }
341    } else {
342        let mut i = start;
343        while i >= end {
344            result.push(Value::BigInt(i));
345            i += step;
346        }
347    }
348
349    Ok(Value::list(List { values: result }))
350}
351
352fn execute_keys(args: &[Value]) -> Result<Value, ExpressionError> {
353    let mut keys: BTreeSet<String> = BTreeSet::new();
354
355    match &args[0] {
356        Value::Vertex(v) => {
357            for key in v.tag.properties.keys() {
358                keys.insert(key.to_string());
359            }
360        }
361        Value::Edge(e) => {
362            for key in e.props.keys() {
363                keys.insert(key.to_string());
364            }
365        }
366        Value::Map(m) => {
367            for key in m.keys() {
368                // Map keys are generalized Values; stringify for the keys()
369                // result list.
370                keys.insert(format!("{}", key));
371            }
372        }
373        Value::Json(j) => {
374            if let Ok(serde_json::Value::Object(m)) = j.to_value() {
375                for key in m.keys() {
376                    keys.insert(key.to_string());
377                }
378            }
379        }
380        Value::JsonB(j) => {
381            if let serde_json::Value::Object(m) = j.as_value() {
382                for key in m.keys() {
383                    keys.insert(key.to_string());
384                }
385            }
386        }
387        Value::Null(_) => return Ok(Value::Null(NullType::Null)),
388        _ => {
389            return Err(ExpressionError::type_error(
390                "keys requires vertex, edge, map or json type",
391            ))
392        }
393    }
394
395    let result: Vec<Value> = keys.into_iter().map(Value::string).collect();
396    Ok(Value::list(List { values: result }))
397}
398
399fn execute_reverse_list(args: &[Value]) -> Result<Value, ExpressionError> {
400    match &args[0] {
401        Value::List(list) => {
402            let mut reversed = list.values.clone();
403            reversed.reverse();
404            Ok(Value::list(List { values: reversed }))
405        }
406        Value::Null(_) => Ok(Value::Null(NullType::Null)),
407        _ => Err(ExpressionError::type_error("reverse requires a list type")),
408    }
409}
410
411fn execute_toset(args: &[Value]) -> Result<Value, ExpressionError> {
412    match &args[0] {
413        Value::List(list) => {
414            let set: std::collections::HashSet<Value> = list.values.iter().cloned().collect();
415            Ok(Value::set(set))
416        }
417        Value::Null(_) => Ok(Value::Null(NullType::Null)),
418        _ => Err(ExpressionError::type_error("toset requires a list type")),
419    }
420}
421
422fn execute_list_contains(args: &[Value]) -> Result<Value, ExpressionError> {
423    match (&args[0], &args[1]) {
424        (Value::List(list), value) => {
425            let contains = list.values.iter().any(|v| v == value);
426            Ok(Value::Bool(contains))
427        }
428        (Value::Null(_), _) | (_, Value::Null(_)) => Ok(Value::Null(NullType::Null)),
429        _ => Err(ExpressionError::type_error(
430            "list_contains requires a list as first argument",
431        )),
432    }
433}
434
435fn execute_list_append(args: &[Value]) -> Result<Value, ExpressionError> {
436    match &args[0] {
437        Value::List(list) => {
438            let mut new_values = list.values.clone();
439            new_values.push(args[1].clone());
440            Ok(Value::list(List { values: new_values }))
441        }
442        Value::Null(_) => Ok(Value::Null(NullType::Null)),
443        _ => Err(ExpressionError::type_error(
444            "list_append requires a list as first argument",
445        )),
446    }
447}
448
449fn execute_list_prepend(args: &[Value]) -> Result<Value, ExpressionError> {
450    match &args[0] {
451        Value::List(list) => {
452            let mut new_values = vec![args[1].clone()];
453            new_values.extend(list.values.clone());
454            Ok(Value::list(List { values: new_values }))
455        }
456        Value::Null(_) => Ok(Value::Null(NullType::Null)),
457        _ => Err(ExpressionError::type_error(
458            "list_prepend requires a list as first argument",
459        )),
460    }
461}
462
463fn execute_list_filter(args: &[Value]) -> Result<Value, ExpressionError> {
464    match (&args[0], &args[1]) {
465        (Value::List(list), Value::List(mask)) => {
466            let min_len = list.values.len().min(mask.values.len());
467            let filtered: Vec<Value> = list.values[..min_len]
468                .iter()
469                .zip(&mask.values[..min_len])
470                .filter(|(_, m)| match m {
471                    Value::Bool(b) => *b,
472                    Value::Null(_) => false,
473                    _ => true,
474                })
475                .map(|(v, _)| v.clone())
476                .collect();
477            Ok(Value::list(List { values: filtered }))
478        }
479        (Value::List(_), _) => Err(ExpressionError::type_error(
480            "list_filter second argument must be a boolean list",
481        )),
482        (Value::Null(_), _) => Ok(Value::Null(NullType::Null)),
483        _ => Err(ExpressionError::type_error(
484            "list_filter requires a list as first argument",
485        )),
486    }
487}
488
489fn execute_list_transform(args: &[Value]) -> Result<Value, ExpressionError> {
490    match (&args[0], &args[1]) {
491        (Value::List(list), Value::List(replacements)) => {
492            let mut result = list.values.clone();
493            for (item, replacement) in result.iter_mut().zip(replacements.values.iter()) {
494                if !replacement.is_null() {
495                    *item = replacement.clone();
496                }
497            }
498            Ok(Value::list(List { values: result }))
499        }
500        (Value::List(_), _) => Err(ExpressionError::type_error(
501            "list_transform second argument must be a list",
502        )),
503        (Value::Null(_), _) => Ok(Value::Null(NullType::Null)),
504        _ => Err(ExpressionError::type_error(
505            "list_transform requires lists as arguments",
506        )),
507    }
508}
509
510fn execute_list_concat(args: &[Value]) -> Result<Value, ExpressionError> {
511    if args.len() < 2 {
512        return Err(ExpressionError::type_error(
513            "list_concat requires at least 2 arguments",
514        ));
515    }
516    let mut result = Vec::new();
517    for arg in args {
518        match arg {
519            Value::List(list) => result.extend(list.values.clone()),
520            Value::Null(_) => return Ok(Value::Null(NullType::Null)),
521            _ => {
522                return Err(ExpressionError::type_error(
523                    "list_concat requires list arguments",
524                ))
525            }
526        }
527    }
528    Ok(Value::list(List { values: result }))
529}
530
531fn execute_list_sort(args: &[Value]) -> Result<Value, ExpressionError> {
532    match &args[0] {
533        Value::List(list) => {
534            let mut sorted = list.values.clone();
535            sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
536            Ok(Value::list(List { values: sorted }))
537        }
538        Value::Null(_) => Ok(Value::Null(NullType::Null)),
539        _ => Err(ExpressionError::type_error(
540            "list_sort requires a list type",
541        )),
542    }
543}
544
545fn execute_list_slice(args: &[Value]) -> Result<Value, ExpressionError> {
546    match (&args[0], &args[1], &args[2]) {
547        (Value::List(list), Value::Int(start), Value::Int(end)) => {
548            let len = list.values.len() as i32;
549            let start = if *start < 0 {
550                (len + *start).max(0)
551            } else {
552                (*start).min(len)
553            } as usize;
554            let end = if *end < 0 {
555                (len + *end).max(0)
556            } else {
557                (*end).min(len)
558            } as usize;
559            if start >= end {
560                Ok(Value::list(List { values: vec![] }))
561            } else {
562                Ok(Value::list(List {
563                    values: list.values[start..end].to_vec(),
564                }))
565            }
566        }
567        (Value::Null(_), _, _) | (_, Value::Null(_), _) | (_, _, Value::Null(_)) => {
568            Ok(Value::Null(NullType::Null))
569        }
570        _ => Err(ExpressionError::type_error(
571            "list_slice requires list, integer, and integer arguments",
572        )),
573    }
574}
575
576fn execute_list_to_string(args: &[Value]) -> Result<Value, ExpressionError> {
577    match (&args[0], &args[1]) {
578        (Value::List(list), Value::String(delimiter)) => {
579            let strings: Vec<String> = list.values.iter().map(value_to_string).collect();
580            Ok(Value::string(strings.join(delimiter)))
581        }
582        (Value::Null(_), _) | (_, Value::Null(_)) => Ok(Value::Null(NullType::Null)),
583        _ => Err(ExpressionError::type_error(
584            "list_to_string requires list and string arguments",
585        )),
586    }
587}
588
589fn execute_list_distinct(args: &[Value]) -> Result<Value, ExpressionError> {
590    match &args[0] {
591        Value::List(list) => {
592            let mut seen = std::collections::HashSet::new();
593            let mut result = Vec::new();
594            for item in &list.values {
595                if seen.insert(item.clone()) {
596                    result.push(item.clone());
597                }
598            }
599            Ok(Value::list(List { values: result }))
600        }
601        Value::Null(_) => Ok(Value::Null(NullType::Null)),
602        _ => Err(ExpressionError::type_error(
603            "list_distinct requires a list type",
604        )),
605    }
606}
607
608fn execute_list_extract(args: &[Value]) -> Result<Value, ExpressionError> {
609    let len = match &args[0] {
610        Value::List(list) => list.values.len() as i64,
611        Value::Null(_) => return Ok(Value::Null(NullType::Null)),
612        _ => {
613            return Err(ExpressionError::type_error(
614                "list_extract requires a list as first argument",
615            ))
616        }
617    };
618    let idx = match &args[1] {
619        Value::Int(i) => {
620            let i = *i as i64;
621            if i < 0 {
622                len + i
623            } else {
624                i
625            }
626        }
627        Value::BigInt(i) => {
628            let i = *i;
629            if i < 0 {
630                len + i
631            } else {
632                i
633            }
634        }
635        Value::Null(_) => return Ok(Value::Null(NullType::Null)),
636        _ => {
637            return Err(ExpressionError::type_error(
638                "list_extract requires an integer as second argument",
639            ))
640        }
641    };
642    let list = match &args[0] {
643        Value::List(list) => list,
644        _ => unreachable!(),
645    };
646    if idx < 0 || idx >= len {
647        Ok(Value::Null(NullType::Null))
648    } else {
649        Ok(list.values[idx as usize].clone())
650    }
651}
652
653fn execute_list_any(args: &[Value]) -> Result<Value, ExpressionError> {
654    match (&args[0], &args[1]) {
655        (Value::List(list), Value::List(mask)) => {
656            let min_len = list.values.len().min(mask.values.len());
657            let any_true = mask.values[..min_len]
658                .iter()
659                .any(|m| matches!(m, Value::Bool(true)));
660            Ok(Value::Bool(any_true))
661        }
662        (Value::List(_), _) => Err(ExpressionError::type_error(
663            "list_any second argument must be a boolean list",
664        )),
665        (Value::Null(_), _) => Ok(Value::Null(NullType::Null)),
666        _ => Err(ExpressionError::type_error(
667            "list_any requires a list as first argument",
668        )),
669    }
670}
671
672fn execute_list_all(args: &[Value]) -> Result<Value, ExpressionError> {
673    match (&args[0], &args[1]) {
674        (Value::List(list), Value::List(mask)) => {
675            let min_len = list.values.len().min(mask.values.len());
676            if min_len == 0 {
677                return Ok(Value::Bool(true));
678            }
679            let all_true = mask.values[..min_len]
680                .iter()
681                .all(|m| matches!(m, Value::Bool(true)));
682            Ok(Value::Bool(all_true))
683        }
684        (Value::List(_), _) => Err(ExpressionError::type_error(
685            "list_all second argument must be a boolean list",
686        )),
687        (Value::Null(_), _) => Ok(Value::Null(NullType::Null)),
688        _ => Err(ExpressionError::type_error(
689            "list_all requires a list as first argument",
690        )),
691    }
692}
693
694fn execute_list_single(args: &[Value]) -> Result<Value, ExpressionError> {
695    match (&args[0], &args[1]) {
696        (Value::List(list), Value::List(mask)) => {
697            let min_len = list.values.len().min(mask.values.len());
698            let count = mask.values[..min_len]
699                .iter()
700                .filter(|m| matches!(m, Value::Bool(true)))
701                .count();
702            Ok(Value::Bool(count == 1))
703        }
704        (Value::List(_), _) => Err(ExpressionError::type_error(
705            "list_single second argument must be a boolean list",
706        )),
707        (Value::Null(_), _) => Ok(Value::Null(NullType::Null)),
708        _ => Err(ExpressionError::type_error(
709            "list_single requires a list as first argument",
710        )),
711    }
712}
713
714fn execute_list_reduce(args: &[Value]) -> Result<Value, ExpressionError> {
715    // Lambda evaluation lives in the expression evaluator, which binds
716    // (acc, item) per element and runs the lambda body. Eager values here
717    // can never represent a lambda, so any direct registry call is a
718    // non-lambda misuse rather than a second reduce implementation.
719    if matches!(&args[0], Value::Null(_)) {
720        return Ok(Value::Null(NullType::Null));
721    }
722    Err(ExpressionError::type_error(
723        "list_reduce requires (source, lambda, initial) with a lambda as second argument",
724    ))
725}
726
727fn value_to_string(v: &Value) -> String {
728    match v {
729        Value::Null(_) => String::new(),
730        Value::Bool(b) => b.to_string(),
731        Value::SmallInt(i) => i.to_string(),
732        Value::Int(i) => i.to_string(),
733        Value::BigInt(i) => i.to_string(),
734        Value::Float(f) => f.to_string(),
735        Value::Double(f) => f.to_string(),
736        Value::String(s) => s.to_string(),
737        _ => format!("{:?}", v),
738    }
739}
740
741fn execute_struct_pack(args: &[Value]) -> Result<Value, ExpressionError> {
742    if !args.len().is_multiple_of(2) {
743        return Err(ExpressionError::type_error(
744            "struct_pack requires an even number of arguments (key-value pairs)",
745        ));
746    }
747    let mut fields = std::collections::HashMap::new();
748    let mut i = 0;
749    while i < args.len() {
750        let key = match &args[i] {
751            Value::String(s) => s.to_string(),
752            Value::Null(_) => return Ok(Value::Null(NullType::Null)),
753            _ => {
754                return Err(ExpressionError::type_error(
755                    "struct_pack keys must be strings",
756                ))
757            }
758        };
759        fields.insert(key, args[i + 1].clone());
760        i += 2;
761    }
762    Ok(Value::string_map(fields))
763}
764
765fn execute_struct_extract(args: &[Value]) -> Result<Value, ExpressionError> {
766    match (&args[0], &args[1]) {
767        (Value::Map(map), Value::String(key)) => {
768            let key_val = Value::string(key);
769            Ok(map
770                .get(&key_val)
771                .cloned()
772                .unwrap_or(Value::Null(NullType::Null)))
773        }
774        (Value::Null(_), _) | (_, Value::Null(_)) => Ok(Value::Null(NullType::Null)),
775        _ => Err(ExpressionError::type_error(
776            "struct_extract requires a map and a string key",
777        )),
778    }
779}
780
781fn execute_map_creation(args: &[Value]) -> Result<Value, ExpressionError> {
782    if !args.len().is_multiple_of(2) {
783        return Err(ExpressionError::type_error(
784            "map requires an even number of arguments (key-value pairs)",
785        ));
786    }
787    let mut fields = std::collections::HashMap::new();
788    let mut i = 0;
789    while i < args.len() {
790        let key = match &args[i] {
791            Value::String(s) => s.to_string(),
792            Value::Null(_) => return Ok(Value::Null(NullType::Null)),
793            _ => return Err(ExpressionError::type_error("map keys must be strings")),
794        };
795        fields.insert(key, args[i + 1].clone());
796        i += 2;
797    }
798    Ok(Value::string_map(fields))
799}
800
801fn execute_map_extract(args: &[Value]) -> Result<Value, ExpressionError> {
802    match (&args[0], &args[1]) {
803        (Value::Map(map), Value::String(key)) => {
804            let key_val = Value::string(key);
805            Ok(map
806                .get(&key_val)
807                .cloned()
808                .unwrap_or(Value::Null(NullType::Null)))
809        }
810        (Value::Null(_), _) | (_, Value::Null(_)) => Ok(Value::Null(NullType::Null)),
811        _ => Err(ExpressionError::type_error(
812            "map_extract requires a map and a string key",
813        )),
814    }
815}
816
817fn execute_element_at(args: &[Value]) -> Result<Value, ExpressionError> {
818    match (&args[0], &args[1]) {
819        (Value::List(list), Value::Int(idx)) => {
820            let idx = *idx;
821            let len = list.values.len() as i32;
822            let actual_idx = if idx < 0 { len + idx } else { idx };
823            if actual_idx < 0 || actual_idx >= len {
824                Ok(Value::Null(NullType::Null))
825            } else {
826                Ok(list.values[actual_idx as usize].clone())
827            }
828        }
829        (Value::List(list), Value::BigInt(idx)) => {
830            let idx = *idx;
831            let len = list.values.len() as i64;
832            let actual_idx = if idx < 0 { len + idx } else { idx };
833            if actual_idx < 0 || actual_idx >= len {
834                Ok(Value::Null(NullType::Null))
835            } else {
836                Ok(list.values[actual_idx as usize].clone())
837            }
838        }
839        (Value::Map(map), Value::String(key)) => {
840            let key_val = Value::string(key);
841            Ok(map
842                .get(&key_val)
843                .cloned()
844                .unwrap_or(Value::Null(NullType::Null)))
845        }
846        (Value::Null(_), _) | (_, Value::Null(_)) => Ok(Value::Null(NullType::Null)),
847        _ => Err(ExpressionError::type_error(
848            "element_at requires a list or map as first argument",
849        )),
850    }
851}
852
853fn execute_cardinality(args: &[Value]) -> Result<Value, ExpressionError> {
854    match &args[0] {
855        Value::Map(map) => Ok(Value::BigInt(map.len() as i64)),
856        Value::Set(set) => Ok(Value::BigInt(set.len() as i64)),
857        Value::List(list) => Ok(Value::BigInt(list.values.len() as i64)),
858        Value::Null(_) => Ok(Value::Null(NullType::Null)),
859        _ => Err(ExpressionError::type_error(
860            "cardinality requires a map, set, or list type",
861        )),
862    }
863}
864
865fn execute_map_keys(args: &[Value]) -> Result<Value, ExpressionError> {
866    match &args[0] {
867        Value::Map(map) => {
868            let keys: Vec<Value> = map.keys().cloned().collect();
869            Ok(Value::list(List { values: keys }))
870        }
871        Value::Null(_) => Ok(Value::Null(NullType::Null)),
872        _ => Err(ExpressionError::type_error("map_keys requires a map type")),
873    }
874}
875
876fn execute_map_values(args: &[Value]) -> Result<Value, ExpressionError> {
877    match &args[0] {
878        Value::Map(map) => {
879            let values: Vec<Value> = map.values().cloned().collect();
880            Ok(Value::list(List { values }))
881        }
882        Value::Null(_) => Ok(Value::Null(NullType::Null)),
883        _ => Err(ExpressionError::type_error(
884            "map_values requires a map type",
885        )),
886    }
887}
888
889#[cfg(test)]
890mod tests;