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