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