1use crate::expression::{AggregateOp, Expression, RankMethod, Transform};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, PartialEq, Default)]
8pub enum Value {
9 #[default]
11 Null,
12 Bool(bool),
14 Number(f64),
16 String(String),
18 Array(Vec<Self>),
20 Object(HashMap<String, Self>),
22}
23
24impl Value {
25 #[must_use]
27 pub const fn null() -> Self {
28 Self::Null
29 }
30
31 #[must_use]
33 pub const fn bool(v: bool) -> Self {
34 Self::Bool(v)
35 }
36
37 #[must_use]
39 pub const fn number(v: f64) -> Self {
40 Self::Number(v)
41 }
42
43 #[must_use]
45 pub fn string(v: impl Into<String>) -> Self {
46 Self::String(v.into())
47 }
48
49 #[must_use]
51 pub const fn array(v: Vec<Self>) -> Self {
52 Self::Array(v)
53 }
54
55 #[must_use]
57 pub const fn object(v: HashMap<String, Self>) -> Self {
58 Self::Object(v)
59 }
60
61 #[must_use]
63 pub const fn is_null(&self) -> bool {
64 matches!(self, Self::Null)
65 }
66
67 #[must_use]
69 pub const fn is_bool(&self) -> bool {
70 matches!(self, Self::Bool(_))
71 }
72
73 #[must_use]
75 pub const fn is_number(&self) -> bool {
76 matches!(self, Self::Number(_))
77 }
78
79 #[must_use]
81 pub const fn is_string(&self) -> bool {
82 matches!(self, Self::String(_))
83 }
84
85 #[must_use]
87 pub const fn is_array(&self) -> bool {
88 matches!(self, Self::Array(_))
89 }
90
91 #[must_use]
93 pub const fn is_object(&self) -> bool {
94 matches!(self, Self::Object(_))
95 }
96
97 #[must_use]
99 pub const fn as_bool(&self) -> Option<bool> {
100 match self {
101 Self::Bool(v) => Some(*v),
102 _ => None,
103 }
104 }
105
106 #[must_use]
108 pub const fn as_number(&self) -> Option<f64> {
109 match self {
110 Self::Number(v) => Some(*v),
111 _ => None,
112 }
113 }
114
115 #[must_use]
117 pub fn as_str(&self) -> Option<&str> {
118 match self {
119 Self::String(v) => Some(v),
120 _ => None,
121 }
122 }
123
124 #[must_use]
126 pub const fn as_array(&self) -> Option<&Vec<Self>> {
127 match self {
128 Self::Array(v) => Some(v),
129 _ => None,
130 }
131 }
132
133 #[must_use]
135 pub fn as_array_mut(&mut self) -> Option<&mut Vec<Self>> {
136 match self {
137 Self::Array(v) => Some(v),
138 _ => None,
139 }
140 }
141
142 #[must_use]
144 pub const fn as_object(&self) -> Option<&HashMap<String, Self>> {
145 match self {
146 Self::Object(v) => Some(v),
147 _ => None,
148 }
149 }
150
151 #[must_use]
153 pub fn get(&self, key: &str) -> Option<&Self> {
154 match self {
155 Self::Object(map) => map.get(key),
156 _ => None,
157 }
158 }
159
160 pub fn require_array(&self) -> Result<&Vec<Self>, ExecutionError> {
162 self.as_array().ok_or(ExecutionError::ExpectedArray)
163 }
164
165 pub fn extract_numbers(&self, field: &str) -> Result<Vec<f64>, ExecutionError> {
167 Ok(self.require_array()?.iter().filter_map(|item| item.get(field)?.as_number()).collect())
168 }
169
170 #[must_use]
172 pub fn len(&self) -> usize {
173 match self {
174 Self::Array(arr) => arr.len(),
175 Self::Object(obj) => obj.len(),
176 Self::String(s) => s.len(),
177 _ => 0,
178 }
179 }
180
181 #[must_use]
183 pub fn is_empty(&self) -> bool {
184 self.len() == 0
185 }
186}
187
188impl From<bool> for Value {
189 fn from(v: bool) -> Self {
190 Self::Bool(v)
191 }
192}
193
194impl From<f64> for Value {
195 fn from(v: f64) -> Self {
196 Self::Number(v)
197 }
198}
199
200impl From<i32> for Value {
201 fn from(v: i32) -> Self {
202 Self::Number(f64::from(v))
203 }
204}
205
206impl From<i64> for Value {
207 fn from(v: i64) -> Self {
208 Self::Number(v as f64)
209 }
210}
211
212impl From<&str> for Value {
213 fn from(v: &str) -> Self {
214 Self::String(v.to_string())
215 }
216}
217
218impl From<String> for Value {
219 fn from(v: String) -> Self {
220 Self::String(v)
221 }
222}
223
224impl<T: Into<Self>> From<Vec<T>> for Value {
225 fn from(v: Vec<T>) -> Self {
226 Self::Array(v.into_iter().map(Into::into).collect())
227 }
228}
229
230#[derive(Debug, Clone, PartialEq, Eq)]
232pub enum ExecutionError {
233 SourceNotFound(String),
235 ExpectedArray,
237 ExpectedObject,
239 FieldNotFound(String),
241 TypeMismatch(String),
243 InvalidTransform(String),
245}
246
247impl std::fmt::Display for ExecutionError {
248 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249 match self {
250 Self::SourceNotFound(name) => write!(f, "source not found: {name}"),
251 Self::ExpectedArray => write!(f, "expected an array"),
252 Self::ExpectedObject => write!(f, "expected an object"),
253 Self::FieldNotFound(name) => write!(f, "field not found: {name}"),
254 Self::TypeMismatch(msg) => write!(f, "type mismatch: {msg}"),
255 Self::InvalidTransform(msg) => write!(f, "invalid transform: {msg}"),
256 }
257 }
258}
259
260impl std::error::Error for ExecutionError {}
261
262#[derive(Debug, Clone, Default)]
264pub struct DataContext {
265 sources: HashMap<String, Value>,
267}
268
269impl DataContext {
270 #[must_use]
272 pub fn new() -> Self {
273 Self::default()
274 }
275
276 pub fn insert(&mut self, name: impl Into<String>, value: Value) {
278 self.sources.insert(name.into(), value);
279 }
280
281 #[must_use]
283 pub fn get(&self, name: &str) -> Option<&Value> {
284 let parts: Vec<&str> = name.split('.').collect();
286 let mut current = self.sources.get(parts[0])?;
287
288 for part in &parts[1..] {
289 current = current.get(part)?;
290 }
291
292 Some(current)
293 }
294
295 #[must_use]
297 pub fn contains(&self, name: &str) -> bool {
298 self.get(name).is_some()
299 }
300}
301
302#[derive(Debug, Default)]
304pub struct ExpressionExecutor;
305
306impl ExpressionExecutor {
307 #[must_use]
309 pub const fn new() -> Self {
310 Self
311 }
312
313 pub fn execute(&self, expr: &Expression, ctx: &DataContext) -> Result<Value, ExecutionError> {
319 let mut value = ctx
321 .get(&expr.source)
322 .cloned()
323 .ok_or_else(|| ExecutionError::SourceNotFound(expr.source.clone()))?;
324
325 for transform in &expr.transforms {
327 value = self.apply_transform(&value, transform, ctx)?;
328 }
329
330 Ok(value)
331 }
332
333 fn apply_transform(
334 &self,
335 value: &Value,
336 transform: &Transform,
337 ctx: &DataContext,
338 ) -> Result<Value, ExecutionError> {
339 match transform {
340 Transform::Filter {
341 field,
342 value: match_value,
343 } => self.apply_filter(value, field, match_value),
344 Transform::Select { fields } => self.apply_select(value, fields),
345 Transform::Sort { field, desc } => self.apply_sort(value, field, *desc),
346 Transform::Count => Ok(self.apply_count(value)),
347 Transform::Sum { field } => self.apply_sum(value, field),
348 Transform::Mean { field } => self.apply_mean(value, field),
349 Transform::Sample { n } => self.apply_sample(value, *n),
350 Transform::Percentage => self.apply_percentage(value),
351 Transform::Rate { window } => self.apply_rate(value, window),
352 Transform::Join { other, on } => self.apply_join(value, other, on, ctx),
353 Transform::GroupBy { field } => self.apply_group_by(value, field),
354 Transform::Distinct { field } => self.apply_distinct(value, field.as_deref()),
355 Transform::Where {
356 field,
357 op,
358 value: match_value,
359 } => self.apply_where(value, field, op, match_value),
360 Transform::Offset { n } => self.apply_offset(value, *n),
361 Transform::Min { field } => self.apply_min(value, field),
362 Transform::Max { field } => self.apply_max(value, field),
363 Transform::First { n } | Transform::Limit { n } => self.apply_limit(value, *n),
364 Transform::Last { n } => self.apply_last(value, *n),
365 Transform::Flatten => self.apply_flatten(value),
366 Transform::Reverse => self.apply_reverse(value),
367 Transform::Map { expr } => self.apply_map(value, expr),
369 Transform::Reduce { initial, expr } => self.apply_reduce(value, initial, expr),
370 Transform::Aggregate { field, op } => self.apply_aggregate(value, field, *op),
371 Transform::Pivot {
372 row_field,
373 col_field,
374 value_field,
375 } => self.apply_pivot(value, row_field, col_field, value_field),
376 Transform::CumulativeSum { field } => self.apply_cumsum(value, field),
377 Transform::Rank { field, method } => self.apply_rank(value, field, *method),
378 Transform::MovingAverage { field, window } => {
379 self.apply_moving_avg(value, field, *window)
380 }
381 Transform::PercentChange { field } => self.apply_pct_change(value, field),
382 Transform::Suggest { prefix, count } => self.apply_suggest(value, prefix, *count),
383 }
384 }
385
386 fn apply_filter(
387 &self,
388 value: &Value,
389 field: &str,
390 match_value: &str,
391 ) -> Result<Value, ExecutionError> {
392 let arr = value.require_array()?;
393
394 let filtered: Vec<Value> = arr
395 .iter()
396 .filter(|item| {
397 if let Some(obj) = item.as_object() {
398 if let Some(val) = obj.get(field) {
399 return self.value_matches(val, match_value);
400 }
401 }
402 false
403 })
404 .cloned()
405 .collect();
406
407 Ok(Value::Array(filtered))
408 }
409
410 fn value_matches(&self, value: &Value, target: &str) -> bool {
411 match value {
412 Value::String(s) => s == target,
413 Value::Number(n) => {
414 if let Ok(t) = target.parse::<f64>() {
415 (*n - t).abs() < f64::EPSILON
416 } else {
417 false
418 }
419 }
420 Value::Bool(b) => {
421 matches!((b, target), (true, "true") | (false, "false"))
422 }
423 _ => false,
424 }
425 }
426
427 fn apply_select(&self, value: &Value, fields: &[String]) -> Result<Value, ExecutionError> {
428 let arr = value.require_array()?;
429
430 let selected: Vec<Value> = arr
431 .iter()
432 .map(|item| {
433 if let Some(obj) = item.as_object() {
434 let mut new_obj = HashMap::new();
435 for field in fields {
436 if let Some(val) = obj.get(field) {
437 new_obj.insert(field.clone(), val.clone());
438 }
439 }
440 Value::Object(new_obj)
441 } else {
442 item.clone()
443 }
444 })
445 .collect();
446
447 Ok(Value::Array(selected))
448 }
449
450 fn apply_sort(&self, value: &Value, field: &str, desc: bool) -> Result<Value, ExecutionError> {
451 let arr = value.require_array()?;
452 let mut sorted = arr.clone();
453
454 sorted.sort_by(|a, b| {
455 let a_val = a.get(field);
456 let b_val = b.get(field);
457
458 let cmp = match (a_val, b_val) {
459 (Some(Value::Number(a)), Some(Value::Number(b))) => {
460 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
461 }
462 (Some(Value::String(a)), Some(Value::String(b))) => a.cmp(b),
463 _ => std::cmp::Ordering::Equal,
464 };
465
466 if desc {
467 cmp.reverse()
468 } else {
469 cmp
470 }
471 });
472
473 Ok(Value::Array(sorted))
474 }
475
476 fn apply_limit(&self, value: &Value, n: usize) -> Result<Value, ExecutionError> {
477 let arr = value.require_array()?;
478 Ok(Value::Array(arr.iter().take(n).cloned().collect()))
479 }
480
481 fn apply_count(&self, value: &Value) -> Value {
482 match value {
483 Value::Array(arr) => Value::Number(arr.len() as f64),
484 Value::Object(obj) => Value::Number(obj.len() as f64),
485 Value::String(s) => Value::Number(s.len() as f64),
486 _ => Value::Number(0.0),
487 }
488 }
489
490 fn apply_sum(&self, value: &Value, field: &str) -> Result<Value, ExecutionError> {
491 let nums = value.extract_numbers(field)?;
492 Ok(Value::Number(nums.iter().sum()))
493 }
494
495 fn apply_mean(&self, value: &Value, field: &str) -> Result<Value, ExecutionError> {
496 let nums = value.extract_numbers(field)?;
497 if nums.is_empty() {
498 return Ok(Value::Number(0.0));
499 }
500 Ok(Value::Number(nums.iter().sum::<f64>() / nums.len() as f64))
501 }
502
503 fn apply_sample(&self, value: &Value, n: usize) -> Result<Value, ExecutionError> {
504 let arr = value.require_array()?;
505
506 Ok(Value::Array(arr.iter().take(n).cloned().collect()))
509 }
510
511 fn apply_percentage(&self, value: &Value) -> Result<Value, ExecutionError> {
512 match value {
513 Value::Number(n) => Ok(Value::Number(n * 100.0)),
514 _ => Err(ExecutionError::TypeMismatch(
515 "percentage requires a number".to_string(),
516 )),
517 }
518 }
519
520 fn apply_rate(&self, value: &Value, window: &str) -> Result<Value, ExecutionError> {
521 let arr = value.require_array()?;
522
523 let window_ms = self.parse_window(window)?;
525
526 let mut values_with_time: Vec<(f64, f64)> = arr
529 .iter()
530 .filter_map(|item| {
531 let obj = item.as_object()?;
532 let time = obj
533 .get("timestamp")
534 .or_else(|| obj.get("time"))
535 .and_then(Value::as_number)?;
536 let val = obj
537 .get("value")
538 .or_else(|| obj.get("count"))
539 .and_then(Value::as_number)
540 .unwrap_or(1.0);
541 Some((time, val))
542 })
543 .collect();
544
545 if values_with_time.len() < 2 {
546 return Ok(Value::Number(0.0));
547 }
548
549 values_with_time.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
551
552 let window_ms_f64 = window_ms as f64;
554 let last_time = values_with_time.last().map_or(0.0, |v| v.0);
555 let window_start = last_time - window_ms_f64;
556
557 let sum_in_window: f64 = values_with_time
558 .iter()
559 .filter(|(t, _)| *t >= window_start)
560 .map(|(_, v)| v)
561 .sum();
562
563 let rate = sum_in_window / (window_ms_f64 / 1000.0);
565
566 Ok(Value::Number(rate))
567 }
568
569 fn parse_window(&self, window: &str) -> Result<u64, ExecutionError> {
570 let window = window.trim();
571 if window.is_empty() {
572 return Err(ExecutionError::InvalidTransform("empty window".to_string()));
573 }
574
575 let (num_str, unit) = if let Some(s) = window.strip_suffix("ms") {
576 (s, "ms")
577 } else if let Some(s) = window.strip_suffix('s') {
578 (s, "s")
579 } else if let Some(s) = window.strip_suffix('m') {
580 (s, "m")
581 } else if let Some(s) = window.strip_suffix('h') {
582 (s, "h")
583 } else if let Some(s) = window.strip_suffix('d') {
584 (s, "d")
585 } else {
586 (window, "ms")
588 };
589
590 let num: u64 = num_str
591 .parse()
592 .map_err(|_| ExecutionError::InvalidTransform(format!("invalid window: {window}")))?;
593
594 let ms = match unit {
595 "s" => num * 1000,
596 "m" => num * 60 * 1000,
597 "h" => num * 60 * 60 * 1000,
598 "d" => num * 24 * 60 * 60 * 1000,
599 _ => num,
601 };
602
603 Ok(ms)
604 }
605
606 fn apply_join(
607 &self,
608 value: &Value,
609 other: &str,
610 on: &str,
611 ctx: &DataContext,
612 ) -> Result<Value, ExecutionError> {
613 let left_arr = value.require_array()?;
614
615 let right_value = ctx
617 .get(other)
618 .ok_or_else(|| ExecutionError::SourceNotFound(other.to_string()))?;
619 let right_arr = right_value
620 .as_array()
621 .ok_or(ExecutionError::ExpectedArray)?;
622
623 let mut right_lookup: HashMap<String, Vec<&Value>> = HashMap::new();
625 for item in right_arr {
626 if let Some(obj) = item.as_object() {
627 if let Some(key_val) = obj.get(on) {
628 let key = self.value_to_string(key_val);
629 right_lookup.entry(key).or_default().push(item);
630 }
631 }
632 }
633
634 let mut result = Vec::new();
636 for left_item in left_arr {
637 if let Some(left_obj) = left_item.as_object() {
638 if let Some(key_val) = left_obj.get(on) {
639 let key = self.value_to_string(key_val);
640 if let Some(right_items) = right_lookup.get(&key) {
641 for right_item in right_items {
643 if let Some(right_obj) = right_item.as_object() {
644 let mut merged = left_obj.clone();
646 for (k, v) in right_obj {
647 if merged.contains_key(k) && k != on {
649 merged.insert(format!("{other}_{k}"), v.clone());
650 } else if k != on {
651 merged.insert(k.clone(), v.clone());
652 }
653 }
654 result.push(Value::Object(merged));
655 }
656 }
657 } else {
658 result.push(left_item.clone());
660 }
661 } else {
662 result.push(left_item.clone());
664 }
665 } else {
666 result.push(left_item.clone());
668 }
669 }
670
671 Ok(Value::Array(result))
672 }
673
674 fn apply_group_by(&self, value: &Value, field: &str) -> Result<Value, ExecutionError> {
675 let arr = value.require_array()?;
676
677 let mut groups: HashMap<String, Vec<Value>> = HashMap::new();
678
679 for item in arr {
680 let key = if let Some(obj) = item.as_object() {
681 if let Some(val) = obj.get(field) {
682 self.value_to_string(val)
683 } else {
684 "_null".to_string()
685 }
686 } else {
687 "_null".to_string()
688 };
689
690 groups.entry(key).or_default().push(item.clone());
691 }
692
693 let result: Vec<Value> = groups
695 .into_iter()
696 .map(|(key, items)| {
697 let mut obj = HashMap::new();
698 obj.insert("key".to_string(), Value::String(key));
699 obj.insert("items".to_string(), Value::Array(items.clone()));
700 obj.insert("count".to_string(), Value::Number(items.len() as f64));
701 Value::Object(obj)
702 })
703 .collect();
704
705 Ok(Value::Array(result))
706 }
707
708 fn value_to_string(&self, value: &Value) -> String {
709 match value {
710 Value::Null => "_null".to_string(),
711 Value::Bool(b) => b.to_string(),
712 Value::Number(n) => n.to_string(),
713 Value::String(s) => s.clone(),
714 Value::Array(_) => "_array".to_string(),
715 Value::Object(_) => "_object".to_string(),
716 }
717 }
718
719 fn apply_distinct(&self, value: &Value, field: Option<&str>) -> Result<Value, ExecutionError> {
720 let arr = value.require_array()?;
721
722 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
723 let mut result = Vec::new();
724
725 for item in arr {
726 let key = if let Some(f) = field {
727 if let Some(obj) = item.as_object() {
728 obj.get(f)
729 .map(|v| self.value_to_string(v))
730 .unwrap_or_default()
731 } else {
732 self.value_to_string(item)
733 }
734 } else {
735 self.value_to_string(item)
736 };
737
738 if seen.insert(key) {
739 result.push(item.clone());
740 }
741 }
742
743 Ok(Value::Array(result))
744 }
745
746 fn apply_where(
747 &self,
748 value: &Value,
749 field: &str,
750 op: &str,
751 match_value: &str,
752 ) -> Result<Value, ExecutionError> {
753 let arr = value.require_array()?;
754
755 let filtered: Vec<Value> = arr
756 .iter()
757 .filter(|item| {
758 if let Some(obj) = item.as_object() {
759 if let Some(val) = obj.get(field) {
760 return self.compare_values(val, op, match_value);
761 }
762 }
763 false
764 })
765 .cloned()
766 .collect();
767
768 Ok(Value::Array(filtered))
769 }
770
771 fn compare_values(&self, value: &Value, op: &str, target: &str) -> bool {
772 match op {
773 "eq" | "==" | "=" => self.value_matches(value, target),
774 "ne" | "!=" | "<>" => !self.value_matches(value, target),
775 "gt" | ">" => {
776 if let (Some(v), Ok(t)) = (value.as_number(), target.parse::<f64>()) {
777 v > t
778 } else {
779 false
780 }
781 }
782 "lt" | "<" => {
783 if let (Some(v), Ok(t)) = (value.as_number(), target.parse::<f64>()) {
784 v < t
785 } else {
786 false
787 }
788 }
789 "gte" | ">=" => {
790 if let (Some(v), Ok(t)) = (value.as_number(), target.parse::<f64>()) {
791 v >= t
792 } else {
793 false
794 }
795 }
796 "lte" | "<=" => {
797 if let (Some(v), Ok(t)) = (value.as_number(), target.parse::<f64>()) {
798 v <= t
799 } else {
800 false
801 }
802 }
803 "contains" => {
804 if let Some(s) = value.as_str() {
805 s.contains(target)
806 } else {
807 false
808 }
809 }
810 "starts_with" => {
811 if let Some(s) = value.as_str() {
812 s.starts_with(target)
813 } else {
814 false
815 }
816 }
817 "ends_with" => {
818 if let Some(s) = value.as_str() {
819 s.ends_with(target)
820 } else {
821 false
822 }
823 }
824 _ => false,
825 }
826 }
827
828 fn apply_offset(&self, value: &Value, n: usize) -> Result<Value, ExecutionError> {
829 let arr = value.require_array()?;
830 Ok(Value::Array(arr.iter().skip(n).cloned().collect()))
831 }
832
833 fn apply_min(&self, value: &Value, field: &str) -> Result<Value, ExecutionError> {
834 let nums = value.extract_numbers(field)?;
835 let min = nums.iter().copied().fold(f64::INFINITY, f64::min);
836 Ok(if min.is_infinite() { Value::Null } else { Value::Number(min) })
837 }
838
839 fn apply_max(&self, value: &Value, field: &str) -> Result<Value, ExecutionError> {
840 let nums = value.extract_numbers(field)?;
841 let max = nums.iter().copied().fold(f64::NEG_INFINITY, f64::max);
842 Ok(if max.is_infinite() { Value::Null } else { Value::Number(max) })
843 }
844
845 fn apply_last(&self, value: &Value, n: usize) -> Result<Value, ExecutionError> {
846 let arr = value.require_array()?;
847 let len = arr.len();
848 let skip = len.saturating_sub(n);
849 Ok(Value::Array(arr.iter().skip(skip).cloned().collect()))
850 }
851
852 fn apply_flatten(&self, value: &Value) -> Result<Value, ExecutionError> {
853 let arr = value.require_array()?;
854
855 let mut result = Vec::new();
856 for item in arr {
857 if let Some(inner) = item.as_array() {
858 result.extend(inner.iter().cloned());
859 } else {
860 result.push(item.clone());
861 }
862 }
863
864 Ok(Value::Array(result))
865 }
866
867 fn apply_reverse(&self, value: &Value) -> Result<Value, ExecutionError> {
868 let arr = value.require_array()?;
869 let mut reversed = arr.clone();
870 reversed.reverse();
871 Ok(Value::Array(reversed))
872 }
873
874 fn apply_map(&self, value: &Value, expr: &str) -> Result<Value, ExecutionError> {
879 let arr = value.require_array()?;
880
881 let mapped: Vec<Value> = arr
884 .iter()
885 .map(|item| {
886 if let Some(field) = expr.strip_prefix("item.") {
888 if let Some(obj) = item.as_object() {
889 obj.get(field).cloned().unwrap_or(Value::Null)
890 } else {
891 item.clone()
892 }
893 } else {
894 item.clone()
896 }
897 })
898 .collect();
899
900 Ok(Value::Array(mapped))
901 }
902
903 fn apply_reduce(
904 &self,
905 value: &Value,
906 initial: &str,
907 _expr: &str,
908 ) -> Result<Value, ExecutionError> {
909 let arr = value.require_array()?;
910
911 let mut acc: f64 = initial.parse().unwrap_or(0.0);
913
914 for item in arr {
916 if let Some(n) = item.as_number() {
917 acc += n;
918 }
919 }
920
921 Ok(Value::Number(acc))
922 }
923
924 fn apply_aggregate(
925 &self,
926 value: &Value,
927 field: &str,
928 op: AggregateOp,
929 ) -> Result<Value, ExecutionError> {
930 let arr = value.require_array()?;
931
932 let values: Vec<f64> = arr
936 .iter()
937 .filter_map(|item| {
938 if let Some(obj) = item.as_object() {
939 if let Some(Value::Array(group_values)) = obj.get("values") {
941 return Some(
942 group_values
943 .iter()
944 .filter_map(|v| v.get(field)?.as_number())
945 .collect::<Vec<_>>(),
946 );
947 }
948 obj.get(field)?.as_number().map(|n| vec![n])
950 } else {
951 None
952 }
953 })
954 .flatten()
955 .collect();
956
957 let result = match op {
958 AggregateOp::Sum => values.iter().sum(),
959 AggregateOp::Count => values.len() as f64,
960 AggregateOp::Mean => {
961 if values.is_empty() {
962 0.0
963 } else {
964 values.iter().sum::<f64>() / values.len() as f64
965 }
966 }
967 AggregateOp::Min => values.iter().cloned().fold(f64::INFINITY, f64::min),
968 AggregateOp::Max => values.iter().cloned().fold(f64::NEG_INFINITY, f64::max),
969 AggregateOp::First => values.first().copied().unwrap_or(0.0),
970 AggregateOp::Last => values.last().copied().unwrap_or(0.0),
971 };
972
973 Ok(Value::Number(result))
974 }
975
976 fn apply_pivot(
977 &self,
978 value: &Value,
979 row_field: &str,
980 col_field: &str,
981 value_field: &str,
982 ) -> Result<Value, ExecutionError> {
983 let arr = value.require_array()?;
984
985 let mut rows: HashMap<String, HashMap<String, f64>> = HashMap::new();
987
988 for item in arr {
989 if let Some(obj) = item.as_object() {
990 let row_key = obj
991 .get(row_field)
992 .map(|v| self.value_to_string(v))
993 .unwrap_or_default();
994 let col_key = obj
995 .get(col_field)
996 .map(|v| self.value_to_string(v))
997 .unwrap_or_default();
998 let val = obj
999 .get(value_field)
1000 .and_then(|v| v.as_number())
1001 .unwrap_or(0.0);
1002
1003 rows.entry(row_key)
1004 .or_default()
1005 .entry(col_key)
1006 .and_modify(|v| *v += val)
1007 .or_insert(val);
1008 }
1009 }
1010
1011 let result: Vec<Value> = rows
1013 .into_iter()
1014 .map(|(row_key, cols)| {
1015 let mut obj = HashMap::new();
1016 obj.insert(row_field.to_string(), Value::String(row_key));
1017 for (col_key, val) in cols {
1018 obj.insert(col_key, Value::Number(val));
1019 }
1020 Value::Object(obj)
1021 })
1022 .collect();
1023
1024 Ok(Value::Array(result))
1025 }
1026
1027 fn apply_cumsum(&self, value: &Value, field: &str) -> Result<Value, ExecutionError> {
1028 let arr = value.require_array()?;
1029
1030 let mut running_sum = 0.0;
1031 let result: Vec<Value> = arr
1032 .iter()
1033 .map(|item| {
1034 if let Some(obj) = item.as_object() {
1035 let val = obj.get(field).and_then(|v| v.as_number()).unwrap_or(0.0);
1036 running_sum += val;
1037
1038 let mut new_obj = obj.clone();
1039 new_obj.insert(format!("{field}_cumsum"), Value::Number(running_sum));
1040 Value::Object(new_obj)
1041 } else {
1042 item.clone()
1043 }
1044 })
1045 .collect();
1046
1047 Ok(Value::Array(result))
1048 }
1049
1050 fn apply_rank(
1051 &self,
1052 value: &Value,
1053 field: &str,
1054 method: RankMethod,
1055 ) -> Result<Value, ExecutionError> {
1056 let arr = value.require_array()?;
1057
1058 let mut indexed: Vec<(usize, f64)> = arr
1060 .iter()
1061 .enumerate()
1062 .filter_map(|(i, item)| item.as_object()?.get(field)?.as_number().map(|n| (i, n)))
1063 .collect();
1064
1065 indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
1067
1068 let mut ranks = vec![0.0; arr.len()];
1070 match method {
1071 RankMethod::Dense => {
1072 let mut rank = 0;
1073 let mut prev_val: Option<f64> = None;
1074 for (i, val) in indexed {
1075 if prev_val != Some(val) {
1076 rank += 1;
1077 }
1078 ranks[i] = rank as f64;
1079 prev_val = Some(val);
1080 }
1081 }
1082 RankMethod::Ordinal => {
1083 for (rank, (i, _)) in indexed.iter().enumerate() {
1084 ranks[*i] = (rank + 1) as f64;
1085 }
1086 }
1087 RankMethod::Average => {
1088 let mut i = 0;
1089 while i < indexed.len() {
1090 let val = indexed[i].1;
1091 let start = i;
1092 while i < indexed.len() && (indexed[i].1 - val).abs() < f64::EPSILON {
1093 i += 1;
1094 }
1095 let avg_rank =
1096 (start + 1..=i).map(|r| r as f64).sum::<f64>() / (i - start) as f64;
1097 for j in start..i {
1098 ranks[indexed[j].0] = avg_rank;
1099 }
1100 }
1101 }
1102 }
1103
1104 let result: Vec<Value> = arr
1106 .iter()
1107 .enumerate()
1108 .map(|(i, item)| {
1109 if let Some(obj) = item.as_object() {
1110 let mut new_obj = obj.clone();
1111 new_obj.insert(format!("{field}_rank"), Value::Number(ranks[i]));
1112 Value::Object(new_obj)
1113 } else {
1114 item.clone()
1115 }
1116 })
1117 .collect();
1118
1119 Ok(Value::Array(result))
1120 }
1121
1122 fn apply_moving_avg(
1123 &self,
1124 value: &Value,
1125 field: &str,
1126 window: usize,
1127 ) -> Result<Value, ExecutionError> {
1128 let arr = value.require_array()?;
1129
1130 let values: Vec<f64> = arr
1131 .iter()
1132 .filter_map(|item| item.as_object()?.get(field)?.as_number())
1133 .collect();
1134
1135 let result: Vec<Value> = arr
1136 .iter()
1137 .enumerate()
1138 .map(|(i, item)| {
1139 if let Some(obj) = item.as_object() {
1140 let start = i.saturating_sub(window - 1);
1141 let window_values = &values[start..=i.min(values.len() - 1)];
1142 let ma = if window_values.is_empty() {
1143 0.0
1144 } else {
1145 window_values.iter().sum::<f64>() / window_values.len() as f64
1146 };
1147
1148 let mut new_obj = obj.clone();
1149 new_obj.insert(format!("{field}_ma{window}"), Value::Number(ma));
1150 Value::Object(new_obj)
1151 } else {
1152 item.clone()
1153 }
1154 })
1155 .collect();
1156
1157 Ok(Value::Array(result))
1158 }
1159
1160 fn apply_pct_change(&self, value: &Value, field: &str) -> Result<Value, ExecutionError> {
1161 let arr = value.require_array()?;
1162
1163 let values: Vec<f64> = arr
1164 .iter()
1165 .filter_map(|item| item.as_object()?.get(field)?.as_number())
1166 .collect();
1167
1168 let result: Vec<Value> = arr
1169 .iter()
1170 .enumerate()
1171 .map(|(i, item)| {
1172 if let Some(obj) = item.as_object() {
1173 let pct = if i == 0 || values.get(i - 1).map_or(true, |&prev| prev == 0.0) {
1174 0.0
1175 } else {
1176 let prev = values[i - 1];
1177 let curr = values.get(i).copied().unwrap_or(prev);
1178 (curr - prev) / prev * 100.0
1179 };
1180
1181 let mut new_obj = obj.clone();
1182 new_obj.insert(format!("{field}_pct_change"), Value::Number(pct));
1183 Value::Object(new_obj)
1184 } else {
1185 item.clone()
1186 }
1187 })
1188 .collect();
1189
1190 Ok(Value::Array(result))
1191 }
1192
1193 #[allow(clippy::unnecessary_wraps)] fn apply_suggest(
1203 &self,
1204 value: &Value,
1205 prefix: &str,
1206 count: usize,
1207 ) -> Result<Value, ExecutionError> {
1208 if let Some(obj) = value.as_object() {
1210 if let Some(suggestions) = obj.get("_suggestions") {
1212 if let Some(arr) = suggestions.as_array() {
1213 return Ok(Value::Array(arr.iter().take(count).cloned().collect()));
1214 }
1215 }
1216
1217 if obj.contains_key("model_type") || obj.contains_key("source") {
1220 return Ok(Value::Array(vec![]));
1222 }
1223 }
1224
1225 if let Some(arr) = value.as_array() {
1227 let filtered: Vec<Value> = arr
1228 .iter()
1229 .filter(|item| {
1230 if let Some(obj) = item.as_object() {
1231 if let Some(text) = obj.get("text").and_then(|v| v.as_str()) {
1232 return text.starts_with(prefix);
1233 }
1234 }
1235 false
1236 })
1237 .take(count)
1238 .cloned()
1239 .collect();
1240 return Ok(Value::Array(filtered));
1241 }
1242
1243 Ok(Value::Array(vec![]))
1245 }
1246}
1247
1248#[cfg(test)]
1249mod tests {
1250 use super::*;
1251 use crate::expression::ExpressionParser;
1252
1253 #[test]
1256 fn test_value_null() {
1257 let v = Value::null();
1258 assert!(v.is_null());
1259 assert!(!v.is_bool());
1260 }
1261
1262 #[test]
1263 fn test_value_bool() {
1264 let v = Value::bool(true);
1265 assert!(v.is_bool());
1266 assert_eq!(v.as_bool(), Some(true));
1267 }
1268
1269 #[test]
1270 fn test_value_number() {
1271 let v = Value::number(42.5);
1272 assert!(v.is_number());
1273 assert_eq!(v.as_number(), Some(42.5));
1274 }
1275
1276 #[test]
1277 fn test_value_string() {
1278 let v = Value::string("hello");
1279 assert!(v.is_string());
1280 assert_eq!(v.as_str(), Some("hello"));
1281 }
1282
1283 #[test]
1284 fn test_value_array() {
1285 let v = Value::array(vec![Value::number(1.0), Value::number(2.0)]);
1286 assert!(v.is_array());
1287 assert_eq!(v.len(), 2);
1288 }
1289
1290 #[test]
1291 fn test_value_object() {
1292 let mut map = HashMap::new();
1293 map.insert("name".to_string(), Value::string("test"));
1294 let v = Value::object(map);
1295 assert!(v.is_object());
1296 assert_eq!(v.get("name").unwrap().as_str(), Some("test"));
1297 }
1298
1299 #[test]
1300 fn test_value_from_bool() {
1301 let v: Value = true.into();
1302 assert_eq!(v, Value::Bool(true));
1303 }
1304
1305 #[test]
1306 fn test_value_from_number() {
1307 let v: Value = 42.0f64.into();
1308 assert_eq!(v, Value::Number(42.0));
1309 }
1310
1311 #[test]
1312 fn test_value_from_i32() {
1313 let v: Value = 42i32.into();
1314 assert_eq!(v, Value::Number(42.0));
1315 }
1316
1317 #[test]
1318 fn test_value_from_str() {
1319 let v: Value = "hello".into();
1320 assert_eq!(v, Value::String("hello".to_string()));
1321 }
1322
1323 #[test]
1324 fn test_value_default() {
1325 assert_eq!(Value::default(), Value::Null);
1326 }
1327
1328 #[test]
1329 fn test_value_is_empty() {
1330 assert!(Value::array(vec![]).is_empty());
1331 assert!(!Value::array(vec![Value::Null]).is_empty());
1332 }
1333
1334 #[test]
1337 fn test_context_new() {
1338 let ctx = DataContext::new();
1339 assert!(!ctx.contains("foo"));
1340 }
1341
1342 #[test]
1343 fn test_context_insert_get() {
1344 let mut ctx = DataContext::new();
1345 ctx.insert("users", Value::array(vec![]));
1346 assert!(ctx.contains("users"));
1347 }
1348
1349 #[test]
1350 fn test_context_dotted_path() {
1351 let mut ctx = DataContext::new();
1352 let mut data = HashMap::new();
1353 data.insert("transactions".to_string(), Value::array(vec![]));
1354 ctx.insert("data", Value::object(data));
1355
1356 assert!(ctx.contains("data.transactions"));
1357 }
1358
1359 #[test]
1362 fn test_error_display() {
1363 assert_eq!(
1364 ExecutionError::SourceNotFound("foo".to_string()).to_string(),
1365 "source not found: foo"
1366 );
1367 assert_eq!(
1368 ExecutionError::ExpectedArray.to_string(),
1369 "expected an array"
1370 );
1371 }
1372
1373 fn make_test_data() -> DataContext {
1376 let mut ctx = DataContext::new();
1377
1378 let transactions: Vec<Value> = vec![
1380 {
1381 let mut t = HashMap::new();
1382 t.insert("id".to_string(), Value::number(1.0));
1383 t.insert("status".to_string(), Value::string("completed"));
1384 t.insert("amount".to_string(), Value::number(100.0));
1385 Value::Object(t)
1386 },
1387 {
1388 let mut t = HashMap::new();
1389 t.insert("id".to_string(), Value::number(2.0));
1390 t.insert("status".to_string(), Value::string("pending"));
1391 t.insert("amount".to_string(), Value::number(50.0));
1392 Value::Object(t)
1393 },
1394 {
1395 let mut t = HashMap::new();
1396 t.insert("id".to_string(), Value::number(3.0));
1397 t.insert("status".to_string(), Value::string("completed"));
1398 t.insert("amount".to_string(), Value::number(75.0));
1399 Value::Object(t)
1400 },
1401 ];
1402
1403 let mut data = HashMap::new();
1404 data.insert("transactions".to_string(), Value::Array(transactions));
1405 ctx.insert("data", Value::object(data));
1406
1407 ctx
1408 }
1409
1410 #[test]
1411 fn test_execute_simple_source() {
1412 let ctx = make_test_data();
1413 let parser = ExpressionParser::new();
1414 let executor = ExpressionExecutor::new();
1415
1416 let expr = parser.parse("data.transactions").unwrap();
1417 let result = executor.execute(&expr, &ctx).unwrap();
1418
1419 assert!(result.is_array());
1420 assert_eq!(result.len(), 3);
1421 }
1422
1423 #[test]
1424 fn test_execute_source_not_found() {
1425 let ctx = DataContext::new();
1426 let parser = ExpressionParser::new();
1427 let executor = ExpressionExecutor::new();
1428
1429 let expr = parser.parse("nonexistent").unwrap();
1430 let result = executor.execute(&expr, &ctx);
1431
1432 assert!(matches!(result, Err(ExecutionError::SourceNotFound(_))));
1433 }
1434
1435 #[test]
1436 fn test_execute_filter() {
1437 let ctx = make_test_data();
1438 let parser = ExpressionParser::new();
1439 let executor = ExpressionExecutor::new();
1440
1441 let expr = parser
1442 .parse("{{ data.transactions | filter(status=completed) }}")
1443 .unwrap();
1444 let result = executor.execute(&expr, &ctx).unwrap();
1445
1446 assert!(result.is_array());
1447 assert_eq!(result.len(), 2);
1448 }
1449
1450 #[test]
1451 fn test_execute_count() {
1452 let ctx = make_test_data();
1453 let parser = ExpressionParser::new();
1454 let executor = ExpressionExecutor::new();
1455
1456 let expr = parser.parse("{{ data.transactions | count }}").unwrap();
1457 let result = executor.execute(&expr, &ctx).unwrap();
1458
1459 assert_eq!(result.as_number(), Some(3.0));
1460 }
1461
1462 #[test]
1463 fn test_execute_filter_then_count() {
1464 let ctx = make_test_data();
1465 let parser = ExpressionParser::new();
1466 let executor = ExpressionExecutor::new();
1467
1468 let expr = parser
1469 .parse("{{ data.transactions | filter(status=completed) | count }}")
1470 .unwrap();
1471 let result = executor.execute(&expr, &ctx).unwrap();
1472
1473 assert_eq!(result.as_number(), Some(2.0));
1474 }
1475
1476 #[test]
1477 fn test_execute_select() {
1478 let ctx = make_test_data();
1479 let parser = ExpressionParser::new();
1480 let executor = ExpressionExecutor::new();
1481
1482 let expr = parser
1483 .parse("{{ data.transactions | select(id, status) }}")
1484 .unwrap();
1485 let result = executor.execute(&expr, &ctx).unwrap();
1486
1487 let arr = result.as_array().unwrap();
1488 assert_eq!(arr.len(), 3);
1489
1490 let first = arr[0].as_object().unwrap();
1492 assert!(first.contains_key("id"));
1493 assert!(first.contains_key("status"));
1494 assert!(!first.contains_key("amount"));
1495 }
1496
1497 #[test]
1498 fn test_execute_sort_asc() {
1499 let ctx = make_test_data();
1500 let parser = ExpressionParser::new();
1501 let executor = ExpressionExecutor::new();
1502
1503 let expr = parser
1504 .parse("{{ data.transactions | sort(amount) }}")
1505 .unwrap();
1506 let result = executor.execute(&expr, &ctx).unwrap();
1507
1508 let arr = result.as_array().unwrap();
1509 let amounts: Vec<f64> = arr
1510 .iter()
1511 .filter_map(|v| v.get("amount")?.as_number())
1512 .collect();
1513 assert_eq!(amounts, vec![50.0, 75.0, 100.0]);
1514 }
1515
1516 #[test]
1517 fn test_execute_sort_desc() {
1518 let ctx = make_test_data();
1519 let parser = ExpressionParser::new();
1520 let executor = ExpressionExecutor::new();
1521
1522 let expr = parser
1523 .parse("{{ data.transactions | sort(amount, desc=true) }}")
1524 .unwrap();
1525 let result = executor.execute(&expr, &ctx).unwrap();
1526
1527 let arr = result.as_array().unwrap();
1528 let amounts: Vec<f64> = arr
1529 .iter()
1530 .filter_map(|v| v.get("amount")?.as_number())
1531 .collect();
1532 assert_eq!(amounts, vec![100.0, 75.0, 50.0]);
1533 }
1534
1535 #[test]
1536 fn test_execute_limit() {
1537 let ctx = make_test_data();
1538 let parser = ExpressionParser::new();
1539 let executor = ExpressionExecutor::new();
1540
1541 let expr = parser.parse("{{ data.transactions | limit(2) }}").unwrap();
1542 let result = executor.execute(&expr, &ctx).unwrap();
1543
1544 assert_eq!(result.len(), 2);
1545 }
1546
1547 #[test]
1548 fn test_execute_sum() {
1549 let ctx = make_test_data();
1550 let parser = ExpressionParser::new();
1551 let executor = ExpressionExecutor::new();
1552
1553 let expr = parser
1554 .parse("{{ data.transactions | sum(amount) }}")
1555 .unwrap();
1556 let result = executor.execute(&expr, &ctx).unwrap();
1557
1558 assert_eq!(result.as_number(), Some(225.0)); }
1560
1561 #[test]
1562 fn test_execute_mean() {
1563 let ctx = make_test_data();
1564 let parser = ExpressionParser::new();
1565 let executor = ExpressionExecutor::new();
1566
1567 let expr = parser
1568 .parse("{{ data.transactions | mean(amount) }}")
1569 .unwrap();
1570 let result = executor.execute(&expr, &ctx).unwrap();
1571
1572 assert_eq!(result.as_number(), Some(75.0)); }
1574
1575 #[test]
1576 fn test_execute_sample() {
1577 let ctx = make_test_data();
1578 let parser = ExpressionParser::new();
1579 let executor = ExpressionExecutor::new();
1580
1581 let expr = parser.parse("{{ data.transactions | sample(2) }}").unwrap();
1582 let result = executor.execute(&expr, &ctx).unwrap();
1583
1584 assert_eq!(result.len(), 2);
1585 }
1586
1587 #[test]
1588 fn test_execute_percentage() {
1589 let mut ctx = DataContext::new();
1590 ctx.insert("ratio", Value::number(0.75));
1591
1592 let parser = ExpressionParser::new();
1593 let executor = ExpressionExecutor::new();
1594
1595 let expr = parser.parse("{{ ratio | percentage }}").unwrap();
1596 let result = executor.execute(&expr, &ctx).unwrap();
1597
1598 assert_eq!(result.as_number(), Some(75.0));
1599 }
1600
1601 #[test]
1602 fn test_execute_chain() {
1603 let ctx = make_test_data();
1604 let parser = ExpressionParser::new();
1605 let executor = ExpressionExecutor::new();
1606
1607 let expr = parser
1608 .parse("{{ data.transactions | filter(status=completed) | sort(amount, desc=true) | limit(1) }}")
1609 .unwrap();
1610 let result = executor.execute(&expr, &ctx).unwrap();
1611
1612 let arr = result.as_array().unwrap();
1613 assert_eq!(arr.len(), 1);
1614 assert_eq!(arr[0].get("amount").unwrap().as_number(), Some(100.0));
1615 }
1616
1617 #[test]
1618 fn test_filter_numeric_match() {
1619 let ctx = make_test_data();
1620 let parser = ExpressionParser::new();
1621 let executor = ExpressionExecutor::new();
1622
1623 let expr = parser
1624 .parse("{{ data.transactions | filter(amount=100) }}")
1625 .unwrap();
1626 let result = executor.execute(&expr, &ctx).unwrap();
1627
1628 assert_eq!(result.len(), 1);
1629 }
1630
1631 #[test]
1632 fn test_execute_on_empty_array() {
1633 let mut ctx = DataContext::new();
1634 ctx.insert("items", Value::array(vec![]));
1635
1636 let parser = ExpressionParser::new();
1637 let executor = ExpressionExecutor::new();
1638
1639 let expr = parser.parse("{{ items | count }}").unwrap();
1640 let result = executor.execute(&expr, &ctx).unwrap();
1641
1642 assert_eq!(result.as_number(), Some(0.0));
1643 }
1644
1645 #[test]
1646 fn test_execute_mean_empty_array() {
1647 let mut ctx = DataContext::new();
1648 ctx.insert("items", Value::array(vec![]));
1649
1650 let parser = ExpressionParser::new();
1651 let executor = ExpressionExecutor::new();
1652
1653 let expr = parser.parse("{{ items | mean(value) }}").unwrap();
1654 let result = executor.execute(&expr, &ctx).unwrap();
1655
1656 assert_eq!(result.as_number(), Some(0.0));
1657 }
1658
1659 fn make_time_series_data() -> DataContext {
1662 let mut ctx = DataContext::new();
1663
1664 let events: Vec<Value> = vec![
1665 {
1666 let mut e = HashMap::new();
1667 e.insert("timestamp".to_string(), Value::number(1000.0));
1668 e.insert("value".to_string(), Value::number(10.0));
1669 Value::Object(e)
1670 },
1671 {
1672 let mut e = HashMap::new();
1673 e.insert("timestamp".to_string(), Value::number(2000.0));
1674 e.insert("value".to_string(), Value::number(20.0));
1675 Value::Object(e)
1676 },
1677 {
1678 let mut e = HashMap::new();
1679 e.insert("timestamp".to_string(), Value::number(3000.0));
1680 e.insert("value".to_string(), Value::number(30.0));
1681 Value::Object(e)
1682 },
1683 ];
1684
1685 ctx.insert("events", Value::Array(events));
1686 ctx
1687 }
1688
1689 #[test]
1690 fn test_execute_rate() {
1691 let ctx = make_time_series_data();
1692 let parser = ExpressionParser::new();
1693 let executor = ExpressionExecutor::new();
1694
1695 let expr = parser.parse("{{ events | rate(5s) }}").unwrap();
1696 let result = executor.execute(&expr, &ctx).unwrap();
1697
1698 assert!(result.is_number());
1701 let rate = result.as_number().unwrap();
1702 assert!(rate > 0.0);
1703 }
1704
1705 #[test]
1706 fn test_execute_rate_minute_window() {
1707 let ctx = make_time_series_data();
1708 let parser = ExpressionParser::new();
1709 let executor = ExpressionExecutor::new();
1710
1711 let expr = parser.parse("{{ events | rate(1m) }}").unwrap();
1712 let result = executor.execute(&expr, &ctx).unwrap();
1713
1714 assert!(result.is_number());
1715 }
1716
1717 #[test]
1720 fn test_execute_group_by() {
1721 let ctx = make_test_data();
1722 let parser = ExpressionParser::new();
1723 let executor = ExpressionExecutor::new();
1724
1725 let expr = parser
1726 .parse("{{ data.transactions | group_by(status) }}")
1727 .unwrap();
1728 let result = executor.execute(&expr, &ctx).unwrap();
1729
1730 let arr = result.as_array().unwrap();
1731 assert_eq!(arr.len(), 2);
1733
1734 for group in arr {
1735 let obj = group.as_object().unwrap();
1736 assert!(obj.contains_key("key"));
1737 assert!(obj.contains_key("items"));
1738 assert!(obj.contains_key("count"));
1739 }
1740 }
1741
1742 #[test]
1745 fn test_execute_distinct_field() {
1746 let ctx = make_test_data();
1747 let parser = ExpressionParser::new();
1748 let executor = ExpressionExecutor::new();
1749
1750 let expr = parser
1751 .parse("{{ data.transactions | distinct(status) }}")
1752 .unwrap();
1753 let result = executor.execute(&expr, &ctx).unwrap();
1754
1755 assert_eq!(result.len(), 2); }
1758
1759 #[test]
1760 fn test_execute_distinct_no_field() {
1761 let mut ctx = DataContext::new();
1762 ctx.insert(
1763 "items",
1764 Value::array(vec![
1765 Value::string("a"),
1766 Value::string("b"),
1767 Value::string("a"),
1768 Value::string("c"),
1769 ]),
1770 );
1771
1772 let parser = ExpressionParser::new();
1773 let executor = ExpressionExecutor::new();
1774
1775 let expr = parser.parse("{{ items | distinct }}").unwrap();
1776 let result = executor.execute(&expr, &ctx).unwrap();
1777
1778 assert_eq!(result.len(), 3); }
1780
1781 #[test]
1784 fn test_execute_where_gt() {
1785 let ctx = make_test_data();
1786 let parser = ExpressionParser::new();
1787 let executor = ExpressionExecutor::new();
1788
1789 let expr = parser
1790 .parse("{{ data.transactions | where(amount, gt, 60) }}")
1791 .unwrap();
1792 let result = executor.execute(&expr, &ctx).unwrap();
1793
1794 assert_eq!(result.len(), 2);
1796 }
1797
1798 #[test]
1799 fn test_execute_where_lt() {
1800 let ctx = make_test_data();
1801 let parser = ExpressionParser::new();
1802 let executor = ExpressionExecutor::new();
1803
1804 let expr = parser
1805 .parse("{{ data.transactions | where(amount, lt, 80) }}")
1806 .unwrap();
1807 let result = executor.execute(&expr, &ctx).unwrap();
1808
1809 assert_eq!(result.len(), 2);
1811 }
1812
1813 #[test]
1814 fn test_execute_where_eq() {
1815 let ctx = make_test_data();
1816 let parser = ExpressionParser::new();
1817 let executor = ExpressionExecutor::new();
1818
1819 let expr = parser
1820 .parse("{{ data.transactions | where(status, eq, pending) }}")
1821 .unwrap();
1822 let result = executor.execute(&expr, &ctx).unwrap();
1823
1824 assert_eq!(result.len(), 1);
1825 }
1826
1827 #[test]
1828 fn test_execute_where_ne() {
1829 let ctx = make_test_data();
1830 let parser = ExpressionParser::new();
1831 let executor = ExpressionExecutor::new();
1832
1833 let expr = parser
1834 .parse("{{ data.transactions | where(status, ne, pending) }}")
1835 .unwrap();
1836 let result = executor.execute(&expr, &ctx).unwrap();
1837
1838 assert_eq!(result.len(), 2);
1839 }
1840
1841 #[test]
1842 fn test_execute_where_contains() {
1843 let mut ctx = DataContext::new();
1844 let items: Vec<Value> = vec![
1845 {
1846 let mut t = HashMap::new();
1847 t.insert("name".to_string(), Value::string("hello world"));
1848 Value::Object(t)
1849 },
1850 {
1851 let mut t = HashMap::new();
1852 t.insert("name".to_string(), Value::string("goodbye"));
1853 Value::Object(t)
1854 },
1855 ];
1856 ctx.insert("items", Value::Array(items));
1857
1858 let parser = ExpressionParser::new();
1859 let executor = ExpressionExecutor::new();
1860
1861 let expr = parser
1862 .parse("{{ items | where(name, contains, world) }}")
1863 .unwrap();
1864 let result = executor.execute(&expr, &ctx).unwrap();
1865
1866 assert_eq!(result.len(), 1);
1867 }
1868
1869 #[test]
1872 fn test_execute_offset() {
1873 let ctx = make_test_data();
1874 let parser = ExpressionParser::new();
1875 let executor = ExpressionExecutor::new();
1876
1877 let expr = parser.parse("{{ data.transactions | offset(1) }}").unwrap();
1878 let result = executor.execute(&expr, &ctx).unwrap();
1879
1880 assert_eq!(result.len(), 2); }
1882
1883 #[test]
1884 fn test_execute_offset_with_limit() {
1885 let ctx = make_test_data();
1886 let parser = ExpressionParser::new();
1887 let executor = ExpressionExecutor::new();
1888
1889 let expr = parser
1890 .parse("{{ data.transactions | offset(1) | limit(1) }}")
1891 .unwrap();
1892 let result = executor.execute(&expr, &ctx).unwrap();
1893
1894 assert_eq!(result.len(), 1);
1895 }
1896
1897 #[test]
1900 fn test_execute_min() {
1901 let ctx = make_test_data();
1902 let parser = ExpressionParser::new();
1903 let executor = ExpressionExecutor::new();
1904
1905 let expr = parser
1906 .parse("{{ data.transactions | min(amount) }}")
1907 .unwrap();
1908 let result = executor.execute(&expr, &ctx).unwrap();
1909
1910 assert_eq!(result.as_number(), Some(50.0));
1911 }
1912
1913 #[test]
1914 fn test_execute_max() {
1915 let ctx = make_test_data();
1916 let parser = ExpressionParser::new();
1917 let executor = ExpressionExecutor::new();
1918
1919 let expr = parser
1920 .parse("{{ data.transactions | max(amount) }}")
1921 .unwrap();
1922 let result = executor.execute(&expr, &ctx).unwrap();
1923
1924 assert_eq!(result.as_number(), Some(100.0));
1925 }
1926
1927 #[test]
1928 fn test_execute_min_empty() {
1929 let mut ctx = DataContext::new();
1930 ctx.insert("items", Value::array(vec![]));
1931
1932 let parser = ExpressionParser::new();
1933 let executor = ExpressionExecutor::new();
1934
1935 let expr = parser.parse("{{ items | min(value) }}").unwrap();
1936 let result = executor.execute(&expr, &ctx).unwrap();
1937
1938 assert!(result.is_null());
1939 }
1940
1941 #[test]
1944 fn test_execute_first() {
1945 let ctx = make_test_data();
1946 let parser = ExpressionParser::new();
1947 let executor = ExpressionExecutor::new();
1948
1949 let expr = parser.parse("{{ data.transactions | first(2) }}").unwrap();
1950 let result = executor.execute(&expr, &ctx).unwrap();
1951
1952 assert_eq!(result.len(), 2);
1953 }
1954
1955 #[test]
1956 fn test_execute_last() {
1957 let ctx = make_test_data();
1958 let parser = ExpressionParser::new();
1959 let executor = ExpressionExecutor::new();
1960
1961 let expr = parser.parse("{{ data.transactions | last(2) }}").unwrap();
1962 let result = executor.execute(&expr, &ctx).unwrap();
1963
1964 let arr = result.as_array().unwrap();
1965 assert_eq!(arr.len(), 2);
1966
1967 assert_eq!(arr[1].get("id").unwrap().as_number(), Some(3.0));
1969 }
1970
1971 #[test]
1974 fn test_execute_flatten() {
1975 let mut ctx = DataContext::new();
1976 ctx.insert(
1977 "nested",
1978 Value::array(vec![
1979 Value::array(vec![Value::number(1.0), Value::number(2.0)]),
1980 Value::array(vec![Value::number(3.0), Value::number(4.0)]),
1981 ]),
1982 );
1983
1984 let parser = ExpressionParser::new();
1985 let executor = ExpressionExecutor::new();
1986
1987 let expr = parser.parse("{{ nested | flatten }}").unwrap();
1988 let result = executor.execute(&expr, &ctx).unwrap();
1989
1990 let arr = result.as_array().unwrap();
1991 assert_eq!(arr.len(), 4);
1992 assert_eq!(arr[0].as_number(), Some(1.0));
1993 assert_eq!(arr[3].as_number(), Some(4.0));
1994 }
1995
1996 #[test]
1997 fn test_execute_flatten_mixed() {
1998 let mut ctx = DataContext::new();
1999 ctx.insert(
2000 "items",
2001 Value::array(vec![
2002 Value::number(1.0),
2003 Value::array(vec![Value::number(2.0), Value::number(3.0)]),
2004 Value::number(4.0),
2005 ]),
2006 );
2007
2008 let parser = ExpressionParser::new();
2009 let executor = ExpressionExecutor::new();
2010
2011 let expr = parser.parse("{{ items | flatten }}").unwrap();
2012 let result = executor.execute(&expr, &ctx).unwrap();
2013
2014 assert_eq!(result.len(), 4);
2015 }
2016
2017 #[test]
2020 fn test_execute_reverse() {
2021 let ctx = make_test_data();
2022 let parser = ExpressionParser::new();
2023 let executor = ExpressionExecutor::new();
2024
2025 let expr = parser.parse("{{ data.transactions | reverse }}").unwrap();
2026 let result = executor.execute(&expr, &ctx).unwrap();
2027
2028 let arr = result.as_array().unwrap();
2029 assert_eq!(arr[0].get("id").unwrap().as_number(), Some(3.0));
2031 }
2032
2033 #[test]
2036 fn test_execute_complex_chain() {
2037 let ctx = make_test_data();
2038 let parser = ExpressionParser::new();
2039 let executor = ExpressionExecutor::new();
2040
2041 let expr = parser
2043 .parse("{{ data.transactions | where(status, eq, completed) | sort(amount, desc=true) | first(1) }}")
2044 .unwrap();
2045 let result = executor.execute(&expr, &ctx).unwrap();
2046
2047 let arr = result.as_array().unwrap();
2048 assert_eq!(arr.len(), 1);
2049 assert_eq!(arr[0].get("amount").unwrap().as_number(), Some(100.0));
2051 }
2052
2053 #[test]
2054 fn test_execute_group_then_count() {
2055 let ctx = make_test_data();
2056 let parser = ExpressionParser::new();
2057 let executor = ExpressionExecutor::new();
2058
2059 let expr = parser
2060 .parse("{{ data.transactions | group_by(status) | count }}")
2061 .unwrap();
2062 let result = executor.execute(&expr, &ctx).unwrap();
2063
2064 assert_eq!(result.as_number(), Some(2.0));
2066 }
2067
2068 fn make_join_test_data() -> DataContext {
2071 let mut ctx = DataContext::new();
2072
2073 let orders = Value::Array(vec![
2075 {
2076 let mut obj = HashMap::new();
2077 obj.insert("id".to_string(), Value::Number(1.0));
2078 obj.insert("customer_id".to_string(), Value::Number(100.0));
2079 obj.insert("amount".to_string(), Value::Number(50.0));
2080 Value::Object(obj)
2081 },
2082 {
2083 let mut obj = HashMap::new();
2084 obj.insert("id".to_string(), Value::Number(2.0));
2085 obj.insert("customer_id".to_string(), Value::Number(101.0));
2086 obj.insert("amount".to_string(), Value::Number(75.0));
2087 Value::Object(obj)
2088 },
2089 {
2090 let mut obj = HashMap::new();
2091 obj.insert("id".to_string(), Value::Number(3.0));
2092 obj.insert("customer_id".to_string(), Value::Number(100.0));
2093 obj.insert("amount".to_string(), Value::Number(25.0));
2094 Value::Object(obj)
2095 },
2096 {
2097 let mut obj = HashMap::new();
2098 obj.insert("id".to_string(), Value::Number(4.0));
2099 obj.insert("customer_id".to_string(), Value::Number(999.0)); obj.insert("amount".to_string(), Value::Number(10.0));
2101 Value::Object(obj)
2102 },
2103 ]);
2104
2105 let customers = Value::Array(vec![
2107 {
2108 let mut obj = HashMap::new();
2109 obj.insert("customer_id".to_string(), Value::Number(100.0));
2110 obj.insert("name".to_string(), Value::String("Alice".to_string()));
2111 obj.insert("tier".to_string(), Value::String("gold".to_string()));
2112 Value::Object(obj)
2113 },
2114 {
2115 let mut obj = HashMap::new();
2116 obj.insert("customer_id".to_string(), Value::Number(101.0));
2117 obj.insert("name".to_string(), Value::String("Bob".to_string()));
2118 obj.insert("tier".to_string(), Value::String("silver".to_string()));
2119 Value::Object(obj)
2120 },
2121 ]);
2122
2123 ctx.insert("orders", orders);
2124 ctx.insert("customers", customers);
2125 ctx
2126 }
2127
2128 #[test]
2129 fn test_execute_join_basic() {
2130 let ctx = make_join_test_data();
2131 let parser = ExpressionParser::new();
2132 let executor = ExpressionExecutor::new();
2133
2134 let expr = parser
2135 .parse("{{ orders | join(customers, on=customer_id) }}")
2136 .unwrap();
2137 let result = executor.execute(&expr, &ctx).unwrap();
2138
2139 let arr = result.as_array().unwrap();
2140 assert_eq!(arr.len(), 4);
2142
2143 let first = arr[0].as_object().unwrap();
2145 assert_eq!(first.get("id").unwrap().as_number(), Some(1.0));
2146 assert_eq!(first.get("name").unwrap().as_str(), Some("Alice"));
2147 assert_eq!(first.get("tier").unwrap().as_str(), Some("gold"));
2148 }
2149
2150 #[test]
2151 fn test_execute_join_multiple_matches() {
2152 let ctx = make_join_test_data();
2153 let executor = ExpressionExecutor::new();
2154
2155 let expr = Expression {
2157 source: "orders".to_string(),
2158 transforms: vec![Transform::Join {
2159 other: "customers".to_string(),
2160 on: "customer_id".to_string(),
2161 }],
2162 };
2163
2164 let result = executor.execute(&expr, &ctx).unwrap();
2165 let arr = result.as_array().unwrap();
2166
2167 let alice_orders: Vec<_> = arr
2169 .iter()
2170 .filter(|v| {
2171 v.as_object()
2172 .and_then(|o| o.get("name"))
2173 .and_then(|n| n.as_str())
2174 == Some("Alice")
2175 })
2176 .collect();
2177 assert_eq!(alice_orders.len(), 2);
2178 }
2179
2180 #[test]
2181 fn test_execute_join_no_match_keeps_left() {
2182 let ctx = make_join_test_data();
2183 let executor = ExpressionExecutor::new();
2184
2185 let expr = Expression {
2186 source: "orders".to_string(),
2187 transforms: vec![Transform::Join {
2188 other: "customers".to_string(),
2189 on: "customer_id".to_string(),
2190 }],
2191 };
2192
2193 let result = executor.execute(&expr, &ctx).unwrap();
2194 let arr = result.as_array().unwrap();
2195
2196 let order4: Vec<_> = arr
2198 .iter()
2199 .filter(|v| {
2200 v.as_object()
2201 .and_then(|o| o.get("id"))
2202 .and_then(|n| n.as_number())
2203 == Some(4.0)
2204 })
2205 .collect();
2206 assert_eq!(order4.len(), 1);
2207
2208 let obj = order4[0].as_object().unwrap();
2210 assert_eq!(obj.get("customer_id").unwrap().as_number(), Some(999.0));
2211 assert!(obj.get("name").is_none());
2212 }
2213
2214 #[test]
2215 fn test_execute_join_other_source_not_found() {
2216 let ctx = make_join_test_data();
2217 let executor = ExpressionExecutor::new();
2218
2219 let expr = Expression {
2220 source: "orders".to_string(),
2221 transforms: vec![Transform::Join {
2222 other: "nonexistent".to_string(),
2223 on: "customer_id".to_string(),
2224 }],
2225 };
2226
2227 let result = executor.execute(&expr, &ctx);
2228 assert!(result.is_err());
2229 assert!(matches!(
2230 result.unwrap_err(),
2231 ExecutionError::SourceNotFound(_)
2232 ));
2233 }
2234
2235 #[test]
2236 fn test_execute_join_chained_with_filter() {
2237 let ctx = make_join_test_data();
2238 let parser = ExpressionParser::new();
2239 let executor = ExpressionExecutor::new();
2240
2241 let expr = parser
2243 .parse("{{ orders | join(customers, on=customer_id) | filter(tier=gold) }}")
2244 .unwrap();
2245 let result = executor.execute(&expr, &ctx).unwrap();
2246
2247 let arr = result.as_array().unwrap();
2248 assert_eq!(arr.len(), 2);
2250 }
2251
2252 #[test]
2253 fn test_execute_join_empty_left() {
2254 let mut ctx = DataContext::new();
2255 ctx.insert("empty", Value::Array(vec![]));
2256 ctx.insert(
2257 "other",
2258 Value::Array(vec![{
2259 let mut obj = HashMap::new();
2260 obj.insert("id".to_string(), Value::Number(1.0));
2261 Value::Object(obj)
2262 }]),
2263 );
2264
2265 let executor = ExpressionExecutor::new();
2266 let expr = Expression {
2267 source: "empty".to_string(),
2268 transforms: vec![Transform::Join {
2269 other: "other".to_string(),
2270 on: "id".to_string(),
2271 }],
2272 };
2273
2274 let result = executor.execute(&expr, &ctx).unwrap();
2275 let arr = result.as_array().unwrap();
2276 assert!(arr.is_empty());
2277 }
2278
2279 #[test]
2280 fn test_execute_join_empty_right() {
2281 let mut ctx = DataContext::new();
2282 ctx.insert(
2283 "orders",
2284 Value::Array(vec![{
2285 let mut obj = HashMap::new();
2286 obj.insert("id".to_string(), Value::Number(1.0));
2287 Value::Object(obj)
2288 }]),
2289 );
2290 ctx.insert("empty", Value::Array(vec![]));
2291
2292 let executor = ExpressionExecutor::new();
2293 let expr = Expression {
2294 source: "orders".to_string(),
2295 transforms: vec![Transform::Join {
2296 other: "empty".to_string(),
2297 on: "id".to_string(),
2298 }],
2299 };
2300
2301 let result = executor.execute(&expr, &ctx).unwrap();
2302 let arr = result.as_array().unwrap();
2303 assert_eq!(arr.len(), 1);
2305 }
2306
2307 #[test]
2308 fn test_execute_join_conflicting_field_names() {
2309 let mut ctx = DataContext::new();
2310
2311 ctx.insert(
2313 "left",
2314 Value::Array(vec![{
2315 let mut obj = HashMap::new();
2316 obj.insert("id".to_string(), Value::Number(1.0));
2317 obj.insert("value".to_string(), Value::String("left_val".to_string()));
2318 Value::Object(obj)
2319 }]),
2320 );
2321 ctx.insert(
2322 "right",
2323 Value::Array(vec![{
2324 let mut obj = HashMap::new();
2325 obj.insert("id".to_string(), Value::Number(1.0));
2326 obj.insert("value".to_string(), Value::String("right_val".to_string()));
2327 obj.insert("extra".to_string(), Value::String("extra_val".to_string()));
2328 Value::Object(obj)
2329 }]),
2330 );
2331
2332 let executor = ExpressionExecutor::new();
2333 let expr = Expression {
2334 source: "left".to_string(),
2335 transforms: vec![Transform::Join {
2336 other: "right".to_string(),
2337 on: "id".to_string(),
2338 }],
2339 };
2340
2341 let result = executor.execute(&expr, &ctx).unwrap();
2342 let arr = result.as_array().unwrap();
2343 assert_eq!(arr.len(), 1);
2344
2345 let obj = arr[0].as_object().unwrap();
2346 assert_eq!(obj.get("value").unwrap().as_str(), Some("left_val"));
2348 assert_eq!(obj.get("right_value").unwrap().as_str(), Some("right_val"));
2350 assert_eq!(obj.get("extra").unwrap().as_str(), Some("extra_val"));
2352 }
2353
2354 #[test]
2355 fn test_execute_join_with_sum() {
2356 let ctx = make_join_test_data();
2357 let parser = ExpressionParser::new();
2358 let executor = ExpressionExecutor::new();
2359
2360 let expr = parser
2362 .parse(
2363 "{{ orders | join(customers, on=customer_id) | filter(tier=gold) | sum(amount) }}",
2364 )
2365 .unwrap();
2366 let result = executor.execute(&expr, &ctx).unwrap();
2367
2368 assert_eq!(result.as_number(), Some(75.0));
2370 }
2371
2372 #[test]
2375 fn test_execute_suggest_with_array() {
2376 let mut ctx = DataContext::new();
2377
2378 let suggestions = Value::Array(vec![
2380 {
2381 let mut obj = HashMap::new();
2382 obj.insert("text".to_string(), Value::String("git status".to_string()));
2383 obj.insert("score".to_string(), Value::Number(0.15));
2384 Value::Object(obj)
2385 },
2386 {
2387 let mut obj = HashMap::new();
2388 obj.insert("text".to_string(), Value::String("git commit".to_string()));
2389 obj.insert("score".to_string(), Value::Number(0.12));
2390 Value::Object(obj)
2391 },
2392 {
2393 let mut obj = HashMap::new();
2394 obj.insert("text".to_string(), Value::String("cargo build".to_string()));
2395 obj.insert("score".to_string(), Value::Number(0.10));
2396 Value::Object(obj)
2397 },
2398 ]);
2399 ctx.insert("suggestions", suggestions);
2400
2401 let parser = ExpressionParser::new();
2402 let executor = ExpressionExecutor::new();
2403
2404 let expr = parser.parse("{{ suggestions | suggest(git, 5) }}").unwrap();
2406 let result = executor.execute(&expr, &ctx).unwrap();
2407
2408 let arr = result.as_array().unwrap();
2409 assert_eq!(arr.len(), 2); }
2411
2412 #[test]
2413 fn test_execute_suggest_with_model_object() {
2414 let mut ctx = DataContext::new();
2415
2416 let mut model = HashMap::new();
2418 model.insert(
2419 "model_type".to_string(),
2420 Value::String("ngram_lm".to_string()),
2421 );
2422 model.insert(
2423 "source".to_string(),
2424 Value::String("./model.apr".to_string()),
2425 );
2426 model.insert(
2427 "_suggestions".to_string(),
2428 Value::Array(vec![
2429 {
2430 let mut obj = HashMap::new();
2431 obj.insert("text".to_string(), Value::String("git status".to_string()));
2432 obj.insert("score".to_string(), Value::Number(0.15));
2433 Value::Object(obj)
2434 },
2435 {
2436 let mut obj = HashMap::new();
2437 obj.insert("text".to_string(), Value::String("git commit".to_string()));
2438 obj.insert("score".to_string(), Value::Number(0.12));
2439 Value::Object(obj)
2440 },
2441 ]),
2442 );
2443 ctx.insert("model", Value::Object(model));
2444
2445 let parser = ExpressionParser::new();
2446 let executor = ExpressionExecutor::new();
2447
2448 let expr = parser.parse("{{ model | suggest(git, 5) }}").unwrap();
2449 let result = executor.execute(&expr, &ctx).unwrap();
2450
2451 let arr = result.as_array().unwrap();
2452 assert_eq!(arr.len(), 2); }
2454
2455 #[test]
2456 fn test_execute_suggest_empty_model() {
2457 let mut ctx = DataContext::new();
2458
2459 let mut model = HashMap::new();
2461 model.insert(
2462 "model_type".to_string(),
2463 Value::String("ngram_lm".to_string()),
2464 );
2465 ctx.insert("model", Value::Object(model));
2466
2467 let parser = ExpressionParser::new();
2468 let executor = ExpressionExecutor::new();
2469
2470 let expr = parser.parse("{{ model | suggest(git, 5) }}").unwrap();
2471 let result = executor.execute(&expr, &ctx).unwrap();
2472
2473 let arr = result.as_array().unwrap();
2474 assert!(arr.is_empty()); }
2476
2477 #[test]
2478 fn test_parse_suggest() {
2479 let parser = ExpressionParser::new();
2480 let expr = parser.parse("{{ model | suggest(git, 8) }}").unwrap();
2481
2482 assert_eq!(expr.source, "model");
2483 assert_eq!(expr.transforms.len(), 1);
2484 assert!(matches!(
2485 &expr.transforms[0],
2486 Transform::Suggest { prefix, count } if prefix == "git" && *count == 8
2487 ));
2488 }
2489
2490 #[test]
2493 fn test_value_from_i64() {
2494 let v: Value = 123i64.into();
2495 assert_eq!(v.as_number(), Some(123.0));
2496 }
2497
2498 #[test]
2499 fn test_value_as_array_mut() {
2500 let mut v = Value::array(vec![Value::number(1.0), Value::number(2.0)]);
2501 if let Some(arr) = v.as_array_mut() {
2502 arr.push(Value::number(3.0));
2503 }
2504 assert_eq!(v.len(), 3);
2505 }
2506
2507 #[test]
2508 fn test_value_as_array_mut_non_array() {
2509 let mut v = Value::string("hello");
2510 assert!(v.as_array_mut().is_none());
2511 }
2512
2513 #[test]
2514 fn test_value_as_object() {
2515 let mut map = HashMap::new();
2516 map.insert("key".to_string(), Value::string("value"));
2517 let v = Value::object(map);
2518 assert!(v.as_object().is_some());
2519 assert_eq!(v.as_object().unwrap().len(), 1);
2520 }
2521
2522 #[test]
2523 fn test_value_as_object_non_object() {
2524 let v = Value::string("hello");
2525 assert!(v.as_object().is_none());
2526 }
2527
2528 #[test]
2529 fn test_value_len_string() {
2530 let v = Value::string("hello");
2531 assert_eq!(v.len(), 5);
2532 }
2533
2534 #[test]
2535 fn test_value_len_null() {
2536 let v = Value::Null;
2537 assert_eq!(v.len(), 0);
2538 }
2539
2540 #[test]
2541 fn test_value_is_empty_string() {
2542 assert!(Value::string("").is_empty());
2543 assert!(!Value::string("a").is_empty());
2544 }
2545
2546 #[test]
2547 fn test_value_get_non_object() {
2548 let v = Value::array(vec![]);
2549 assert!(v.get("key").is_none());
2550 }
2551
2552 #[test]
2553 fn test_value_as_bool_non_bool() {
2554 let v = Value::string("true");
2555 assert!(v.as_bool().is_none());
2556 }
2557
2558 #[test]
2559 fn test_value_as_number_non_number() {
2560 let v = Value::string("42");
2561 assert!(v.as_number().is_none());
2562 }
2563
2564 #[test]
2565 fn test_value_as_str_non_string() {
2566 let v = Value::number(42.0);
2567 assert!(v.as_str().is_none());
2568 }
2569
2570 #[test]
2571 fn test_value_as_array_non_array() {
2572 let v = Value::string("hello");
2573 assert!(v.as_array().is_none());
2574 }
2575
2576 #[test]
2577 fn test_value_from_vec() {
2578 let v: Value = vec![1i32, 2, 3].into();
2579 assert!(v.is_array());
2580 assert_eq!(v.len(), 3);
2581 }
2582
2583 #[test]
2586 fn test_error_display_expected_object() {
2587 assert_eq!(
2588 ExecutionError::ExpectedObject.to_string(),
2589 "expected an object"
2590 );
2591 }
2592
2593 #[test]
2594 fn test_error_display_field_not_found() {
2595 assert_eq!(
2596 ExecutionError::FieldNotFound("missing".to_string()).to_string(),
2597 "field not found: missing"
2598 );
2599 }
2600
2601 #[test]
2602 fn test_error_display_type_mismatch() {
2603 assert_eq!(
2604 ExecutionError::TypeMismatch("expected number".to_string()).to_string(),
2605 "type mismatch: expected number"
2606 );
2607 }
2608
2609 #[test]
2610 fn test_error_display_invalid_transform() {
2611 assert_eq!(
2612 ExecutionError::InvalidTransform("bad window".to_string()).to_string(),
2613 "invalid transform: bad window"
2614 );
2615 }
2616
2617 #[test]
2620 fn test_parse_window_seconds() {
2621 let executor = ExpressionExecutor::new();
2622 assert_eq!(executor.parse_window("5s").unwrap(), 5000);
2623 }
2624
2625 #[test]
2626 fn test_parse_window_minutes() {
2627 let executor = ExpressionExecutor::new();
2628 assert_eq!(executor.parse_window("2m").unwrap(), 120000);
2629 }
2630
2631 #[test]
2632 fn test_parse_window_hours() {
2633 let executor = ExpressionExecutor::new();
2634 assert_eq!(executor.parse_window("1h").unwrap(), 3600000);
2635 }
2636
2637 #[test]
2638 fn test_parse_window_days() {
2639 let executor = ExpressionExecutor::new();
2640 assert_eq!(executor.parse_window("1d").unwrap(), 86400000);
2641 }
2642
2643 #[test]
2644 fn test_parse_window_milliseconds() {
2645 let executor = ExpressionExecutor::new();
2646 assert_eq!(executor.parse_window("500ms").unwrap(), 500);
2647 }
2648
2649 #[test]
2650 fn test_parse_window_no_unit() {
2651 let executor = ExpressionExecutor::new();
2652 assert_eq!(executor.parse_window("1000").unwrap(), 1000);
2654 }
2655
2656 #[test]
2657 fn test_parse_window_empty() {
2658 let executor = ExpressionExecutor::new();
2659 let result = executor.parse_window("");
2660 assert!(result.is_err());
2661 }
2662
2663 #[test]
2664 fn test_parse_window_invalid() {
2665 let executor = ExpressionExecutor::new();
2666 let result = executor.parse_window("abc");
2667 assert!(result.is_err());
2668 }
2669
2670 #[test]
2673 fn test_compare_values_gte() {
2674 let executor = ExpressionExecutor::new();
2675 assert!(executor.compare_values(&Value::Number(50.0), ">=", "50"));
2676 assert!(executor.compare_values(&Value::Number(51.0), "gte", "50"));
2677 assert!(!executor.compare_values(&Value::Number(49.0), ">=", "50"));
2678 }
2679
2680 #[test]
2681 fn test_compare_values_lte() {
2682 let executor = ExpressionExecutor::new();
2683 assert!(executor.compare_values(&Value::Number(50.0), "<=", "50"));
2684 assert!(executor.compare_values(&Value::Number(49.0), "lte", "50"));
2685 assert!(!executor.compare_values(&Value::Number(51.0), "<=", "50"));
2686 }
2687
2688 #[test]
2689 fn test_compare_values_starts_with() {
2690 let executor = ExpressionExecutor::new();
2691 assert!(executor.compare_values(
2692 &Value::String("hello world".to_string()),
2693 "starts_with",
2694 "hello"
2695 ));
2696 assert!(!executor.compare_values(
2697 &Value::String("world hello".to_string()),
2698 "starts_with",
2699 "hello"
2700 ));
2701 assert!(!executor.compare_values(&Value::Number(123.0), "starts_with", "hello"));
2702 }
2703
2704 #[test]
2705 fn test_compare_values_ends_with() {
2706 let executor = ExpressionExecutor::new();
2707 assert!(executor.compare_values(
2708 &Value::String("hello world".to_string()),
2709 "ends_with",
2710 "world"
2711 ));
2712 assert!(!executor.compare_values(
2713 &Value::String("world hello".to_string()),
2714 "ends_with",
2715 "world"
2716 ));
2717 assert!(!executor.compare_values(&Value::Number(123.0), "ends_with", "world"));
2718 }
2719
2720 #[test]
2721 fn test_compare_values_unknown_op() {
2722 let executor = ExpressionExecutor::new();
2723 assert!(!executor.compare_values(&Value::Number(50.0), "unknown", "50"));
2724 }
2725
2726 #[test]
2727 fn test_compare_values_non_numeric_gt() {
2728 let executor = ExpressionExecutor::new();
2729 assert!(!executor.compare_values(&Value::String("abc".to_string()), ">", "50"));
2730 }
2731
2732 #[test]
2735 fn test_value_matches_bool_true() {
2736 let executor = ExpressionExecutor::new();
2737 assert!(executor.value_matches(&Value::Bool(true), "true"));
2738 assert!(!executor.value_matches(&Value::Bool(true), "false"));
2739 }
2740
2741 #[test]
2742 fn test_value_matches_bool_false() {
2743 let executor = ExpressionExecutor::new();
2744 assert!(executor.value_matches(&Value::Bool(false), "false"));
2745 assert!(!executor.value_matches(&Value::Bool(false), "true"));
2746 }
2747
2748 #[test]
2749 fn test_value_matches_number_parse_failure() {
2750 let executor = ExpressionExecutor::new();
2751 assert!(!executor.value_matches(&Value::Number(42.0), "not_a_number"));
2752 }
2753
2754 #[test]
2755 fn test_value_matches_null() {
2756 let executor = ExpressionExecutor::new();
2757 assert!(!executor.value_matches(&Value::Null, "null"));
2758 }
2759
2760 #[test]
2761 fn test_value_matches_array() {
2762 let executor = ExpressionExecutor::new();
2763 assert!(!executor.value_matches(&Value::Array(vec![]), "[]"));
2764 }
2765
2766 #[test]
2769 fn test_value_to_string_null() {
2770 let executor = ExpressionExecutor::new();
2771 assert_eq!(executor.value_to_string(&Value::Null), "_null");
2772 }
2773
2774 #[test]
2775 fn test_value_to_string_bool() {
2776 let executor = ExpressionExecutor::new();
2777 assert_eq!(executor.value_to_string(&Value::Bool(true)), "true");
2778 assert_eq!(executor.value_to_string(&Value::Bool(false)), "false");
2779 }
2780
2781 #[test]
2782 fn test_value_to_string_array() {
2783 let executor = ExpressionExecutor::new();
2784 assert_eq!(executor.value_to_string(&Value::Array(vec![])), "_array");
2785 }
2786
2787 #[test]
2788 fn test_value_to_string_object() {
2789 let executor = ExpressionExecutor::new();
2790 assert_eq!(
2791 executor.value_to_string(&Value::Object(HashMap::new())),
2792 "_object"
2793 );
2794 }
2795
2796 #[test]
2799 fn test_apply_count_object() {
2800 let executor = ExpressionExecutor::new();
2801 let mut obj = HashMap::new();
2802 obj.insert("a".to_string(), Value::number(1.0));
2803 obj.insert("b".to_string(), Value::number(2.0));
2804 let value = Value::Object(obj);
2805 let result = executor.apply_count(&value);
2806 assert_eq!(result.as_number(), Some(2.0));
2807 }
2808
2809 #[test]
2810 fn test_apply_count_string() {
2811 let executor = ExpressionExecutor::new();
2812 let result = executor.apply_count(&Value::String("hello".to_string()));
2813 assert_eq!(result.as_number(), Some(5.0));
2814 }
2815
2816 #[test]
2817 fn test_apply_count_null() {
2818 let executor = ExpressionExecutor::new();
2819 let result = executor.apply_count(&Value::Null);
2820 assert_eq!(result.as_number(), Some(0.0));
2821 }
2822
2823 #[test]
2824 fn test_apply_percentage_non_number() {
2825 let executor = ExpressionExecutor::new();
2826 let result = executor.apply_percentage(&Value::String("hello".to_string()));
2827 assert!(result.is_err());
2828 }
2829
2830 #[test]
2833 fn test_apply_map_field_extract() {
2834 let mut ctx = DataContext::new();
2835 let items = Value::Array(vec![
2836 {
2837 let mut obj = HashMap::new();
2838 obj.insert("name".to_string(), Value::String("Alice".to_string()));
2839 obj.insert("age".to_string(), Value::Number(30.0));
2840 Value::Object(obj)
2841 },
2842 {
2843 let mut obj = HashMap::new();
2844 obj.insert("name".to_string(), Value::String("Bob".to_string()));
2845 obj.insert("age".to_string(), Value::Number(25.0));
2846 Value::Object(obj)
2847 },
2848 ]);
2849 ctx.insert("users", items);
2850
2851 let executor = ExpressionExecutor::new();
2852 let expr = Expression {
2853 source: "users".to_string(),
2854 transforms: vec![Transform::Map {
2855 expr: "item.name".to_string(),
2856 }],
2857 };
2858 let result = executor.execute(&expr, &ctx).unwrap();
2859 let arr = result.as_array().unwrap();
2860 assert_eq!(arr.len(), 2);
2861 assert_eq!(arr[0].as_str(), Some("Alice"));
2862 assert_eq!(arr[1].as_str(), Some("Bob"));
2863 }
2864
2865 #[test]
2866 fn test_apply_map_non_field() {
2867 let mut ctx = DataContext::new();
2868 ctx.insert(
2869 "items",
2870 Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]),
2871 );
2872
2873 let executor = ExpressionExecutor::new();
2874 let expr = Expression {
2875 source: "items".to_string(),
2876 transforms: vec![Transform::Map {
2877 expr: "unknown".to_string(),
2878 }],
2879 };
2880 let result = executor.execute(&expr, &ctx).unwrap();
2881 let arr = result.as_array().unwrap();
2882 assert_eq!(arr[0].as_number(), Some(1.0));
2884 }
2885
2886 #[test]
2887 fn test_apply_map_missing_field() {
2888 let mut ctx = DataContext::new();
2889 let items = Value::Array(vec![{
2890 let mut obj = HashMap::new();
2891 obj.insert("x".to_string(), Value::Number(1.0));
2892 Value::Object(obj)
2893 }]);
2894 ctx.insert("items", items);
2895
2896 let executor = ExpressionExecutor::new();
2897 let expr = Expression {
2898 source: "items".to_string(),
2899 transforms: vec![Transform::Map {
2900 expr: "item.missing".to_string(),
2901 }],
2902 };
2903 let result = executor.execute(&expr, &ctx).unwrap();
2904 let arr = result.as_array().unwrap();
2905 assert!(arr[0].is_null());
2906 }
2907
2908 #[test]
2911 fn test_apply_reduce() {
2912 let mut ctx = DataContext::new();
2913 ctx.insert(
2914 "numbers",
2915 Value::Array(vec![
2916 Value::Number(1.0),
2917 Value::Number(2.0),
2918 Value::Number(3.0),
2919 ]),
2920 );
2921
2922 let executor = ExpressionExecutor::new();
2923 let expr = Expression {
2924 source: "numbers".to_string(),
2925 transforms: vec![Transform::Reduce {
2926 initial: "0".to_string(),
2927 expr: "acc + item".to_string(),
2928 }],
2929 };
2930 let result = executor.execute(&expr, &ctx).unwrap();
2931 assert_eq!(result.as_number(), Some(6.0));
2932 }
2933
2934 #[test]
2935 fn test_apply_reduce_with_initial() {
2936 let mut ctx = DataContext::new();
2937 ctx.insert("numbers", Value::Array(vec![Value::Number(5.0)]));
2938
2939 let executor = ExpressionExecutor::new();
2940 let expr = Expression {
2941 source: "numbers".to_string(),
2942 transforms: vec![Transform::Reduce {
2943 initial: "10".to_string(),
2944 expr: "acc + item".to_string(),
2945 }],
2946 };
2947 let result = executor.execute(&expr, &ctx).unwrap();
2948 assert_eq!(result.as_number(), Some(15.0));
2949 }
2950
2951 #[test]
2952 fn test_apply_reduce_non_numeric() {
2953 let mut ctx = DataContext::new();
2954 ctx.insert(
2955 "items",
2956 Value::Array(vec![
2957 Value::String("a".to_string()),
2958 Value::String("b".to_string()),
2959 ]),
2960 );
2961
2962 let executor = ExpressionExecutor::new();
2963 let expr = Expression {
2964 source: "items".to_string(),
2965 transforms: vec![Transform::Reduce {
2966 initial: "0".to_string(),
2967 expr: "acc".to_string(),
2968 }],
2969 };
2970 let result = executor.execute(&expr, &ctx).unwrap();
2971 assert_eq!(result.as_number(), Some(0.0));
2973 }
2974
2975 #[test]
2978 fn test_apply_aggregate_sum() {
2979 let ctx = make_test_data();
2980 let executor = ExpressionExecutor::new();
2981 let expr = Expression {
2982 source: "data.transactions".to_string(),
2983 transforms: vec![Transform::Aggregate {
2984 field: "amount".to_string(),
2985 op: AggregateOp::Sum,
2986 }],
2987 };
2988 let result = executor.execute(&expr, &ctx).unwrap();
2989 assert_eq!(result.as_number(), Some(225.0));
2990 }
2991
2992 #[test]
2993 fn test_apply_aggregate_count() {
2994 let ctx = make_test_data();
2995 let executor = ExpressionExecutor::new();
2996 let expr = Expression {
2997 source: "data.transactions".to_string(),
2998 transforms: vec![Transform::Aggregate {
2999 field: "amount".to_string(),
3000 op: AggregateOp::Count,
3001 }],
3002 };
3003 let result = executor.execute(&expr, &ctx).unwrap();
3004 assert_eq!(result.as_number(), Some(3.0));
3005 }
3006
3007 #[test]
3008 fn test_apply_aggregate_mean() {
3009 let ctx = make_test_data();
3010 let executor = ExpressionExecutor::new();
3011 let expr = Expression {
3012 source: "data.transactions".to_string(),
3013 transforms: vec![Transform::Aggregate {
3014 field: "amount".to_string(),
3015 op: AggregateOp::Mean,
3016 }],
3017 };
3018 let result = executor.execute(&expr, &ctx).unwrap();
3019 assert_eq!(result.as_number(), Some(75.0));
3020 }
3021
3022 #[test]
3023 fn test_apply_aggregate_min() {
3024 let ctx = make_test_data();
3025 let executor = ExpressionExecutor::new();
3026 let expr = Expression {
3027 source: "data.transactions".to_string(),
3028 transforms: vec![Transform::Aggregate {
3029 field: "amount".to_string(),
3030 op: AggregateOp::Min,
3031 }],
3032 };
3033 let result = executor.execute(&expr, &ctx).unwrap();
3034 assert_eq!(result.as_number(), Some(50.0));
3035 }
3036
3037 #[test]
3038 fn test_apply_aggregate_max() {
3039 let ctx = make_test_data();
3040 let executor = ExpressionExecutor::new();
3041 let expr = Expression {
3042 source: "data.transactions".to_string(),
3043 transforms: vec![Transform::Aggregate {
3044 field: "amount".to_string(),
3045 op: AggregateOp::Max,
3046 }],
3047 };
3048 let result = executor.execute(&expr, &ctx).unwrap();
3049 assert_eq!(result.as_number(), Some(100.0));
3050 }
3051
3052 #[test]
3053 fn test_apply_aggregate_first() {
3054 let ctx = make_test_data();
3055 let executor = ExpressionExecutor::new();
3056 let expr = Expression {
3057 source: "data.transactions".to_string(),
3058 transforms: vec![Transform::Aggregate {
3059 field: "amount".to_string(),
3060 op: AggregateOp::First,
3061 }],
3062 };
3063 let result = executor.execute(&expr, &ctx).unwrap();
3064 assert_eq!(result.as_number(), Some(100.0));
3065 }
3066
3067 #[test]
3068 fn test_apply_aggregate_last() {
3069 let ctx = make_test_data();
3070 let executor = ExpressionExecutor::new();
3071 let expr = Expression {
3072 source: "data.transactions".to_string(),
3073 transforms: vec![Transform::Aggregate {
3074 field: "amount".to_string(),
3075 op: AggregateOp::Last,
3076 }],
3077 };
3078 let result = executor.execute(&expr, &ctx).unwrap();
3079 assert_eq!(result.as_number(), Some(75.0));
3080 }
3081
3082 #[test]
3083 fn test_apply_aggregate_mean_empty() {
3084 let mut ctx = DataContext::new();
3085 ctx.insert("items", Value::Array(vec![]));
3086
3087 let executor = ExpressionExecutor::new();
3088 let expr = Expression {
3089 source: "items".to_string(),
3090 transforms: vec![Transform::Aggregate {
3091 field: "value".to_string(),
3092 op: AggregateOp::Mean,
3093 }],
3094 };
3095 let result = executor.execute(&expr, &ctx).unwrap();
3096 assert_eq!(result.as_number(), Some(0.0));
3097 }
3098
3099 #[test]
3102 fn test_apply_pivot() {
3103 let mut ctx = DataContext::new();
3104 let sales = Value::Array(vec![
3105 {
3106 let mut obj = HashMap::new();
3107 obj.insert("region".to_string(), Value::String("North".to_string()));
3108 obj.insert("product".to_string(), Value::String("A".to_string()));
3109 obj.insert("revenue".to_string(), Value::Number(100.0));
3110 Value::Object(obj)
3111 },
3112 {
3113 let mut obj = HashMap::new();
3114 obj.insert("region".to_string(), Value::String("North".to_string()));
3115 obj.insert("product".to_string(), Value::String("B".to_string()));
3116 obj.insert("revenue".to_string(), Value::Number(150.0));
3117 Value::Object(obj)
3118 },
3119 {
3120 let mut obj = HashMap::new();
3121 obj.insert("region".to_string(), Value::String("South".to_string()));
3122 obj.insert("product".to_string(), Value::String("A".to_string()));
3123 obj.insert("revenue".to_string(), Value::Number(200.0));
3124 Value::Object(obj)
3125 },
3126 ]);
3127 ctx.insert("sales", sales);
3128
3129 let executor = ExpressionExecutor::new();
3130 let expr = Expression {
3131 source: "sales".to_string(),
3132 transforms: vec![Transform::Pivot {
3133 row_field: "region".to_string(),
3134 col_field: "product".to_string(),
3135 value_field: "revenue".to_string(),
3136 }],
3137 };
3138 let result = executor.execute(&expr, &ctx).unwrap();
3139 let arr = result.as_array().unwrap();
3140 assert_eq!(arr.len(), 2); }
3142
3143 #[test]
3146 fn test_apply_cumsum() {
3147 let mut ctx = DataContext::new();
3148 let items = Value::Array(vec![
3149 {
3150 let mut obj = HashMap::new();
3151 obj.insert("value".to_string(), Value::Number(10.0));
3152 Value::Object(obj)
3153 },
3154 {
3155 let mut obj = HashMap::new();
3156 obj.insert("value".to_string(), Value::Number(20.0));
3157 Value::Object(obj)
3158 },
3159 {
3160 let mut obj = HashMap::new();
3161 obj.insert("value".to_string(), Value::Number(30.0));
3162 Value::Object(obj)
3163 },
3164 ]);
3165 ctx.insert("items", items);
3166
3167 let executor = ExpressionExecutor::new();
3168 let expr = Expression {
3169 source: "items".to_string(),
3170 transforms: vec![Transform::CumulativeSum {
3171 field: "value".to_string(),
3172 }],
3173 };
3174 let result = executor.execute(&expr, &ctx).unwrap();
3175 let arr = result.as_array().unwrap();
3176
3177 assert_eq!(arr[0].get("value_cumsum").unwrap().as_number(), Some(10.0));
3178 assert_eq!(arr[1].get("value_cumsum").unwrap().as_number(), Some(30.0));
3179 assert_eq!(arr[2].get("value_cumsum").unwrap().as_number(), Some(60.0));
3180 }
3181
3182 #[test]
3183 fn test_apply_cumsum_non_object() {
3184 let mut ctx = DataContext::new();
3185 ctx.insert(
3186 "items",
3187 Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]),
3188 );
3189
3190 let executor = ExpressionExecutor::new();
3191 let expr = Expression {
3192 source: "items".to_string(),
3193 transforms: vec![Transform::CumulativeSum {
3194 field: "x".to_string(),
3195 }],
3196 };
3197 let result = executor.execute(&expr, &ctx).unwrap();
3198 let arr = result.as_array().unwrap();
3199 assert_eq!(arr[0].as_number(), Some(1.0));
3201 }
3202
3203 #[test]
3206 fn test_apply_rank_dense() {
3207 let mut ctx = DataContext::new();
3208 let items = Value::Array(vec![
3209 {
3210 let mut obj = HashMap::new();
3211 obj.insert("score".to_string(), Value::Number(100.0));
3212 Value::Object(obj)
3213 },
3214 {
3215 let mut obj = HashMap::new();
3216 obj.insert("score".to_string(), Value::Number(90.0));
3217 Value::Object(obj)
3218 },
3219 {
3220 let mut obj = HashMap::new();
3221 obj.insert("score".to_string(), Value::Number(100.0));
3222 Value::Object(obj)
3223 },
3224 ]);
3225 ctx.insert("items", items);
3226
3227 let executor = ExpressionExecutor::new();
3228 let expr = Expression {
3229 source: "items".to_string(),
3230 transforms: vec![Transform::Rank {
3231 field: "score".to_string(),
3232 method: RankMethod::Dense,
3233 }],
3234 };
3235 let result = executor.execute(&expr, &ctx).unwrap();
3236 let arr = result.as_array().unwrap();
3237
3238 assert_eq!(arr[0].get("score_rank").unwrap().as_number(), Some(1.0));
3240 assert_eq!(arr[1].get("score_rank").unwrap().as_number(), Some(2.0));
3241 assert_eq!(arr[2].get("score_rank").unwrap().as_number(), Some(1.0));
3242 }
3243
3244 #[test]
3245 fn test_apply_rank_ordinal() {
3246 let mut ctx = DataContext::new();
3247 let items = Value::Array(vec![
3248 {
3249 let mut obj = HashMap::new();
3250 obj.insert("score".to_string(), Value::Number(100.0));
3251 Value::Object(obj)
3252 },
3253 {
3254 let mut obj = HashMap::new();
3255 obj.insert("score".to_string(), Value::Number(90.0));
3256 Value::Object(obj)
3257 },
3258 ]);
3259 ctx.insert("items", items);
3260
3261 let executor = ExpressionExecutor::new();
3262 let expr = Expression {
3263 source: "items".to_string(),
3264 transforms: vec![Transform::Rank {
3265 field: "score".to_string(),
3266 method: RankMethod::Ordinal,
3267 }],
3268 };
3269 let result = executor.execute(&expr, &ctx).unwrap();
3270 let arr = result.as_array().unwrap();
3271
3272 assert_eq!(arr[0].get("score_rank").unwrap().as_number(), Some(1.0));
3273 assert_eq!(arr[1].get("score_rank").unwrap().as_number(), Some(2.0));
3274 }
3275
3276 #[test]
3277 fn test_apply_rank_average() {
3278 let mut ctx = DataContext::new();
3279 let items = Value::Array(vec![
3280 {
3281 let mut obj = HashMap::new();
3282 obj.insert("score".to_string(), Value::Number(100.0));
3283 Value::Object(obj)
3284 },
3285 {
3286 let mut obj = HashMap::new();
3287 obj.insert("score".to_string(), Value::Number(100.0));
3288 Value::Object(obj)
3289 },
3290 {
3291 let mut obj = HashMap::new();
3292 obj.insert("score".to_string(), Value::Number(80.0));
3293 Value::Object(obj)
3294 },
3295 ]);
3296 ctx.insert("items", items);
3297
3298 let executor = ExpressionExecutor::new();
3299 let expr = Expression {
3300 source: "items".to_string(),
3301 transforms: vec![Transform::Rank {
3302 field: "score".to_string(),
3303 method: RankMethod::Average,
3304 }],
3305 };
3306 let result = executor.execute(&expr, &ctx).unwrap();
3307 let arr = result.as_array().unwrap();
3308
3309 assert_eq!(arr[0].get("score_rank").unwrap().as_number(), Some(1.5));
3311 assert_eq!(arr[1].get("score_rank").unwrap().as_number(), Some(1.5));
3312 assert_eq!(arr[2].get("score_rank").unwrap().as_number(), Some(3.0));
3313 }
3314
3315 #[test]
3316 fn test_apply_rank_non_object() {
3317 let mut ctx = DataContext::new();
3318 ctx.insert(
3319 "items",
3320 Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]),
3321 );
3322
3323 let executor = ExpressionExecutor::new();
3324 let expr = Expression {
3325 source: "items".to_string(),
3326 transforms: vec![Transform::Rank {
3327 field: "x".to_string(),
3328 method: RankMethod::Dense,
3329 }],
3330 };
3331 let result = executor.execute(&expr, &ctx).unwrap();
3332 let arr = result.as_array().unwrap();
3333 assert_eq!(arr[0].as_number(), Some(1.0));
3335 }
3336
3337 #[test]
3340 fn test_apply_moving_average() {
3341 let mut ctx = DataContext::new();
3342 let items = Value::Array(vec![
3343 {
3344 let mut obj = HashMap::new();
3345 obj.insert("value".to_string(), Value::Number(10.0));
3346 Value::Object(obj)
3347 },
3348 {
3349 let mut obj = HashMap::new();
3350 obj.insert("value".to_string(), Value::Number(20.0));
3351 Value::Object(obj)
3352 },
3353 {
3354 let mut obj = HashMap::new();
3355 obj.insert("value".to_string(), Value::Number(30.0));
3356 Value::Object(obj)
3357 },
3358 {
3359 let mut obj = HashMap::new();
3360 obj.insert("value".to_string(), Value::Number(40.0));
3361 Value::Object(obj)
3362 },
3363 ]);
3364 ctx.insert("items", items);
3365
3366 let executor = ExpressionExecutor::new();
3367 let expr = Expression {
3368 source: "items".to_string(),
3369 transforms: vec![Transform::MovingAverage {
3370 field: "value".to_string(),
3371 window: 2,
3372 }],
3373 };
3374 let result = executor.execute(&expr, &ctx).unwrap();
3375 let arr = result.as_array().unwrap();
3376
3377 assert_eq!(arr[0].get("value_ma2").unwrap().as_number(), Some(10.0));
3379 assert_eq!(arr[1].get("value_ma2").unwrap().as_number(), Some(15.0));
3381 assert_eq!(arr[2].get("value_ma2").unwrap().as_number(), Some(25.0));
3383 }
3384
3385 #[test]
3386 fn test_apply_moving_average_non_object() {
3387 let mut ctx = DataContext::new();
3388 ctx.insert("items", Value::Array(vec![Value::Number(1.0)]));
3389
3390 let executor = ExpressionExecutor::new();
3391 let expr = Expression {
3392 source: "items".to_string(),
3393 transforms: vec![Transform::MovingAverage {
3394 field: "x".to_string(),
3395 window: 3,
3396 }],
3397 };
3398 let result = executor.execute(&expr, &ctx).unwrap();
3399 let arr = result.as_array().unwrap();
3400 assert_eq!(arr[0].as_number(), Some(1.0));
3401 }
3402
3403 #[test]
3406 fn test_apply_percent_change() {
3407 let mut ctx = DataContext::new();
3408 let items = Value::Array(vec![
3409 {
3410 let mut obj = HashMap::new();
3411 obj.insert("price".to_string(), Value::Number(100.0));
3412 Value::Object(obj)
3413 },
3414 {
3415 let mut obj = HashMap::new();
3416 obj.insert("price".to_string(), Value::Number(110.0));
3417 Value::Object(obj)
3418 },
3419 {
3420 let mut obj = HashMap::new();
3421 obj.insert("price".to_string(), Value::Number(99.0));
3422 Value::Object(obj)
3423 },
3424 ]);
3425 ctx.insert("items", items);
3426
3427 let executor = ExpressionExecutor::new();
3428 let expr = Expression {
3429 source: "items".to_string(),
3430 transforms: vec![Transform::PercentChange {
3431 field: "price".to_string(),
3432 }],
3433 };
3434 let result = executor.execute(&expr, &ctx).unwrap();
3435 let arr = result.as_array().unwrap();
3436
3437 assert_eq!(
3439 arr[0].get("price_pct_change").unwrap().as_number(),
3440 Some(0.0)
3441 );
3442 assert_eq!(
3444 arr[1].get("price_pct_change").unwrap().as_number(),
3445 Some(10.0)
3446 );
3447 let pct = arr[2].get("price_pct_change").unwrap().as_number().unwrap();
3449 assert!((pct - (-10.0)).abs() < 0.1);
3450 }
3451
3452 #[test]
3453 fn test_apply_percent_change_zero_prev() {
3454 let mut ctx = DataContext::new();
3455 let items = Value::Array(vec![
3456 {
3457 let mut obj = HashMap::new();
3458 obj.insert("value".to_string(), Value::Number(0.0));
3459 Value::Object(obj)
3460 },
3461 {
3462 let mut obj = HashMap::new();
3463 obj.insert("value".to_string(), Value::Number(10.0));
3464 Value::Object(obj)
3465 },
3466 ]);
3467 ctx.insert("items", items);
3468
3469 let executor = ExpressionExecutor::new();
3470 let expr = Expression {
3471 source: "items".to_string(),
3472 transforms: vec![Transform::PercentChange {
3473 field: "value".to_string(),
3474 }],
3475 };
3476 let result = executor.execute(&expr, &ctx).unwrap();
3477 let arr = result.as_array().unwrap();
3478
3479 assert_eq!(
3481 arr[1].get("value_pct_change").unwrap().as_number(),
3482 Some(0.0)
3483 );
3484 }
3485
3486 #[test]
3487 fn test_apply_percent_change_non_object() {
3488 let mut ctx = DataContext::new();
3489 ctx.insert("items", Value::Array(vec![Value::Number(1.0)]));
3490
3491 let executor = ExpressionExecutor::new();
3492 let expr = Expression {
3493 source: "items".to_string(),
3494 transforms: vec![Transform::PercentChange {
3495 field: "x".to_string(),
3496 }],
3497 };
3498 let result = executor.execute(&expr, &ctx).unwrap();
3499 let arr = result.as_array().unwrap();
3500 assert_eq!(arr[0].as_number(), Some(1.0));
3501 }
3502
3503 #[test]
3506 fn test_sort_by_string() {
3507 let mut ctx = DataContext::new();
3508 let items = Value::Array(vec![
3509 {
3510 let mut obj = HashMap::new();
3511 obj.insert("name".to_string(), Value::String("Charlie".to_string()));
3512 Value::Object(obj)
3513 },
3514 {
3515 let mut obj = HashMap::new();
3516 obj.insert("name".to_string(), Value::String("Alice".to_string()));
3517 Value::Object(obj)
3518 },
3519 {
3520 let mut obj = HashMap::new();
3521 obj.insert("name".to_string(), Value::String("Bob".to_string()));
3522 Value::Object(obj)
3523 },
3524 ]);
3525 ctx.insert("items", items);
3526
3527 let parser = ExpressionParser::new();
3528 let executor = ExpressionExecutor::new();
3529
3530 let expr = parser.parse("{{ items | sort(name) }}").unwrap();
3531 let result = executor.execute(&expr, &ctx).unwrap();
3532 let arr = result.as_array().unwrap();
3533
3534 assert_eq!(arr[0].get("name").unwrap().as_str(), Some("Alice"));
3535 assert_eq!(arr[1].get("name").unwrap().as_str(), Some("Bob"));
3536 assert_eq!(arr[2].get("name").unwrap().as_str(), Some("Charlie"));
3537 }
3538
3539 #[test]
3540 fn test_sort_missing_field() {
3541 let mut ctx = DataContext::new();
3542 let items = Value::Array(vec![
3543 {
3544 let mut obj = HashMap::new();
3545 obj.insert("x".to_string(), Value::Number(1.0));
3546 Value::Object(obj)
3547 },
3548 {
3549 let mut obj = HashMap::new();
3550 obj.insert("y".to_string(), Value::Number(2.0));
3551 Value::Object(obj)
3552 },
3553 ]);
3554 ctx.insert("items", items);
3555
3556 let parser = ExpressionParser::new();
3557 let executor = ExpressionExecutor::new();
3558
3559 let expr = parser.parse("{{ items | sort(z) }}").unwrap();
3560 let result = executor.execute(&expr, &ctx).unwrap();
3561 assert_eq!(result.len(), 2);
3563 }
3564
3565 #[test]
3568 fn test_select_non_object_items() {
3569 let mut ctx = DataContext::new();
3570 ctx.insert(
3571 "items",
3572 Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]),
3573 );
3574
3575 let parser = ExpressionParser::new();
3576 let executor = ExpressionExecutor::new();
3577
3578 let expr = parser.parse("{{ items | select(x) }}").unwrap();
3579 let result = executor.execute(&expr, &ctx).unwrap();
3580 let arr = result.as_array().unwrap();
3581 assert_eq!(arr[0].as_number(), Some(1.0));
3583 }
3584
3585 #[test]
3588 fn test_rate_insufficient_data() {
3589 let mut ctx = DataContext::new();
3590 ctx.insert(
3591 "events",
3592 Value::Array(vec![{
3593 let mut e = HashMap::new();
3594 e.insert("timestamp".to_string(), Value::Number(1000.0));
3595 Value::Object(e)
3596 }]),
3597 );
3598
3599 let parser = ExpressionParser::new();
3600 let executor = ExpressionExecutor::new();
3601
3602 let expr = parser.parse("{{ events | rate(1s) }}").unwrap();
3603 let result = executor.execute(&expr, &ctx).unwrap();
3604 assert_eq!(result.as_number(), Some(0.0));
3606 }
3607
3608 #[test]
3609 fn test_rate_with_time_field() {
3610 let mut ctx = DataContext::new();
3611 let events: Vec<Value> = vec![
3612 {
3613 let mut e = HashMap::new();
3614 e.insert("time".to_string(), Value::Number(1000.0));
3615 e.insert("count".to_string(), Value::Number(5.0));
3616 Value::Object(e)
3617 },
3618 {
3619 let mut e = HashMap::new();
3620 e.insert("time".to_string(), Value::Number(2000.0));
3621 e.insert("count".to_string(), Value::Number(10.0));
3622 Value::Object(e)
3623 },
3624 ];
3625 ctx.insert("events", Value::Array(events));
3626
3627 let parser = ExpressionParser::new();
3628 let executor = ExpressionExecutor::new();
3629
3630 let expr = parser.parse("{{ events | rate(5s) }}").unwrap();
3631 let result = executor.execute(&expr, &ctx).unwrap();
3632 assert!(result.is_number());
3633 }
3634
3635 #[test]
3638 fn test_suggest_count_limit() {
3639 let mut ctx = DataContext::new();
3640 let suggestions = Value::Array(vec![
3641 {
3642 let mut obj = HashMap::new();
3643 obj.insert("text".to_string(), Value::String("git status".to_string()));
3644 Value::Object(obj)
3645 },
3646 {
3647 let mut obj = HashMap::new();
3648 obj.insert("text".to_string(), Value::String("git commit".to_string()));
3649 Value::Object(obj)
3650 },
3651 {
3652 let mut obj = HashMap::new();
3653 obj.insert("text".to_string(), Value::String("git push".to_string()));
3654 Value::Object(obj)
3655 },
3656 ]);
3657 ctx.insert("suggestions", suggestions);
3658
3659 let parser = ExpressionParser::new();
3660 let executor = ExpressionExecutor::new();
3661
3662 let expr = parser.parse("{{ suggestions | suggest(git, 2) }}").unwrap();
3663 let result = executor.execute(&expr, &ctx).unwrap();
3664 let arr = result.as_array().unwrap();
3665 assert_eq!(arr.len(), 2); }
3667
3668 #[test]
3669 fn test_suggest_with_source_field() {
3670 let mut ctx = DataContext::new();
3671 let mut model = HashMap::new();
3672 model.insert("source".to_string(), Value::String("model.apr".to_string()));
3673 ctx.insert("model", Value::Object(model));
3674
3675 let parser = ExpressionParser::new();
3676 let executor = ExpressionExecutor::new();
3677
3678 let expr = parser.parse("{{ model | suggest(git, 5) }}").unwrap();
3679 let result = executor.execute(&expr, &ctx).unwrap();
3680 let arr = result.as_array().unwrap();
3681 assert!(arr.is_empty());
3683 }
3684
3685 #[test]
3686 fn test_suggest_non_array_non_object() {
3687 let mut ctx = DataContext::new();
3688 ctx.insert("value", Value::Number(42.0));
3689
3690 let parser = ExpressionParser::new();
3691 let executor = ExpressionExecutor::new();
3692
3693 let expr = parser.parse("{{ value | suggest(x, 5) }}").unwrap();
3694 let result = executor.execute(&expr, &ctx).unwrap();
3695 let arr = result.as_array().unwrap();
3696 assert!(arr.is_empty());
3697 }
3698
3699 #[test]
3702 fn test_value_from_string_owned() {
3703 let s = String::from("owned");
3704 let v: Value = s.into();
3705 assert_eq!(v.as_str(), Some("owned"));
3706 }
3707
3708 #[test]
3709 fn test_value_is_type_methods() {
3710 assert!(Value::Null.is_null());
3711 assert!(!Value::Null.is_bool());
3712 assert!(!Value::Null.is_number());
3713 assert!(!Value::Null.is_string());
3714 assert!(!Value::Null.is_array());
3715 assert!(!Value::Null.is_object());
3716
3717 assert!(Value::Bool(true).is_bool());
3718 assert!(Value::Number(1.0).is_number());
3719 assert!(Value::String("s".to_string()).is_string());
3720 assert!(Value::Array(vec![]).is_array());
3721 assert!(Value::Object(HashMap::new()).is_object());
3722 }
3723
3724 #[test]
3725 fn test_execution_error_is_error_trait() {
3726 let err = ExecutionError::SourceNotFound("test".to_string());
3727 let _: &dyn std::error::Error = &err;
3728 }
3729
3730 #[test]
3731 fn test_data_context_get_nested_path() {
3732 let mut ctx = DataContext::new();
3733 let mut inner = HashMap::new();
3734 let mut deep = HashMap::new();
3735 deep.insert("value".to_string(), Value::number(42.0));
3736 inner.insert("nested".to_string(), Value::Object(deep));
3737 ctx.insert("data", Value::Object(inner));
3738
3739 assert_eq!(
3740 ctx.get("data.nested.value").unwrap().as_number(),
3741 Some(42.0)
3742 );
3743 assert!(ctx.get("data.nonexistent").is_none());
3744 assert!(ctx.get("nonexistent.path").is_none());
3745 }
3746
3747 #[test]
3748 fn test_filter_on_non_object_items() {
3749 let mut ctx = DataContext::new();
3750 ctx.insert(
3751 "items",
3752 Value::Array(vec![Value::Number(1.0), Value::String("test".to_string())]),
3753 );
3754
3755 let parser = ExpressionParser::new();
3756 let executor = ExpressionExecutor::new();
3757
3758 let expr = parser.parse("{{ items | filter(x=1) }}").unwrap();
3759 let result = executor.execute(&expr, &ctx).unwrap();
3760 assert!(result.as_array().unwrap().is_empty());
3762 }
3763
3764 #[test]
3765 fn test_filter_non_array() {
3766 let mut ctx = DataContext::new();
3767 ctx.insert("value", Value::Number(42.0));
3768
3769 let executor = ExpressionExecutor::new();
3770 let expr = Expression {
3771 source: "value".to_string(),
3772 transforms: vec![Transform::Filter {
3773 field: "x".to_string(),
3774 value: "1".to_string(),
3775 }],
3776 };
3777 let result = executor.execute(&expr, &ctx);
3778 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
3779 }
3780
3781 #[test]
3782 fn test_select_non_array() {
3783 let mut ctx = DataContext::new();
3784 ctx.insert("value", Value::Number(42.0));
3785
3786 let executor = ExpressionExecutor::new();
3787 let expr = Expression {
3788 source: "value".to_string(),
3789 transforms: vec![Transform::Select {
3790 fields: vec!["x".to_string()],
3791 }],
3792 };
3793 let result = executor.execute(&expr, &ctx);
3794 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
3795 }
3796
3797 #[test]
3798 fn test_sort_non_array() {
3799 let mut ctx = DataContext::new();
3800 ctx.insert("value", Value::Number(42.0));
3801
3802 let executor = ExpressionExecutor::new();
3803 let expr = Expression {
3804 source: "value".to_string(),
3805 transforms: vec![Transform::Sort {
3806 field: "x".to_string(),
3807 desc: false,
3808 }],
3809 };
3810 let result = executor.execute(&expr, &ctx);
3811 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
3812 }
3813
3814 #[test]
3815 fn test_limit_non_array() {
3816 let mut ctx = DataContext::new();
3817 ctx.insert("value", Value::Number(42.0));
3818
3819 let executor = ExpressionExecutor::new();
3820 let expr = Expression {
3821 source: "value".to_string(),
3822 transforms: vec![Transform::Limit { n: 5 }],
3823 };
3824 let result = executor.execute(&expr, &ctx);
3825 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
3826 }
3827
3828 #[test]
3829 fn test_sum_non_array() {
3830 let mut ctx = DataContext::new();
3831 ctx.insert("value", Value::Number(42.0));
3832
3833 let executor = ExpressionExecutor::new();
3834 let expr = Expression {
3835 source: "value".to_string(),
3836 transforms: vec![Transform::Sum {
3837 field: "x".to_string(),
3838 }],
3839 };
3840 let result = executor.execute(&expr, &ctx);
3841 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
3842 }
3843
3844 #[test]
3845 fn test_mean_non_array() {
3846 let mut ctx = DataContext::new();
3847 ctx.insert("value", Value::Number(42.0));
3848
3849 let executor = ExpressionExecutor::new();
3850 let expr = Expression {
3851 source: "value".to_string(),
3852 transforms: vec![Transform::Mean {
3853 field: "x".to_string(),
3854 }],
3855 };
3856 let result = executor.execute(&expr, &ctx);
3857 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
3858 }
3859
3860 #[test]
3861 fn test_sample_non_array() {
3862 let mut ctx = DataContext::new();
3863 ctx.insert("value", Value::Number(42.0));
3864
3865 let executor = ExpressionExecutor::new();
3866 let expr = Expression {
3867 source: "value".to_string(),
3868 transforms: vec![Transform::Sample { n: 5 }],
3869 };
3870 let result = executor.execute(&expr, &ctx);
3871 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
3872 }
3873
3874 #[test]
3875 fn test_rate_non_array() {
3876 let mut ctx = DataContext::new();
3877 ctx.insert("value", Value::Number(42.0));
3878
3879 let executor = ExpressionExecutor::new();
3880 let expr = Expression {
3881 source: "value".to_string(),
3882 transforms: vec![Transform::Rate {
3883 window: "1s".to_string(),
3884 }],
3885 };
3886 let result = executor.execute(&expr, &ctx);
3887 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
3888 }
3889
3890 #[test]
3891 fn test_join_non_array_left() {
3892 let mut ctx = DataContext::new();
3893 ctx.insert("left", Value::Number(42.0));
3894 ctx.insert("right", Value::Array(vec![]));
3895
3896 let executor = ExpressionExecutor::new();
3897 let expr = Expression {
3898 source: "left".to_string(),
3899 transforms: vec![Transform::Join {
3900 other: "right".to_string(),
3901 on: "id".to_string(),
3902 }],
3903 };
3904 let result = executor.execute(&expr, &ctx);
3905 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
3906 }
3907
3908 #[test]
3909 fn test_join_non_array_right() {
3910 let mut ctx = DataContext::new();
3911 ctx.insert("left", Value::Array(vec![]));
3912 ctx.insert("right", Value::Number(42.0));
3913
3914 let executor = ExpressionExecutor::new();
3915 let expr = Expression {
3916 source: "left".to_string(),
3917 transforms: vec![Transform::Join {
3918 other: "right".to_string(),
3919 on: "id".to_string(),
3920 }],
3921 };
3922 let result = executor.execute(&expr, &ctx);
3923 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
3924 }
3925
3926 #[test]
3927 fn test_group_by_non_array() {
3928 let mut ctx = DataContext::new();
3929 ctx.insert("value", Value::Number(42.0));
3930
3931 let executor = ExpressionExecutor::new();
3932 let expr = Expression {
3933 source: "value".to_string(),
3934 transforms: vec![Transform::GroupBy {
3935 field: "x".to_string(),
3936 }],
3937 };
3938 let result = executor.execute(&expr, &ctx);
3939 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
3940 }
3941
3942 #[test]
3943 fn test_group_by_non_object_items() {
3944 let mut ctx = DataContext::new();
3945 ctx.insert(
3946 "items",
3947 Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]),
3948 );
3949
3950 let executor = ExpressionExecutor::new();
3951 let expr = Expression {
3952 source: "items".to_string(),
3953 transforms: vec![Transform::GroupBy {
3954 field: "x".to_string(),
3955 }],
3956 };
3957 let result = executor.execute(&expr, &ctx).unwrap();
3958 let arr = result.as_array().unwrap();
3959 assert_eq!(arr.len(), 1);
3961 assert_eq!(arr[0].get("key").unwrap().as_str(), Some("_null"));
3962 }
3963
3964 #[test]
3965 fn test_group_by_missing_field() {
3966 let mut ctx = DataContext::new();
3967 let items = Value::Array(vec![{
3968 let mut obj = HashMap::new();
3969 obj.insert("x".to_string(), Value::Number(1.0));
3970 Value::Object(obj)
3971 }]);
3972 ctx.insert("items", items);
3973
3974 let executor = ExpressionExecutor::new();
3975 let expr = Expression {
3976 source: "items".to_string(),
3977 transforms: vec![Transform::GroupBy {
3978 field: "missing".to_string(),
3979 }],
3980 };
3981 let result = executor.execute(&expr, &ctx).unwrap();
3982 let arr = result.as_array().unwrap();
3983 assert_eq!(arr.len(), 1);
3985 assert_eq!(arr[0].get("key").unwrap().as_str(), Some("_null"));
3986 }
3987
3988 #[test]
3989 fn test_distinct_non_array() {
3990 let mut ctx = DataContext::new();
3991 ctx.insert("value", Value::Number(42.0));
3992
3993 let executor = ExpressionExecutor::new();
3994 let expr = Expression {
3995 source: "value".to_string(),
3996 transforms: vec![Transform::Distinct { field: None }],
3997 };
3998 let result = executor.execute(&expr, &ctx);
3999 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4000 }
4001
4002 #[test]
4003 fn test_distinct_with_field_non_object() {
4004 let mut ctx = DataContext::new();
4005 ctx.insert(
4006 "items",
4007 Value::Array(vec![
4008 Value::Number(1.0),
4009 Value::Number(1.0),
4010 Value::Number(2.0),
4011 ]),
4012 );
4013
4014 let executor = ExpressionExecutor::new();
4015 let expr = Expression {
4016 source: "items".to_string(),
4017 transforms: vec![Transform::Distinct {
4018 field: Some("x".to_string()),
4019 }],
4020 };
4021 let result = executor.execute(&expr, &ctx).unwrap();
4022 let arr = result.as_array().unwrap();
4023 assert_eq!(arr.len(), 2);
4025 }
4026
4027 #[test]
4028 fn test_where_non_array() {
4029 let mut ctx = DataContext::new();
4030 ctx.insert("value", Value::Number(42.0));
4031
4032 let executor = ExpressionExecutor::new();
4033 let expr = Expression {
4034 source: "value".to_string(),
4035 transforms: vec![Transform::Where {
4036 field: "x".to_string(),
4037 op: "eq".to_string(),
4038 value: "1".to_string(),
4039 }],
4040 };
4041 let result = executor.execute(&expr, &ctx);
4042 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4043 }
4044
4045 #[test]
4046 fn test_offset_non_array() {
4047 let mut ctx = DataContext::new();
4048 ctx.insert("value", Value::Number(42.0));
4049
4050 let executor = ExpressionExecutor::new();
4051 let expr = Expression {
4052 source: "value".to_string(),
4053 transforms: vec![Transform::Offset { n: 5 }],
4054 };
4055 let result = executor.execute(&expr, &ctx);
4056 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4057 }
4058
4059 #[test]
4060 fn test_min_non_array() {
4061 let mut ctx = DataContext::new();
4062 ctx.insert("value", Value::Number(42.0));
4063
4064 let executor = ExpressionExecutor::new();
4065 let expr = Expression {
4066 source: "value".to_string(),
4067 transforms: vec![Transform::Min {
4068 field: "x".to_string(),
4069 }],
4070 };
4071 let result = executor.execute(&expr, &ctx);
4072 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4073 }
4074
4075 #[test]
4076 fn test_max_non_array() {
4077 let mut ctx = DataContext::new();
4078 ctx.insert("value", Value::Number(42.0));
4079
4080 let executor = ExpressionExecutor::new();
4081 let expr = Expression {
4082 source: "value".to_string(),
4083 transforms: vec![Transform::Max {
4084 field: "x".to_string(),
4085 }],
4086 };
4087 let result = executor.execute(&expr, &ctx);
4088 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4089 }
4090
4091 #[test]
4092 fn test_max_empty() {
4093 let mut ctx = DataContext::new();
4094 ctx.insert("items", Value::Array(vec![]));
4095
4096 let executor = ExpressionExecutor::new();
4097 let expr = Expression {
4098 source: "items".to_string(),
4099 transforms: vec![Transform::Max {
4100 field: "x".to_string(),
4101 }],
4102 };
4103 let result = executor.execute(&expr, &ctx).unwrap();
4104 assert!(result.is_null());
4105 }
4106
4107 #[test]
4108 fn test_first_non_array() {
4109 let mut ctx = DataContext::new();
4110 ctx.insert("value", Value::Number(42.0));
4111
4112 let executor = ExpressionExecutor::new();
4113 let expr = Expression {
4114 source: "value".to_string(),
4115 transforms: vec![Transform::First { n: 5 }],
4116 };
4117 let result = executor.execute(&expr, &ctx);
4118 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4119 }
4120
4121 #[test]
4122 fn test_last_non_array() {
4123 let mut ctx = DataContext::new();
4124 ctx.insert("value", Value::Number(42.0));
4125
4126 let executor = ExpressionExecutor::new();
4127 let expr = Expression {
4128 source: "value".to_string(),
4129 transforms: vec![Transform::Last { n: 5 }],
4130 };
4131 let result = executor.execute(&expr, &ctx);
4132 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4133 }
4134
4135 #[test]
4136 fn test_flatten_non_array() {
4137 let mut ctx = DataContext::new();
4138 ctx.insert("value", Value::Number(42.0));
4139
4140 let executor = ExpressionExecutor::new();
4141 let expr = Expression {
4142 source: "value".to_string(),
4143 transforms: vec![Transform::Flatten],
4144 };
4145 let result = executor.execute(&expr, &ctx);
4146 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4147 }
4148
4149 #[test]
4150 fn test_reverse_non_array() {
4151 let mut ctx = DataContext::new();
4152 ctx.insert("value", Value::Number(42.0));
4153
4154 let executor = ExpressionExecutor::new();
4155 let expr = Expression {
4156 source: "value".to_string(),
4157 transforms: vec![Transform::Reverse],
4158 };
4159 let result = executor.execute(&expr, &ctx);
4160 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4161 }
4162
4163 #[test]
4164 fn test_map_non_array() {
4165 let mut ctx = DataContext::new();
4166 ctx.insert("value", Value::Number(42.0));
4167
4168 let executor = ExpressionExecutor::new();
4169 let expr = Expression {
4170 source: "value".to_string(),
4171 transforms: vec![Transform::Map {
4172 expr: "item.x".to_string(),
4173 }],
4174 };
4175 let result = executor.execute(&expr, &ctx);
4176 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4177 }
4178
4179 #[test]
4180 fn test_reduce_non_array() {
4181 let mut ctx = DataContext::new();
4182 ctx.insert("value", Value::Number(42.0));
4183
4184 let executor = ExpressionExecutor::new();
4185 let expr = Expression {
4186 source: "value".to_string(),
4187 transforms: vec![Transform::Reduce {
4188 initial: "0".to_string(),
4189 expr: "acc + item".to_string(),
4190 }],
4191 };
4192 let result = executor.execute(&expr, &ctx);
4193 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4194 }
4195
4196 #[test]
4197 fn test_reduce_invalid_initial() {
4198 let mut ctx = DataContext::new();
4199 ctx.insert("items", Value::Array(vec![Value::Number(1.0)]));
4200
4201 let executor = ExpressionExecutor::new();
4202 let expr = Expression {
4203 source: "items".to_string(),
4204 transforms: vec![Transform::Reduce {
4205 initial: "not_a_number".to_string(),
4206 expr: "acc + item".to_string(),
4207 }],
4208 };
4209 let result = executor.execute(&expr, &ctx).unwrap();
4210 assert_eq!(result.as_number(), Some(1.0));
4212 }
4213
4214 #[test]
4215 fn test_aggregate_non_array() {
4216 let mut ctx = DataContext::new();
4217 ctx.insert("value", Value::Number(42.0));
4218
4219 let executor = ExpressionExecutor::new();
4220 let expr = Expression {
4221 source: "value".to_string(),
4222 transforms: vec![Transform::Aggregate {
4223 field: "x".to_string(),
4224 op: AggregateOp::Sum,
4225 }],
4226 };
4227 let result = executor.execute(&expr, &ctx);
4228 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4229 }
4230
4231 #[test]
4232 fn test_aggregate_with_grouped_data() {
4233 let mut ctx = DataContext::new();
4234 let grouped = Value::Array(vec![{
4235 let mut obj = HashMap::new();
4236 obj.insert("key".to_string(), Value::String("A".to_string()));
4237 obj.insert(
4238 "values".to_string(),
4239 Value::Array(vec![
4240 {
4241 let mut v = HashMap::new();
4242 v.insert("amount".to_string(), Value::Number(10.0));
4243 Value::Object(v)
4244 },
4245 {
4246 let mut v = HashMap::new();
4247 v.insert("amount".to_string(), Value::Number(20.0));
4248 Value::Object(v)
4249 },
4250 ]),
4251 );
4252 Value::Object(obj)
4253 }]);
4254 ctx.insert("groups", grouped);
4255
4256 let executor = ExpressionExecutor::new();
4257 let expr = Expression {
4258 source: "groups".to_string(),
4259 transforms: vec![Transform::Aggregate {
4260 field: "amount".to_string(),
4261 op: AggregateOp::Sum,
4262 }],
4263 };
4264 let result = executor.execute(&expr, &ctx).unwrap();
4265 assert_eq!(result.as_number(), Some(30.0));
4266 }
4267
4268 #[test]
4269 fn test_aggregate_first_empty() {
4270 let mut ctx = DataContext::new();
4271 ctx.insert("items", Value::Array(vec![]));
4272
4273 let executor = ExpressionExecutor::new();
4274 let expr = Expression {
4275 source: "items".to_string(),
4276 transforms: vec![Transform::Aggregate {
4277 field: "x".to_string(),
4278 op: AggregateOp::First,
4279 }],
4280 };
4281 let result = executor.execute(&expr, &ctx).unwrap();
4282 assert_eq!(result.as_number(), Some(0.0));
4283 }
4284
4285 #[test]
4286 fn test_aggregate_last_empty() {
4287 let mut ctx = DataContext::new();
4288 ctx.insert("items", Value::Array(vec![]));
4289
4290 let executor = ExpressionExecutor::new();
4291 let expr = Expression {
4292 source: "items".to_string(),
4293 transforms: vec![Transform::Aggregate {
4294 field: "x".to_string(),
4295 op: AggregateOp::Last,
4296 }],
4297 };
4298 let result = executor.execute(&expr, &ctx).unwrap();
4299 assert_eq!(result.as_number(), Some(0.0));
4300 }
4301
4302 #[test]
4303 fn test_pivot_non_array() {
4304 let mut ctx = DataContext::new();
4305 ctx.insert("value", Value::Number(42.0));
4306
4307 let executor = ExpressionExecutor::new();
4308 let expr = Expression {
4309 source: "value".to_string(),
4310 transforms: vec![Transform::Pivot {
4311 row_field: "r".to_string(),
4312 col_field: "c".to_string(),
4313 value_field: "v".to_string(),
4314 }],
4315 };
4316 let result = executor.execute(&expr, &ctx);
4317 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4318 }
4319
4320 #[test]
4321 fn test_pivot_non_object_items() {
4322 let mut ctx = DataContext::new();
4323 ctx.insert("items", Value::Array(vec![Value::Number(1.0)]));
4324
4325 let executor = ExpressionExecutor::new();
4326 let expr = Expression {
4327 source: "items".to_string(),
4328 transforms: vec![Transform::Pivot {
4329 row_field: "r".to_string(),
4330 col_field: "c".to_string(),
4331 value_field: "v".to_string(),
4332 }],
4333 };
4334 let result = executor.execute(&expr, &ctx).unwrap();
4335 assert!(result.as_array().unwrap().is_empty());
4337 }
4338
4339 #[test]
4340 fn test_cumsum_non_array() {
4341 let mut ctx = DataContext::new();
4342 ctx.insert("value", Value::Number(42.0));
4343
4344 let executor = ExpressionExecutor::new();
4345 let expr = Expression {
4346 source: "value".to_string(),
4347 transforms: vec![Transform::CumulativeSum {
4348 field: "x".to_string(),
4349 }],
4350 };
4351 let result = executor.execute(&expr, &ctx);
4352 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4353 }
4354
4355 #[test]
4356 fn test_rank_non_array() {
4357 let mut ctx = DataContext::new();
4358 ctx.insert("value", Value::Number(42.0));
4359
4360 let executor = ExpressionExecutor::new();
4361 let expr = Expression {
4362 source: "value".to_string(),
4363 transforms: vec![Transform::Rank {
4364 field: "x".to_string(),
4365 method: RankMethod::Dense,
4366 }],
4367 };
4368 let result = executor.execute(&expr, &ctx);
4369 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4370 }
4371
4372 #[test]
4373 fn test_moving_average_non_array() {
4374 let mut ctx = DataContext::new();
4375 ctx.insert("value", Value::Number(42.0));
4376
4377 let executor = ExpressionExecutor::new();
4378 let expr = Expression {
4379 source: "value".to_string(),
4380 transforms: vec![Transform::MovingAverage {
4381 field: "x".to_string(),
4382 window: 3,
4383 }],
4384 };
4385 let result = executor.execute(&expr, &ctx);
4386 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4387 }
4388
4389 #[test]
4390 fn test_pct_change_non_array() {
4391 let mut ctx = DataContext::new();
4392 ctx.insert("value", Value::Number(42.0));
4393
4394 let executor = ExpressionExecutor::new();
4395 let expr = Expression {
4396 source: "value".to_string(),
4397 transforms: vec![Transform::PercentChange {
4398 field: "x".to_string(),
4399 }],
4400 };
4401 let result = executor.execute(&expr, &ctx);
4402 assert!(matches!(result, Err(ExecutionError::ExpectedArray)));
4403 }
4404
4405 #[test]
4406 fn test_compare_values_invalid_target() {
4407 let executor = ExpressionExecutor::new();
4408 assert!(!executor.compare_values(&Value::Number(50.0), ">", "not_number"));
4410 assert!(!executor.compare_values(&Value::Number(50.0), "<", "not_number"));
4411 assert!(!executor.compare_values(&Value::Number(50.0), ">=", "not_number"));
4412 assert!(!executor.compare_values(&Value::Number(50.0), "<=", "not_number"));
4413 }
4414
4415 #[test]
4416 fn test_compare_values_contains_non_string() {
4417 let executor = ExpressionExecutor::new();
4418 assert!(!executor.compare_values(&Value::Number(123.0), "contains", "12"));
4419 }
4420
4421 #[test]
4422 fn test_join_left_item_not_object() {
4423 let mut ctx = DataContext::new();
4424 ctx.insert("left", Value::Array(vec![Value::Number(1.0)]));
4425 ctx.insert(
4426 "right",
4427 Value::Array(vec![{
4428 let mut obj = HashMap::new();
4429 obj.insert("id".to_string(), Value::Number(1.0));
4430 Value::Object(obj)
4431 }]),
4432 );
4433
4434 let executor = ExpressionExecutor::new();
4435 let expr = Expression {
4436 source: "left".to_string(),
4437 transforms: vec![Transform::Join {
4438 other: "right".to_string(),
4439 on: "id".to_string(),
4440 }],
4441 };
4442 let result = executor.execute(&expr, &ctx).unwrap();
4443 let arr = result.as_array().unwrap();
4444 assert_eq!(arr.len(), 1);
4446 assert_eq!(arr[0].as_number(), Some(1.0));
4447 }
4448
4449 #[test]
4450 fn test_join_left_item_missing_key() {
4451 let mut ctx = DataContext::new();
4452 ctx.insert(
4453 "left",
4454 Value::Array(vec![{
4455 let mut obj = HashMap::new();
4456 obj.insert("other".to_string(), Value::Number(1.0));
4457 Value::Object(obj)
4458 }]),
4459 );
4460 ctx.insert(
4461 "right",
4462 Value::Array(vec![{
4463 let mut obj = HashMap::new();
4464 obj.insert("id".to_string(), Value::Number(1.0));
4465 Value::Object(obj)
4466 }]),
4467 );
4468
4469 let executor = ExpressionExecutor::new();
4470 let expr = Expression {
4471 source: "left".to_string(),
4472 transforms: vec![Transform::Join {
4473 other: "right".to_string(),
4474 on: "id".to_string(),
4475 }],
4476 };
4477 let result = executor.execute(&expr, &ctx).unwrap();
4478 let arr = result.as_array().unwrap();
4479 assert_eq!(arr.len(), 1);
4481 }
4482
4483 #[test]
4484 fn test_join_right_item_not_object() {
4485 let mut ctx = DataContext::new();
4486 ctx.insert(
4487 "left",
4488 Value::Array(vec![{
4489 let mut obj = HashMap::new();
4490 obj.insert("id".to_string(), Value::Number(1.0));
4491 Value::Object(obj)
4492 }]),
4493 );
4494 ctx.insert("right", Value::Array(vec![Value::Number(1.0)]));
4495
4496 let executor = ExpressionExecutor::new();
4497 let expr = Expression {
4498 source: "left".to_string(),
4499 transforms: vec![Transform::Join {
4500 other: "right".to_string(),
4501 on: "id".to_string(),
4502 }],
4503 };
4504 let result = executor.execute(&expr, &ctx).unwrap();
4505 let arr = result.as_array().unwrap();
4506 assert_eq!(arr.len(), 1);
4508 }
4509}