1use crate::error::DkitError;
2use crate::query::filter::evaluate_condition;
3use crate::query::parser::{ArithmeticOp, Expr, LiteralValue};
4use crate::value::Value;
5
6pub fn evaluate_expr(row: &Value, expr: &Expr) -> Result<Value, DkitError> {
8 match expr {
9 Expr::Field(name) => match row {
10 Value::Object(map) => Ok(map.get(name).cloned().unwrap_or(Value::Null)),
11 _ => Err(DkitError::QueryError(format!(
12 "cannot access field '{}' on non-object value",
13 name
14 ))),
15 },
16 Expr::Literal(lit) => Ok(literal_to_value(lit)),
17 Expr::FuncCall { name, args } => {
18 let evaluated: Result<Vec<Value>, DkitError> =
19 args.iter().map(|a| evaluate_expr(row, a)).collect();
20 call_function(name, evaluated?)
21 }
22 Expr::BinaryOp { op, left, right } => {
23 let lv = evaluate_expr(row, left)?;
24 let rv = evaluate_expr(row, right)?;
25 evaluate_binary_op(op, &lv, &rv)
26 }
27 Expr::If {
28 condition,
29 then_expr,
30 else_expr,
31 } => {
32 if evaluate_condition(row, condition)? {
33 evaluate_expr(row, then_expr)
34 } else {
35 evaluate_expr(row, else_expr)
36 }
37 }
38 Expr::Case { branches, default } => {
39 for (condition, expr) in branches {
40 if evaluate_condition(row, condition)? {
41 return evaluate_expr(row, expr);
42 }
43 }
44 match default {
45 Some(expr) => evaluate_expr(row, expr),
46 None => Ok(Value::Null),
47 }
48 }
49 }
50}
51
52fn evaluate_binary_op(op: &ArithmeticOp, left: &Value, right: &Value) -> Result<Value, DkitError> {
54 if matches!(op, ArithmeticOp::Add) {
56 if let (Value::String(a), Value::String(b)) = (left, right) {
57 return Ok(Value::String(format!("{}{}", a, b)));
58 }
59 if matches!(left, Value::String(_)) || matches!(right, Value::String(_)) {
61 let a = value_to_display_string(left);
62 let b = value_to_display_string(right);
63 return Ok(Value::String(format!("{}{}", a, b)));
64 }
65 }
66
67 if matches!(left, Value::Null) || matches!(right, Value::Null) {
69 return Ok(Value::Null);
70 }
71
72 let (lf, li) = to_numeric(left)?;
74 let (rf, ri) = to_numeric(right)?;
75
76 if let (Some(a), Some(b)) = (li, ri) {
78 return match op {
79 ArithmeticOp::Add => Ok(Value::Integer(a.wrapping_add(b))),
80 ArithmeticOp::Sub => Ok(Value::Integer(a.wrapping_sub(b))),
81 ArithmeticOp::Mul => Ok(Value::Integer(a.wrapping_mul(b))),
82 ArithmeticOp::Div => {
83 if b == 0 {
84 return Err(DkitError::QueryError("division by zero".to_string()));
85 }
86 if a % b == 0 {
88 Ok(Value::Integer(a / b))
89 } else {
90 Ok(Value::Float(lf / rf))
91 }
92 }
93 };
94 }
95
96 match op {
98 ArithmeticOp::Add => Ok(Value::Float(lf + rf)),
99 ArithmeticOp::Sub => Ok(Value::Float(lf - rf)),
100 ArithmeticOp::Mul => Ok(Value::Float(lf * rf)),
101 ArithmeticOp::Div => {
102 if rf == 0.0 {
103 return Err(DkitError::QueryError("division by zero".to_string()));
104 }
105 Ok(Value::Float(lf / rf))
106 }
107 }
108}
109
110fn to_numeric(v: &Value) -> Result<(f64, Option<i64>), DkitError> {
112 match v {
113 Value::Integer(n) => Ok((*n as f64, Some(*n))),
114 Value::Float(f) => Ok((*f, None)),
115 Value::Bool(b) => {
116 let n = if *b { 1 } else { 0 };
117 Ok((n as f64, Some(n)))
118 }
119 _ => Err(DkitError::QueryError(format!(
120 "cannot perform arithmetic on {} value",
121 value_type_name(v)
122 ))),
123 }
124}
125
126fn value_to_display_string(v: &Value) -> String {
128 match v {
129 Value::String(s) => s.clone(),
130 Value::Integer(n) => n.to_string(),
131 Value::Float(f) => f.to_string(),
132 Value::Bool(b) => b.to_string(),
133 Value::Null => "null".to_string(),
134 _ => format!("{}", v),
135 }
136}
137
138pub fn expr_default_key(expr: &Expr) -> String {
140 match expr {
141 Expr::Field(f) => f.clone(),
142 Expr::Literal(_) => "value".to_string(),
143 Expr::FuncCall { name, args } => {
144 if let Some(first) = args.first() {
145 format!("{}_{}", name, expr_default_key(first))
146 } else {
147 name.clone()
148 }
149 }
150 Expr::BinaryOp { left, .. } => expr_default_key(left),
151 Expr::If { then_expr, .. } => expr_default_key(then_expr),
152 Expr::Case { branches, .. } => {
153 if let Some((_, expr)) = branches.first() {
154 expr_default_key(expr)
155 } else {
156 "case".to_string()
157 }
158 }
159 }
160}
161
162fn literal_to_value(lit: &LiteralValue) -> Value {
163 match lit {
164 LiteralValue::String(s) => Value::String(s.clone()),
165 LiteralValue::Integer(n) => Value::Integer(*n),
166 LiteralValue::Float(f) => Value::Float(*f),
167 LiteralValue::Bool(b) => Value::Bool(*b),
168 LiteralValue::Null => Value::Null,
169 LiteralValue::List(items) => Value::Array(items.iter().map(literal_to_value).collect()),
170 }
171}
172
173fn call_function(name: &str, args: Vec<Value>) -> Result<Value, DkitError> {
175 match name {
176 "upper" => {
178 let s = require_one_string(name, &args)?;
179 Ok(Value::String(s.to_uppercase()))
180 }
181 "lower" => {
182 let s = require_one_string(name, &args)?;
183 Ok(Value::String(s.to_lowercase()))
184 }
185 "trim" => {
186 let s = require_one_string(name, &args)?;
187 Ok(Value::String(s.trim().to_string()))
188 }
189 "ltrim" => {
190 let s = require_one_string(name, &args)?;
191 Ok(Value::String(s.trim_start().to_string()))
192 }
193 "rtrim" => {
194 let s = require_one_string(name, &args)?;
195 Ok(Value::String(s.trim_end().to_string()))
196 }
197 "length" => match args.as_slice() {
198 [Value::String(s)] => Ok(Value::Integer(s.chars().count() as i64)),
199 [Value::Array(a)] => Ok(Value::Integer(a.len() as i64)),
200 [Value::Null] => Ok(Value::Integer(0)),
201 [v] => Err(DkitError::QueryError(format!(
202 "length() requires a string or array argument, got {}",
203 value_type_name(v)
204 ))),
205 _ => Err(DkitError::QueryError(format!(
206 "length() takes 1 argument, got {}",
207 args.len()
208 ))),
209 },
210 "substr" => {
211 if args.len() < 2 || args.len() > 3 {
212 return Err(DkitError::QueryError(format!(
213 "substr() takes 2 or 3 arguments, got {}",
214 args.len()
215 )));
216 }
217 let s = match &args[0] {
218 Value::String(s) => s.clone(),
219 Value::Null => return Ok(Value::Null),
220 v => {
221 return Err(DkitError::QueryError(format!(
222 "substr() first argument must be a string, got {}",
223 value_type_name(v)
224 )))
225 }
226 };
227 let start = require_integer_arg("substr", &args[1], "start")? as usize;
228 let chars: Vec<char> = s.chars().collect();
229 let start = start.min(chars.len());
230 if args.len() == 3 {
231 let len = require_integer_arg("substr", &args[2], "length")? as usize;
232 let end = (start + len).min(chars.len());
233 Ok(Value::String(chars[start..end].iter().collect()))
234 } else {
235 Ok(Value::String(chars[start..].iter().collect()))
236 }
237 }
238 "concat" => {
239 if args.is_empty() {
240 return Ok(Value::String(String::new()));
241 }
242 let mut result = String::new();
243 for arg in &args {
244 match arg {
245 Value::String(s) => result.push_str(s),
246 Value::Null => {}
247 v => result.push_str(&v.to_string()),
248 }
249 }
250 Ok(Value::String(result))
251 }
252 "replace" => {
253 if args.len() != 3 {
254 return Err(DkitError::QueryError(format!(
255 "replace() takes 3 arguments (string, from, to), got {}",
256 args.len()
257 )));
258 }
259 let s = match &args[0] {
260 Value::String(s) => s.clone(),
261 Value::Null => return Ok(Value::Null),
262 v => {
263 return Err(DkitError::QueryError(format!(
264 "replace() first argument must be a string, got {}",
265 value_type_name(v)
266 )))
267 }
268 };
269 let from = match &args[1] {
270 Value::String(s) => s.clone(),
271 v => {
272 return Err(DkitError::QueryError(format!(
273 "replace() second argument must be a string, got {}",
274 value_type_name(v)
275 )))
276 }
277 };
278 let to = match &args[2] {
279 Value::String(s) => s.clone(),
280 v => {
281 return Err(DkitError::QueryError(format!(
282 "replace() third argument must be a string, got {}",
283 value_type_name(v)
284 )))
285 }
286 };
287 Ok(Value::String(s.replace(&*from, &to)))
288 }
289 "split" => {
290 if args.len() != 2 {
291 return Err(DkitError::QueryError(format!(
292 "split() takes 2 arguments (string, separator), got {}",
293 args.len()
294 )));
295 }
296 let s = match &args[0] {
297 Value::String(s) => s.clone(),
298 Value::Null => return Ok(Value::Array(vec![])),
299 v => {
300 return Err(DkitError::QueryError(format!(
301 "split() first argument must be a string, got {}",
302 value_type_name(v)
303 )))
304 }
305 };
306 let sep = match &args[1] {
307 Value::String(s) => s.clone(),
308 v => {
309 return Err(DkitError::QueryError(format!(
310 "split() second argument must be a string, got {}",
311 value_type_name(v)
312 )))
313 }
314 };
315 Ok(Value::Array(
316 s.split(&*sep)
317 .map(|p| Value::String(p.to_string()))
318 .collect(),
319 ))
320 }
321
322 "index_of" => {
323 if args.len() != 2 {
324 return Err(DkitError::QueryError(format!(
325 "index_of() takes 2 arguments (string, substr), got {}",
326 args.len()
327 )));
328 }
329 let s = match &args[0] {
330 Value::String(s) => s.clone(),
331 Value::Null => return Ok(Value::Integer(-1)),
332 v => {
333 return Err(DkitError::QueryError(format!(
334 "index_of() first argument must be a string, got {}",
335 value_type_name(v)
336 )))
337 }
338 };
339 let substr = match &args[1] {
340 Value::String(s) => s.clone(),
341 v => {
342 return Err(DkitError::QueryError(format!(
343 "index_of() second argument must be a string, got {}",
344 value_type_name(v)
345 )))
346 }
347 };
348 match s.find(&*substr) {
349 Some(pos) => {
350 let char_pos = s[..pos].chars().count() as i64;
351 Ok(Value::Integer(char_pos))
352 }
353 None => Ok(Value::Integer(-1)),
354 }
355 }
356 "rindex_of" => {
357 if args.len() != 2 {
358 return Err(DkitError::QueryError(format!(
359 "rindex_of() takes 2 arguments (string, substr), got {}",
360 args.len()
361 )));
362 }
363 let s = match &args[0] {
364 Value::String(s) => s.clone(),
365 Value::Null => return Ok(Value::Integer(-1)),
366 v => {
367 return Err(DkitError::QueryError(format!(
368 "rindex_of() first argument must be a string, got {}",
369 value_type_name(v)
370 )))
371 }
372 };
373 let substr = match &args[1] {
374 Value::String(s) => s.clone(),
375 v => {
376 return Err(DkitError::QueryError(format!(
377 "rindex_of() second argument must be a string, got {}",
378 value_type_name(v)
379 )))
380 }
381 };
382 match s.rfind(&*substr) {
383 Some(pos) => {
384 let char_pos = s[..pos].chars().count() as i64;
385 Ok(Value::Integer(char_pos))
386 }
387 None => Ok(Value::Integer(-1)),
388 }
389 }
390 "starts_with" => {
391 if args.len() != 2 {
392 return Err(DkitError::QueryError(format!(
393 "starts_with() takes 2 arguments (string, prefix), got {}",
394 args.len()
395 )));
396 }
397 let s = match &args[0] {
398 Value::String(s) => s.clone(),
399 Value::Null => return Ok(Value::Bool(false)),
400 v => {
401 return Err(DkitError::QueryError(format!(
402 "starts_with() first argument must be a string, got {}",
403 value_type_name(v)
404 )))
405 }
406 };
407 let prefix = match &args[1] {
408 Value::String(s) => s.clone(),
409 v => {
410 return Err(DkitError::QueryError(format!(
411 "starts_with() second argument must be a string, got {}",
412 value_type_name(v)
413 )))
414 }
415 };
416 Ok(Value::Bool(s.starts_with(&*prefix)))
417 }
418 "ends_with" => {
419 if args.len() != 2 {
420 return Err(DkitError::QueryError(format!(
421 "ends_with() takes 2 arguments (string, suffix), got {}",
422 args.len()
423 )));
424 }
425 let s = match &args[0] {
426 Value::String(s) => s.clone(),
427 Value::Null => return Ok(Value::Bool(false)),
428 v => {
429 return Err(DkitError::QueryError(format!(
430 "ends_with() first argument must be a string, got {}",
431 value_type_name(v)
432 )))
433 }
434 };
435 let suffix = match &args[1] {
436 Value::String(s) => s.clone(),
437 v => {
438 return Err(DkitError::QueryError(format!(
439 "ends_with() second argument must be a string, got {}",
440 value_type_name(v)
441 )))
442 }
443 };
444 Ok(Value::Bool(s.ends_with(&*suffix)))
445 }
446 "reverse" => {
447 let s = require_one_string(name, &args)?;
448 Ok(Value::String(s.chars().rev().collect()))
449 }
450 "repeat" => {
451 if args.len() != 2 {
452 return Err(DkitError::QueryError(format!(
453 "repeat() takes 2 arguments (string, count), got {}",
454 args.len()
455 )));
456 }
457 let s = match &args[0] {
458 Value::String(s) => s.clone(),
459 Value::Null => return Ok(Value::Null),
460 v => {
461 return Err(DkitError::QueryError(format!(
462 "repeat() first argument must be a string, got {}",
463 value_type_name(v)
464 )))
465 }
466 };
467 let n = require_integer_arg("repeat", &args[1], "count")?;
468 if n < 0 {
469 return Err(DkitError::QueryError(
470 "repeat() count must be non-negative".to_string(),
471 ));
472 }
473 Ok(Value::String(s.repeat(n as usize)))
474 }
475 "pad_left" => {
476 if args.len() != 3 {
477 return Err(DkitError::QueryError(format!(
478 "pad_left() takes 3 arguments (string, width, char), got {}",
479 args.len()
480 )));
481 }
482 let s = match &args[0] {
483 Value::String(s) => s.clone(),
484 Value::Null => return Ok(Value::Null),
485 v => {
486 return Err(DkitError::QueryError(format!(
487 "pad_left() first argument must be a string, got {}",
488 value_type_name(v)
489 )))
490 }
491 };
492 let width = require_integer_arg("pad_left", &args[1], "width")? as usize;
493 let pad_char = match &args[2] {
494 Value::String(s) => {
495 let mut chars = s.chars();
496 match (chars.next(), chars.next()) {
497 (Some(c), None) => c,
498 _ => {
499 return Err(DkitError::QueryError(
500 "pad_left() third argument must be a single character".to_string(),
501 ))
502 }
503 }
504 }
505 v => {
506 return Err(DkitError::QueryError(format!(
507 "pad_left() third argument must be a string, got {}",
508 value_type_name(v)
509 )))
510 }
511 };
512 let char_count = s.chars().count();
513 if char_count >= width {
514 Ok(Value::String(s))
515 } else {
516 let padding: String = std::iter::repeat(pad_char)
517 .take(width - char_count)
518 .collect();
519 Ok(Value::String(format!("{}{}", padding, s)))
520 }
521 }
522 "pad_right" => {
523 if args.len() != 3 {
524 return Err(DkitError::QueryError(format!(
525 "pad_right() takes 3 arguments (string, width, char), got {}",
526 args.len()
527 )));
528 }
529 let s = match &args[0] {
530 Value::String(s) => s.clone(),
531 Value::Null => return Ok(Value::Null),
532 v => {
533 return Err(DkitError::QueryError(format!(
534 "pad_right() first argument must be a string, got {}",
535 value_type_name(v)
536 )))
537 }
538 };
539 let width = require_integer_arg("pad_right", &args[1], "width")? as usize;
540 let pad_char = match &args[2] {
541 Value::String(s) => {
542 let mut chars = s.chars();
543 match (chars.next(), chars.next()) {
544 (Some(c), None) => c,
545 _ => {
546 return Err(DkitError::QueryError(
547 "pad_right() third argument must be a single character".to_string(),
548 ))
549 }
550 }
551 }
552 v => {
553 return Err(DkitError::QueryError(format!(
554 "pad_right() third argument must be a string, got {}",
555 value_type_name(v)
556 )))
557 }
558 };
559 let char_count = s.chars().count();
560 if char_count >= width {
561 Ok(Value::String(s))
562 } else {
563 let padding: String = std::iter::repeat(pad_char)
564 .take(width - char_count)
565 .collect();
566 Ok(Value::String(format!("{}{}", s, padding)))
567 }
568 }
569
570 "round" => {
572 let n = require_numeric_arg(name, &args)?;
573 if args.len() == 2 {
574 let decimals = require_integer_arg("round", &args[1], "decimals")?;
575 let factor = 10_f64.powi(decimals as i32);
576 Ok(Value::Float((n * factor).round() / factor))
577 } else if args.len() == 1 {
578 Ok(Value::Integer(n.round() as i64))
579 } else {
580 Err(DkitError::QueryError(format!(
581 "round() takes 1 or 2 arguments, got {}",
582 args.len()
583 )))
584 }
585 }
586 "ceil" => {
587 let n = require_numeric_arg(name, &args)?;
588 Ok(Value::Integer(n.ceil() as i64))
589 }
590 "floor" => {
591 let n = require_numeric_arg(name, &args)?;
592 Ok(Value::Integer(n.floor() as i64))
593 }
594 "abs" => match args.as_slice() {
595 [Value::Integer(n)] => Ok(Value::Integer(n.abs())),
596 [Value::Float(f)] => Ok(Value::Float(f.abs())),
597 [Value::Null] => Ok(Value::Null),
598 [v] => Err(DkitError::QueryError(format!(
599 "abs() requires a numeric argument, got {}",
600 value_type_name(v)
601 ))),
602 _ => Err(DkitError::QueryError(format!(
603 "abs() takes 1 argument, got {}",
604 args.len()
605 ))),
606 },
607 "sqrt" => {
608 let n = require_numeric_arg(name, &args)?;
609 if n < 0.0 {
610 return Err(DkitError::QueryError(
611 "sqrt() requires a non-negative argument".to_string(),
612 ));
613 }
614 Ok(Value::Float(n.sqrt()))
615 }
616 "pow" => {
617 if args.len() != 2 {
618 return Err(DkitError::QueryError(format!(
619 "pow() takes 2 arguments, got {}",
620 args.len()
621 )));
622 }
623 let base = require_numeric_arg("pow base", &args[..1])?;
624 let exp = require_numeric_arg("pow exp", &args[1..])?;
625 Ok(Value::Float(base.powf(exp)))
626 }
627
628 "now" => {
630 if !args.is_empty() {
631 return Err(DkitError::QueryError(
632 "now() takes no arguments".to_string(),
633 ));
634 }
635 Ok(Value::String(current_datetime_utc()))
636 }
637 "date" => {
638 let s = require_one_string(name, &args)?;
639 let normalized = normalize_date_str(&s)?;
641 Ok(Value::String(normalized))
642 }
643 "year" => {
644 let s = require_one_string(name, &args)?;
645 let y = extract_year(&s)?;
646 Ok(Value::Integer(y))
647 }
648 "month" => {
649 let s = require_one_string(name, &args)?;
650 let m = extract_month(&s)?;
651 Ok(Value::Integer(m))
652 }
653 "day" => {
654 let s = require_one_string(name, &args)?;
655 let d = extract_day(&s)?;
656 Ok(Value::Integer(d))
657 }
658
659 "to_int" | "int" => match args.as_slice() {
661 [Value::Integer(n)] => Ok(Value::Integer(*n)),
662 [Value::Float(f)] => Ok(Value::Integer(*f as i64)),
663 [Value::String(s)] => s.trim().parse::<i64>().map(Value::Integer).map_err(|_| {
664 DkitError::QueryError(format!("to_int(): cannot parse '{}' as integer", s))
665 }),
666 [Value::Bool(b)] => Ok(Value::Integer(if *b { 1 } else { 0 })),
667 [Value::Null] => Ok(Value::Null),
668 [v] => Err(DkitError::QueryError(format!(
669 "to_int() cannot convert {}",
670 value_type_name(v)
671 ))),
672 _ => Err(DkitError::QueryError(format!(
673 "to_int() takes 1 argument, got {}",
674 args.len()
675 ))),
676 },
677 "to_float" | "float" => match args.as_slice() {
678 [Value::Float(f)] => Ok(Value::Float(*f)),
679 [Value::Integer(n)] => Ok(Value::Float(*n as f64)),
680 [Value::String(s)] => s.trim().parse::<f64>().map(Value::Float).map_err(|_| {
681 DkitError::QueryError(format!("to_float(): cannot parse '{}' as float", s))
682 }),
683 [Value::Bool(b)] => Ok(Value::Float(if *b { 1.0 } else { 0.0 })),
684 [Value::Null] => Ok(Value::Null),
685 [v] => Err(DkitError::QueryError(format!(
686 "to_float() cannot convert {}",
687 value_type_name(v)
688 ))),
689 _ => Err(DkitError::QueryError(format!(
690 "to_float() takes 1 argument, got {}",
691 args.len()
692 ))),
693 },
694 "to_string" | "str" => match args.as_slice() {
695 [Value::String(s)] => Ok(Value::String(s.clone())),
696 [Value::Integer(n)] => Ok(Value::String(n.to_string())),
697 [Value::Float(f)] => Ok(Value::String(f.to_string())),
698 [Value::Bool(b)] => Ok(Value::String(b.to_string())),
699 [Value::Null] => Ok(Value::String("null".to_string())),
700 [v] => Ok(Value::String(v.to_string())),
701 _ => Err(DkitError::QueryError(format!(
702 "to_string() takes 1 argument, got {}",
703 args.len()
704 ))),
705 },
706 "to_bool" | "bool" => match args.as_slice() {
707 [Value::Bool(b)] => Ok(Value::Bool(*b)),
708 [Value::Integer(n)] => Ok(Value::Bool(*n != 0)),
709 [Value::Float(f)] => Ok(Value::Bool(*f != 0.0)),
710 [Value::String(s)] => match s.trim().to_lowercase().as_str() {
711 "true" | "yes" | "1" | "on" => Ok(Value::Bool(true)),
712 "false" | "no" | "0" | "off" | "" => Ok(Value::Bool(false)),
713 _ => Err(DkitError::QueryError(format!(
714 "to_bool(): cannot parse '{}' as boolean",
715 s
716 ))),
717 },
718 [Value::Null] => Ok(Value::Bool(false)),
719 [v] => Err(DkitError::QueryError(format!(
720 "to_bool() cannot convert {}",
721 value_type_name(v)
722 ))),
723 _ => Err(DkitError::QueryError(format!(
724 "to_bool() takes 1 argument, got {}",
725 args.len()
726 ))),
727 },
728
729 "coalesce" => {
731 for arg in &args {
732 if !matches!(arg, Value::Null) {
733 return Ok(arg.clone());
734 }
735 }
736 Ok(Value::Null)
737 }
738 "if_null" => match args.as_slice() {
739 [Value::Null, default] => Ok(default.clone()),
740 [v, _] => Ok(v.clone()),
741 _ => Err(DkitError::QueryError(format!(
742 "if_null() takes 2 arguments, got {}",
743 args.len()
744 ))),
745 },
746
747 _ => Err(DkitError::QueryError(format!(
748 "unknown function '{}'",
749 name
750 ))),
751 }
752}
753
754fn require_one_string(func: &str, args: &[Value]) -> Result<String, DkitError> {
757 match args {
758 [Value::String(s)] => Ok(s.clone()),
759 [Value::Null] => Err(DkitError::QueryError(format!(
760 "{}() argument is null",
761 func
762 ))),
763 [v] => Err(DkitError::QueryError(format!(
764 "{}() requires a string argument, got {}",
765 func,
766 value_type_name(v)
767 ))),
768 _ => Err(DkitError::QueryError(format!(
769 "{}() takes 1 argument, got {}",
770 func,
771 args.len()
772 ))),
773 }
774}
775
776fn require_numeric_arg(func: &str, args: &[Value]) -> Result<f64, DkitError> {
777 match args.first() {
778 Some(Value::Float(f)) => Ok(*f),
779 Some(Value::Integer(n)) => Ok(*n as f64),
780 Some(Value::Null) => Err(DkitError::QueryError(format!(
781 "{}() argument is null",
782 func
783 ))),
784 Some(v) => Err(DkitError::QueryError(format!(
785 "{}() requires a numeric argument, got {}",
786 func,
787 value_type_name(v)
788 ))),
789 None => Err(DkitError::QueryError(format!(
790 "{}() takes at least 1 argument",
791 func
792 ))),
793 }
794}
795
796fn require_integer_arg(func: &str, val: &Value, param: &str) -> Result<i64, DkitError> {
797 match val {
798 Value::Integer(n) => Ok(*n),
799 Value::Float(f) => Ok(*f as i64),
800 v => Err(DkitError::QueryError(format!(
801 "{}() '{}' argument must be an integer, got {}",
802 func,
803 param,
804 value_type_name(v)
805 ))),
806 }
807}
808
809fn value_type_name(v: &Value) -> &'static str {
810 match v {
811 Value::Null => "null",
812 Value::Bool(_) => "bool",
813 Value::Integer(_) => "integer",
814 Value::Float(_) => "float",
815 Value::String(_) => "string",
816 Value::Array(_) => "array",
817 Value::Object(_) => "object",
818 }
819}
820
821fn extract_year(s: &str) -> Result<i64, DkitError> {
825 let date_part = s.split('T').next().unwrap_or(s);
826 let parts: Vec<&str> = date_part.split('-').collect();
827 if parts.is_empty() {
828 return Err(DkitError::QueryError(format!(
829 "year(): cannot parse date from '{}'",
830 s
831 )));
832 }
833 parts[0]
834 .parse::<i64>()
835 .map_err(|_| DkitError::QueryError(format!("year(): cannot parse year from '{}'", s)))
836}
837
838fn extract_month(s: &str) -> Result<i64, DkitError> {
840 let date_part = s.split('T').next().unwrap_or(s);
841 let parts: Vec<&str> = date_part.split('-').collect();
842 if parts.len() < 2 {
843 return Err(DkitError::QueryError(format!(
844 "month(): cannot parse month from '{}'",
845 s
846 )));
847 }
848 parts[1]
849 .parse::<i64>()
850 .map_err(|_| DkitError::QueryError(format!("month(): cannot parse month from '{}'", s)))
851}
852
853fn extract_day(s: &str) -> Result<i64, DkitError> {
855 let date_part = s.split('T').next().unwrap_or(s);
856 let parts: Vec<&str> = date_part.split('-').collect();
857 if parts.len() < 3 {
858 return Err(DkitError::QueryError(format!(
859 "day(): cannot parse day from '{}'",
860 s
861 )));
862 }
863 let day_str = parts[2].split(&['T', ' '][..]).next().unwrap_or(parts[2]);
865 day_str
866 .parse::<i64>()
867 .map_err(|_| DkitError::QueryError(format!("day(): cannot parse day from '{}'", s)))
868}
869
870fn normalize_date_str(s: &str) -> Result<String, DkitError> {
872 let date_part = s.split('T').next().unwrap_or(s).trim();
873 let parts: Vec<&str> = date_part.split('-').collect();
874 if parts.len() != 3 {
875 return Err(DkitError::QueryError(format!(
876 "date(): expected yyyy-MM-dd format, got '{}'",
877 s
878 )));
879 }
880 let year = parts[0]
881 .parse::<i32>()
882 .map_err(|_| DkitError::QueryError(format!("date(): invalid year in '{}'", s)))?;
883 let month = parts[1]
884 .parse::<u32>()
885 .map_err(|_| DkitError::QueryError(format!("date(): invalid month in '{}'", s)))?;
886 let day = parts[2]
887 .split(&[' ', 'T'][..])
888 .next()
889 .unwrap_or(parts[2])
890 .parse::<u32>()
891 .map_err(|_| DkitError::QueryError(format!("date(): invalid day in '{}'", s)))?;
892 if !(1..=12).contains(&month) {
893 return Err(DkitError::QueryError(format!(
894 "date(): month {} out of range in '{}'",
895 month, s
896 )));
897 }
898 if !(1..=31).contains(&day) {
899 return Err(DkitError::QueryError(format!(
900 "date(): day {} out of range in '{}'",
901 day, s
902 )));
903 }
904 Ok(format!("{:04}-{:02}-{:02}", year, month, day))
905}
906
907fn current_datetime_utc() -> String {
909 use std::time::{SystemTime, UNIX_EPOCH};
910 let secs = SystemTime::now()
911 .duration_since(UNIX_EPOCH)
912 .unwrap_or_default()
913 .as_secs();
914 let days_since_epoch = secs / 86400;
916 let time_of_day = secs % 86400;
917 let (year, month, day) = days_to_ymd(days_since_epoch);
918 let hour = time_of_day / 3600;
919 let minute = (time_of_day % 3600) / 60;
920 let second = time_of_day % 60;
921 format!(
922 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
923 year, month, day, hour, minute, second
924 )
925}
926
927fn days_to_ymd(days: u64) -> (i64, u64, u64) {
929 let z = days as i64 + 719468;
931 let era = if z >= 0 { z } else { z - 146096 } / 146097;
932 let doe = z - era * 146097;
933 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
934 let y = yoe + era * 400;
935 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
936 let mp = (5 * doy + 2) / 153;
937 let d = doy - (153 * mp + 2) / 5 + 1;
938 let m = if mp < 10 { mp + 3 } else { mp - 9 };
939 let y = if m <= 2 { y + 1 } else { y };
940 (y, m as u64, d as u64)
941}
942
943#[cfg(test)]
944mod tests {
945 use super::*;
946 use crate::query::parser::Expr;
947 use indexmap::IndexMap;
948
949 fn obj(fields: &[(&str, Value)]) -> Value {
950 let mut map = IndexMap::new();
951 for (k, v) in fields {
952 map.insert(k.to_string(), v.clone());
953 }
954 Value::Object(map)
955 }
956
957 fn eval(row: &Value, expr: &Expr) -> Result<Value, DkitError> {
958 evaluate_expr(row, expr)
959 }
960
961 fn func(name: &str, args: Vec<Expr>) -> Expr {
962 Expr::FuncCall {
963 name: name.to_string(),
964 args,
965 }
966 }
967
968 fn field(name: &str) -> Expr {
969 Expr::Field(name.to_string())
970 }
971
972 fn lit_str(s: &str) -> Expr {
973 Expr::Literal(LiteralValue::String(s.to_string()))
974 }
975
976 fn lit_int(n: i64) -> Expr {
977 Expr::Literal(LiteralValue::Integer(n))
978 }
979
980 #[test]
983 fn test_upper() {
984 let row = obj(&[("name", Value::String("hello".to_string()))]);
985 let result = eval(&row, &func("upper", vec![field("name")])).unwrap();
986 assert_eq!(result, Value::String("HELLO".to_string()));
987 }
988
989 #[test]
990 fn test_lower() {
991 let row = obj(&[("name", Value::String("WORLD".to_string()))]);
992 let result = eval(&row, &func("lower", vec![field("name")])).unwrap();
993 assert_eq!(result, Value::String("world".to_string()));
994 }
995
996 #[test]
997 fn test_trim() {
998 let row = obj(&[("name", Value::String(" hello ".to_string()))]);
999 let result = eval(&row, &func("trim", vec![field("name")])).unwrap();
1000 assert_eq!(result, Value::String("hello".to_string()));
1001 }
1002
1003 #[test]
1004 fn test_length_string() {
1005 let row = obj(&[("name", Value::String("hello".to_string()))]);
1006 let result = eval(&row, &func("length", vec![field("name")])).unwrap();
1007 assert_eq!(result, Value::Integer(5));
1008 }
1009
1010 #[test]
1011 fn test_substr() {
1012 let row = obj(&[("name", Value::String("hello world".to_string()))]);
1013 let result = eval(
1014 &row,
1015 &func("substr", vec![field("name"), lit_int(0), lit_int(5)]),
1016 )
1017 .unwrap();
1018 assert_eq!(result, Value::String("hello".to_string()));
1019 }
1020
1021 #[test]
1022 fn test_concat() {
1023 let row = obj(&[
1024 ("first", Value::String("hello".to_string())),
1025 ("last", Value::String(" world".to_string())),
1026 ]);
1027 let result = eval(&row, &func("concat", vec![field("first"), field("last")])).unwrap();
1028 assert_eq!(result, Value::String("hello world".to_string()));
1029 }
1030
1031 #[test]
1032 fn test_replace() {
1033 let row = obj(&[("name", Value::String("hello world".to_string()))]);
1034 let result = eval(
1035 &row,
1036 &func(
1037 "replace",
1038 vec![field("name"), lit_str("world"), lit_str("dkit")],
1039 ),
1040 )
1041 .unwrap();
1042 assert_eq!(result, Value::String("hello dkit".to_string()));
1043 }
1044
1045 #[test]
1046 fn test_index_of() {
1047 let row = obj(&[("email", Value::String("user@example.com".to_string()))]);
1048 let result = eval(&row, &func("index_of", vec![field("email"), lit_str("@")])).unwrap();
1049 assert_eq!(result, Value::Integer(4));
1050 }
1051
1052 #[test]
1053 fn test_index_of_not_found() {
1054 let row = obj(&[("s", Value::String("hello".to_string()))]);
1055 let result = eval(&row, &func("index_of", vec![field("s"), lit_str("xyz")])).unwrap();
1056 assert_eq!(result, Value::Integer(-1));
1057 }
1058
1059 #[test]
1060 fn test_rindex_of() {
1061 let row = obj(&[("s", Value::String("abcabc".to_string()))]);
1062 let result = eval(&row, &func("rindex_of", vec![field("s"), lit_str("abc")])).unwrap();
1063 assert_eq!(result, Value::Integer(3));
1064 }
1065
1066 #[test]
1067 fn test_rindex_of_not_found() {
1068 let row = obj(&[("s", Value::String("hello".to_string()))]);
1069 let result = eval(&row, &func("rindex_of", vec![field("s"), lit_str("xyz")])).unwrap();
1070 assert_eq!(result, Value::Integer(-1));
1071 }
1072
1073 #[test]
1074 fn test_starts_with() {
1075 let row = obj(&[("name", Value::String("Dr. Smith".to_string()))]);
1076 let result = eval(
1077 &row,
1078 &func("starts_with", vec![field("name"), lit_str("Dr.")]),
1079 )
1080 .unwrap();
1081 assert_eq!(result, Value::Bool(true));
1082 }
1083
1084 #[test]
1085 fn test_starts_with_false() {
1086 let row = obj(&[("name", Value::String("Mr. Smith".to_string()))]);
1087 let result = eval(
1088 &row,
1089 &func("starts_with", vec![field("name"), lit_str("Dr.")]),
1090 )
1091 .unwrap();
1092 assert_eq!(result, Value::Bool(false));
1093 }
1094
1095 #[test]
1096 fn test_ends_with() {
1097 let row = obj(&[("file", Value::String("data.json".to_string()))]);
1098 let result = eval(
1099 &row,
1100 &func("ends_with", vec![field("file"), lit_str(".json")]),
1101 )
1102 .unwrap();
1103 assert_eq!(result, Value::Bool(true));
1104 }
1105
1106 #[test]
1107 fn test_ends_with_false() {
1108 let row = obj(&[("file", Value::String("data.csv".to_string()))]);
1109 let result = eval(
1110 &row,
1111 &func("ends_with", vec![field("file"), lit_str(".json")]),
1112 )
1113 .unwrap();
1114 assert_eq!(result, Value::Bool(false));
1115 }
1116
1117 #[test]
1118 fn test_reverse() {
1119 let row = obj(&[("s", Value::String("hello".to_string()))]);
1120 let result = eval(&row, &func("reverse", vec![field("s")])).unwrap();
1121 assert_eq!(result, Value::String("olleh".to_string()));
1122 }
1123
1124 #[test]
1125 fn test_repeat() {
1126 let row = obj(&[("s", Value::String("ab".to_string()))]);
1127 let result = eval(&row, &func("repeat", vec![field("s"), lit_int(3)])).unwrap();
1128 assert_eq!(result, Value::String("ababab".to_string()));
1129 }
1130
1131 #[test]
1132 fn test_repeat_zero() {
1133 let row = obj(&[("s", Value::String("ab".to_string()))]);
1134 let result = eval(&row, &func("repeat", vec![field("s"), lit_int(0)])).unwrap();
1135 assert_eq!(result, Value::String(String::new()));
1136 }
1137
1138 #[test]
1139 fn test_pad_left() {
1140 let row = obj(&[("id", Value::String("42".to_string()))]);
1141 let result = eval(
1142 &row,
1143 &func("pad_left", vec![field("id"), lit_int(5), lit_str("0")]),
1144 )
1145 .unwrap();
1146 assert_eq!(result, Value::String("00042".to_string()));
1147 }
1148
1149 #[test]
1150 fn test_pad_left_no_padding_needed() {
1151 let row = obj(&[("id", Value::String("12345".to_string()))]);
1152 let result = eval(
1153 &row,
1154 &func("pad_left", vec![field("id"), lit_int(3), lit_str("0")]),
1155 )
1156 .unwrap();
1157 assert_eq!(result, Value::String("12345".to_string()));
1158 }
1159
1160 #[test]
1161 fn test_pad_right() {
1162 let row = obj(&[("s", Value::String("hi".to_string()))]);
1163 let result = eval(
1164 &row,
1165 &func("pad_right", vec![field("s"), lit_int(5), lit_str(".")]),
1166 )
1167 .unwrap();
1168 assert_eq!(result, Value::String("hi...".to_string()));
1169 }
1170
1171 #[test]
1172 fn test_pad_right_no_padding_needed() {
1173 let row = obj(&[("s", Value::String("hello".to_string()))]);
1174 let result = eval(
1175 &row,
1176 &func("pad_right", vec![field("s"), lit_int(3), lit_str(".")]),
1177 )
1178 .unwrap();
1179 assert_eq!(result, Value::String("hello".to_string()));
1180 }
1181
1182 #[test]
1185 fn test_round_no_decimals() {
1186 let row = obj(&[("price", Value::Float(3.7))]);
1187 let result = eval(&row, &func("round", vec![field("price")])).unwrap();
1188 assert_eq!(result, Value::Integer(4));
1189 }
1190
1191 #[test]
1192 fn test_round_with_decimals() {
1193 let row = obj(&[("price", Value::Float(3.14159))]);
1194 let result = eval(&row, &func("round", vec![field("price"), lit_int(2)])).unwrap();
1195 match result {
1196 Value::Float(v) => assert!((v - 3.14).abs() < 1e-10, "expected ~3.14, got {v}"),
1197 other => panic!("expected Float, got {other:?}"),
1198 }
1199 }
1200
1201 #[test]
1202 fn test_ceil() {
1203 let row = obj(&[("price", Value::Float(3.2))]);
1204 let result = eval(&row, &func("ceil", vec![field("price")])).unwrap();
1205 assert_eq!(result, Value::Integer(4));
1206 }
1207
1208 #[test]
1209 fn test_floor() {
1210 let row = obj(&[("price", Value::Float(3.9))]);
1211 let result = eval(&row, &func("floor", vec![field("price")])).unwrap();
1212 assert_eq!(result, Value::Integer(3));
1213 }
1214
1215 #[test]
1216 fn test_abs_negative() {
1217 let row = obj(&[("score", Value::Integer(-5))]);
1218 let result = eval(&row, &func("abs", vec![field("score")])).unwrap();
1219 assert_eq!(result, Value::Integer(5));
1220 }
1221
1222 #[test]
1225 fn test_year() {
1226 let row = obj(&[("date", Value::String("2024-03-15".to_string()))]);
1227 let result = eval(&row, &func("year", vec![field("date")])).unwrap();
1228 assert_eq!(result, Value::Integer(2024));
1229 }
1230
1231 #[test]
1232 fn test_month() {
1233 let row = obj(&[("date", Value::String("2024-03-15".to_string()))]);
1234 let result = eval(&row, &func("month", vec![field("date")])).unwrap();
1235 assert_eq!(result, Value::Integer(3));
1236 }
1237
1238 #[test]
1239 fn test_day() {
1240 let row = obj(&[("date", Value::String("2024-03-15".to_string()))]);
1241 let result = eval(&row, &func("day", vec![field("date")])).unwrap();
1242 assert_eq!(result, Value::Integer(15));
1243 }
1244
1245 #[test]
1246 fn test_date_normalize() {
1247 let row = obj(&[("d", Value::String("2024-3-5".to_string()))]);
1248 let result = eval(&row, &func("date", vec![field("d")])).unwrap();
1249 assert_eq!(result, Value::String("2024-03-05".to_string()));
1250 }
1251
1252 #[test]
1253 fn test_now_returns_string() {
1254 let row = obj(&[]);
1255 let result = eval(&row, &func("now", vec![])).unwrap();
1256 assert!(matches!(result, Value::String(_)));
1257 if let Value::String(s) = result {
1258 assert!(s.contains('T'), "now() should return ISO 8601 format");
1259 }
1260 }
1261
1262 #[test]
1265 fn test_to_int_from_string() {
1266 let row = obj(&[("n", Value::String("42".to_string()))]);
1267 let result = eval(&row, &func("to_int", vec![field("n")])).unwrap();
1268 assert_eq!(result, Value::Integer(42));
1269 }
1270
1271 #[test]
1272 fn test_to_float_from_int() {
1273 let row = obj(&[("n", Value::Integer(5))]);
1274 let result = eval(&row, &func("to_float", vec![field("n")])).unwrap();
1275 assert_eq!(result, Value::Float(5.0));
1276 }
1277
1278 #[test]
1279 fn test_to_string_from_int() {
1280 let row = obj(&[("n", Value::Integer(42))]);
1281 let result = eval(&row, &func("to_string", vec![field("n")])).unwrap();
1282 assert_eq!(result, Value::String("42".to_string()));
1283 }
1284
1285 #[test]
1286 fn test_to_bool_from_string() {
1287 let row = obj(&[("flag", Value::String("true".to_string()))]);
1288 let result = eval(&row, &func("to_bool", vec![field("flag")])).unwrap();
1289 assert_eq!(result, Value::Bool(true));
1290 }
1291
1292 #[test]
1295 fn test_nested_upper_trim() {
1296 let row = obj(&[("name", Value::String(" hello ".to_string()))]);
1297 let result = eval(
1298 &row,
1299 &func("upper", vec![func("trim", vec![field("name")])]),
1300 )
1301 .unwrap();
1302 assert_eq!(result, Value::String("HELLO".to_string()));
1303 }
1304
1305 #[test]
1308 fn test_default_key_field() {
1309 assert_eq!(expr_default_key(&Expr::Field("name".to_string())), "name");
1310 }
1311
1312 #[test]
1313 fn test_default_key_func() {
1314 let expr = Expr::FuncCall {
1315 name: "upper".to_string(),
1316 args: vec![Expr::Field("name".to_string())],
1317 };
1318 assert_eq!(expr_default_key(&expr), "upper_name");
1319 }
1320
1321 #[test]
1322 fn test_default_key_nested_func() {
1323 let expr = Expr::FuncCall {
1324 name: "upper".to_string(),
1325 args: vec![Expr::FuncCall {
1326 name: "trim".to_string(),
1327 args: vec![Expr::Field("name".to_string())],
1328 }],
1329 };
1330 assert_eq!(expr_default_key(&expr), "upper_trim_name");
1331 }
1332
1333 #[test]
1336 fn test_binary_add_integers() {
1337 let row = Value::Object(indexmap::IndexMap::from([
1338 ("a".to_string(), Value::Integer(10)),
1339 ("b".to_string(), Value::Integer(20)),
1340 ]));
1341 let expr = Expr::BinaryOp {
1342 op: ArithmeticOp::Add,
1343 left: Box::new(Expr::Field("a".to_string())),
1344 right: Box::new(Expr::Field("b".to_string())),
1345 };
1346 assert_eq!(evaluate_expr(&row, &expr).unwrap(), Value::Integer(30));
1347 }
1348
1349 #[test]
1350 fn test_binary_mul_float() {
1351 let row = Value::Object(indexmap::IndexMap::from([(
1352 "price".to_string(),
1353 Value::Float(100.0),
1354 )]));
1355 let expr = Expr::BinaryOp {
1356 op: ArithmeticOp::Mul,
1357 left: Box::new(Expr::Field("price".to_string())),
1358 right: Box::new(Expr::Literal(LiteralValue::Float(0.1))),
1359 };
1360 let result = evaluate_expr(&row, &expr).unwrap();
1361 if let Value::Float(f) = result {
1362 assert!((f - 10.0).abs() < 0.001);
1363 } else {
1364 panic!("expected float");
1365 }
1366 }
1367
1368 #[test]
1369 fn test_binary_string_concat() {
1370 let row = Value::Object(indexmap::IndexMap::from([
1371 ("first".to_string(), Value::String("Hello".to_string())),
1372 ("last".to_string(), Value::String("World".to_string())),
1373 ]));
1374 let expr = Expr::BinaryOp {
1375 op: ArithmeticOp::Add,
1376 left: Box::new(Expr::Field("first".to_string())),
1377 right: Box::new(Expr::BinaryOp {
1378 op: ArithmeticOp::Add,
1379 left: Box::new(Expr::Literal(LiteralValue::String(" ".to_string()))),
1380 right: Box::new(Expr::Field("last".to_string())),
1381 }),
1382 };
1383 assert_eq!(
1384 evaluate_expr(&row, &expr).unwrap(),
1385 Value::String("Hello World".to_string())
1386 );
1387 }
1388
1389 #[test]
1390 fn test_binary_div_by_zero() {
1391 let row = Value::Object(indexmap::IndexMap::from([
1392 ("a".to_string(), Value::Integer(10)),
1393 ("b".to_string(), Value::Integer(0)),
1394 ]));
1395 let expr = Expr::BinaryOp {
1396 op: ArithmeticOp::Div,
1397 left: Box::new(Expr::Field("a".to_string())),
1398 right: Box::new(Expr::Field("b".to_string())),
1399 };
1400 assert!(evaluate_expr(&row, &expr).is_err());
1401 }
1402
1403 #[test]
1404 fn test_binary_null_propagation() {
1405 let row = Value::Object(indexmap::IndexMap::from([
1406 ("a".to_string(), Value::Integer(10)),
1407 ("b".to_string(), Value::Null),
1408 ]));
1409 let expr = Expr::BinaryOp {
1410 op: ArithmeticOp::Add,
1411 left: Box::new(Expr::Field("a".to_string())),
1412 right: Box::new(Expr::Field("b".to_string())),
1413 };
1414 assert_eq!(evaluate_expr(&row, &expr).unwrap(), Value::Null);
1415 }
1416
1417 #[test]
1418 fn test_binary_int_div_non_exact() {
1419 let row = Value::Object(indexmap::IndexMap::from([
1420 ("a".to_string(), Value::Integer(10)),
1421 ("b".to_string(), Value::Integer(3)),
1422 ]));
1423 let expr = Expr::BinaryOp {
1424 op: ArithmeticOp::Div,
1425 left: Box::new(Expr::Field("a".to_string())),
1426 right: Box::new(Expr::Field("b".to_string())),
1427 };
1428 let result = evaluate_expr(&row, &expr).unwrap();
1430 assert!(matches!(result, Value::Float(_)));
1431 }
1432
1433 #[test]
1434 fn test_binary_mixed_string_number_concat() {
1435 let row = Value::Object(indexmap::IndexMap::from([
1436 ("name".to_string(), Value::String("Item".to_string())),
1437 ("id".to_string(), Value::Integer(42)),
1438 ]));
1439 let expr = Expr::BinaryOp {
1440 op: ArithmeticOp::Add,
1441 left: Box::new(Expr::Field("name".to_string())),
1442 right: Box::new(Expr::Field("id".to_string())),
1443 };
1444 assert_eq!(
1445 evaluate_expr(&row, &expr).unwrap(),
1446 Value::String("Item42".to_string())
1447 );
1448 }
1449
1450 #[test]
1453 fn test_eval_if_true_branch() {
1454 use crate::query::parser::{CompareOp, Comparison, Condition};
1455 let row = Value::Object(
1456 vec![
1457 ("age".to_string(), Value::Integer(15)),
1458 ("name".to_string(), Value::String("Alice".to_string())),
1459 ]
1460 .into_iter()
1461 .collect(),
1462 );
1463 let expr = Expr::If {
1464 condition: Condition::Comparison(Comparison {
1465 field: "age".to_string(),
1466 op: CompareOp::Lt,
1467 value: LiteralValue::Integer(18),
1468 }),
1469 then_expr: Box::new(Expr::Literal(LiteralValue::String("minor".to_string()))),
1470 else_expr: Box::new(Expr::Literal(LiteralValue::String("adult".to_string()))),
1471 };
1472 assert_eq!(
1473 evaluate_expr(&row, &expr).unwrap(),
1474 Value::String("minor".to_string())
1475 );
1476 }
1477
1478 #[test]
1479 fn test_eval_if_false_branch() {
1480 use crate::query::parser::{CompareOp, Comparison, Condition};
1481 let row = Value::Object(
1482 vec![("age".to_string(), Value::Integer(30))]
1483 .into_iter()
1484 .collect(),
1485 );
1486 let expr = Expr::If {
1487 condition: Condition::Comparison(Comparison {
1488 field: "age".to_string(),
1489 op: CompareOp::Lt,
1490 value: LiteralValue::Integer(18),
1491 }),
1492 then_expr: Box::new(Expr::Literal(LiteralValue::String("minor".to_string()))),
1493 else_expr: Box::new(Expr::Literal(LiteralValue::String("adult".to_string()))),
1494 };
1495 assert_eq!(
1496 evaluate_expr(&row, &expr).unwrap(),
1497 Value::String("adult".to_string())
1498 );
1499 }
1500
1501 #[test]
1502 fn test_eval_if_nested() {
1503 use crate::query::parser::{CompareOp, Comparison, Condition};
1504 let row = Value::Object(
1505 vec![("age".to_string(), Value::Integer(70))]
1506 .into_iter()
1507 .collect(),
1508 );
1509 let expr = Expr::If {
1510 condition: Condition::Comparison(Comparison {
1511 field: "age".to_string(),
1512 op: CompareOp::Lt,
1513 value: LiteralValue::Integer(18),
1514 }),
1515 then_expr: Box::new(Expr::Literal(LiteralValue::String("minor".to_string()))),
1516 else_expr: Box::new(Expr::If {
1517 condition: Condition::Comparison(Comparison {
1518 field: "age".to_string(),
1519 op: CompareOp::Lt,
1520 value: LiteralValue::Integer(65),
1521 }),
1522 then_expr: Box::new(Expr::Literal(LiteralValue::String("adult".to_string()))),
1523 else_expr: Box::new(Expr::Literal(LiteralValue::String("senior".to_string()))),
1524 }),
1525 };
1526 assert_eq!(
1527 evaluate_expr(&row, &expr).unwrap(),
1528 Value::String("senior".to_string())
1529 );
1530 }
1531
1532 #[test]
1535 fn test_eval_case_first_branch() {
1536 use crate::query::parser::{CompareOp, Comparison, Condition};
1537 let row = Value::Object(
1538 vec![("status".to_string(), Value::String("active".to_string()))]
1539 .into_iter()
1540 .collect(),
1541 );
1542 let expr = Expr::Case {
1543 branches: vec![
1544 (
1545 Condition::Comparison(Comparison {
1546 field: "status".to_string(),
1547 op: CompareOp::Eq,
1548 value: LiteralValue::String("active".to_string()),
1549 }),
1550 Expr::Literal(LiteralValue::String("A".to_string())),
1551 ),
1552 (
1553 Condition::Comparison(Comparison {
1554 field: "status".to_string(),
1555 op: CompareOp::Eq,
1556 value: LiteralValue::String("inactive".to_string()),
1557 }),
1558 Expr::Literal(LiteralValue::String("I".to_string())),
1559 ),
1560 ],
1561 default: Some(Box::new(Expr::Literal(LiteralValue::String(
1562 "U".to_string(),
1563 )))),
1564 };
1565 assert_eq!(
1566 evaluate_expr(&row, &expr).unwrap(),
1567 Value::String("A".to_string())
1568 );
1569 }
1570
1571 #[test]
1572 fn test_eval_case_second_branch() {
1573 use crate::query::parser::{CompareOp, Comparison, Condition};
1574 let row = Value::Object(
1575 vec![("status".to_string(), Value::String("inactive".to_string()))]
1576 .into_iter()
1577 .collect(),
1578 );
1579 let expr = Expr::Case {
1580 branches: vec![
1581 (
1582 Condition::Comparison(Comparison {
1583 field: "status".to_string(),
1584 op: CompareOp::Eq,
1585 value: LiteralValue::String("active".to_string()),
1586 }),
1587 Expr::Literal(LiteralValue::String("A".to_string())),
1588 ),
1589 (
1590 Condition::Comparison(Comparison {
1591 field: "status".to_string(),
1592 op: CompareOp::Eq,
1593 value: LiteralValue::String("inactive".to_string()),
1594 }),
1595 Expr::Literal(LiteralValue::String("I".to_string())),
1596 ),
1597 ],
1598 default: Some(Box::new(Expr::Literal(LiteralValue::String(
1599 "U".to_string(),
1600 )))),
1601 };
1602 assert_eq!(
1603 evaluate_expr(&row, &expr).unwrap(),
1604 Value::String("I".to_string())
1605 );
1606 }
1607
1608 #[test]
1609 fn test_eval_case_default() {
1610 use crate::query::parser::{CompareOp, Comparison, Condition};
1611 let row = Value::Object(
1612 vec![("status".to_string(), Value::String("pending".to_string()))]
1613 .into_iter()
1614 .collect(),
1615 );
1616 let expr = Expr::Case {
1617 branches: vec![(
1618 Condition::Comparison(Comparison {
1619 field: "status".to_string(),
1620 op: CompareOp::Eq,
1621 value: LiteralValue::String("active".to_string()),
1622 }),
1623 Expr::Literal(LiteralValue::String("A".to_string())),
1624 )],
1625 default: Some(Box::new(Expr::Literal(LiteralValue::String(
1626 "other".to_string(),
1627 )))),
1628 };
1629 assert_eq!(
1630 evaluate_expr(&row, &expr).unwrap(),
1631 Value::String("other".to_string())
1632 );
1633 }
1634
1635 #[test]
1636 fn test_eval_case_no_default_returns_null() {
1637 use crate::query::parser::{CompareOp, Comparison, Condition};
1638 let row = Value::Object(
1639 vec![("status".to_string(), Value::String("pending".to_string()))]
1640 .into_iter()
1641 .collect(),
1642 );
1643 let expr = Expr::Case {
1644 branches: vec![(
1645 Condition::Comparison(Comparison {
1646 field: "status".to_string(),
1647 op: CompareOp::Eq,
1648 value: LiteralValue::String("active".to_string()),
1649 }),
1650 Expr::Literal(LiteralValue::String("A".to_string())),
1651 )],
1652 default: None,
1653 };
1654 assert_eq!(evaluate_expr(&row, &expr).unwrap(), Value::Null);
1655 }
1656}