1use super::comparison::double_cmp;
2use super::get_string;
3use crate::registry::*;
4use akar_common::types::Value;
5
6pub(crate) fn evaluate_list(op: ListOp, args: &[Value]) -> Result<Value, String> {
9 match op {
10 ListOp::Creation => {
11 Ok(Value::List(args.to_vec()))
13 }
14 ListOp::Len => match &args[0] {
15 Value::List(items) => Ok(Value::Int64(items.len() as i64)),
16 _ => Err("Expected list".into()),
17 },
18 ListOp::Extract => {
19 let list = match &args[0] {
20 Value::List(items) => items,
21 _ => return Err("Expected list".into()),
22 };
23 let idx = match &args[1] {
24 Value::Int64(i) => {
25 if *i < 1 {
27 return Err("List index must be >= 1".into());
28 }
29 (*i - 1) as usize
30 }
31 _ => return Err("Index must be integer".into()),
32 };
33 list.get(idx)
34 .cloned()
35 .ok_or_else(|| format!("Index {idx} out of bounds"))
36 }
37 ListOp::Concat => {
38 let mut result = Vec::new();
39 for arg in args {
40 match arg {
41 Value::List(items) => result.extend(items.clone()),
42 _ => result.push(arg.clone()),
43 }
44 }
45 Ok(Value::List(result))
46 }
47 ListOp::Sort => {
48 let mut list = match args[0].clone() {
49 Value::List(items) => items,
50 _ => return Err("Expected list".into()),
51 };
52 list.sort_by(|a, b| {
54 match compare_values_for_sort(a, b) {
55 Ok(ord) => ord,
56 Err(_) => std::cmp::Ordering::Equal, }
58 });
59 Ok(Value::List(list))
60 }
61 ListOp::Contains => {
62 let list = match &args[0] {
63 Value::List(items) => items,
64 _ => return Err("Expected list".into()),
65 };
66 Ok(Value::Bool(list.contains(&args[1])))
67 }
68 ListOp::Append => {
69 let mut list = match args[0].clone() {
70 Value::List(items) => items,
71 _ => return Err("Expected list".into()),
72 };
73 list.push(args[1].clone());
74 Ok(Value::List(list))
75 }
76 ListOp::Prepend => {
77 let mut list = match args[0].clone() {
78 Value::List(items) => items,
79 _ => return Err("Expected list".into()),
80 };
81 list.insert(0, args[1].clone());
82 Ok(Value::List(list))
83 }
84 ListOp::Reverse => {
85 let mut list = match args[0].clone() {
86 Value::List(items) => items,
87 _ => return Err("Expected list".into()),
88 };
89 list.reverse();
90 Ok(Value::List(list))
91 }
92 ListOp::Slice => {
93 let list = match &args[0] {
94 Value::List(items) => items,
95 _ => return Err("Expected list".into()),
96 };
97 let start = match &args[1] {
98 Value::Int64(i) => {
99 if *i < 1 {
100 return Err("Slice start index must be >= 1".into());
101 }
102 (*i - 1) as usize
103 }
104 _ => return Err("Slice start must be integer".into()),
105 };
106 if start >= list.len() {
107 return Err("Slice start index out of bounds".into());
108 }
109 if args.len() >= 3 {
110 let end = match &args[2] {
112 Value::Int64(i) => {
113 if *i < 1 {
114 return Err("Slice end index must be >= 1".into());
115 }
116 (*i - 1) as usize
117 }
118 _ => return Err("Slice end must be integer".into()),
119 };
120 if end >= list.len() || end < start {
121 return Err("Slice end index out of bounds".into());
122 }
123 Ok(Value::List(list[start..=end].to_vec()))
124 } else {
125 Ok(Value::List(list[start..].to_vec()))
127 }
128 }
129 ListOp::Range => {
131 let step = if args.len() >= 3 {
132 match &args[2] {
133 Value::Int64(s) => *s,
134 _ => 1i64,
135 }
136 } else {
137 1i64
138 };
139 let (start, end) = if args.len() >= 2 {
140 match (&args[0], &args[1]) {
141 (Value::Int64(s), Value::Int64(e)) => (*s, *e),
142 _ => return Err("RANGE requires integer arguments".into()),
143 }
144 } else {
145 match &args[0] {
146 Value::Int64(e) => (0i64, *e),
147 _ => return Err("RANGE requires integer arguments".into()),
148 }
149 };
150 if step == 0 {
151 return Err("Step of range cannot be 0".into());
152 }
153 if (end - start).signum() != step.signum() && end != start {
154 Ok(Value::List(vec![]))
155 } else {
156 let size = ((end - start).unsigned_abs() / step.unsigned_abs()) + 1;
157 let items: Vec<Value> = (0..size).map(|i| Value::Int64(start + step * i as i64)).collect();
158 Ok(Value::List(items))
159 }
160 }
161 ListOp::Distinct => {
162 let list = match &args[0] {
163 Value::List(items) => items,
164 _ => return Err("Expected list".into()),
165 };
166 let mut seen = hashbrown::HashSet::new();
167 let mut result = Vec::new();
168 for item in list {
169 if !matches!(item, Value::Null) && seen.insert(format!("{:?}", item)) {
170 result.push(item.clone());
171 }
172 }
173 Ok(Value::List(result))
174 }
175 ListOp::Unique => {
176 let list = match &args[0] {
177 Value::List(items) => items,
178 _ => return Err("Expected list".into()),
179 };
180 let mut seen = hashbrown::HashSet::new();
181 for item in list {
182 if !matches!(item, Value::Null) {
183 seen.insert(format!("{:?}", item));
184 }
185 }
186 Ok(Value::Int64(seen.len() as i64))
187 }
188 ListOp::Count => {
189 let list = match &args[0] {
190 Value::List(items) => items,
191 _ => return Err("Expected list".into()),
192 };
193 let val = &args[1];
194 let mut count = 0;
195 for item in list {
196 if item == val {
197 count += 1;
198 }
199 }
200 Ok(Value::Int64(count as i64))
201 }
202 ListOp::Min => {
203 let list = match &args[0] {
204 Value::List(items) => items,
205 _ => return Err("Expected list".into()),
206 };
207 if list.is_empty() {
208 return Ok(Value::Null);
209 }
210 let mut min_val = list[0].clone();
211 for item in list.iter().skip(1) {
212 if let Ok(std::cmp::Ordering::Less) = compare_values_for_sort(item, &min_val) {
213 min_val = item.clone();
214 }
215 }
216 Ok(min_val)
217 }
218 ListOp::Max => {
219 let list = match &args[0] {
220 Value::List(items) => items,
221 _ => return Err("Expected list".into()),
222 };
223 if list.is_empty() {
224 return Ok(Value::Null);
225 }
226 let mut max_val = list[0].clone();
227 for item in list.iter().skip(1) {
228 if let Ok(std::cmp::Ordering::Greater) = compare_values_for_sort(item, &max_val) {
229 max_val = item.clone();
230 }
231 }
232 Ok(max_val)
233 }
234 ListOp::HasAny => {
235 let list = match &args[0] {
236 Value::List(items) => items,
237 _ => return Err("Expected list".into()),
238 };
239 let search_items = match &args[1] {
240 Value::List(items) => items,
241 _ => return Err("Expected list for second argument".into()),
242 };
243 for search_item in search_items {
244 if list.contains(search_item) {
245 return Ok(Value::Bool(true));
246 }
247 }
248 Ok(Value::Bool(false))
249 }
250 ListOp::Sum => {
251 let list = match &args[0] {
252 Value::List(items) => items,
253 _ => return Err("Expected list".into()),
254 };
255 let mut sum: f64 = 0.0;
256 let mut is_int = true;
257 for item in list {
258 match item {
259 Value::Null => continue,
260 Value::Int64(x) => sum += *x as f64,
261 Value::Double(x) => {
262 sum += x;
263 is_int = false;
264 }
265 _ => return Err("LIST_SUM requires numeric list".into()),
266 }
267 }
268 if is_int {
269 Ok(Value::Int64(sum as i64))
270 } else {
271 Ok(Value::Double(sum))
272 }
273 }
274 ListOp::Product => {
275 let list = match &args[0] {
276 Value::List(items) => items,
277 _ => return Err("Expected list".into()),
278 };
279 let mut prod: f64 = 1.0;
280 let mut is_int = true;
281 for item in list {
282 match item {
283 Value::Null => continue,
284 Value::Int64(x) => prod *= *x as f64,
285 Value::Double(x) => {
286 prod *= x;
287 is_int = false;
288 }
289 _ => return Err("LIST_PRODUCT requires numeric list".into()),
290 }
291 }
292 if is_int {
293 Ok(Value::Int64(prod as i64))
294 } else {
295 Ok(Value::Double(prod))
296 }
297 }
298 ListOp::AnyValue => {
299 let list = match &args[0] {
300 Value::List(items) => items,
301 _ => return Err("Expected list".into()),
302 };
303 match list.iter().find(|v| !matches!(v, Value::Null)) {
305 Some(v) => Ok(v.clone()),
306 None => Ok(Value::Null),
307 }
308 }
309 ListOp::ToString => {
310 if args.len() < 2 {
312 return Err("list_to_string requires delimiter and list arguments".into());
313 }
314 let delim = get_string(&args[0])?;
315 let list = match &args[1] {
316 Value::List(items) => items,
317 _ => return Err("Expected list".into()),
318 };
319 let mut result = String::new();
320 let mut first = true;
321 for item in list {
322 if matches!(item, Value::Null) {
323 continue;
324 }
325 if !first {
326 result.push_str(&delim);
327 }
328 match item {
329 Value::String(s) => result.push_str(s),
330 other => result.push_str(&format!("{:?}", other)),
331 }
332 first = false;
333 }
334 Ok(Value::String(result))
335 }
336 ListOp::Position => {
337 let list = match &args[0] {
338 Value::List(items) => items,
339 _ => return Err("Expected list".into()),
340 };
341 let target = &args[1];
342 for (i, item) in list.iter().enumerate() {
344 if item == target {
345 return Ok(Value::Int64((i + 1) as i64));
346 }
347 }
348 Ok(Value::Int64(0))
349 }
350 ListOp::HasAll => {
351 let left = match &args[0] {
352 Value::List(items) => items,
353 _ => return Err("Expected list".into()),
354 };
355 let right = match &args[1] {
356 Value::List(items) => items,
357 _ => return Err("Expected list".into()),
358 };
359 for target in right {
360 if matches!(target, Value::Null) {
361 continue;
362 }
363 if !left.contains(target) {
364 return Ok(Value::Bool(false));
365 }
366 }
367 Ok(Value::Bool(true))
368 }
369 ListOp::ReverseSort => {
370 let mut list = match args[0].clone() {
371 Value::List(items) => items,
372 _ => return Err("Expected list".into()),
373 };
374 list.sort_by(|a, b| match compare_values_for_sort(a, b) {
375 Ok(ord) => ord.reverse(),
376 Err(_) => std::cmp::Ordering::Equal,
377 });
378 Ok(Value::List(list))
379 }
380 ListOp::Any => {
382 let list = match &args[0] {
383 Value::List(items) => items,
384 _ => return Err("Expected list".into()),
385 };
386 Ok(Value::Bool(list.iter().any(is_truthy)))
387 }
388 ListOp::All => {
389 let list = match &args[0] {
390 Value::List(items) => items,
391 _ => return Err("Expected list".into()),
392 };
393 Ok(Value::Bool(!list.is_empty() && list.iter().all(is_truthy)))
394 }
395 ListOp::None => {
396 let list = match &args[0] {
397 Value::List(items) => items,
398 _ => return Err("Expected list".into()),
399 };
400 Ok(Value::Bool(list.iter().all(|v| !is_truthy(v))))
401 }
402 ListOp::Single => {
403 let list = match &args[0] {
404 Value::List(items) => items,
405 _ => return Err("Expected list".into()),
406 };
407 let count = list.iter().filter(|v| is_truthy(v)).count();
408 Ok(Value::Bool(count == 1))
409 }
410 ListOp::Transform => Err("list_transform requires a lambda expression — use in a query context".into()),
412 ListOp::Filter => Err("list_filter requires a lambda expression — use in a query context".into()),
413 ListOp::Reduce => Err("list_reduce requires a lambda expression — use in a query context".into()),
414 }
415}
416
417fn is_truthy(v: &Value) -> bool {
419 match v {
420 Value::Bool(b) => *b,
421 Value::Int64(x) => *x != 0,
422 Value::Double(x) => *x != 0.0,
423 _ => false,
424 }
425}
426
427pub(crate) fn compare_values_for_sort(a: &Value, b: &Value) -> Result<std::cmp::Ordering, String> {
429 match (a, b) {
430 (Value::Null, Value::Null) => Ok(std::cmp::Ordering::Equal),
431 (Value::Null, _) => Ok(std::cmp::Ordering::Less),
432 (_, Value::Null) => Ok(std::cmp::Ordering::Greater),
433 (Value::Int64(x), Value::Int64(y)) => Ok(x.cmp(y)),
434 (Value::Int32(x), Value::Int32(y)) => Ok(x.cmp(y)),
435 (Value::Int16(x), Value::Int16(y)) => Ok(x.cmp(y)),
436 (Value::Int8(x), Value::Int8(y)) => Ok(x.cmp(y)),
437 (Value::UInt64(x), Value::UInt64(y)) => Ok(x.cmp(y)),
438 (Value::UInt32(x), Value::UInt32(y)) => Ok(x.cmp(y)),
439 (Value::UInt16(x), Value::UInt16(y)) => Ok(x.cmp(y)),
440 (Value::UInt8(x), Value::UInt8(y)) => Ok(x.cmp(y)),
441 (Value::Double(x), Value::Double(y)) => Ok(double_cmp(*x, *y)),
442 (Value::Float(x), Value::Float(y)) => Ok(double_cmp(*x as f64, *y as f64)),
443 (Value::String(x), Value::String(y)) => Ok(x.cmp(y)),
444 (Value::Bool(x), Value::Bool(y)) => Ok(x.cmp(y)),
445 (Value::Date(x), Value::Date(y)) => Ok(x.cmp(y)),
446 (Value::Timestamp(x), Value::Timestamp(y)) => Ok(x.cmp(y)),
447 (Value::Int64(x), Value::Double(y)) => Ok(double_cmp(*x as f64, *y)),
449 (Value::Double(x), Value::Int64(y)) => Ok(double_cmp(*x, *y as f64)),
450 _ => Err("Cannot compare types for sort".into()),
451 }
452}