1use crate::error::DkitError;
2use crate::query::parser::{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 }
22}
23
24pub fn expr_default_key(expr: &Expr) -> String {
26 match expr {
27 Expr::Field(f) => f.clone(),
28 Expr::Literal(_) => "value".to_string(),
29 Expr::FuncCall { name, args } => {
30 if let Some(first) = args.first() {
31 format!("{}_{}", name, expr_default_key(first))
32 } else {
33 name.clone()
34 }
35 }
36 }
37}
38
39fn literal_to_value(lit: &LiteralValue) -> Value {
40 match lit {
41 LiteralValue::String(s) => Value::String(s.clone()),
42 LiteralValue::Integer(n) => Value::Integer(*n),
43 LiteralValue::Float(f) => Value::Float(*f),
44 LiteralValue::Bool(b) => Value::Bool(*b),
45 LiteralValue::Null => Value::Null,
46 }
47}
48
49fn call_function(name: &str, args: Vec<Value>) -> Result<Value, DkitError> {
51 match name {
52 "upper" => {
54 let s = require_one_string(name, &args)?;
55 Ok(Value::String(s.to_uppercase()))
56 }
57 "lower" => {
58 let s = require_one_string(name, &args)?;
59 Ok(Value::String(s.to_lowercase()))
60 }
61 "trim" => {
62 let s = require_one_string(name, &args)?;
63 Ok(Value::String(s.trim().to_string()))
64 }
65 "ltrim" => {
66 let s = require_one_string(name, &args)?;
67 Ok(Value::String(s.trim_start().to_string()))
68 }
69 "rtrim" => {
70 let s = require_one_string(name, &args)?;
71 Ok(Value::String(s.trim_end().to_string()))
72 }
73 "length" => match args.as_slice() {
74 [Value::String(s)] => Ok(Value::Integer(s.chars().count() as i64)),
75 [Value::Array(a)] => Ok(Value::Integer(a.len() as i64)),
76 [Value::Null] => Ok(Value::Integer(0)),
77 [v] => Err(DkitError::QueryError(format!(
78 "length() requires a string or array argument, got {}",
79 value_type_name(v)
80 ))),
81 _ => Err(DkitError::QueryError(format!(
82 "length() takes 1 argument, got {}",
83 args.len()
84 ))),
85 },
86 "substr" => {
87 if args.len() < 2 || args.len() > 3 {
88 return Err(DkitError::QueryError(format!(
89 "substr() takes 2 or 3 arguments, got {}",
90 args.len()
91 )));
92 }
93 let s = match &args[0] {
94 Value::String(s) => s.clone(),
95 Value::Null => return Ok(Value::Null),
96 v => {
97 return Err(DkitError::QueryError(format!(
98 "substr() first argument must be a string, got {}",
99 value_type_name(v)
100 )))
101 }
102 };
103 let start = require_integer_arg("substr", &args[1], "start")? as usize;
104 let chars: Vec<char> = s.chars().collect();
105 let start = start.min(chars.len());
106 if args.len() == 3 {
107 let len = require_integer_arg("substr", &args[2], "length")? as usize;
108 let end = (start + len).min(chars.len());
109 Ok(Value::String(chars[start..end].iter().collect()))
110 } else {
111 Ok(Value::String(chars[start..].iter().collect()))
112 }
113 }
114 "concat" => {
115 if args.is_empty() {
116 return Ok(Value::String(String::new()));
117 }
118 let mut result = String::new();
119 for arg in &args {
120 match arg {
121 Value::String(s) => result.push_str(s),
122 Value::Null => {}
123 v => result.push_str(&v.to_string()),
124 }
125 }
126 Ok(Value::String(result))
127 }
128 "replace" => {
129 if args.len() != 3 {
130 return Err(DkitError::QueryError(format!(
131 "replace() takes 3 arguments (string, from, to), got {}",
132 args.len()
133 )));
134 }
135 let s = match &args[0] {
136 Value::String(s) => s.clone(),
137 Value::Null => return Ok(Value::Null),
138 v => {
139 return Err(DkitError::QueryError(format!(
140 "replace() first argument must be a string, got {}",
141 value_type_name(v)
142 )))
143 }
144 };
145 let from = match &args[1] {
146 Value::String(s) => s.clone(),
147 v => {
148 return Err(DkitError::QueryError(format!(
149 "replace() second argument must be a string, got {}",
150 value_type_name(v)
151 )))
152 }
153 };
154 let to = match &args[2] {
155 Value::String(s) => s.clone(),
156 v => {
157 return Err(DkitError::QueryError(format!(
158 "replace() third argument must be a string, got {}",
159 value_type_name(v)
160 )))
161 }
162 };
163 Ok(Value::String(s.replace(&*from, &to)))
164 }
165 "split" => {
166 if args.len() != 2 {
167 return Err(DkitError::QueryError(format!(
168 "split() takes 2 arguments (string, separator), got {}",
169 args.len()
170 )));
171 }
172 let s = match &args[0] {
173 Value::String(s) => s.clone(),
174 Value::Null => return Ok(Value::Array(vec![])),
175 v => {
176 return Err(DkitError::QueryError(format!(
177 "split() first argument must be a string, got {}",
178 value_type_name(v)
179 )))
180 }
181 };
182 let sep = match &args[1] {
183 Value::String(s) => s.clone(),
184 v => {
185 return Err(DkitError::QueryError(format!(
186 "split() second argument must be a string, got {}",
187 value_type_name(v)
188 )))
189 }
190 };
191 Ok(Value::Array(
192 s.split(&*sep)
193 .map(|p| Value::String(p.to_string()))
194 .collect(),
195 ))
196 }
197
198 "round" => {
200 let n = require_numeric_arg(name, &args)?;
201 if args.len() == 2 {
202 let decimals = require_integer_arg("round", &args[1], "decimals")?;
203 let factor = 10_f64.powi(decimals as i32);
204 Ok(Value::Float((n * factor).round() / factor))
205 } else if args.len() == 1 {
206 Ok(Value::Integer(n.round() as i64))
207 } else {
208 Err(DkitError::QueryError(format!(
209 "round() takes 1 or 2 arguments, got {}",
210 args.len()
211 )))
212 }
213 }
214 "ceil" => {
215 let n = require_numeric_arg(name, &args)?;
216 Ok(Value::Integer(n.ceil() as i64))
217 }
218 "floor" => {
219 let n = require_numeric_arg(name, &args)?;
220 Ok(Value::Integer(n.floor() as i64))
221 }
222 "abs" => match args.as_slice() {
223 [Value::Integer(n)] => Ok(Value::Integer(n.abs())),
224 [Value::Float(f)] => Ok(Value::Float(f.abs())),
225 [Value::Null] => Ok(Value::Null),
226 [v] => Err(DkitError::QueryError(format!(
227 "abs() requires a numeric argument, got {}",
228 value_type_name(v)
229 ))),
230 _ => Err(DkitError::QueryError(format!(
231 "abs() takes 1 argument, got {}",
232 args.len()
233 ))),
234 },
235 "sqrt" => {
236 let n = require_numeric_arg(name, &args)?;
237 if n < 0.0 {
238 return Err(DkitError::QueryError(
239 "sqrt() requires a non-negative argument".to_string(),
240 ));
241 }
242 Ok(Value::Float(n.sqrt()))
243 }
244 "pow" => {
245 if args.len() != 2 {
246 return Err(DkitError::QueryError(format!(
247 "pow() takes 2 arguments, got {}",
248 args.len()
249 )));
250 }
251 let base = require_numeric_arg("pow base", &args[..1])?;
252 let exp = require_numeric_arg("pow exp", &args[1..])?;
253 Ok(Value::Float(base.powf(exp)))
254 }
255
256 "now" => {
258 if !args.is_empty() {
259 return Err(DkitError::QueryError(
260 "now() takes no arguments".to_string(),
261 ));
262 }
263 Ok(Value::String(current_datetime_utc()))
264 }
265 "date" => {
266 let s = require_one_string(name, &args)?;
267 let normalized = normalize_date_str(&s)?;
269 Ok(Value::String(normalized))
270 }
271 "year" => {
272 let s = require_one_string(name, &args)?;
273 let y = extract_year(&s)?;
274 Ok(Value::Integer(y))
275 }
276 "month" => {
277 let s = require_one_string(name, &args)?;
278 let m = extract_month(&s)?;
279 Ok(Value::Integer(m))
280 }
281 "day" => {
282 let s = require_one_string(name, &args)?;
283 let d = extract_day(&s)?;
284 Ok(Value::Integer(d))
285 }
286
287 "to_int" | "int" => match args.as_slice() {
289 [Value::Integer(n)] => Ok(Value::Integer(*n)),
290 [Value::Float(f)] => Ok(Value::Integer(*f as i64)),
291 [Value::String(s)] => s.trim().parse::<i64>().map(Value::Integer).map_err(|_| {
292 DkitError::QueryError(format!("to_int(): cannot parse '{}' as integer", s))
293 }),
294 [Value::Bool(b)] => Ok(Value::Integer(if *b { 1 } else { 0 })),
295 [Value::Null] => Ok(Value::Null),
296 [v] => Err(DkitError::QueryError(format!(
297 "to_int() cannot convert {}",
298 value_type_name(v)
299 ))),
300 _ => Err(DkitError::QueryError(format!(
301 "to_int() takes 1 argument, got {}",
302 args.len()
303 ))),
304 },
305 "to_float" | "float" => match args.as_slice() {
306 [Value::Float(f)] => Ok(Value::Float(*f)),
307 [Value::Integer(n)] => Ok(Value::Float(*n as f64)),
308 [Value::String(s)] => s.trim().parse::<f64>().map(Value::Float).map_err(|_| {
309 DkitError::QueryError(format!("to_float(): cannot parse '{}' as float", s))
310 }),
311 [Value::Bool(b)] => Ok(Value::Float(if *b { 1.0 } else { 0.0 })),
312 [Value::Null] => Ok(Value::Null),
313 [v] => Err(DkitError::QueryError(format!(
314 "to_float() cannot convert {}",
315 value_type_name(v)
316 ))),
317 _ => Err(DkitError::QueryError(format!(
318 "to_float() takes 1 argument, got {}",
319 args.len()
320 ))),
321 },
322 "to_string" | "str" => match args.as_slice() {
323 [Value::String(s)] => Ok(Value::String(s.clone())),
324 [Value::Integer(n)] => Ok(Value::String(n.to_string())),
325 [Value::Float(f)] => Ok(Value::String(f.to_string())),
326 [Value::Bool(b)] => Ok(Value::String(b.to_string())),
327 [Value::Null] => Ok(Value::String("null".to_string())),
328 [v] => Ok(Value::String(v.to_string())),
329 _ => Err(DkitError::QueryError(format!(
330 "to_string() takes 1 argument, got {}",
331 args.len()
332 ))),
333 },
334 "to_bool" | "bool" => match args.as_slice() {
335 [Value::Bool(b)] => Ok(Value::Bool(*b)),
336 [Value::Integer(n)] => Ok(Value::Bool(*n != 0)),
337 [Value::Float(f)] => Ok(Value::Bool(*f != 0.0)),
338 [Value::String(s)] => match s.trim().to_lowercase().as_str() {
339 "true" | "yes" | "1" | "on" => Ok(Value::Bool(true)),
340 "false" | "no" | "0" | "off" | "" => Ok(Value::Bool(false)),
341 _ => Err(DkitError::QueryError(format!(
342 "to_bool(): cannot parse '{}' as boolean",
343 s
344 ))),
345 },
346 [Value::Null] => Ok(Value::Bool(false)),
347 [v] => Err(DkitError::QueryError(format!(
348 "to_bool() cannot convert {}",
349 value_type_name(v)
350 ))),
351 _ => Err(DkitError::QueryError(format!(
352 "to_bool() takes 1 argument, got {}",
353 args.len()
354 ))),
355 },
356
357 "coalesce" => {
359 for arg in &args {
360 if !matches!(arg, Value::Null) {
361 return Ok(arg.clone());
362 }
363 }
364 Ok(Value::Null)
365 }
366 "if_null" => match args.as_slice() {
367 [Value::Null, default] => Ok(default.clone()),
368 [v, _] => Ok(v.clone()),
369 _ => Err(DkitError::QueryError(format!(
370 "if_null() takes 2 arguments, got {}",
371 args.len()
372 ))),
373 },
374
375 _ => Err(DkitError::QueryError(format!(
376 "unknown function '{}'",
377 name
378 ))),
379 }
380}
381
382fn require_one_string(func: &str, args: &[Value]) -> Result<String, DkitError> {
385 match args {
386 [Value::String(s)] => Ok(s.clone()),
387 [Value::Null] => Err(DkitError::QueryError(format!(
388 "{}() argument is null",
389 func
390 ))),
391 [v] => Err(DkitError::QueryError(format!(
392 "{}() requires a string argument, got {}",
393 func,
394 value_type_name(v)
395 ))),
396 _ => Err(DkitError::QueryError(format!(
397 "{}() takes 1 argument, got {}",
398 func,
399 args.len()
400 ))),
401 }
402}
403
404fn require_numeric_arg(func: &str, args: &[Value]) -> Result<f64, DkitError> {
405 match args.first() {
406 Some(Value::Float(f)) => Ok(*f),
407 Some(Value::Integer(n)) => Ok(*n as f64),
408 Some(Value::Null) => Err(DkitError::QueryError(format!(
409 "{}() argument is null",
410 func
411 ))),
412 Some(v) => Err(DkitError::QueryError(format!(
413 "{}() requires a numeric argument, got {}",
414 func,
415 value_type_name(v)
416 ))),
417 None => Err(DkitError::QueryError(format!(
418 "{}() takes at least 1 argument",
419 func
420 ))),
421 }
422}
423
424fn require_integer_arg(func: &str, val: &Value, param: &str) -> Result<i64, DkitError> {
425 match val {
426 Value::Integer(n) => Ok(*n),
427 Value::Float(f) => Ok(*f as i64),
428 v => Err(DkitError::QueryError(format!(
429 "{}() '{}' argument must be an integer, got {}",
430 func,
431 param,
432 value_type_name(v)
433 ))),
434 }
435}
436
437fn value_type_name(v: &Value) -> &'static str {
438 match v {
439 Value::Null => "null",
440 Value::Bool(_) => "bool",
441 Value::Integer(_) => "integer",
442 Value::Float(_) => "float",
443 Value::String(_) => "string",
444 Value::Array(_) => "array",
445 Value::Object(_) => "object",
446 }
447}
448
449fn extract_year(s: &str) -> Result<i64, DkitError> {
453 let date_part = s.split('T').next().unwrap_or(s);
454 let parts: Vec<&str> = date_part.split('-').collect();
455 if parts.is_empty() {
456 return Err(DkitError::QueryError(format!(
457 "year(): cannot parse date from '{}'",
458 s
459 )));
460 }
461 parts[0]
462 .parse::<i64>()
463 .map_err(|_| DkitError::QueryError(format!("year(): cannot parse year from '{}'", s)))
464}
465
466fn extract_month(s: &str) -> Result<i64, DkitError> {
468 let date_part = s.split('T').next().unwrap_or(s);
469 let parts: Vec<&str> = date_part.split('-').collect();
470 if parts.len() < 2 {
471 return Err(DkitError::QueryError(format!(
472 "month(): cannot parse month from '{}'",
473 s
474 )));
475 }
476 parts[1]
477 .parse::<i64>()
478 .map_err(|_| DkitError::QueryError(format!("month(): cannot parse month from '{}'", s)))
479}
480
481fn extract_day(s: &str) -> Result<i64, DkitError> {
483 let date_part = s.split('T').next().unwrap_or(s);
484 let parts: Vec<&str> = date_part.split('-').collect();
485 if parts.len() < 3 {
486 return Err(DkitError::QueryError(format!(
487 "day(): cannot parse day from '{}'",
488 s
489 )));
490 }
491 let day_str = parts[2].split(&['T', ' '][..]).next().unwrap_or(parts[2]);
493 day_str
494 .parse::<i64>()
495 .map_err(|_| DkitError::QueryError(format!("day(): cannot parse day from '{}'", s)))
496}
497
498fn normalize_date_str(s: &str) -> Result<String, DkitError> {
500 let date_part = s.split('T').next().unwrap_or(s).trim();
501 let parts: Vec<&str> = date_part.split('-').collect();
502 if parts.len() != 3 {
503 return Err(DkitError::QueryError(format!(
504 "date(): expected yyyy-MM-dd format, got '{}'",
505 s
506 )));
507 }
508 let year = parts[0]
509 .parse::<i32>()
510 .map_err(|_| DkitError::QueryError(format!("date(): invalid year in '{}'", s)))?;
511 let month = parts[1]
512 .parse::<u32>()
513 .map_err(|_| DkitError::QueryError(format!("date(): invalid month in '{}'", s)))?;
514 let day = parts[2]
515 .split(&[' ', 'T'][..])
516 .next()
517 .unwrap_or(parts[2])
518 .parse::<u32>()
519 .map_err(|_| DkitError::QueryError(format!("date(): invalid day in '{}'", s)))?;
520 if !(1..=12).contains(&month) {
521 return Err(DkitError::QueryError(format!(
522 "date(): month {} out of range in '{}'",
523 month, s
524 )));
525 }
526 if !(1..=31).contains(&day) {
527 return Err(DkitError::QueryError(format!(
528 "date(): day {} out of range in '{}'",
529 day, s
530 )));
531 }
532 Ok(format!("{:04}-{:02}-{:02}", year, month, day))
533}
534
535fn current_datetime_utc() -> String {
537 use std::time::{SystemTime, UNIX_EPOCH};
538 let secs = SystemTime::now()
539 .duration_since(UNIX_EPOCH)
540 .unwrap_or_default()
541 .as_secs();
542 let days_since_epoch = secs / 86400;
544 let time_of_day = secs % 86400;
545 let (year, month, day) = days_to_ymd(days_since_epoch);
546 let hour = time_of_day / 3600;
547 let minute = (time_of_day % 3600) / 60;
548 let second = time_of_day % 60;
549 format!(
550 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
551 year, month, day, hour, minute, second
552 )
553}
554
555fn days_to_ymd(days: u64) -> (i64, u64, u64) {
557 let z = days as i64 + 719468;
559 let era = if z >= 0 { z } else { z - 146096 } / 146097;
560 let doe = z - era * 146097;
561 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
562 let y = yoe + era * 400;
563 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
564 let mp = (5 * doy + 2) / 153;
565 let d = doy - (153 * mp + 2) / 5 + 1;
566 let m = if mp < 10 { mp + 3 } else { mp - 9 };
567 let y = if m <= 2 { y + 1 } else { y };
568 (y, m as u64, d as u64)
569}
570
571#[cfg(test)]
572mod tests {
573 use super::*;
574 use crate::query::parser::Expr;
575 use indexmap::IndexMap;
576
577 fn obj(fields: &[(&str, Value)]) -> Value {
578 let mut map = IndexMap::new();
579 for (k, v) in fields {
580 map.insert(k.to_string(), v.clone());
581 }
582 Value::Object(map)
583 }
584
585 fn eval(row: &Value, expr: &Expr) -> Result<Value, DkitError> {
586 evaluate_expr(row, expr)
587 }
588
589 fn func(name: &str, args: Vec<Expr>) -> Expr {
590 Expr::FuncCall {
591 name: name.to_string(),
592 args,
593 }
594 }
595
596 fn field(name: &str) -> Expr {
597 Expr::Field(name.to_string())
598 }
599
600 fn lit_str(s: &str) -> Expr {
601 Expr::Literal(LiteralValue::String(s.to_string()))
602 }
603
604 fn lit_int(n: i64) -> Expr {
605 Expr::Literal(LiteralValue::Integer(n))
606 }
607
608 #[test]
611 fn test_upper() {
612 let row = obj(&[("name", Value::String("hello".to_string()))]);
613 let result = eval(&row, &func("upper", vec![field("name")])).unwrap();
614 assert_eq!(result, Value::String("HELLO".to_string()));
615 }
616
617 #[test]
618 fn test_lower() {
619 let row = obj(&[("name", Value::String("WORLD".to_string()))]);
620 let result = eval(&row, &func("lower", vec![field("name")])).unwrap();
621 assert_eq!(result, Value::String("world".to_string()));
622 }
623
624 #[test]
625 fn test_trim() {
626 let row = obj(&[("name", Value::String(" hello ".to_string()))]);
627 let result = eval(&row, &func("trim", vec![field("name")])).unwrap();
628 assert_eq!(result, Value::String("hello".to_string()));
629 }
630
631 #[test]
632 fn test_length_string() {
633 let row = obj(&[("name", Value::String("hello".to_string()))]);
634 let result = eval(&row, &func("length", vec![field("name")])).unwrap();
635 assert_eq!(result, Value::Integer(5));
636 }
637
638 #[test]
639 fn test_substr() {
640 let row = obj(&[("name", Value::String("hello world".to_string()))]);
641 let result = eval(
642 &row,
643 &func("substr", vec![field("name"), lit_int(0), lit_int(5)]),
644 )
645 .unwrap();
646 assert_eq!(result, Value::String("hello".to_string()));
647 }
648
649 #[test]
650 fn test_concat() {
651 let row = obj(&[
652 ("first", Value::String("hello".to_string())),
653 ("last", Value::String(" world".to_string())),
654 ]);
655 let result = eval(&row, &func("concat", vec![field("first"), field("last")])).unwrap();
656 assert_eq!(result, Value::String("hello world".to_string()));
657 }
658
659 #[test]
660 fn test_replace() {
661 let row = obj(&[("name", Value::String("hello world".to_string()))]);
662 let result = eval(
663 &row,
664 &func(
665 "replace",
666 vec![field("name"), lit_str("world"), lit_str("dkit")],
667 ),
668 )
669 .unwrap();
670 assert_eq!(result, Value::String("hello dkit".to_string()));
671 }
672
673 #[test]
676 fn test_round_no_decimals() {
677 let row = obj(&[("price", Value::Float(3.7))]);
678 let result = eval(&row, &func("round", vec![field("price")])).unwrap();
679 assert_eq!(result, Value::Integer(4));
680 }
681
682 #[test]
683 fn test_round_with_decimals() {
684 let row = obj(&[("price", Value::Float(3.14159))]);
685 let result = eval(&row, &func("round", vec![field("price"), lit_int(2)])).unwrap();
686 match result {
687 Value::Float(v) => assert!((v - 3.14).abs() < 1e-10, "expected ~3.14, got {v}"),
688 other => panic!("expected Float, got {other:?}"),
689 }
690 }
691
692 #[test]
693 fn test_ceil() {
694 let row = obj(&[("price", Value::Float(3.2))]);
695 let result = eval(&row, &func("ceil", vec![field("price")])).unwrap();
696 assert_eq!(result, Value::Integer(4));
697 }
698
699 #[test]
700 fn test_floor() {
701 let row = obj(&[("price", Value::Float(3.9))]);
702 let result = eval(&row, &func("floor", vec![field("price")])).unwrap();
703 assert_eq!(result, Value::Integer(3));
704 }
705
706 #[test]
707 fn test_abs_negative() {
708 let row = obj(&[("score", Value::Integer(-5))]);
709 let result = eval(&row, &func("abs", vec![field("score")])).unwrap();
710 assert_eq!(result, Value::Integer(5));
711 }
712
713 #[test]
716 fn test_year() {
717 let row = obj(&[("date", Value::String("2024-03-15".to_string()))]);
718 let result = eval(&row, &func("year", vec![field("date")])).unwrap();
719 assert_eq!(result, Value::Integer(2024));
720 }
721
722 #[test]
723 fn test_month() {
724 let row = obj(&[("date", Value::String("2024-03-15".to_string()))]);
725 let result = eval(&row, &func("month", vec![field("date")])).unwrap();
726 assert_eq!(result, Value::Integer(3));
727 }
728
729 #[test]
730 fn test_day() {
731 let row = obj(&[("date", Value::String("2024-03-15".to_string()))]);
732 let result = eval(&row, &func("day", vec![field("date")])).unwrap();
733 assert_eq!(result, Value::Integer(15));
734 }
735
736 #[test]
737 fn test_date_normalize() {
738 let row = obj(&[("d", Value::String("2024-3-5".to_string()))]);
739 let result = eval(&row, &func("date", vec![field("d")])).unwrap();
740 assert_eq!(result, Value::String("2024-03-05".to_string()));
741 }
742
743 #[test]
744 fn test_now_returns_string() {
745 let row = obj(&[]);
746 let result = eval(&row, &func("now", vec![])).unwrap();
747 assert!(matches!(result, Value::String(_)));
748 if let Value::String(s) = result {
749 assert!(s.contains('T'), "now() should return ISO 8601 format");
750 }
751 }
752
753 #[test]
756 fn test_to_int_from_string() {
757 let row = obj(&[("n", Value::String("42".to_string()))]);
758 let result = eval(&row, &func("to_int", vec![field("n")])).unwrap();
759 assert_eq!(result, Value::Integer(42));
760 }
761
762 #[test]
763 fn test_to_float_from_int() {
764 let row = obj(&[("n", Value::Integer(5))]);
765 let result = eval(&row, &func("to_float", vec![field("n")])).unwrap();
766 assert_eq!(result, Value::Float(5.0));
767 }
768
769 #[test]
770 fn test_to_string_from_int() {
771 let row = obj(&[("n", Value::Integer(42))]);
772 let result = eval(&row, &func("to_string", vec![field("n")])).unwrap();
773 assert_eq!(result, Value::String("42".to_string()));
774 }
775
776 #[test]
777 fn test_to_bool_from_string() {
778 let row = obj(&[("flag", Value::String("true".to_string()))]);
779 let result = eval(&row, &func("to_bool", vec![field("flag")])).unwrap();
780 assert_eq!(result, Value::Bool(true));
781 }
782
783 #[test]
786 fn test_nested_upper_trim() {
787 let row = obj(&[("name", Value::String(" hello ".to_string()))]);
788 let result = eval(
789 &row,
790 &func("upper", vec![func("trim", vec![field("name")])]),
791 )
792 .unwrap();
793 assert_eq!(result, Value::String("HELLO".to_string()));
794 }
795
796 #[test]
799 fn test_default_key_field() {
800 assert_eq!(expr_default_key(&Expr::Field("name".to_string())), "name");
801 }
802
803 #[test]
804 fn test_default_key_func() {
805 let expr = Expr::FuncCall {
806 name: "upper".to_string(),
807 args: vec![Expr::Field("name".to_string())],
808 };
809 assert_eq!(expr_default_key(&expr), "upper_name");
810 }
811
812 #[test]
813 fn test_default_key_nested_func() {
814 let expr = Expr::FuncCall {
815 name: "upper".to_string(),
816 args: vec![Expr::FuncCall {
817 name: "trim".to_string(),
818 args: vec![Expr::Field("name".to_string())],
819 }],
820 };
821 assert_eq!(expr_default_key(&expr), "upper_trim_name");
822 }
823}