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