1use crate::error::DkitError;
2
3#[derive(Debug, Clone, PartialEq)]
5pub struct Query {
6 pub path: Path,
8 pub operations: Vec<Operation>,
10}
11
12#[derive(Debug, Clone, PartialEq)]
14pub struct Path {
15 pub segments: Vec<Segment>,
16}
17
18#[derive(Debug, Clone, PartialEq)]
20#[non_exhaustive]
21pub enum Segment {
22 Field(String),
24 Index(i64),
26 Iterate,
28 Slice {
30 start: Option<i64>,
31 end: Option<i64>,
32 step: Option<i64>,
33 },
34 Wildcard,
36 RecursiveDescent(String),
38}
39
40#[derive(Debug, Clone, PartialEq)]
42#[non_exhaustive]
43pub enum Operation {
44 Where(Condition),
46 Select(Vec<SelectExpr>),
48 Sort { field: String, descending: bool },
50 Limit(usize),
52 Count { field: Option<String> },
54 Sum { field: String },
56 Avg { field: String },
58 Min { field: String },
60 Max { field: String },
62 Distinct { field: String },
64 Median { field: String },
66 Percentile { field: String, p: f64 },
68 Stddev { field: String },
70 Variance { field: String },
72 Mode { field: String },
74 GroupConcat { field: String, separator: String },
76 GroupBy {
79 fields: Vec<String>,
80 having: Option<Condition>,
81 aggregates: Vec<GroupAggregate>,
82 },
83 Unique,
85 UniqueBy { field: String },
87 AddField { name: String, expr: Expr },
89 MapField { name: String, expr: Expr },
91 Explode { field: String },
93 Unpivot {
96 value_columns: Vec<String>,
98 key_name: String,
100 value_name: String,
102 },
103 Pivot {
106 index_fields: Vec<String>,
108 columns_field: String,
110 values_field: String,
112 },
113}
114
115#[derive(Debug, Clone, PartialEq)]
117pub struct GroupAggregate {
118 pub func: AggregateFunc,
119 pub field: Option<String>,
120 pub alias: String,
121}
122
123#[derive(Debug, Clone, PartialEq)]
125#[non_exhaustive]
126pub enum AggregateFunc {
127 Count,
128 Sum,
129 Avg,
130 Min,
131 Max,
132 Median,
133 Percentile(f64),
134 Stddev,
135 Variance,
136 Mode,
137 GroupConcat(String),
138}
139
140#[derive(Debug, Clone, PartialEq)]
142#[non_exhaustive]
143pub enum Condition {
144 Comparison(Comparison),
146 And(Box<Condition>, Box<Condition>),
148 Or(Box<Condition>, Box<Condition>),
150}
151
152#[derive(Debug, Clone, PartialEq)]
154pub struct Comparison {
155 pub field: String,
156 pub op: CompareOp,
157 pub value: LiteralValue,
158}
159
160#[derive(Debug, Clone, PartialEq)]
162#[non_exhaustive]
163pub enum CompareOp {
164 Eq, Ne, Gt, Lt, Ge, Le, Contains, StartsWith, EndsWith, In, NotIn, Matches, NotMatches, }
178
179#[derive(Debug, Clone, PartialEq)]
181#[non_exhaustive]
182pub enum LiteralValue {
183 String(String),
184 Integer(i64),
185 Float(f64),
186 Bool(bool),
187 Null,
188 List(Vec<LiteralValue>),
189}
190
191#[derive(Debug, Clone, PartialEq)]
193pub enum ArithmeticOp {
194 Add, Sub, Mul, Div, }
199
200#[derive(Debug, Clone, PartialEq)]
202#[non_exhaustive]
203pub enum Expr {
204 Field(String),
206 Literal(LiteralValue),
208 FuncCall { name: String, args: Vec<Expr> },
210 BinaryOp {
212 op: ArithmeticOp,
213 left: Box<Expr>,
214 right: Box<Expr>,
215 },
216 If {
218 condition: Condition,
219 then_expr: Box<Expr>,
220 else_expr: Box<Expr>,
221 },
222 Case {
224 branches: Vec<(Condition, Expr)>,
225 default: Option<Box<Expr>>,
226 },
227 Window { func: WindowFunc, over: WindowSpec },
229}
230
231#[derive(Debug, Clone, PartialEq)]
233#[non_exhaustive]
234pub enum WindowFunc {
235 RowNumber,
237 Rank,
239 DenseRank,
241 Lag { expr: Box<Expr>, offset: usize },
243 Lead { expr: Box<Expr>, offset: usize },
245 FirstValue { expr: Box<Expr> },
247 LastValue { expr: Box<Expr> },
249 Aggregate {
251 func: AggregateFunc,
252 expr: Box<Expr>,
253 },
254}
255
256#[derive(Debug, Clone, PartialEq)]
258pub struct WindowSpec {
259 pub partition_by: Vec<String>,
261 pub order_by: Vec<WindowOrderBy>,
263}
264
265#[derive(Debug, Clone, PartialEq)]
267pub struct WindowOrderBy {
268 pub field: String,
269 pub descending: bool,
270}
271
272#[derive(Debug, Clone, PartialEq)]
274pub struct SelectExpr {
275 pub expr: Expr,
276 pub alias: Option<String>,
278}
279
280pub(crate) struct Parser {
284 input: Vec<char>,
285 pos: usize,
286}
287
288impl Parser {
289 pub(crate) fn new(input: &str) -> Self {
290 Self {
291 input: input.chars().collect(),
292 pos: 0,
293 }
294 }
295
296 pub(crate) fn parse(&mut self) -> Result<Query, DkitError> {
298 self.skip_whitespace();
299 let path = self.parse_path()?;
300 self.skip_whitespace();
301
302 let mut operations = Vec::new();
304 while self.peek() == Some('|') {
305 self.advance(); self.skip_whitespace();
307 operations.push(self.parse_operation()?);
308 self.skip_whitespace();
309 }
310
311 if self.pos != self.input.len() {
312 return Err(DkitError::QueryError(format!(
313 "unexpected character '{}' at position {}",
314 self.input[self.pos], self.pos
315 )));
316 }
317
318 Ok(Query { path, operations })
319 }
320
321 fn parse_path(&mut self) -> Result<Path, DkitError> {
323 if !self.consume_char('.') {
324 return Err(DkitError::QueryError(
325 "query must start with '.'".to_string(),
326 ));
327 }
328
329 let mut segments = Vec::new();
330
331 if self.is_at_end() {
333 return Ok(Path { segments });
334 }
335
336 if self.peek() == Some('.') {
338 self.advance(); let field = self.parse_field()?;
340 if let Segment::Field(name) = field {
341 segments.push(Segment::RecursiveDescent(name));
342 }
343 return Ok(Path { segments });
344 }
345
346 if self.peek() == Some('[') {
349 segments.push(self.parse_bracket()?);
350 } else if self.peek_is_identifier_start() {
351 segments.push(self.parse_field()?);
352 }
353
354 while !self.is_at_end() {
356 self.skip_whitespace();
357 if self.peek() == Some('.') {
358 if self.pos + 1 < self.input.len() && self.input[self.pos + 1] == '.' {
360 self.advance(); self.advance(); let field = self.parse_field()?;
363 if let Segment::Field(name) = field {
364 segments.push(Segment::RecursiveDescent(name));
365 }
366 } else {
367 self.advance(); if self.peek() == Some('[') {
369 segments.push(self.parse_bracket()?);
370 } else {
371 segments.push(self.parse_field()?);
372 }
373 }
374 } else if self.peek() == Some('[') {
375 segments.push(self.parse_bracket()?);
376 } else {
377 break;
378 }
379 }
380
381 Ok(Path { segments })
382 }
383
384 fn parse_field(&mut self) -> Result<Segment, DkitError> {
386 let start = self.pos;
387 while !self.is_at_end() {
388 let c = self.input[self.pos];
389 if c.is_alphanumeric() || c == '_' || c == '-' {
390 self.pos += 1;
391 } else {
392 break;
393 }
394 }
395
396 if self.pos == start {
397 return Err(DkitError::QueryError(format!(
398 "expected field name at position {}",
399 self.pos
400 )));
401 }
402
403 let name: String = self.input[start..self.pos].iter().collect();
404 Ok(Segment::Field(name))
405 }
406
407 fn parse_bracket(&mut self) -> Result<Segment, DkitError> {
409 if !self.consume_char('[') {
410 return Err(DkitError::QueryError(format!(
411 "expected '[' at position {}",
412 self.pos
413 )));
414 }
415
416 self.skip_whitespace();
417
418 if self.peek() == Some(']') {
420 self.advance();
421 return Ok(Segment::Iterate);
422 }
423
424 if self.peek() == Some('*') {
426 self.advance();
427 self.skip_whitespace();
428 if !self.consume_char(']') {
429 return Err(DkitError::QueryError(format!(
430 "expected ']' after '*' at position {}",
431 self.pos
432 )));
433 }
434 return Ok(Segment::Wildcard);
435 }
436
437 if self.peek() == Some(':') {
439 return self.parse_slice(None);
440 }
441
442 let negative = self.consume_char('-');
444 let start = self.pos;
445 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
446 self.pos += 1;
447 }
448 if self.pos == start {
449 return Err(DkitError::QueryError(format!(
450 "expected integer index at position {}",
451 self.pos
452 )));
453 }
454
455 let num_str: String = self.input[start..self.pos].iter().collect();
456 let num: i64 = num_str.parse().map_err(|_| {
457 DkitError::QueryError(format!("invalid index '{}' at position {}", num_str, start))
458 })?;
459 let num = if negative { -num } else { num };
460
461 self.skip_whitespace();
462
463 if self.peek() == Some(':') {
465 return self.parse_slice(Some(num));
466 }
467
468 if !self.consume_char(']') {
470 return Err(DkitError::QueryError(format!(
471 "expected ']' or ':' at position {}",
472 self.pos
473 )));
474 }
475
476 Ok(Segment::Index(num))
477 }
478
479 fn parse_slice(&mut self, start: Option<i64>) -> Result<Segment, DkitError> {
481 if !self.consume_char(':') {
483 return Err(DkitError::QueryError(format!(
484 "expected ':' at position {}",
485 self.pos
486 )));
487 }
488
489 self.skip_whitespace();
490
491 let end = if self.peek() == Some(']') || self.peek() == Some(':') {
493 None
494 } else {
495 Some(self.parse_signed_integer()?)
496 };
497
498 self.skip_whitespace();
499
500 let step = if self.peek() == Some(':') {
502 self.advance();
503 self.skip_whitespace();
504 if self.peek() == Some(']') {
505 None
506 } else {
507 Some(self.parse_signed_integer()?)
508 }
509 } else {
510 None
511 };
512
513 self.skip_whitespace();
514 if !self.consume_char(']') {
515 return Err(DkitError::QueryError(format!(
516 "expected ']' at position {}",
517 self.pos
518 )));
519 }
520
521 Ok(Segment::Slice { start, end, step })
522 }
523
524 fn parse_signed_integer(&mut self) -> Result<i64, DkitError> {
526 let negative = self.consume_char('-');
527 let start = self.pos;
528 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
529 self.pos += 1;
530 }
531 if self.pos == start {
532 return Err(DkitError::QueryError(format!(
533 "expected integer at position {}",
534 self.pos
535 )));
536 }
537 let num_str: String = self.input[start..self.pos].iter().collect();
538 let num: i64 = num_str.parse().map_err(|_| {
539 DkitError::QueryError(format!(
540 "invalid integer '{}' at position {}",
541 num_str, start
542 ))
543 })?;
544 Ok(if negative { -num } else { num })
545 }
546
547 fn parse_operation(&mut self) -> Result<Operation, DkitError> {
551 let keyword = self.parse_keyword()?;
552 match keyword.as_str() {
553 "where" => {
554 self.skip_whitespace();
555 let condition = self.parse_condition()?;
556 Ok(Operation::Where(condition))
557 }
558 "select" => {
559 self.skip_whitespace();
560 let exprs = self.parse_select_expr_list()?;
561 Ok(Operation::Select(exprs))
562 }
563 "sort" => {
564 self.skip_whitespace();
565 let field = self.parse_identifier()?;
566 self.skip_whitespace();
567 let descending = self.try_consume_keyword("desc");
568 Ok(Operation::Sort { field, descending })
569 }
570 "limit" => {
571 self.skip_whitespace();
572 let n = self.parse_positive_integer()?;
573 Ok(Operation::Limit(n))
574 }
575 "count" => {
576 self.skip_whitespace();
577 let field = self.try_parse_identifier();
578 Ok(Operation::Count { field })
579 }
580 "sum" => {
581 self.skip_whitespace();
582 let field = self.parse_identifier()?;
583 Ok(Operation::Sum { field })
584 }
585 "avg" => {
586 self.skip_whitespace();
587 let field = self.parse_identifier()?;
588 Ok(Operation::Avg { field })
589 }
590 "min" => {
591 self.skip_whitespace();
592 let field = self.parse_identifier()?;
593 Ok(Operation::Min { field })
594 }
595 "max" => {
596 self.skip_whitespace();
597 let field = self.parse_identifier()?;
598 Ok(Operation::Max { field })
599 }
600 "distinct" => {
601 self.skip_whitespace();
602 let field = self.parse_identifier()?;
603 Ok(Operation::Distinct { field })
604 }
605 "median" => {
606 self.skip_whitespace();
607 let field = self.parse_identifier()?;
608 Ok(Operation::Median { field })
609 }
610 "percentile" => {
611 self.skip_whitespace();
612 let field = self.parse_identifier()?;
613 self.skip_whitespace();
614 let p = self.parse_float_value()?;
615 if !(0.0..=1.0).contains(&p) {
616 return Err(DkitError::QueryError(
617 "percentile: p must be between 0.0 and 1.0".to_string(),
618 ));
619 }
620 Ok(Operation::Percentile { field, p })
621 }
622 "stddev" => {
623 self.skip_whitespace();
624 let field = self.parse_identifier()?;
625 Ok(Operation::Stddev { field })
626 }
627 "variance" => {
628 self.skip_whitespace();
629 let field = self.parse_identifier()?;
630 Ok(Operation::Variance { field })
631 }
632 "mode" => {
633 self.skip_whitespace();
634 let field = self.parse_identifier()?;
635 Ok(Operation::Mode { field })
636 }
637 "group_concat" => {
638 self.skip_whitespace();
639 let field = self.parse_identifier()?;
640 self.skip_whitespace();
641 let separator = if self.peek() == Some('"') {
642 match self.parse_string_literal()? {
643 LiteralValue::String(s) => s,
644 _ => ", ".to_string(),
645 }
646 } else {
647 ", ".to_string()
648 };
649 Ok(Operation::GroupConcat { field, separator })
650 }
651 "group_by" => {
652 self.skip_whitespace();
653 let fields = self.parse_identifier_list()?;
654 self.skip_whitespace();
655
656 let aggregates = self.parse_group_aggregates()?;
658
659 let having = if self.try_consume_keyword("having") {
661 self.skip_whitespace();
662 Some(self.parse_condition()?)
663 } else {
664 None
665 };
666
667 Ok(Operation::GroupBy {
668 fields,
669 having,
670 aggregates,
671 })
672 }
673 _ => Err(DkitError::QueryError(format!(
674 "unknown operation '{}' at position {}",
675 keyword,
676 self.pos - keyword.chars().count()
677 ))),
678 }
679 }
680
681 fn parse_group_aggregates(&mut self) -> Result<Vec<GroupAggregate>, DkitError> {
683 let mut aggregates = Vec::new();
684
685 loop {
686 let saved_pos = self.pos;
687 if let Some(agg) = self.try_parse_single_aggregate()? {
688 aggregates.push(agg);
689 self.skip_whitespace();
690 if !self.consume_char(',') {
691 break;
693 }
694 self.skip_whitespace();
695 } else {
696 self.pos = saved_pos;
697 break;
698 }
699 }
700
701 Ok(aggregates)
702 }
703
704 fn try_parse_single_aggregate(&mut self) -> Result<Option<GroupAggregate>, DkitError> {
706 let saved_pos = self.pos;
707
708 let func_name = match self.parse_keyword() {
710 Ok(name) => name,
711 Err(_) => {
712 self.pos = saved_pos;
713 return Ok(None);
714 }
715 };
716
717 let is_known = matches!(
719 func_name.as_str(),
720 "count"
721 | "sum"
722 | "avg"
723 | "min"
724 | "max"
725 | "median"
726 | "percentile"
727 | "stddev"
728 | "variance"
729 | "mode"
730 | "group_concat"
731 );
732 if !is_known {
733 self.pos = saved_pos;
734 return Ok(None);
735 }
736
737 self.skip_whitespace();
738
739 if !self.consume_char('(') {
741 self.pos = saved_pos;
742 return Ok(None);
743 }
744
745 self.skip_whitespace();
746
747 let field = if self.peek() == Some(')') {
749 None
750 } else {
751 Some(self.parse_identifier()?)
752 };
753
754 self.skip_whitespace();
755
756 let func = match func_name.as_str() {
758 "count" => AggregateFunc::Count,
759 "sum" => AggregateFunc::Sum,
760 "avg" => AggregateFunc::Avg,
761 "min" => AggregateFunc::Min,
762 "max" => AggregateFunc::Max,
763 "median" => AggregateFunc::Median,
764 "percentile" => {
765 if !self.consume_char(',') {
766 return Err(DkitError::QueryError(format!(
767 "percentile() requires a second argument (p value) at position {}",
768 self.pos
769 )));
770 }
771 self.skip_whitespace();
772 let p = self.parse_float_value()?;
773 if !(0.0..=1.0).contains(&p) {
774 return Err(DkitError::QueryError(
775 "percentile: p must be between 0.0 and 1.0".to_string(),
776 ));
777 }
778 AggregateFunc::Percentile(p)
779 }
780 "stddev" => AggregateFunc::Stddev,
781 "variance" => AggregateFunc::Variance,
782 "mode" => AggregateFunc::Mode,
783 "group_concat" => {
784 let separator = if self.consume_char(',') {
785 self.skip_whitespace();
786 match self.parse_string_literal()? {
787 LiteralValue::String(s) => s,
788 _ => ", ".to_string(),
789 }
790 } else {
791 ", ".to_string()
792 };
793 AggregateFunc::GroupConcat(separator)
794 }
795 _ => unreachable!(),
796 };
797
798 self.skip_whitespace();
799
800 if !self.consume_char(')') {
801 return Err(DkitError::QueryError(format!(
802 "expected ')' at position {}",
803 self.pos
804 )));
805 }
806
807 let alias = match &field {
809 Some(f) => format!("{}_{}", func_name, f),
810 None => func_name.clone(),
811 };
812
813 Ok(Some(GroupAggregate { func, field, alias }))
814 }
815
816 fn parse_select_expr_list(&mut self) -> Result<Vec<SelectExpr>, DkitError> {
818 let mut exprs = vec![self.parse_select_expr()?];
819 loop {
820 self.skip_whitespace();
821 if self.consume_char(',') {
822 self.skip_whitespace();
823 exprs.push(self.parse_select_expr()?);
824 } else {
825 break;
826 }
827 }
828 Ok(exprs)
829 }
830
831 fn parse_select_expr(&mut self) -> Result<SelectExpr, DkitError> {
833 let expr = self.parse_expr()?;
834 self.skip_whitespace();
835 let alias = {
837 let saved = self.pos;
838 if let Ok(keyword) = self.parse_keyword() {
839 if keyword == "as" {
840 self.skip_whitespace();
841 Some(self.parse_identifier()?)
842 } else {
843 self.pos = saved;
844 None
845 }
846 } else {
847 self.pos = saved;
848 None
849 }
850 };
851 Ok(SelectExpr { expr, alias })
852 }
853
854 fn parse_expr(&mut self) -> Result<Expr, DkitError> {
856 self.parse_additive_expr()
857 }
858
859 fn parse_additive_expr(&mut self) -> Result<Expr, DkitError> {
861 let mut left = self.parse_multiplicative_expr()?;
862
863 loop {
864 self.skip_whitespace();
865 match self.peek() {
866 Some('+') => {
867 self.advance();
868 self.skip_whitespace();
869 let right = self.parse_multiplicative_expr()?;
870 left = Expr::BinaryOp {
871 op: ArithmeticOp::Add,
872 left: Box::new(left),
873 right: Box::new(right),
874 };
875 }
876 Some('-') => {
877 self.advance();
880 self.skip_whitespace();
881 let right = self.parse_multiplicative_expr()?;
882 left = Expr::BinaryOp {
883 op: ArithmeticOp::Sub,
884 left: Box::new(left),
885 right: Box::new(right),
886 };
887 }
888 _ => break,
889 }
890 }
891
892 Ok(left)
893 }
894
895 fn parse_multiplicative_expr(&mut self) -> Result<Expr, DkitError> {
897 let mut left = self.parse_atom_expr()?;
898
899 loop {
900 self.skip_whitespace();
901 match self.peek() {
902 Some('*') => {
903 self.advance();
904 self.skip_whitespace();
905 let right = self.parse_atom_expr()?;
906 left = Expr::BinaryOp {
907 op: ArithmeticOp::Mul,
908 left: Box::new(left),
909 right: Box::new(right),
910 };
911 }
912 Some('/') => {
913 self.advance();
914 self.skip_whitespace();
915 let right = self.parse_atom_expr()?;
916 left = Expr::BinaryOp {
917 op: ArithmeticOp::Div,
918 left: Box::new(left),
919 right: Box::new(right),
920 };
921 }
922 _ => break,
923 }
924 }
925
926 Ok(left)
927 }
928
929 fn parse_atom_expr(&mut self) -> Result<Expr, DkitError> {
931 match self.peek() {
932 Some('(') => {
933 self.advance(); self.skip_whitespace();
935 let expr = self.parse_expr()?;
936 self.skip_whitespace();
937 if !self.consume_char(')') {
938 return Err(DkitError::QueryError(format!(
939 "expected ')' at position {}",
940 self.pos
941 )));
942 }
943 Ok(expr)
944 }
945 Some('"') => {
946 let lit = self.parse_string_literal()?;
947 Ok(Expr::Literal(lit))
948 }
949 Some(c) if c.is_ascii_digit() => {
950 let lit = self.parse_number_literal()?;
951 Ok(Expr::Literal(lit))
952 }
953 Some(c) if c.is_alphabetic() || c == '_' => {
954 let name = self.parse_identifier()?;
955 match name.as_str() {
957 "true" => return Ok(Expr::Literal(LiteralValue::Bool(true))),
958 "false" => return Ok(Expr::Literal(LiteralValue::Bool(false))),
959 "null" => return Ok(Expr::Literal(LiteralValue::Null)),
960 "if" => return self.parse_if_expr(),
962 "case" => return self.parse_case_expr(),
964 _ => {}
965 }
966 if self.peek() == Some('(') {
968 self.advance(); self.skip_whitespace();
970 let mut args = Vec::new();
971 if self.peek() != Some(')') {
972 args.push(self.parse_expr()?);
973 loop {
974 self.skip_whitespace();
975 if self.consume_char(',') {
976 self.skip_whitespace();
977 args.push(self.parse_expr()?);
978 } else {
979 break;
980 }
981 }
982 }
983 self.skip_whitespace();
984 if !self.consume_char(')') {
985 return Err(DkitError::QueryError(format!(
986 "expected ')' at position {}",
987 self.pos
988 )));
989 }
990 let saved_over = self.pos;
992 self.skip_whitespace();
993 if self.try_consume_keyword("over") {
994 let window_func = self.parse_window_func(&name, args)?;
995 let over = self.parse_window_spec()?;
996 Ok(Expr::Window {
997 func: window_func,
998 over,
999 })
1000 } else {
1001 self.pos = saved_over;
1002 Ok(Expr::FuncCall { name, args })
1003 }
1004 } else {
1005 Ok(Expr::Field(name))
1006 }
1007 }
1008 Some(c) => Err(DkitError::QueryError(format!(
1009 "expected expression at position {}, found '{}'",
1010 self.pos, c
1011 ))),
1012 None => Err(DkitError::QueryError(format!(
1013 "expected expression at position {}",
1014 self.pos
1015 ))),
1016 }
1017 }
1018
1019 fn parse_window_func(&self, name: &str, args: Vec<Expr>) -> Result<WindowFunc, DkitError> {
1021 match name {
1022 "row_number" => {
1023 if !args.is_empty() {
1024 return Err(DkitError::QueryError(
1025 "row_number() takes no arguments".to_string(),
1026 ));
1027 }
1028 Ok(WindowFunc::RowNumber)
1029 }
1030 "rank" => {
1031 if !args.is_empty() {
1032 return Err(DkitError::QueryError(
1033 "rank() takes no arguments".to_string(),
1034 ));
1035 }
1036 Ok(WindowFunc::Rank)
1037 }
1038 "dense_rank" => {
1039 if !args.is_empty() {
1040 return Err(DkitError::QueryError(
1041 "dense_rank() takes no arguments".to_string(),
1042 ));
1043 }
1044 Ok(WindowFunc::DenseRank)
1045 }
1046 "lag" => {
1047 if args.is_empty() || args.len() > 2 {
1048 return Err(DkitError::QueryError(
1049 "lag() requires 1 or 2 arguments: lag(expr[, offset])".to_string(),
1050 ));
1051 }
1052 let expr = Box::new(args[0].clone());
1053 let offset = if args.len() == 2 {
1054 match &args[1] {
1055 Expr::Literal(LiteralValue::Integer(n)) if *n >= 0 => *n as usize,
1056 _ => {
1057 return Err(DkitError::QueryError(
1058 "lag() offset must be a non-negative integer literal".to_string(),
1059 ))
1060 }
1061 }
1062 } else {
1063 1
1064 };
1065 Ok(WindowFunc::Lag { expr, offset })
1066 }
1067 "lead" => {
1068 if args.is_empty() || args.len() > 2 {
1069 return Err(DkitError::QueryError(
1070 "lead() requires 1 or 2 arguments: lead(expr[, offset])".to_string(),
1071 ));
1072 }
1073 let expr = Box::new(args[0].clone());
1074 let offset = if args.len() == 2 {
1075 match &args[1] {
1076 Expr::Literal(LiteralValue::Integer(n)) if *n >= 0 => *n as usize,
1077 _ => {
1078 return Err(DkitError::QueryError(
1079 "lead() offset must be a non-negative integer literal".to_string(),
1080 ))
1081 }
1082 }
1083 } else {
1084 1
1085 };
1086 Ok(WindowFunc::Lead { expr, offset })
1087 }
1088 "first_value" => {
1089 if args.len() != 1 {
1090 return Err(DkitError::QueryError(
1091 "first_value() requires exactly 1 argument".to_string(),
1092 ));
1093 }
1094 Ok(WindowFunc::FirstValue {
1095 expr: Box::new(args[0].clone()),
1096 })
1097 }
1098 "last_value" => {
1099 if args.len() != 1 {
1100 return Err(DkitError::QueryError(
1101 "last_value() requires exactly 1 argument".to_string(),
1102 ));
1103 }
1104 Ok(WindowFunc::LastValue {
1105 expr: Box::new(args[0].clone()),
1106 })
1107 }
1108 "sum" | "avg" | "count" | "min" | "max" => {
1110 let agg_func = match name {
1111 "sum" => AggregateFunc::Sum,
1112 "avg" => AggregateFunc::Avg,
1113 "count" => AggregateFunc::Count,
1114 "min" => AggregateFunc::Min,
1115 "max" => AggregateFunc::Max,
1116 _ => unreachable!(),
1117 };
1118 let expr = if args.len() == 1 {
1119 Box::new(args[0].clone())
1120 } else if args.is_empty() && name == "count" {
1121 Box::new(Expr::Literal(LiteralValue::Null))
1122 } else {
1123 return Err(DkitError::QueryError(format!(
1124 "{}() over requires exactly 1 argument",
1125 name
1126 )));
1127 };
1128 Ok(WindowFunc::Aggregate {
1129 func: agg_func,
1130 expr,
1131 })
1132 }
1133 _ => Err(DkitError::QueryError(format!(
1134 "unknown window function: {}",
1135 name
1136 ))),
1137 }
1138 }
1139
1140 fn parse_window_spec(&mut self) -> Result<WindowSpec, DkitError> {
1142 self.skip_whitespace();
1143 if !self.consume_char('(') {
1144 return Err(DkitError::QueryError(format!(
1145 "expected '(' after 'over' at position {}",
1146 self.pos
1147 )));
1148 }
1149 self.skip_whitespace();
1150
1151 let mut partition_by = Vec::new();
1152 let mut order_by = Vec::new();
1153
1154 if self.try_consume_keyword("partition") {
1156 self.skip_whitespace();
1157 if !self.try_consume_keyword("by") {
1158 return Err(DkitError::QueryError(format!(
1159 "expected 'by' after 'partition' at position {}",
1160 self.pos
1161 )));
1162 }
1163 self.skip_whitespace();
1164 partition_by = self.parse_identifier_list()?;
1165 self.skip_whitespace();
1166 }
1167
1168 if self.try_consume_keyword("order") {
1170 self.skip_whitespace();
1171 if !self.try_consume_keyword("by") {
1172 return Err(DkitError::QueryError(format!(
1173 "expected 'by' after 'order' at position {}",
1174 self.pos
1175 )));
1176 }
1177 self.skip_whitespace();
1178 order_by = self.parse_window_order_by_list()?;
1179 self.skip_whitespace();
1180 }
1181
1182 if !self.consume_char(')') {
1183 return Err(DkitError::QueryError(format!(
1184 "expected ')' to close window spec at position {}",
1185 self.pos
1186 )));
1187 }
1188
1189 Ok(WindowSpec {
1190 partition_by,
1191 order_by,
1192 })
1193 }
1194
1195 fn parse_window_order_by_list(&mut self) -> Result<Vec<WindowOrderBy>, DkitError> {
1197 let mut items = Vec::new();
1198 let field = self.parse_identifier()?;
1199 self.skip_whitespace();
1200 let descending = self.try_consume_keyword("desc");
1201 if !descending {
1202 self.try_consume_keyword("asc");
1203 }
1204 items.push(WindowOrderBy { field, descending });
1205
1206 loop {
1207 self.skip_whitespace();
1208 if self.consume_char(',') {
1209 self.skip_whitespace();
1210 let field = self.parse_identifier()?;
1211 self.skip_whitespace();
1212 let descending = self.try_consume_keyword("desc");
1213 if !descending {
1214 self.try_consume_keyword("asc");
1215 }
1216 items.push(WindowOrderBy { field, descending });
1217 } else {
1218 break;
1219 }
1220 }
1221
1222 Ok(items)
1223 }
1224
1225 fn parse_if_expr(&mut self) -> Result<Expr, DkitError> {
1227 self.skip_whitespace();
1228 if !self.consume_char('(') {
1229 return Err(DkitError::QueryError(format!(
1230 "expected '(' after 'if' at position {}",
1231 self.pos
1232 )));
1233 }
1234 self.skip_whitespace();
1235 let condition = self.parse_condition()?;
1236 self.skip_whitespace();
1237 if !self.consume_char(',') {
1238 return Err(DkitError::QueryError(format!(
1239 "expected ',' after condition in if() at position {}",
1240 self.pos
1241 )));
1242 }
1243 self.skip_whitespace();
1244 let then_expr = self.parse_expr()?;
1245 self.skip_whitespace();
1246 if !self.consume_char(',') {
1247 return Err(DkitError::QueryError(format!(
1248 "expected ',' after then-expression in if() at position {}",
1249 self.pos
1250 )));
1251 }
1252 self.skip_whitespace();
1253 let else_expr = self.parse_expr()?;
1254 self.skip_whitespace();
1255 if !self.consume_char(')') {
1256 return Err(DkitError::QueryError(format!(
1257 "expected ')' at position {}",
1258 self.pos
1259 )));
1260 }
1261 Ok(Expr::If {
1262 condition,
1263 then_expr: Box::new(then_expr),
1264 else_expr: Box::new(else_expr),
1265 })
1266 }
1267
1268 fn parse_case_expr(&mut self) -> Result<Expr, DkitError> {
1270 let mut branches = Vec::new();
1271 let mut default = None;
1272
1273 loop {
1274 self.skip_whitespace();
1275 let saved_pos = self.pos;
1276 let keyword = self.parse_keyword().unwrap_or_default();
1277 match keyword.as_str() {
1278 "when" => {
1279 self.skip_whitespace();
1280 let condition = self.parse_condition()?;
1281 self.skip_whitespace();
1282 let then_kw = self.parse_keyword()?;
1283 if then_kw != "then" {
1284 return Err(DkitError::QueryError(format!(
1285 "expected 'then' after condition in case expression, found '{}'",
1286 then_kw
1287 )));
1288 }
1289 self.skip_whitespace();
1290 let expr = self.parse_expr()?;
1291 branches.push((condition, expr));
1292 }
1293 "else" => {
1294 self.skip_whitespace();
1295 default = Some(Box::new(self.parse_expr()?));
1296 }
1297 "end" => {
1298 break;
1299 }
1300 _ => {
1301 self.pos = saved_pos;
1302 return Err(DkitError::QueryError(format!(
1303 "expected 'when', 'else', or 'end' in case expression at position {}",
1304 self.pos
1305 )));
1306 }
1307 }
1308 }
1309
1310 if branches.is_empty() {
1311 return Err(DkitError::QueryError(
1312 "case expression requires at least one 'when' branch".to_string(),
1313 ));
1314 }
1315
1316 Ok(Expr::Case { branches, default })
1317 }
1318
1319 fn parse_identifier_list(&mut self) -> Result<Vec<String>, DkitError> {
1321 let mut fields = vec![self.parse_identifier()?];
1322 loop {
1323 self.skip_whitespace();
1324 if self.consume_char(',') {
1325 self.skip_whitespace();
1326 fields.push(self.parse_identifier()?);
1327 } else {
1328 break;
1329 }
1330 }
1331 Ok(fields)
1332 }
1333
1334 fn parse_keyword(&mut self) -> Result<String, DkitError> {
1336 let start = self.pos;
1337 while !self.is_at_end() {
1338 let c = self.input[self.pos];
1339 if c.is_alphabetic() || c == '_' {
1340 self.pos += 1;
1341 } else {
1342 break;
1343 }
1344 }
1345 if self.pos == start {
1346 return Err(DkitError::QueryError(format!(
1347 "expected operation keyword at position {}",
1348 self.pos
1349 )));
1350 }
1351 Ok(self.input[start..self.pos].iter().collect())
1352 }
1353
1354 fn parse_condition(&mut self) -> Result<Condition, DkitError> {
1356 let mut left = Condition::Comparison(self.parse_comparison()?);
1357
1358 loop {
1359 self.skip_whitespace();
1360 let saved_pos = self.pos;
1361 if let Ok(keyword) = self.parse_keyword() {
1362 match keyword.as_str() {
1363 "and" => {
1364 self.skip_whitespace();
1365 let right = Condition::Comparison(self.parse_comparison()?);
1366 left = Condition::And(Box::new(left), Box::new(right));
1367 }
1368 "or" => {
1369 self.skip_whitespace();
1370 let right = Condition::Comparison(self.parse_comparison()?);
1371 left = Condition::Or(Box::new(left), Box::new(right));
1372 }
1373 _ => {
1374 self.pos = saved_pos;
1376 break;
1377 }
1378 }
1379 } else {
1380 break;
1381 }
1382 }
1383
1384 Ok(left)
1385 }
1386
1387 fn parse_comparison(&mut self) -> Result<Comparison, DkitError> {
1390 let field = self.parse_identifier()?;
1392 self.skip_whitespace();
1393
1394 let saved_pos = self.pos;
1396 if let Ok(keyword) = self.parse_keyword() {
1397 match keyword.as_str() {
1398 "in" => {
1399 self.skip_whitespace();
1400 let list = self.parse_literal_list()?;
1401 return Ok(Comparison {
1402 field,
1403 op: CompareOp::In,
1404 value: LiteralValue::List(list),
1405 });
1406 }
1407 "not" => {
1408 self.skip_whitespace();
1409 let saved_pos2 = self.pos;
1410 if let Ok(kw2) = self.parse_keyword() {
1411 if kw2 == "in" {
1412 self.skip_whitespace();
1413 let list = self.parse_literal_list()?;
1414 return Ok(Comparison {
1415 field,
1416 op: CompareOp::NotIn,
1417 value: LiteralValue::List(list),
1418 });
1419 } else if kw2 == "matches" {
1420 self.skip_whitespace();
1421 let value = self.parse_literal_value()?;
1422 return Ok(Comparison {
1423 field,
1424 op: CompareOp::NotMatches,
1425 value,
1426 });
1427 }
1428 }
1429 self.pos = saved_pos2;
1430 self.pos = saved_pos;
1431 }
1432 _ => {
1433 self.pos = saved_pos;
1434 }
1435 }
1436 } else {
1437 self.pos = saved_pos;
1438 }
1439
1440 let op = self.parse_compare_op()?;
1442 self.skip_whitespace();
1443
1444 let value = self.parse_literal_value()?;
1446
1447 Ok(Comparison { field, op, value })
1448 }
1449
1450 fn parse_identifier(&mut self) -> Result<String, DkitError> {
1452 let start = self.pos;
1453 while !self.is_at_end() {
1454 let c = self.input[self.pos];
1455 if c.is_alphanumeric() || c == '_' || c == '-' {
1456 self.pos += 1;
1457 } else {
1458 break;
1459 }
1460 }
1461 if self.pos == start {
1462 return Err(DkitError::QueryError(format!(
1463 "expected field name at position {}",
1464 self.pos
1465 )));
1466 }
1467 Ok(self.input[start..self.pos].iter().collect())
1468 }
1469
1470 fn parse_compare_op(&mut self) -> Result<CompareOp, DkitError> {
1472 let c1 = self.peek().ok_or_else(|| {
1473 DkitError::QueryError(format!(
1474 "expected comparison operator at position {}",
1475 self.pos
1476 ))
1477 })?;
1478
1479 match c1 {
1480 '=' => {
1481 self.advance();
1482 if self.consume_char('=') {
1483 Ok(CompareOp::Eq)
1484 } else {
1485 Err(DkitError::QueryError(format!(
1486 "expected '==' at position {}",
1487 self.pos - 1
1488 )))
1489 }
1490 }
1491 '!' => {
1492 self.advance();
1493 if self.consume_char('=') {
1494 Ok(CompareOp::Ne)
1495 } else {
1496 Err(DkitError::QueryError(format!(
1497 "expected '!=' at position {}",
1498 self.pos - 1
1499 )))
1500 }
1501 }
1502 '>' => {
1503 self.advance();
1504 if self.consume_char('=') {
1505 Ok(CompareOp::Ge)
1506 } else {
1507 Ok(CompareOp::Gt)
1508 }
1509 }
1510 '<' => {
1511 self.advance();
1512 if self.consume_char('=') {
1513 Ok(CompareOp::Le)
1514 } else {
1515 Ok(CompareOp::Lt)
1516 }
1517 }
1518 c if c.is_alphabetic() => {
1519 let saved_pos = self.pos;
1520 let keyword = self.parse_keyword()?;
1521 match keyword.as_str() {
1522 "contains" => Ok(CompareOp::Contains),
1523 "starts_with" => Ok(CompareOp::StartsWith),
1524 "ends_with" => Ok(CompareOp::EndsWith),
1525 "matches" => Ok(CompareOp::Matches),
1526 _ => {
1527 self.pos = saved_pos;
1528 Err(DkitError::QueryError(format!(
1529 "expected comparison operator at position {}, found '{}'",
1530 saved_pos, keyword
1531 )))
1532 }
1533 }
1534 }
1535 _ => Err(DkitError::QueryError(format!(
1536 "expected comparison operator at position {}, found '{}'",
1537 self.pos, c1
1538 ))),
1539 }
1540 }
1541
1542 fn parse_literal_value(&mut self) -> Result<LiteralValue, DkitError> {
1544 match self.peek() {
1545 Some('"') => self.parse_string_literal(),
1546 Some(c) if c.is_ascii_digit() || c == '-' => self.parse_number_literal(),
1547 Some(c) if c.is_alphabetic() => {
1548 let word = self.parse_keyword()?;
1549 match word.as_str() {
1550 "true" => Ok(LiteralValue::Bool(true)),
1551 "false" => Ok(LiteralValue::Bool(false)),
1552 "null" => Ok(LiteralValue::Null),
1553 _ => Err(DkitError::QueryError(format!(
1554 "unexpected value '{}' at position {}",
1555 word,
1556 self.pos - word.len()
1557 ))),
1558 }
1559 }
1560 Some(c) => Err(DkitError::QueryError(format!(
1561 "unexpected character '{}' at position {}",
1562 c, self.pos
1563 ))),
1564 None => Err(DkitError::QueryError(format!(
1565 "expected value at position {}",
1566 self.pos
1567 ))),
1568 }
1569 }
1570
1571 fn parse_literal_list(&mut self) -> Result<Vec<LiteralValue>, DkitError> {
1573 if !self.consume_char('(') {
1574 return Err(DkitError::QueryError(format!(
1575 "expected '(' at position {}",
1576 self.pos
1577 )));
1578 }
1579
1580 let mut values = Vec::new();
1581 self.skip_whitespace();
1582
1583 if self.peek() == Some(')') {
1585 self.advance();
1586 return Ok(values);
1587 }
1588
1589 values.push(self.parse_literal_value()?);
1591
1592 loop {
1593 self.skip_whitespace();
1594 if self.consume_char(')') {
1595 break;
1596 }
1597 if !self.consume_char(',') {
1598 return Err(DkitError::QueryError(format!(
1599 "expected ',' or ')' at position {}",
1600 self.pos
1601 )));
1602 }
1603 self.skip_whitespace();
1604 values.push(self.parse_literal_value()?);
1605 }
1606
1607 Ok(values)
1608 }
1609
1610 fn parse_string_literal(&mut self) -> Result<LiteralValue, DkitError> {
1612 if !self.consume_char('"') {
1613 return Err(DkitError::QueryError(format!(
1614 "expected '\"' at position {}",
1615 self.pos
1616 )));
1617 }
1618 let start = self.pos;
1619 while !self.is_at_end() && self.input[self.pos] != '"' {
1620 self.pos += 1;
1621 }
1622 if self.is_at_end() {
1623 return Err(DkitError::QueryError(format!(
1624 "unterminated string starting at position {}",
1625 start - 1
1626 )));
1627 }
1628 let s: String = self.input[start..self.pos].iter().collect();
1629 self.advance(); Ok(LiteralValue::String(s))
1631 }
1632
1633 fn parse_number_literal(&mut self) -> Result<LiteralValue, DkitError> {
1635 let start = self.pos;
1636 if self.peek() == Some('-') {
1637 self.advance();
1638 }
1639 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1640 self.pos += 1;
1641 }
1642 let mut is_float = false;
1643 if self.peek() == Some('.') {
1644 is_float = true;
1645 self.advance();
1646 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1647 self.pos += 1;
1648 }
1649 }
1650 if self.pos == start || (self.pos == start + 1 && self.input[start] == '-') {
1651 return Err(DkitError::QueryError(format!(
1652 "expected number at position {}",
1653 start
1654 )));
1655 }
1656 let num_str: String = self.input[start..self.pos].iter().collect();
1657 if is_float {
1658 let f: f64 = num_str.parse().map_err(|_| {
1659 DkitError::QueryError(format!(
1660 "invalid number '{}' at position {}",
1661 num_str, start
1662 ))
1663 })?;
1664 Ok(LiteralValue::Float(f))
1665 } else {
1666 let n: i64 = num_str.parse().map_err(|_| {
1667 DkitError::QueryError(format!(
1668 "invalid number '{}' at position {}",
1669 num_str, start
1670 ))
1671 })?;
1672 Ok(LiteralValue::Integer(n))
1673 }
1674 }
1675
1676 fn peek(&self) -> Option<char> {
1679 self.input.get(self.pos).copied()
1680 }
1681
1682 fn peek_is_identifier_start(&self) -> bool {
1683 self.peek().is_some_and(|c| c.is_alphabetic() || c == '_')
1684 }
1685
1686 fn advance(&mut self) {
1687 self.pos += 1;
1688 }
1689
1690 fn consume_char(&mut self, expected: char) -> bool {
1691 if self.peek() == Some(expected) {
1692 self.advance();
1693 true
1694 } else {
1695 false
1696 }
1697 }
1698
1699 fn skip_whitespace(&mut self) {
1700 while self.peek().is_some_and(|c| c.is_whitespace()) {
1701 self.advance();
1702 }
1703 }
1704
1705 fn is_at_end(&self) -> bool {
1706 self.pos >= self.input.len()
1707 }
1708
1709 fn try_parse_identifier(&mut self) -> Option<String> {
1711 if !self.peek_is_identifier_start() {
1712 return None;
1713 }
1714 let saved_pos = self.pos;
1715 match self.parse_identifier() {
1716 Ok(id) => Some(id),
1717 Err(_) => {
1718 self.pos = saved_pos;
1719 None
1720 }
1721 }
1722 }
1723
1724 fn try_consume_keyword(&mut self, keyword: &str) -> bool {
1726 let saved_pos = self.pos;
1727 if let Ok(word) = self.parse_keyword() {
1728 if word == keyword {
1729 return true;
1730 }
1731 }
1732 self.pos = saved_pos;
1733 false
1734 }
1735
1736 fn parse_float_value(&mut self) -> Result<f64, DkitError> {
1739 let start = self.pos;
1740 if self.peek() == Some('-') {
1741 self.advance();
1742 }
1743 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1744 self.pos += 1;
1745 }
1746 if self.peek() == Some('.') {
1747 self.advance();
1748 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1749 self.pos += 1;
1750 }
1751 }
1752 if self.pos == start {
1753 return Err(DkitError::QueryError(format!(
1754 "expected number at position {}",
1755 self.pos
1756 )));
1757 }
1758 let num_str: String = self.input[start..self.pos].iter().collect();
1759 num_str.parse().map_err(|_| {
1760 DkitError::QueryError(format!(
1761 "invalid number '{}' at position {}",
1762 num_str, start
1763 ))
1764 })
1765 }
1766
1767 fn parse_positive_integer(&mut self) -> Result<usize, DkitError> {
1768 let start = self.pos;
1769 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1770 self.pos += 1;
1771 }
1772 if self.pos == start {
1773 return Err(DkitError::QueryError(format!(
1774 "expected positive integer at position {}",
1775 self.pos
1776 )));
1777 }
1778 let num_str: String = self.input[start..self.pos].iter().collect();
1779 num_str.parse().map_err(|_| {
1780 DkitError::QueryError(format!(
1781 "invalid integer '{}' at position {}",
1782 num_str, start
1783 ))
1784 })
1785 }
1786}
1787
1788pub fn parse_query(input: &str) -> Result<Query, DkitError> {
1790 Parser::new(input).parse()
1791}
1792
1793pub fn parse_add_field_expr(input: &str) -> Result<(String, Expr), DkitError> {
1796 let mut parser = Parser::new(input);
1797 parser.skip_whitespace();
1798 let name = parser.parse_identifier().map_err(|_| {
1799 DkitError::QueryError(format!(
1800 "expected field name in --add-field expression: '{input}'"
1801 ))
1802 })?;
1803 parser.skip_whitespace();
1804 if !parser.consume_char('=') {
1805 return Err(DkitError::QueryError(format!(
1806 "expected '=' after field name in --add-field expression: '{input}'"
1807 )));
1808 }
1809 parser.skip_whitespace();
1810 let expr = parser.parse_expr()?;
1811 parser.skip_whitespace();
1812 if parser.pos != parser.input.len() {
1813 return Err(DkitError::QueryError(format!(
1814 "unexpected character '{}' at position {} in --add-field expression",
1815 parser.input[parser.pos], parser.pos
1816 )));
1817 }
1818 Ok((name, expr))
1819}
1820
1821pub fn parse_condition_expr(input: &str) -> Result<Condition, DkitError> {
1824 let mut parser = Parser::new(input);
1825 parser.skip_whitespace();
1826 let condition = parser.parse_condition()?;
1827 parser.skip_whitespace();
1828 if parser.pos != parser.input.len() {
1829 return Err(DkitError::QueryError(format!(
1830 "unexpected character '{}' at position {} in where expression",
1831 parser.input[parser.pos], parser.pos
1832 )));
1833 }
1834 Ok(condition)
1835}
1836
1837#[cfg(test)]
1838mod tests {
1839 use super::*;
1840
1841 #[test]
1844 fn test_root_path() {
1845 let q = parse_query(".").unwrap();
1846 assert!(q.path.segments.is_empty());
1847 }
1848
1849 #[test]
1850 fn test_single_field() {
1851 let q = parse_query(".name").unwrap();
1852 assert_eq!(q.path.segments, vec![Segment::Field("name".to_string())]);
1853 }
1854
1855 #[test]
1856 fn test_nested_fields() {
1857 let q = parse_query(".database.host").unwrap();
1858 assert_eq!(
1859 q.path.segments,
1860 vec![
1861 Segment::Field("database".to_string()),
1862 Segment::Field("host".to_string()),
1863 ]
1864 );
1865 }
1866
1867 #[test]
1868 fn test_deeply_nested_fields() {
1869 let q = parse_query(".a.b.c.d").unwrap();
1870 assert_eq!(q.path.segments.len(), 4);
1871 assert_eq!(q.path.segments[3], Segment::Field("d".to_string()));
1872 }
1873
1874 #[test]
1877 fn test_array_index() {
1878 let q = parse_query(".users[0]").unwrap();
1879 assert_eq!(
1880 q.path.segments,
1881 vec![Segment::Field("users".to_string()), Segment::Index(0),]
1882 );
1883 }
1884
1885 #[test]
1886 fn test_array_negative_index() {
1887 let q = parse_query(".users[-1]").unwrap();
1888 assert_eq!(
1889 q.path.segments,
1890 vec![Segment::Field("users".to_string()), Segment::Index(-1),]
1891 );
1892 }
1893
1894 #[test]
1895 fn test_array_index_with_field_after() {
1896 let q = parse_query(".users[0].name").unwrap();
1897 assert_eq!(
1898 q.path.segments,
1899 vec![
1900 Segment::Field("users".to_string()),
1901 Segment::Index(0),
1902 Segment::Field("name".to_string()),
1903 ]
1904 );
1905 }
1906
1907 #[test]
1908 fn test_large_index() {
1909 let q = parse_query(".items[999]").unwrap();
1910 assert_eq!(
1911 q.path.segments,
1912 vec![Segment::Field("items".to_string()), Segment::Index(999),]
1913 );
1914 }
1915
1916 #[test]
1919 fn test_array_iterate() {
1920 let q = parse_query(".users[]").unwrap();
1921 assert_eq!(
1922 q.path.segments,
1923 vec![Segment::Field("users".to_string()), Segment::Iterate,]
1924 );
1925 }
1926
1927 #[test]
1928 fn test_array_iterate_with_field() {
1929 let q = parse_query(".users[].name").unwrap();
1930 assert_eq!(
1931 q.path.segments,
1932 vec![
1933 Segment::Field("users".to_string()),
1934 Segment::Iterate,
1935 Segment::Field("name".to_string()),
1936 ]
1937 );
1938 }
1939
1940 #[test]
1941 fn test_array_iterate_nested() {
1942 let q = parse_query(".data[].items[].name").unwrap();
1943 assert_eq!(
1944 q.path.segments,
1945 vec![
1946 Segment::Field("data".to_string()),
1947 Segment::Iterate,
1948 Segment::Field("items".to_string()),
1949 Segment::Iterate,
1950 Segment::Field("name".to_string()),
1951 ]
1952 );
1953 }
1954
1955 #[test]
1958 fn test_array_wildcard() {
1959 let q = parse_query(".[*]").unwrap();
1960 assert_eq!(q.path.segments, vec![Segment::Wildcard]);
1961 }
1962
1963 #[test]
1964 fn test_array_wildcard_with_field() {
1965 let q = parse_query(".users[*].name").unwrap();
1966 assert_eq!(
1967 q.path.segments,
1968 vec![
1969 Segment::Field("users".to_string()),
1970 Segment::Wildcard,
1971 Segment::Field("name".to_string()),
1972 ]
1973 );
1974 }
1975
1976 #[test]
1979 fn test_array_slice_basic() {
1980 let q = parse_query(".[0:3]").unwrap();
1981 assert_eq!(
1982 q.path.segments,
1983 vec![Segment::Slice {
1984 start: Some(0),
1985 end: Some(3),
1986 step: None
1987 }]
1988 );
1989 }
1990
1991 #[test]
1992 fn test_array_slice_open_end() {
1993 let q = parse_query(".[1:]").unwrap();
1994 assert_eq!(
1995 q.path.segments,
1996 vec![Segment::Slice {
1997 start: Some(1),
1998 end: None,
1999 step: None
2000 }]
2001 );
2002 }
2003
2004 #[test]
2005 fn test_array_slice_open_start() {
2006 let q = parse_query(".[:3]").unwrap();
2007 assert_eq!(
2008 q.path.segments,
2009 vec![Segment::Slice {
2010 start: None,
2011 end: Some(3),
2012 step: None
2013 }]
2014 );
2015 }
2016
2017 #[test]
2018 fn test_array_slice_negative() {
2019 let q = parse_query(".[-2:]").unwrap();
2020 assert_eq!(
2021 q.path.segments,
2022 vec![Segment::Slice {
2023 start: Some(-2),
2024 end: None,
2025 step: None
2026 }]
2027 );
2028 }
2029
2030 #[test]
2031 fn test_array_slice_with_step() {
2032 let q = parse_query(".[1:5:2]").unwrap();
2033 assert_eq!(
2034 q.path.segments,
2035 vec![Segment::Slice {
2036 start: Some(1),
2037 end: Some(5),
2038 step: Some(2)
2039 }]
2040 );
2041 }
2042
2043 #[test]
2044 fn test_array_slice_full_open() {
2045 let q = parse_query(".[:]").unwrap();
2046 assert_eq!(
2047 q.path.segments,
2048 vec![Segment::Slice {
2049 start: None,
2050 end: None,
2051 step: None
2052 }]
2053 );
2054 }
2055
2056 #[test]
2057 fn test_array_slice_with_field() {
2058 let q = parse_query(".users[0:3].name").unwrap();
2059 assert_eq!(
2060 q.path.segments,
2061 vec![
2062 Segment::Field("users".to_string()),
2063 Segment::Slice {
2064 start: Some(0),
2065 end: Some(3),
2066 step: None
2067 },
2068 Segment::Field("name".to_string()),
2069 ]
2070 );
2071 }
2072
2073 #[test]
2074 fn test_array_slice_reverse_step() {
2075 let q = parse_query(".[::-1]").unwrap();
2076 assert_eq!(
2077 q.path.segments,
2078 vec![Segment::Slice {
2079 start: None,
2080 end: None,
2081 step: Some(-1)
2082 }]
2083 );
2084 }
2085
2086 #[test]
2089 fn test_complex_path() {
2090 let q = parse_query(".data.users[0].address.city").unwrap();
2091 assert_eq!(
2092 q.path.segments,
2093 vec![
2094 Segment::Field("data".to_string()),
2095 Segment::Field("users".to_string()),
2096 Segment::Index(0),
2097 Segment::Field("address".to_string()),
2098 Segment::Field("city".to_string()),
2099 ]
2100 );
2101 }
2102
2103 #[test]
2106 fn test_field_with_underscore() {
2107 let q = parse_query(".user_name").unwrap();
2108 assert_eq!(
2109 q.path.segments,
2110 vec![Segment::Field("user_name".to_string())]
2111 );
2112 }
2113
2114 #[test]
2115 fn test_field_with_hyphen() {
2116 let q = parse_query(".content-type").unwrap();
2117 assert_eq!(
2118 q.path.segments,
2119 vec![Segment::Field("content-type".to_string())]
2120 );
2121 }
2122
2123 #[test]
2124 fn test_field_with_digits() {
2125 let q = parse_query(".field1").unwrap();
2126 assert_eq!(q.path.segments, vec![Segment::Field("field1".to_string())]);
2127 }
2128
2129 #[test]
2132 fn test_error_no_dot() {
2133 let err = parse_query("name").unwrap_err();
2134 assert!(matches!(err, DkitError::QueryError(_)));
2135 }
2136
2137 #[test]
2138 fn test_error_empty() {
2139 let err = parse_query("").unwrap_err();
2140 assert!(matches!(err, DkitError::QueryError(_)));
2141 }
2142
2143 #[test]
2144 fn test_error_unclosed_bracket() {
2145 let err = parse_query(".users[0").unwrap_err();
2146 assert!(matches!(err, DkitError::QueryError(_)));
2147 }
2148
2149 #[test]
2150 fn test_error_invalid_index() {
2151 let err = parse_query(".users[abc]").unwrap_err();
2152 assert!(matches!(err, DkitError::QueryError(_)));
2153 }
2154
2155 #[test]
2156 fn test_error_trailing_garbage() {
2157 let err = parse_query(".name xyz").unwrap_err();
2158 assert!(matches!(err, DkitError::QueryError(_)));
2159 }
2160
2161 #[test]
2164 fn test_whitespace_around() {
2165 let q = parse_query(" .name ").unwrap();
2166 assert_eq!(q.path.segments, vec![Segment::Field("name".to_string())]);
2167 }
2168
2169 #[test]
2172 fn test_root_array_index() {
2173 let q = parse_query(".[0]").unwrap();
2174 assert_eq!(q.path.segments, vec![Segment::Index(0)]);
2175 }
2176
2177 #[test]
2178 fn test_root_array_iterate() {
2179 let q = parse_query(".[]").unwrap();
2180 assert_eq!(q.path.segments, vec![Segment::Iterate]);
2181 }
2182
2183 #[test]
2184 fn test_root_iterate_with_field() {
2185 let q = parse_query(".[].name").unwrap();
2186 assert_eq!(
2187 q.path.segments,
2188 vec![Segment::Iterate, Segment::Field("name".to_string()),]
2189 );
2190 }
2191
2192 #[test]
2195 fn test_where_eq_integer() {
2196 let q = parse_query(".users[] | where age == 30").unwrap();
2197 assert_eq!(
2198 q.path.segments,
2199 vec![Segment::Field("users".to_string()), Segment::Iterate]
2200 );
2201 assert_eq!(q.operations.len(), 1);
2202 assert_eq!(
2203 q.operations[0],
2204 Operation::Where(Condition::Comparison(Comparison {
2205 field: "age".to_string(),
2206 op: CompareOp::Eq,
2207 value: LiteralValue::Integer(30),
2208 }))
2209 );
2210 }
2211
2212 #[test]
2213 fn test_where_ne_string() {
2214 let q = parse_query(".items[] | where status != \"inactive\"").unwrap();
2215 assert_eq!(
2216 q.operations[0],
2217 Operation::Where(Condition::Comparison(Comparison {
2218 field: "status".to_string(),
2219 op: CompareOp::Ne,
2220 value: LiteralValue::String("inactive".to_string()),
2221 }))
2222 );
2223 }
2224
2225 #[test]
2226 fn test_where_gt() {
2227 let q = parse_query(".[] | where age > 25").unwrap();
2228 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2229 panic!("expected Comparison");
2230 };
2231 assert_eq!(cmp.field, "age");
2232 assert_eq!(cmp.op, CompareOp::Gt);
2233 assert_eq!(cmp.value, LiteralValue::Integer(25));
2234 }
2235
2236 #[test]
2237 fn test_where_lt() {
2238 let q = parse_query(".[] | where price < 100").unwrap();
2239 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2240 panic!("expected Comparison");
2241 };
2242 assert_eq!(cmp.op, CompareOp::Lt);
2243 assert_eq!(cmp.value, LiteralValue::Integer(100));
2244 }
2245
2246 #[test]
2247 fn test_where_ge() {
2248 let q = parse_query(".[] | where score >= 80").unwrap();
2249 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2250 panic!("expected Comparison");
2251 };
2252 assert_eq!(cmp.op, CompareOp::Ge);
2253 assert_eq!(cmp.value, LiteralValue::Integer(80));
2254 }
2255
2256 #[test]
2257 fn test_where_le() {
2258 let q = parse_query(".[] | where price <= 1000").unwrap();
2259 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2260 panic!("expected Comparison");
2261 };
2262 assert_eq!(cmp.op, CompareOp::Le);
2263 assert_eq!(cmp.value, LiteralValue::Integer(1000));
2264 }
2265
2266 #[test]
2267 fn test_where_float_literal() {
2268 let q = parse_query(".[] | where score > 3.14").unwrap();
2269 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2270 panic!("expected Comparison");
2271 };
2272 assert_eq!(cmp.value, LiteralValue::Float(3.14));
2273 }
2274
2275 #[test]
2276 fn test_where_negative_number() {
2277 let q = parse_query(".[] | where temp > -10").unwrap();
2278 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2279 panic!("expected Comparison");
2280 };
2281 assert_eq!(cmp.value, LiteralValue::Integer(-10));
2282 }
2283
2284 #[test]
2285 fn test_where_bool_literal() {
2286 let q = parse_query(".[] | where active == true").unwrap();
2287 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2288 panic!("expected Comparison");
2289 };
2290 assert_eq!(cmp.value, LiteralValue::Bool(true));
2291 }
2292
2293 #[test]
2294 fn test_where_null_literal() {
2295 let q = parse_query(".[] | where value == null").unwrap();
2296 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2297 panic!("expected Comparison");
2298 };
2299 assert_eq!(cmp.value, LiteralValue::Null);
2300 }
2301
2302 #[test]
2303 fn test_where_no_operations_for_path_only() {
2304 let q = parse_query(".users[0].name").unwrap();
2305 assert!(q.operations.is_empty());
2306 }
2307
2308 #[test]
2309 fn test_where_with_extra_whitespace() {
2310 let q = parse_query(".[] | where age > 30 ").unwrap();
2311 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2312 panic!("expected Comparison");
2313 };
2314 assert_eq!(cmp.field, "age");
2315 assert_eq!(cmp.op, CompareOp::Gt);
2316 assert_eq!(cmp.value, LiteralValue::Integer(30));
2317 }
2318
2319 #[test]
2322 fn test_error_where_missing_field() {
2323 let err = parse_query(".[] | where == 30").unwrap_err();
2324 assert!(matches!(err, DkitError::QueryError(_)));
2325 }
2326
2327 #[test]
2328 fn test_error_where_missing_operator() {
2329 let err = parse_query(".[] | where age 30").unwrap_err();
2330 assert!(matches!(err, DkitError::QueryError(_)));
2331 }
2332
2333 #[test]
2334 fn test_error_where_missing_value() {
2335 let err = parse_query(".[] | where age >").unwrap_err();
2336 assert!(matches!(err, DkitError::QueryError(_)));
2337 }
2338
2339 #[test]
2340 fn test_error_where_unterminated_string() {
2341 let err = parse_query(".[] | where name == \"hello").unwrap_err();
2342 assert!(matches!(err, DkitError::QueryError(_)));
2343 }
2344
2345 #[test]
2346 fn test_error_unknown_operation() {
2347 let err = parse_query(".[] | foobar age > 30").unwrap_err();
2348 assert!(matches!(err, DkitError::QueryError(_)));
2349 }
2350
2351 #[test]
2354 fn test_where_contains() {
2355 let q = parse_query(".[] | where email contains \"@gmail\"").unwrap();
2356 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2357 panic!("expected Comparison");
2358 };
2359 assert_eq!(cmp.field, "email");
2360 assert_eq!(cmp.op, CompareOp::Contains);
2361 assert_eq!(cmp.value, LiteralValue::String("@gmail".to_string()));
2362 }
2363
2364 #[test]
2365 fn test_where_starts_with() {
2366 let q = parse_query(".[] | where name starts_with \"A\"").unwrap();
2367 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2368 panic!("expected Comparison");
2369 };
2370 assert_eq!(cmp.field, "name");
2371 assert_eq!(cmp.op, CompareOp::StartsWith);
2372 assert_eq!(cmp.value, LiteralValue::String("A".to_string()));
2373 }
2374
2375 #[test]
2376 fn test_where_ends_with() {
2377 let q = parse_query(".[] | where file ends_with \".json\"").unwrap();
2378 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2379 panic!("expected Comparison");
2380 };
2381 assert_eq!(cmp.field, "file");
2382 assert_eq!(cmp.op, CompareOp::EndsWith);
2383 assert_eq!(cmp.value, LiteralValue::String(".json".to_string()));
2384 }
2385
2386 #[test]
2387 fn test_where_matches() {
2388 let q = parse_query(".[] | where email matches \".*@gmail\\.com$\"").unwrap();
2389 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2390 panic!("expected Comparison");
2391 };
2392 assert_eq!(cmp.field, "email");
2393 assert_eq!(cmp.op, CompareOp::Matches);
2394 assert_eq!(
2395 cmp.value,
2396 LiteralValue::String(".*@gmail\\.com$".to_string())
2397 );
2398 }
2399
2400 #[test]
2401 fn test_where_not_matches() {
2402 let q = parse_query(".[] | where name not matches \"^test_\"").unwrap();
2403 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
2404 panic!("expected Comparison");
2405 };
2406 assert_eq!(cmp.field, "name");
2407 assert_eq!(cmp.op, CompareOp::NotMatches);
2408 assert_eq!(cmp.value, LiteralValue::String("^test_".to_string()));
2409 }
2410
2411 #[test]
2414 fn test_where_and() {
2415 let q = parse_query(".[] | where age > 25 and city == \"Seoul\"").unwrap();
2416 let Operation::Where(cond) = &q.operations[0] else {
2417 panic!("expected Where operation");
2418 };
2419 match cond {
2420 Condition::And(left, right) => {
2421 let Condition::Comparison(l) = left.as_ref() else {
2422 panic!("expected left Comparison");
2423 };
2424 assert_eq!(l.field, "age");
2425 assert_eq!(l.op, CompareOp::Gt);
2426 assert_eq!(l.value, LiteralValue::Integer(25));
2427 let Condition::Comparison(r) = right.as_ref() else {
2428 panic!("expected right Comparison");
2429 };
2430 assert_eq!(r.field, "city");
2431 assert_eq!(r.op, CompareOp::Eq);
2432 assert_eq!(r.value, LiteralValue::String("Seoul".to_string()));
2433 }
2434 _ => panic!("expected And condition"),
2435 }
2436 }
2437
2438 #[test]
2439 fn test_where_or() {
2440 let q = parse_query(".[] | where role == \"admin\" or role == \"manager\"").unwrap();
2441 let Operation::Where(cond) = &q.operations[0] else {
2442 panic!("expected Where operation");
2443 };
2444 match cond {
2445 Condition::Or(left, right) => {
2446 let Condition::Comparison(l) = left.as_ref() else {
2447 panic!("expected left Comparison");
2448 };
2449 assert_eq!(l.field, "role");
2450 assert_eq!(l.value, LiteralValue::String("admin".to_string()));
2451 let Condition::Comparison(r) = right.as_ref() else {
2452 panic!("expected right Comparison");
2453 };
2454 assert_eq!(r.field, "role");
2455 assert_eq!(r.value, LiteralValue::String("manager".to_string()));
2456 }
2457 _ => panic!("expected Or condition"),
2458 }
2459 }
2460
2461 #[test]
2462 fn test_where_and_with_string_op() {
2463 let q = parse_query(".[] | where name starts_with \"A\" and age > 20").unwrap();
2464 let Operation::Where(cond) = &q.operations[0] else {
2465 panic!("expected Where operation");
2466 };
2467 assert!(matches!(cond, Condition::And(_, _)));
2468 }
2469
2470 #[test]
2471 fn test_where_chained_and() {
2472 let q = parse_query(".[] | where a == 1 and b == 2 and c == 3").unwrap();
2473 let Operation::Where(cond) = &q.operations[0] else {
2474 panic!("expected Where operation");
2475 };
2476 match cond {
2478 Condition::And(left, right) => {
2479 assert!(matches!(left.as_ref(), Condition::And(_, _)));
2480 assert!(matches!(right.as_ref(), Condition::Comparison(_)));
2481 }
2482 _ => panic!("expected And condition"),
2483 }
2484 }
2485
2486 fn field(name: &str) -> SelectExpr {
2489 SelectExpr {
2490 expr: Expr::Field(name.to_string()),
2491 alias: None,
2492 }
2493 }
2494
2495 fn fields(names: &[&str]) -> Operation {
2496 Operation::Select(names.iter().map(|n| field(n)).collect())
2497 }
2498
2499 #[test]
2500 fn test_select_single_field() {
2501 let q = parse_query(".users[] | select name").unwrap();
2502 assert_eq!(q.operations.len(), 1);
2503 assert_eq!(q.operations[0], fields(&["name"]));
2504 }
2505
2506 #[test]
2507 fn test_select_multiple_fields() {
2508 let q = parse_query(".users[] | select name, email").unwrap();
2509 assert_eq!(q.operations[0], fields(&["name", "email"]));
2510 }
2511
2512 #[test]
2513 fn test_select_three_fields() {
2514 let q = parse_query(".users[] | select name, age, email").unwrap();
2515 assert_eq!(q.operations[0], fields(&["name", "age", "email"]));
2516 }
2517
2518 #[test]
2519 fn test_select_with_extra_whitespace() {
2520 let q = parse_query(".[] | select name , email ").unwrap();
2521 assert_eq!(q.operations[0], fields(&["name", "email"]));
2522 }
2523
2524 #[test]
2525 fn test_select_field_with_underscore() {
2526 let q = parse_query(".[] | select user_name, created_at").unwrap();
2527 assert_eq!(q.operations[0], fields(&["user_name", "created_at"]));
2528 }
2529
2530 #[test]
2531 fn test_select_field_with_hyphen() {
2532 let q = parse_query(".[] | select content-type").unwrap();
2533 assert_eq!(q.operations[0], fields(&["content-type"]));
2534 }
2535
2536 #[test]
2537 fn test_where_then_select() {
2538 let q = parse_query(".users[] | where age > 30 | select name, email").unwrap();
2539 assert_eq!(q.operations.len(), 2);
2540 assert!(matches!(&q.operations[0], Operation::Where(_)));
2541 assert_eq!(q.operations[1], fields(&["name", "email"]));
2542 }
2543
2544 #[test]
2545 fn test_error_select_missing_fields() {
2546 let err = parse_query(".[] | select").unwrap_err();
2547 assert!(matches!(err, DkitError::QueryError(_)));
2548 }
2549
2550 #[test]
2551 fn test_select_func_single() {
2552 let q = parse_query(".[] | select upper(name)").unwrap();
2553 assert_eq!(
2554 q.operations[0],
2555 Operation::Select(vec![SelectExpr {
2556 expr: Expr::FuncCall {
2557 name: "upper".to_string(),
2558 args: vec![Expr::Field("name".to_string())],
2559 },
2560 alias: None,
2561 }])
2562 );
2563 }
2564
2565 #[test]
2566 fn test_select_func_with_alias() {
2567 let q = parse_query(".[] | select upper(name) as NAME").unwrap();
2568 assert_eq!(
2569 q.operations[0],
2570 Operation::Select(vec![SelectExpr {
2571 expr: Expr::FuncCall {
2572 name: "upper".to_string(),
2573 args: vec![Expr::Field("name".to_string())],
2574 },
2575 alias: Some("NAME".to_string()),
2576 }])
2577 );
2578 }
2579
2580 #[test]
2581 fn test_select_func_nested() {
2582 let q = parse_query(".[] | select upper(trim(name))").unwrap();
2583 assert_eq!(
2584 q.operations[0],
2585 Operation::Select(vec![SelectExpr {
2586 expr: Expr::FuncCall {
2587 name: "upper".to_string(),
2588 args: vec![Expr::FuncCall {
2589 name: "trim".to_string(),
2590 args: vec![Expr::Field("name".to_string())],
2591 }],
2592 },
2593 alias: None,
2594 }])
2595 );
2596 }
2597
2598 #[test]
2599 fn test_select_func_with_literal_arg() {
2600 let q = parse_query(".[] | select round(price, 2)").unwrap();
2601 assert_eq!(
2602 q.operations[0],
2603 Operation::Select(vec![SelectExpr {
2604 expr: Expr::FuncCall {
2605 name: "round".to_string(),
2606 args: vec![
2607 Expr::Field("price".to_string()),
2608 Expr::Literal(LiteralValue::Integer(2)),
2609 ],
2610 },
2611 alias: None,
2612 }])
2613 );
2614 }
2615
2616 #[test]
2617 fn test_select_mixed_fields_and_funcs() {
2618 let q = parse_query(".[] | select name, upper(city)").unwrap();
2619 assert_eq!(
2620 q.operations[0],
2621 Operation::Select(vec![
2622 field("name"),
2623 SelectExpr {
2624 expr: Expr::FuncCall {
2625 name: "upper".to_string(),
2626 args: vec![Expr::Field("city".to_string())],
2627 },
2628 alias: None,
2629 }
2630 ])
2631 );
2632 }
2633
2634 #[test]
2637 fn test_sort_asc() {
2638 let q = parse_query(".users[] | sort age").unwrap();
2639 assert_eq!(q.operations.len(), 1);
2640 assert_eq!(
2641 q.operations[0],
2642 Operation::Sort {
2643 field: "age".to_string(),
2644 descending: false,
2645 }
2646 );
2647 }
2648
2649 #[test]
2650 fn test_sort_desc() {
2651 let q = parse_query(".users[] | sort age desc").unwrap();
2652 assert_eq!(q.operations.len(), 1);
2653 assert_eq!(
2654 q.operations[0],
2655 Operation::Sort {
2656 field: "age".to_string(),
2657 descending: true,
2658 }
2659 );
2660 }
2661
2662 #[test]
2663 fn test_sort_with_extra_whitespace() {
2664 let q = parse_query(".[] | sort name ").unwrap();
2665 assert_eq!(
2666 q.operations[0],
2667 Operation::Sort {
2668 field: "name".to_string(),
2669 descending: false,
2670 }
2671 );
2672 }
2673
2674 #[test]
2675 fn test_sort_desc_with_extra_whitespace() {
2676 let q = parse_query(".[] | sort name desc ").unwrap();
2677 assert_eq!(
2678 q.operations[0],
2679 Operation::Sort {
2680 field: "name".to_string(),
2681 descending: true,
2682 }
2683 );
2684 }
2685
2686 #[test]
2687 fn test_sort_field_with_underscore() {
2688 let q = parse_query(".[] | sort created_at").unwrap();
2689 assert_eq!(
2690 q.operations[0],
2691 Operation::Sort {
2692 field: "created_at".to_string(),
2693 descending: false,
2694 }
2695 );
2696 }
2697
2698 #[test]
2699 fn test_error_sort_missing_field() {
2700 let err = parse_query(".[] | sort").unwrap_err();
2701 assert!(matches!(err, DkitError::QueryError(_)));
2702 }
2703
2704 #[test]
2707 fn test_limit() {
2708 let q = parse_query(".users[] | limit 10").unwrap();
2709 assert_eq!(q.operations.len(), 1);
2710 assert_eq!(q.operations[0], Operation::Limit(10));
2711 }
2712
2713 #[test]
2714 fn test_limit_one() {
2715 let q = parse_query(".[] | limit 1").unwrap();
2716 assert_eq!(q.operations[0], Operation::Limit(1));
2717 }
2718
2719 #[test]
2720 fn test_limit_with_extra_whitespace() {
2721 let q = parse_query(".[] | limit 5 ").unwrap();
2722 assert_eq!(q.operations[0], Operation::Limit(5));
2723 }
2724
2725 #[test]
2726 fn test_error_limit_missing_number() {
2727 let err = parse_query(".[] | limit").unwrap_err();
2728 assert!(matches!(err, DkitError::QueryError(_)));
2729 }
2730
2731 #[test]
2732 fn test_error_limit_negative() {
2733 let err = parse_query(".[] | limit -5").unwrap_err();
2734 assert!(matches!(err, DkitError::QueryError(_)));
2735 }
2736
2737 #[test]
2740 fn test_where_sort_limit() {
2741 let q = parse_query(".users[] | where age > 20 | sort age desc | limit 5").unwrap();
2742 assert_eq!(q.operations.len(), 3);
2743 assert!(matches!(&q.operations[0], Operation::Where(_)));
2744 assert_eq!(
2745 q.operations[1],
2746 Operation::Sort {
2747 field: "age".to_string(),
2748 descending: true,
2749 }
2750 );
2751 assert_eq!(q.operations[2], Operation::Limit(5));
2752 }
2753
2754 #[test]
2755 fn test_where_select_sort() {
2756 let q = parse_query(".users[] | where age > 30 | select name, email | sort name").unwrap();
2757 assert_eq!(q.operations.len(), 3);
2758 assert!(matches!(&q.operations[0], Operation::Where(_)));
2759 assert_eq!(q.operations[1], fields(&["name", "email"]));
2760 assert_eq!(
2761 q.operations[2],
2762 Operation::Sort {
2763 field: "name".to_string(),
2764 descending: false,
2765 }
2766 );
2767 }
2768
2769 #[test]
2770 fn test_group_by_single_field() {
2771 let q = parse_query(".[] | group_by category").unwrap();
2772 assert_eq!(q.operations.len(), 1);
2773 match &q.operations[0] {
2774 Operation::GroupBy {
2775 fields,
2776 having,
2777 aggregates,
2778 } => {
2779 assert_eq!(fields, &vec!["category".to_string()]);
2780 assert!(having.is_none());
2781 assert!(aggregates.is_empty());
2782 }
2783 _ => panic!("expected GroupBy"),
2784 }
2785 }
2786
2787 #[test]
2788 fn test_group_by_multiple_fields() {
2789 let q = parse_query(".[] | group_by region, category").unwrap();
2790 match &q.operations[0] {
2791 Operation::GroupBy { fields, .. } => {
2792 assert_eq!(fields, &vec!["region".to_string(), "category".to_string()]);
2793 }
2794 _ => panic!("expected GroupBy"),
2795 }
2796 }
2797
2798 #[test]
2799 fn test_group_by_with_aggregates() {
2800 let q = parse_query(".[] | group_by category count(), sum(price), avg(score)").unwrap();
2801 match &q.operations[0] {
2802 Operation::GroupBy { aggregates, .. } => {
2803 assert_eq!(aggregates.len(), 3);
2804 assert_eq!(aggregates[0].func, AggregateFunc::Count);
2805 assert_eq!(aggregates[0].field, None);
2806 assert_eq!(aggregates[0].alias, "count");
2807 assert_eq!(aggregates[1].func, AggregateFunc::Sum);
2808 assert_eq!(aggregates[1].field, Some("price".to_string()));
2809 assert_eq!(aggregates[1].alias, "sum_price");
2810 assert_eq!(aggregates[2].func, AggregateFunc::Avg);
2811 assert_eq!(aggregates[2].field, Some("score".to_string()));
2812 assert_eq!(aggregates[2].alias, "avg_score");
2813 }
2814 _ => panic!("expected GroupBy"),
2815 }
2816 }
2817
2818 #[test]
2819 fn test_group_by_with_having() {
2820 let q = parse_query(".[] | group_by category count() having count > 5").unwrap();
2821 match &q.operations[0] {
2822 Operation::GroupBy {
2823 fields,
2824 having,
2825 aggregates,
2826 } => {
2827 assert_eq!(fields, &vec!["category".to_string()]);
2828 assert!(having.is_some());
2829 assert_eq!(aggregates.len(), 1);
2830 }
2831 _ => panic!("expected GroupBy"),
2832 }
2833 }
2834
2835 #[test]
2836 fn test_group_by_with_min_max() {
2837 let q = parse_query(".[] | group_by category min(price), max(price)").unwrap();
2838 match &q.operations[0] {
2839 Operation::GroupBy { aggregates, .. } => {
2840 assert_eq!(aggregates.len(), 2);
2841 assert_eq!(aggregates[0].func, AggregateFunc::Min);
2842 assert_eq!(aggregates[1].func, AggregateFunc::Max);
2843 }
2844 _ => panic!("expected GroupBy"),
2845 }
2846 }
2847
2848 #[test]
2849 fn test_group_by_pipeline() {
2850 let q = parse_query(".[] | group_by category count() | sort count desc | limit 5").unwrap();
2851 assert_eq!(q.operations.len(), 3);
2852 assert!(matches!(&q.operations[0], Operation::GroupBy { .. }));
2853 assert!(matches!(
2854 &q.operations[1],
2855 Operation::Sort {
2856 descending: true,
2857 ..
2858 }
2859 ));
2860 assert_eq!(q.operations[2], Operation::Limit(5));
2861 }
2862
2863 #[test]
2866 fn test_add_field_simple_arithmetic() {
2867 let (name, expr) = parse_add_field_expr("total = amount * quantity").unwrap();
2868 assert_eq!(name, "total");
2869 assert!(matches!(
2870 expr,
2871 Expr::BinaryOp {
2872 op: ArithmeticOp::Mul,
2873 ..
2874 }
2875 ));
2876 }
2877
2878 #[test]
2879 fn test_add_field_string_concat() {
2880 let (name, expr) =
2881 parse_add_field_expr("full_name = first_name + \" \" + last_name").unwrap();
2882 assert_eq!(name, "full_name");
2883 assert!(matches!(
2885 expr,
2886 Expr::BinaryOp {
2887 op: ArithmeticOp::Add,
2888 ..
2889 }
2890 ));
2891 }
2892
2893 #[test]
2894 fn test_add_field_with_literal() {
2895 let (name, expr) = parse_add_field_expr("tax = price * 0.1").unwrap();
2896 assert_eq!(name, "tax");
2897 assert!(matches!(
2898 expr,
2899 Expr::BinaryOp {
2900 op: ArithmeticOp::Mul,
2901 ..
2902 }
2903 ));
2904 }
2905
2906 #[test]
2907 fn test_add_field_complex_expr() {
2908 let (name, expr) = parse_add_field_expr("total = price + price * 0.1").unwrap();
2909 assert_eq!(name, "total");
2910 if let Expr::BinaryOp { op, left, right } = &expr {
2912 assert_eq!(*op, ArithmeticOp::Add);
2913 assert!(matches!(left.as_ref(), Expr::Field(f) if f == "price"));
2914 assert!(matches!(
2915 right.as_ref(),
2916 Expr::BinaryOp {
2917 op: ArithmeticOp::Mul,
2918 ..
2919 }
2920 ));
2921 } else {
2922 panic!("expected BinaryOp");
2923 }
2924 }
2925
2926 #[test]
2927 fn test_add_field_with_parens() {
2928 let (name, expr) = parse_add_field_expr("total = (price + tax) * quantity").unwrap();
2929 assert_eq!(name, "total");
2930 if let Expr::BinaryOp { op, left, right } = &expr {
2931 assert_eq!(*op, ArithmeticOp::Mul);
2932 assert!(matches!(
2933 left.as_ref(),
2934 Expr::BinaryOp {
2935 op: ArithmeticOp::Add,
2936 ..
2937 }
2938 ));
2939 assert!(matches!(right.as_ref(), Expr::Field(f) if f == "quantity"));
2940 } else {
2941 panic!("expected BinaryOp");
2942 }
2943 }
2944
2945 #[test]
2946 fn test_add_field_with_function() {
2947 let (name, expr) = parse_add_field_expr("name_upper = upper(name)").unwrap();
2948 assert_eq!(name, "name_upper");
2949 assert!(matches!(expr, Expr::FuncCall { .. }));
2950 }
2951
2952 #[test]
2953 fn test_add_field_missing_equals() {
2954 let result = parse_add_field_expr("total amount * quantity");
2955 assert!(result.is_err());
2956 }
2957
2958 #[test]
2961 fn test_expr_division() {
2962 let q = parse_query(".items[] | select total / count as avg").unwrap();
2963 assert_eq!(q.operations.len(), 1);
2964 }
2965
2966 #[test]
2967 fn test_expr_subtraction() {
2968 let q = parse_query(".items[] | select price - discount as net").unwrap();
2969 assert_eq!(q.operations.len(), 1);
2970 }
2971
2972 #[test]
2975 fn test_recursive_descent_root() {
2976 let q = parse_query("..name").unwrap();
2977 assert_eq!(
2978 q.path.segments,
2979 vec![Segment::RecursiveDescent("name".to_string())]
2980 );
2981 assert!(q.operations.is_empty());
2982 }
2983
2984 #[test]
2985 fn test_recursive_descent_after_field() {
2986 let q = parse_query(".config..host").unwrap();
2987 assert_eq!(
2988 q.path.segments,
2989 vec![
2990 Segment::Field("config".to_string()),
2991 Segment::RecursiveDescent("host".to_string()),
2992 ]
2993 );
2994 }
2995
2996 #[test]
2997 fn test_recursive_descent_with_pipeline() {
2998 let q = parse_query("..name | where name == \"Alice\"").unwrap();
2999 assert_eq!(
3000 q.path.segments,
3001 vec![Segment::RecursiveDescent("name".to_string())]
3002 );
3003 assert_eq!(q.operations.len(), 1);
3004 }
3005
3006 #[test]
3009 fn test_if_expr_simple() {
3010 let q = parse_query(".items[] | select if(age < 18, \"minor\", \"adult\") as category")
3011 .unwrap();
3012 assert_eq!(q.operations.len(), 1);
3013 if let Operation::Select(exprs) = &q.operations[0] {
3014 assert_eq!(exprs.len(), 1);
3015 assert_eq!(exprs[0].alias, Some("category".to_string()));
3016 assert!(matches!(&exprs[0].expr, Expr::If { .. }));
3017 } else {
3018 panic!("expected Select operation");
3019 }
3020 }
3021
3022 #[test]
3023 fn test_if_expr_nested() {
3024 let q = parse_query(
3025 ".[] | select if(age < 18, \"minor\", if(age < 65, \"adult\", \"senior\")) as cat",
3026 )
3027 .unwrap();
3028 if let Operation::Select(exprs) = &q.operations[0] {
3029 if let Expr::If { else_expr, .. } = &exprs[0].expr {
3030 assert!(matches!(else_expr.as_ref(), Expr::If { .. }));
3031 } else {
3032 panic!("expected If expression");
3033 }
3034 } else {
3035 panic!("expected Select operation");
3036 }
3037 }
3038
3039 #[test]
3040 fn test_if_expr_with_and_condition() {
3041 let q = parse_query(".[] | select if(age > 18 and age < 65, \"adult\", \"other\") as cat")
3042 .unwrap();
3043 if let Operation::Select(exprs) = &q.operations[0] {
3044 if let Expr::If { condition, .. } = &exprs[0].expr {
3045 assert!(matches!(condition, Condition::And(_, _)));
3046 } else {
3047 panic!("expected If expression");
3048 }
3049 } else {
3050 panic!("expected Select operation");
3051 }
3052 }
3053
3054 #[test]
3055 fn test_if_expr_missing_paren() {
3056 let result = parse_query(".[] | select if age < 18, \"minor\", \"adult\"");
3057 assert!(result.is_err());
3058 }
3059
3060 #[test]
3063 fn test_case_simple() {
3064 let q = parse_query(
3065 ".[] | select case when status == \"active\" then \"A\" else \"I\" end as code",
3066 )
3067 .unwrap();
3068 if let Operation::Select(exprs) = &q.operations[0] {
3069 if let Expr::Case { branches, default } = &exprs[0].expr {
3070 assert_eq!(branches.len(), 1);
3071 assert!(default.is_some());
3072 } else {
3073 panic!("expected Case expression");
3074 }
3075 } else {
3076 panic!("expected Select operation");
3077 }
3078 }
3079
3080 #[test]
3081 fn test_case_multiple_when() {
3082 let q = parse_query(
3083 ".[] | select case when age < 18 then \"minor\" when age < 65 then \"adult\" else \"senior\" end as category",
3084 )
3085 .unwrap();
3086 if let Operation::Select(exprs) = &q.operations[0] {
3087 if let Expr::Case { branches, default } = &exprs[0].expr {
3088 assert_eq!(branches.len(), 2);
3089 assert!(default.is_some());
3090 } else {
3091 panic!("expected Case expression");
3092 }
3093 } else {
3094 panic!("expected Select operation");
3095 }
3096 }
3097
3098 #[test]
3099 fn test_case_no_else() {
3100 let q =
3101 parse_query(".[] | select case when status == \"active\" then \"yes\" end as active")
3102 .unwrap();
3103 if let Operation::Select(exprs) = &q.operations[0] {
3104 if let Expr::Case { branches, default } = &exprs[0].expr {
3105 assert_eq!(branches.len(), 1);
3106 assert!(default.is_none());
3107 } else {
3108 panic!("expected Case expression");
3109 }
3110 } else {
3111 panic!("expected Select operation");
3112 }
3113 }
3114
3115 #[test]
3116 fn test_case_no_when_fails() {
3117 let result = parse_query(".[] | select case else \"x\" end as y");
3118 assert!(result.is_err());
3119 }
3120
3121 #[test]
3124 fn test_add_field_with_if() {
3125 let (name, expr) =
3126 parse_add_field_expr("tier = if(revenue > 10000, \"gold\", \"silver\")").unwrap();
3127 assert_eq!(name, "tier");
3128 assert!(matches!(expr, Expr::If { .. }));
3129 }
3130
3131 #[test]
3132 fn test_add_field_with_case() {
3133 let (name, expr) = parse_add_field_expr(
3134 "tier = case when revenue > 10000 then \"gold\" when revenue > 5000 then \"silver\" else \"bronze\" end",
3135 )
3136 .unwrap();
3137 assert_eq!(name, "tier");
3138 if let Expr::Case { branches, default } = expr {
3139 assert_eq!(branches.len(), 2);
3140 assert!(default.is_some());
3141 } else {
3142 panic!("expected Case expression");
3143 }
3144 }
3145
3146 #[test]
3149 fn test_parse_median() {
3150 let q = parse_query(".[] | median score").unwrap();
3151 assert!(matches!(&q.operations[0], Operation::Median { field } if field == "score"));
3152 }
3153
3154 #[test]
3155 fn test_parse_percentile() {
3156 let q = parse_query(".[] | percentile score 0.95").unwrap();
3157 if let Operation::Percentile { field, p } = &q.operations[0] {
3158 assert_eq!(field, "score");
3159 assert!((p - 0.95).abs() < f64::EPSILON);
3160 } else {
3161 panic!("expected Percentile operation");
3162 }
3163 }
3164
3165 #[test]
3166 fn test_parse_percentile_invalid_p() {
3167 let result = parse_query(".[] | percentile score 1.5");
3168 assert!(result.is_err());
3169 }
3170
3171 #[test]
3172 fn test_parse_stddev() {
3173 let q = parse_query(".[] | stddev salary").unwrap();
3174 assert!(matches!(&q.operations[0], Operation::Stddev { field } if field == "salary"));
3175 }
3176
3177 #[test]
3178 fn test_parse_variance() {
3179 let q = parse_query(".[] | variance salary").unwrap();
3180 assert!(matches!(&q.operations[0], Operation::Variance { field } if field == "salary"));
3181 }
3182
3183 #[test]
3184 fn test_parse_mode() {
3185 let q = parse_query(".[] | mode category").unwrap();
3186 assert!(matches!(&q.operations[0], Operation::Mode { field } if field == "category"));
3187 }
3188
3189 #[test]
3190 fn test_parse_group_concat() {
3191 let q = parse_query(".[] | group_concat name \", \"").unwrap();
3192 if let Operation::GroupConcat { field, separator } = &q.operations[0] {
3193 assert_eq!(field, "name");
3194 assert_eq!(separator, ", ");
3195 } else {
3196 panic!("expected GroupConcat operation");
3197 }
3198 }
3199
3200 #[test]
3201 fn test_parse_group_concat_default_separator() {
3202 let q = parse_query(".[] | group_concat name").unwrap();
3203 if let Operation::GroupConcat { field, separator } = &q.operations[0] {
3204 assert_eq!(field, "name");
3205 assert_eq!(separator, ", ");
3206 } else {
3207 panic!("expected GroupConcat operation");
3208 }
3209 }
3210
3211 #[test]
3212 fn test_parse_group_by_with_median() {
3213 let q = parse_query(".[] | group_by dept median(score)").unwrap();
3214 if let Operation::GroupBy {
3215 fields, aggregates, ..
3216 } = &q.operations[0]
3217 {
3218 assert_eq!(fields, &["dept"]);
3219 assert_eq!(aggregates.len(), 1);
3220 assert!(matches!(aggregates[0].func, AggregateFunc::Median));
3221 assert_eq!(aggregates[0].field, Some("score".to_string()));
3222 assert_eq!(aggregates[0].alias, "median_score");
3223 } else {
3224 panic!("expected GroupBy operation");
3225 }
3226 }
3227
3228 #[test]
3229 fn test_parse_group_by_with_percentile() {
3230 let q = parse_query(".[] | group_by dept percentile(score, 0.95)").unwrap();
3231 if let Operation::GroupBy { aggregates, .. } = &q.operations[0] {
3232 assert_eq!(aggregates.len(), 1);
3233 if let AggregateFunc::Percentile(p) = aggregates[0].func {
3234 assert!((p - 0.95).abs() < f64::EPSILON);
3235 } else {
3236 panic!("expected Percentile aggregate");
3237 }
3238 } else {
3239 panic!("expected GroupBy operation");
3240 }
3241 }
3242
3243 #[test]
3244 fn test_parse_group_by_with_group_concat() {
3245 let q = parse_query(".[] | group_by dept group_concat(name, \", \")").unwrap();
3246 if let Operation::GroupBy { aggregates, .. } = &q.operations[0] {
3247 assert_eq!(aggregates.len(), 1);
3248 if let AggregateFunc::GroupConcat(sep) = &aggregates[0].func {
3249 assert_eq!(sep, ", ");
3250 } else {
3251 panic!("expected GroupConcat aggregate");
3252 }
3253 } else {
3254 panic!("expected GroupBy operation");
3255 }
3256 }
3257
3258 #[test]
3259 fn test_parse_group_by_mixed_aggregates() {
3260 let q = parse_query(".[] | group_by dept count(), median(score), stddev(score)").unwrap();
3261 if let Operation::GroupBy { aggregates, .. } = &q.operations[0] {
3262 assert_eq!(aggregates.len(), 3);
3263 assert!(matches!(aggregates[0].func, AggregateFunc::Count));
3264 assert!(matches!(aggregates[1].func, AggregateFunc::Median));
3265 assert!(matches!(aggregates[2].func, AggregateFunc::Stddev));
3266 } else {
3267 panic!("expected GroupBy operation");
3268 }
3269 }
3270
3271 #[test]
3274 fn test_parse_row_number_over() {
3275 let q =
3276 parse_query(".[] | select row_number() over (order by score desc) as rank").unwrap();
3277 if let Operation::Select(exprs) = &q.operations[0] {
3278 assert_eq!(exprs.len(), 1);
3279 assert_eq!(exprs[0].alias, Some("rank".to_string()));
3280 if let Expr::Window { func, over } = &exprs[0].expr {
3281 assert!(matches!(func, WindowFunc::RowNumber));
3282 assert!(over.partition_by.is_empty());
3283 assert_eq!(over.order_by.len(), 1);
3284 assert_eq!(over.order_by[0].field, "score");
3285 assert!(over.order_by[0].descending);
3286 } else {
3287 panic!("expected Window expression");
3288 }
3289 } else {
3290 panic!("expected Select operation");
3291 }
3292 }
3293
3294 #[test]
3295 fn test_parse_rank_with_partition() {
3296 let q = parse_query(
3297 ".[] | select name, rank() over (partition by dept order by score desc) as dept_rank",
3298 )
3299 .unwrap();
3300 if let Operation::Select(exprs) = &q.operations[0] {
3301 assert_eq!(exprs.len(), 2);
3302 if let Expr::Window { func, over } = &exprs[1].expr {
3303 assert!(matches!(func, WindowFunc::Rank));
3304 assert_eq!(over.partition_by, vec!["dept"]);
3305 assert_eq!(over.order_by[0].field, "score");
3306 assert!(over.order_by[0].descending);
3307 } else {
3308 panic!("expected Window expression");
3309 }
3310 } else {
3311 panic!("expected Select operation");
3312 }
3313 }
3314
3315 #[test]
3316 fn test_parse_dense_rank() {
3317 let q = parse_query(".[] | select dense_rank() over (order by score) as drank").unwrap();
3318 if let Operation::Select(exprs) = &q.operations[0] {
3319 if let Expr::Window { func, over } = &exprs[0].expr {
3320 assert!(matches!(func, WindowFunc::DenseRank));
3321 assert!(!over.order_by[0].descending);
3322 } else {
3323 panic!("expected Window expression");
3324 }
3325 } else {
3326 panic!("expected Select operation");
3327 }
3328 }
3329
3330 #[test]
3331 fn test_parse_lag_default_offset() {
3332 let q = parse_query(".[] | select lag(value) over (order by date) as prev_value").unwrap();
3333 if let Operation::Select(exprs) = &q.operations[0] {
3334 if let Expr::Window { func, .. } = &exprs[0].expr {
3335 if let WindowFunc::Lag { expr, offset } = func {
3336 assert!(matches!(expr.as_ref(), Expr::Field(f) if f == "value"));
3337 assert_eq!(*offset, 1);
3338 } else {
3339 panic!("expected Lag");
3340 }
3341 } else {
3342 panic!("expected Window expression");
3343 }
3344 } else {
3345 panic!("expected Select operation");
3346 }
3347 }
3348
3349 #[test]
3350 fn test_parse_lead_with_offset() {
3351 let q =
3352 parse_query(".[] | select lead(value, 2) over (order by date) as next2_value").unwrap();
3353 if let Operation::Select(exprs) = &q.operations[0] {
3354 if let Expr::Window { func, .. } = &exprs[0].expr {
3355 if let WindowFunc::Lead { offset, .. } = func {
3356 assert_eq!(*offset, 2);
3357 } else {
3358 panic!("expected Lead");
3359 }
3360 } else {
3361 panic!("expected Window expression");
3362 }
3363 } else {
3364 panic!("expected Select operation");
3365 }
3366 }
3367
3368 #[test]
3369 fn test_parse_first_value() {
3370 let q =
3371 parse_query(".[] | select first_value(name) over (order by score desc) as top_name")
3372 .unwrap();
3373 if let Operation::Select(exprs) = &q.operations[0] {
3374 if let Expr::Window { func, .. } = &exprs[0].expr {
3375 assert!(matches!(func, WindowFunc::FirstValue { .. }));
3376 } else {
3377 panic!("expected Window expression");
3378 }
3379 } else {
3380 panic!("expected Select operation");
3381 }
3382 }
3383
3384 #[test]
3385 fn test_parse_last_value() {
3386 let q = parse_query(".[] | select last_value(name) over (order by score) as last_name")
3387 .unwrap();
3388 if let Operation::Select(exprs) = &q.operations[0] {
3389 if let Expr::Window { func, .. } = &exprs[0].expr {
3390 assert!(matches!(func, WindowFunc::LastValue { .. }));
3391 } else {
3392 panic!("expected Window expression");
3393 }
3394 } else {
3395 panic!("expected Select operation");
3396 }
3397 }
3398
3399 #[test]
3400 fn test_parse_window_aggregate_sum() {
3401 let q =
3402 parse_query(".[] | select sum(amount) over (order by date) as running_total").unwrap();
3403 if let Operation::Select(exprs) = &q.operations[0] {
3404 if let Expr::Window { func, .. } = &exprs[0].expr {
3405 assert!(matches!(
3406 func,
3407 WindowFunc::Aggregate {
3408 func: AggregateFunc::Sum,
3409 ..
3410 }
3411 ));
3412 } else {
3413 panic!("expected Window expression");
3414 }
3415 } else {
3416 panic!("expected Select operation");
3417 }
3418 }
3419
3420 #[test]
3421 fn test_parse_window_empty_over() {
3422 let q = parse_query(".[] | select row_number() over () as rn").unwrap();
3423 if let Operation::Select(exprs) = &q.operations[0] {
3424 if let Expr::Window { func, over } = &exprs[0].expr {
3425 assert!(matches!(func, WindowFunc::RowNumber));
3426 assert!(over.partition_by.is_empty());
3427 assert!(over.order_by.is_empty());
3428 } else {
3429 panic!("expected Window expression");
3430 }
3431 } else {
3432 panic!("expected Select operation");
3433 }
3434 }
3435
3436 #[test]
3437 fn test_parse_window_partition_only() {
3438 let q = parse_query(".[] | select count(score) over (partition by dept) as dept_count")
3439 .unwrap();
3440 if let Operation::Select(exprs) = &q.operations[0] {
3441 if let Expr::Window { over, .. } = &exprs[0].expr {
3442 assert_eq!(over.partition_by, vec!["dept"]);
3443 assert!(over.order_by.is_empty());
3444 } else {
3445 panic!("expected Window expression");
3446 }
3447 } else {
3448 panic!("expected Select operation");
3449 }
3450 }
3451
3452 #[test]
3453 fn test_parse_window_multiple_order_by() {
3454 let q =
3455 parse_query(".[] | select rank() over (order by dept asc, score desc) as r").unwrap();
3456 if let Operation::Select(exprs) = &q.operations[0] {
3457 if let Expr::Window { over, .. } = &exprs[0].expr {
3458 assert_eq!(over.order_by.len(), 2);
3459 assert_eq!(over.order_by[0].field, "dept");
3460 assert!(!over.order_by[0].descending);
3461 assert_eq!(over.order_by[1].field, "score");
3462 assert!(over.order_by[1].descending);
3463 } else {
3464 panic!("expected Window expression");
3465 }
3466 } else {
3467 panic!("expected Select operation");
3468 }
3469 }
3470}