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}
37
38#[derive(Debug, Clone, PartialEq)]
40#[non_exhaustive]
41pub enum Operation {
42 Where(Condition),
44 Select(Vec<SelectExpr>),
46 Sort { field: String, descending: bool },
48 Limit(usize),
50 Count { field: Option<String> },
52 Sum { field: String },
54 Avg { field: String },
56 Min { field: String },
58 Max { field: String },
60 Distinct { field: String },
62 GroupBy {
65 fields: Vec<String>,
66 having: Option<Condition>,
67 aggregates: Vec<GroupAggregate>,
68 },
69 Unique,
71 UniqueBy { field: String },
73 AddField { name: String, expr: Expr },
75 MapField { name: String, expr: Expr },
77}
78
79#[derive(Debug, Clone, PartialEq)]
81pub struct GroupAggregate {
82 pub func: AggregateFunc,
83 pub field: Option<String>,
84 pub alias: String,
85}
86
87#[derive(Debug, Clone, PartialEq)]
89#[non_exhaustive]
90pub enum AggregateFunc {
91 Count,
92 Sum,
93 Avg,
94 Min,
95 Max,
96}
97
98#[derive(Debug, Clone, PartialEq)]
100#[non_exhaustive]
101pub enum Condition {
102 Comparison(Comparison),
104 And(Box<Condition>, Box<Condition>),
106 Or(Box<Condition>, Box<Condition>),
108}
109
110#[derive(Debug, Clone, PartialEq)]
112pub struct Comparison {
113 pub field: String,
114 pub op: CompareOp,
115 pub value: LiteralValue,
116}
117
118#[derive(Debug, Clone, PartialEq)]
120#[non_exhaustive]
121pub enum CompareOp {
122 Eq, Ne, Gt, Lt, Ge, Le, Contains, StartsWith, EndsWith, In, NotIn, Matches, NotMatches, }
136
137#[derive(Debug, Clone, PartialEq)]
139#[non_exhaustive]
140pub enum LiteralValue {
141 String(String),
142 Integer(i64),
143 Float(f64),
144 Bool(bool),
145 Null,
146 List(Vec<LiteralValue>),
147}
148
149#[derive(Debug, Clone, PartialEq)]
151pub enum ArithmeticOp {
152 Add, Sub, Mul, Div, }
157
158#[derive(Debug, Clone, PartialEq)]
160#[non_exhaustive]
161pub enum Expr {
162 Field(String),
164 Literal(LiteralValue),
166 FuncCall { name: String, args: Vec<Expr> },
168 BinaryOp {
170 op: ArithmeticOp,
171 left: Box<Expr>,
172 right: Box<Expr>,
173 },
174}
175
176#[derive(Debug, Clone, PartialEq)]
178pub struct SelectExpr {
179 pub expr: Expr,
180 pub alias: Option<String>,
182}
183
184pub(crate) struct Parser {
188 input: Vec<char>,
189 pos: usize,
190}
191
192impl Parser {
193 pub(crate) fn new(input: &str) -> Self {
194 Self {
195 input: input.chars().collect(),
196 pos: 0,
197 }
198 }
199
200 pub(crate) fn parse(&mut self) -> Result<Query, DkitError> {
202 self.skip_whitespace();
203 let path = self.parse_path()?;
204 self.skip_whitespace();
205
206 let mut operations = Vec::new();
208 while self.peek() == Some('|') {
209 self.advance(); self.skip_whitespace();
211 operations.push(self.parse_operation()?);
212 self.skip_whitespace();
213 }
214
215 if self.pos != self.input.len() {
216 return Err(DkitError::QueryError(format!(
217 "unexpected character '{}' at position {}",
218 self.input[self.pos], self.pos
219 )));
220 }
221
222 Ok(Query { path, operations })
223 }
224
225 fn parse_path(&mut self) -> Result<Path, DkitError> {
227 if !self.consume_char('.') {
228 return Err(DkitError::QueryError(
229 "query must start with '.'".to_string(),
230 ));
231 }
232
233 let mut segments = Vec::new();
234
235 if self.is_at_end() {
237 return Ok(Path { segments });
238 }
239
240 if self.peek() == Some('[') {
242 segments.push(self.parse_bracket()?);
243 } else if self.peek_is_identifier_start() {
244 segments.push(self.parse_field()?);
245 }
246
247 while !self.is_at_end() {
249 self.skip_whitespace();
250 if self.peek() == Some('.') {
251 self.advance(); if self.peek() == Some('[') {
253 segments.push(self.parse_bracket()?);
254 } else {
255 segments.push(self.parse_field()?);
256 }
257 } else if self.peek() == Some('[') {
258 segments.push(self.parse_bracket()?);
259 } else {
260 break;
261 }
262 }
263
264 Ok(Path { segments })
265 }
266
267 fn parse_field(&mut self) -> Result<Segment, DkitError> {
269 let start = self.pos;
270 while !self.is_at_end() {
271 let c = self.input[self.pos];
272 if c.is_alphanumeric() || c == '_' || c == '-' {
273 self.pos += 1;
274 } else {
275 break;
276 }
277 }
278
279 if self.pos == start {
280 return Err(DkitError::QueryError(format!(
281 "expected field name at position {}",
282 self.pos
283 )));
284 }
285
286 let name: String = self.input[start..self.pos].iter().collect();
287 Ok(Segment::Field(name))
288 }
289
290 fn parse_bracket(&mut self) -> Result<Segment, DkitError> {
292 if !self.consume_char('[') {
293 return Err(DkitError::QueryError(format!(
294 "expected '[' at position {}",
295 self.pos
296 )));
297 }
298
299 self.skip_whitespace();
300
301 if self.peek() == Some(']') {
303 self.advance();
304 return Ok(Segment::Iterate);
305 }
306
307 if self.peek() == Some('*') {
309 self.advance();
310 self.skip_whitespace();
311 if !self.consume_char(']') {
312 return Err(DkitError::QueryError(format!(
313 "expected ']' after '*' at position {}",
314 self.pos
315 )));
316 }
317 return Ok(Segment::Wildcard);
318 }
319
320 if self.peek() == Some(':') {
322 return self.parse_slice(None);
323 }
324
325 let negative = self.consume_char('-');
327 let start = self.pos;
328 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
329 self.pos += 1;
330 }
331 if self.pos == start {
332 return Err(DkitError::QueryError(format!(
333 "expected integer index at position {}",
334 self.pos
335 )));
336 }
337
338 let num_str: String = self.input[start..self.pos].iter().collect();
339 let num: i64 = num_str.parse().map_err(|_| {
340 DkitError::QueryError(format!("invalid index '{}' at position {}", num_str, start))
341 })?;
342 let num = if negative { -num } else { num };
343
344 self.skip_whitespace();
345
346 if self.peek() == Some(':') {
348 return self.parse_slice(Some(num));
349 }
350
351 if !self.consume_char(']') {
353 return Err(DkitError::QueryError(format!(
354 "expected ']' or ':' at position {}",
355 self.pos
356 )));
357 }
358
359 Ok(Segment::Index(num))
360 }
361
362 fn parse_slice(&mut self, start: Option<i64>) -> Result<Segment, DkitError> {
364 if !self.consume_char(':') {
366 return Err(DkitError::QueryError(format!(
367 "expected ':' at position {}",
368 self.pos
369 )));
370 }
371
372 self.skip_whitespace();
373
374 let end = if self.peek() == Some(']') || self.peek() == Some(':') {
376 None
377 } else {
378 Some(self.parse_signed_integer()?)
379 };
380
381 self.skip_whitespace();
382
383 let step = if self.peek() == Some(':') {
385 self.advance();
386 self.skip_whitespace();
387 if self.peek() == Some(']') {
388 None
389 } else {
390 Some(self.parse_signed_integer()?)
391 }
392 } else {
393 None
394 };
395
396 self.skip_whitespace();
397 if !self.consume_char(']') {
398 return Err(DkitError::QueryError(format!(
399 "expected ']' at position {}",
400 self.pos
401 )));
402 }
403
404 Ok(Segment::Slice { start, end, step })
405 }
406
407 fn parse_signed_integer(&mut self) -> Result<i64, DkitError> {
409 let negative = self.consume_char('-');
410 let start = self.pos;
411 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
412 self.pos += 1;
413 }
414 if self.pos == start {
415 return Err(DkitError::QueryError(format!(
416 "expected integer at position {}",
417 self.pos
418 )));
419 }
420 let num_str: String = self.input[start..self.pos].iter().collect();
421 let num: i64 = num_str.parse().map_err(|_| {
422 DkitError::QueryError(format!(
423 "invalid integer '{}' at position {}",
424 num_str, start
425 ))
426 })?;
427 Ok(if negative { -num } else { num })
428 }
429
430 fn parse_operation(&mut self) -> Result<Operation, DkitError> {
434 let keyword = self.parse_keyword()?;
435 match keyword.as_str() {
436 "where" => {
437 self.skip_whitespace();
438 let condition = self.parse_condition()?;
439 Ok(Operation::Where(condition))
440 }
441 "select" => {
442 self.skip_whitespace();
443 let exprs = self.parse_select_expr_list()?;
444 Ok(Operation::Select(exprs))
445 }
446 "sort" => {
447 self.skip_whitespace();
448 let field = self.parse_identifier()?;
449 self.skip_whitespace();
450 let descending = self.try_consume_keyword("desc");
451 Ok(Operation::Sort { field, descending })
452 }
453 "limit" => {
454 self.skip_whitespace();
455 let n = self.parse_positive_integer()?;
456 Ok(Operation::Limit(n))
457 }
458 "count" => {
459 self.skip_whitespace();
460 let field = self.try_parse_identifier();
461 Ok(Operation::Count { field })
462 }
463 "sum" => {
464 self.skip_whitespace();
465 let field = self.parse_identifier()?;
466 Ok(Operation::Sum { field })
467 }
468 "avg" => {
469 self.skip_whitespace();
470 let field = self.parse_identifier()?;
471 Ok(Operation::Avg { field })
472 }
473 "min" => {
474 self.skip_whitespace();
475 let field = self.parse_identifier()?;
476 Ok(Operation::Min { field })
477 }
478 "max" => {
479 self.skip_whitespace();
480 let field = self.parse_identifier()?;
481 Ok(Operation::Max { field })
482 }
483 "distinct" => {
484 self.skip_whitespace();
485 let field = self.parse_identifier()?;
486 Ok(Operation::Distinct { field })
487 }
488 "group_by" => {
489 self.skip_whitespace();
490 let fields = self.parse_identifier_list()?;
491 self.skip_whitespace();
492
493 let aggregates = self.parse_group_aggregates()?;
495
496 let having = if self.try_consume_keyword("having") {
498 self.skip_whitespace();
499 Some(self.parse_condition()?)
500 } else {
501 None
502 };
503
504 Ok(Operation::GroupBy {
505 fields,
506 having,
507 aggregates,
508 })
509 }
510 _ => Err(DkitError::QueryError(format!(
511 "unknown operation '{}' at position {}",
512 keyword,
513 self.pos - keyword.chars().count()
514 ))),
515 }
516 }
517
518 fn parse_group_aggregates(&mut self) -> Result<Vec<GroupAggregate>, DkitError> {
520 let mut aggregates = Vec::new();
521
522 loop {
523 let saved_pos = self.pos;
524 if let Some(agg) = self.try_parse_single_aggregate()? {
525 aggregates.push(agg);
526 self.skip_whitespace();
527 if !self.consume_char(',') {
528 break;
530 }
531 self.skip_whitespace();
532 } else {
533 self.pos = saved_pos;
534 break;
535 }
536 }
537
538 Ok(aggregates)
539 }
540
541 fn try_parse_single_aggregate(&mut self) -> Result<Option<GroupAggregate>, DkitError> {
543 let saved_pos = self.pos;
544
545 let func_name = match self.parse_keyword() {
547 Ok(name) => name,
548 Err(_) => {
549 self.pos = saved_pos;
550 return Ok(None);
551 }
552 };
553
554 let func = match func_name.as_str() {
555 "count" => AggregateFunc::Count,
556 "sum" => AggregateFunc::Sum,
557 "avg" => AggregateFunc::Avg,
558 "min" => AggregateFunc::Min,
559 "max" => AggregateFunc::Max,
560 _ => {
561 self.pos = saved_pos;
563 return Ok(None);
564 }
565 };
566
567 self.skip_whitespace();
568
569 if !self.consume_char('(') {
571 self.pos = saved_pos;
572 return Ok(None);
573 }
574
575 self.skip_whitespace();
576
577 let field = if self.peek() == Some(')') {
579 None
580 } else {
581 Some(self.parse_identifier()?)
582 };
583
584 self.skip_whitespace();
585
586 if !self.consume_char(')') {
587 return Err(DkitError::QueryError(format!(
588 "expected ')' at position {}",
589 self.pos
590 )));
591 }
592
593 let alias = match &field {
595 Some(f) => format!("{}_{}", func_name, f),
596 None => func_name.clone(),
597 };
598
599 Ok(Some(GroupAggregate { func, field, alias }))
600 }
601
602 fn parse_select_expr_list(&mut self) -> Result<Vec<SelectExpr>, DkitError> {
604 let mut exprs = vec![self.parse_select_expr()?];
605 loop {
606 self.skip_whitespace();
607 if self.consume_char(',') {
608 self.skip_whitespace();
609 exprs.push(self.parse_select_expr()?);
610 } else {
611 break;
612 }
613 }
614 Ok(exprs)
615 }
616
617 fn parse_select_expr(&mut self) -> Result<SelectExpr, DkitError> {
619 let expr = self.parse_expr()?;
620 self.skip_whitespace();
621 let alias = {
623 let saved = self.pos;
624 if let Ok(keyword) = self.parse_keyword() {
625 if keyword == "as" {
626 self.skip_whitespace();
627 Some(self.parse_identifier()?)
628 } else {
629 self.pos = saved;
630 None
631 }
632 } else {
633 self.pos = saved;
634 None
635 }
636 };
637 Ok(SelectExpr { expr, alias })
638 }
639
640 fn parse_expr(&mut self) -> Result<Expr, DkitError> {
642 self.parse_additive_expr()
643 }
644
645 fn parse_additive_expr(&mut self) -> Result<Expr, DkitError> {
647 let mut left = self.parse_multiplicative_expr()?;
648
649 loop {
650 self.skip_whitespace();
651 match self.peek() {
652 Some('+') => {
653 self.advance();
654 self.skip_whitespace();
655 let right = self.parse_multiplicative_expr()?;
656 left = Expr::BinaryOp {
657 op: ArithmeticOp::Add,
658 left: Box::new(left),
659 right: Box::new(right),
660 };
661 }
662 Some('-') => {
663 self.advance();
666 self.skip_whitespace();
667 let right = self.parse_multiplicative_expr()?;
668 left = Expr::BinaryOp {
669 op: ArithmeticOp::Sub,
670 left: Box::new(left),
671 right: Box::new(right),
672 };
673 }
674 _ => break,
675 }
676 }
677
678 Ok(left)
679 }
680
681 fn parse_multiplicative_expr(&mut self) -> Result<Expr, DkitError> {
683 let mut left = self.parse_atom_expr()?;
684
685 loop {
686 self.skip_whitespace();
687 match self.peek() {
688 Some('*') => {
689 self.advance();
690 self.skip_whitespace();
691 let right = self.parse_atom_expr()?;
692 left = Expr::BinaryOp {
693 op: ArithmeticOp::Mul,
694 left: Box::new(left),
695 right: Box::new(right),
696 };
697 }
698 Some('/') => {
699 self.advance();
700 self.skip_whitespace();
701 let right = self.parse_atom_expr()?;
702 left = Expr::BinaryOp {
703 op: ArithmeticOp::Div,
704 left: Box::new(left),
705 right: Box::new(right),
706 };
707 }
708 _ => break,
709 }
710 }
711
712 Ok(left)
713 }
714
715 fn parse_atom_expr(&mut self) -> Result<Expr, DkitError> {
717 match self.peek() {
718 Some('(') => {
719 self.advance(); self.skip_whitespace();
721 let expr = self.parse_expr()?;
722 self.skip_whitespace();
723 if !self.consume_char(')') {
724 return Err(DkitError::QueryError(format!(
725 "expected ')' at position {}",
726 self.pos
727 )));
728 }
729 Ok(expr)
730 }
731 Some('"') => {
732 let lit = self.parse_string_literal()?;
733 Ok(Expr::Literal(lit))
734 }
735 Some(c) if c.is_ascii_digit() => {
736 let lit = self.parse_number_literal()?;
737 Ok(Expr::Literal(lit))
738 }
739 Some(c) if c.is_alphabetic() || c == '_' => {
740 let name = self.parse_identifier()?;
741 match name.as_str() {
743 "true" => return Ok(Expr::Literal(LiteralValue::Bool(true))),
744 "false" => return Ok(Expr::Literal(LiteralValue::Bool(false))),
745 "null" => return Ok(Expr::Literal(LiteralValue::Null)),
746 _ => {}
747 }
748 if self.peek() == Some('(') {
750 self.advance(); self.skip_whitespace();
752 let mut args = Vec::new();
753 if self.peek() != Some(')') {
754 args.push(self.parse_expr()?);
755 loop {
756 self.skip_whitespace();
757 if self.consume_char(',') {
758 self.skip_whitespace();
759 args.push(self.parse_expr()?);
760 } else {
761 break;
762 }
763 }
764 }
765 self.skip_whitespace();
766 if !self.consume_char(')') {
767 return Err(DkitError::QueryError(format!(
768 "expected ')' at position {}",
769 self.pos
770 )));
771 }
772 Ok(Expr::FuncCall { name, args })
773 } else {
774 Ok(Expr::Field(name))
775 }
776 }
777 Some(c) => Err(DkitError::QueryError(format!(
778 "expected expression at position {}, found '{}'",
779 self.pos, c
780 ))),
781 None => Err(DkitError::QueryError(format!(
782 "expected expression at position {}",
783 self.pos
784 ))),
785 }
786 }
787
788 fn parse_identifier_list(&mut self) -> Result<Vec<String>, DkitError> {
790 let mut fields = vec![self.parse_identifier()?];
791 loop {
792 self.skip_whitespace();
793 if self.consume_char(',') {
794 self.skip_whitespace();
795 fields.push(self.parse_identifier()?);
796 } else {
797 break;
798 }
799 }
800 Ok(fields)
801 }
802
803 fn parse_keyword(&mut self) -> Result<String, DkitError> {
805 let start = self.pos;
806 while !self.is_at_end() {
807 let c = self.input[self.pos];
808 if c.is_alphabetic() || c == '_' {
809 self.pos += 1;
810 } else {
811 break;
812 }
813 }
814 if self.pos == start {
815 return Err(DkitError::QueryError(format!(
816 "expected operation keyword at position {}",
817 self.pos
818 )));
819 }
820 Ok(self.input[start..self.pos].iter().collect())
821 }
822
823 fn parse_condition(&mut self) -> Result<Condition, DkitError> {
825 let mut left = Condition::Comparison(self.parse_comparison()?);
826
827 loop {
828 self.skip_whitespace();
829 let saved_pos = self.pos;
830 if let Ok(keyword) = self.parse_keyword() {
831 match keyword.as_str() {
832 "and" => {
833 self.skip_whitespace();
834 let right = Condition::Comparison(self.parse_comparison()?);
835 left = Condition::And(Box::new(left), Box::new(right));
836 }
837 "or" => {
838 self.skip_whitespace();
839 let right = Condition::Comparison(self.parse_comparison()?);
840 left = Condition::Or(Box::new(left), Box::new(right));
841 }
842 _ => {
843 self.pos = saved_pos;
845 break;
846 }
847 }
848 } else {
849 break;
850 }
851 }
852
853 Ok(left)
854 }
855
856 fn parse_comparison(&mut self) -> Result<Comparison, DkitError> {
859 let field = self.parse_identifier()?;
861 self.skip_whitespace();
862
863 let saved_pos = self.pos;
865 if let Ok(keyword) = self.parse_keyword() {
866 match keyword.as_str() {
867 "in" => {
868 self.skip_whitespace();
869 let list = self.parse_literal_list()?;
870 return Ok(Comparison {
871 field,
872 op: CompareOp::In,
873 value: LiteralValue::List(list),
874 });
875 }
876 "not" => {
877 self.skip_whitespace();
878 let saved_pos2 = self.pos;
879 if let Ok(kw2) = self.parse_keyword() {
880 if kw2 == "in" {
881 self.skip_whitespace();
882 let list = self.parse_literal_list()?;
883 return Ok(Comparison {
884 field,
885 op: CompareOp::NotIn,
886 value: LiteralValue::List(list),
887 });
888 } else if kw2 == "matches" {
889 self.skip_whitespace();
890 let value = self.parse_literal_value()?;
891 return Ok(Comparison {
892 field,
893 op: CompareOp::NotMatches,
894 value,
895 });
896 }
897 }
898 self.pos = saved_pos2;
899 self.pos = saved_pos;
900 }
901 _ => {
902 self.pos = saved_pos;
903 }
904 }
905 } else {
906 self.pos = saved_pos;
907 }
908
909 let op = self.parse_compare_op()?;
911 self.skip_whitespace();
912
913 let value = self.parse_literal_value()?;
915
916 Ok(Comparison { field, op, value })
917 }
918
919 fn parse_identifier(&mut self) -> Result<String, DkitError> {
921 let start = self.pos;
922 while !self.is_at_end() {
923 let c = self.input[self.pos];
924 if c.is_alphanumeric() || c == '_' || c == '-' {
925 self.pos += 1;
926 } else {
927 break;
928 }
929 }
930 if self.pos == start {
931 return Err(DkitError::QueryError(format!(
932 "expected field name at position {}",
933 self.pos
934 )));
935 }
936 Ok(self.input[start..self.pos].iter().collect())
937 }
938
939 fn parse_compare_op(&mut self) -> Result<CompareOp, DkitError> {
941 let c1 = self.peek().ok_or_else(|| {
942 DkitError::QueryError(format!(
943 "expected comparison operator at position {}",
944 self.pos
945 ))
946 })?;
947
948 match c1 {
949 '=' => {
950 self.advance();
951 if self.consume_char('=') {
952 Ok(CompareOp::Eq)
953 } else {
954 Err(DkitError::QueryError(format!(
955 "expected '==' at position {}",
956 self.pos - 1
957 )))
958 }
959 }
960 '!' => {
961 self.advance();
962 if self.consume_char('=') {
963 Ok(CompareOp::Ne)
964 } else {
965 Err(DkitError::QueryError(format!(
966 "expected '!=' at position {}",
967 self.pos - 1
968 )))
969 }
970 }
971 '>' => {
972 self.advance();
973 if self.consume_char('=') {
974 Ok(CompareOp::Ge)
975 } else {
976 Ok(CompareOp::Gt)
977 }
978 }
979 '<' => {
980 self.advance();
981 if self.consume_char('=') {
982 Ok(CompareOp::Le)
983 } else {
984 Ok(CompareOp::Lt)
985 }
986 }
987 c if c.is_alphabetic() => {
988 let saved_pos = self.pos;
989 let keyword = self.parse_keyword()?;
990 match keyword.as_str() {
991 "contains" => Ok(CompareOp::Contains),
992 "starts_with" => Ok(CompareOp::StartsWith),
993 "ends_with" => Ok(CompareOp::EndsWith),
994 "matches" => Ok(CompareOp::Matches),
995 _ => {
996 self.pos = saved_pos;
997 Err(DkitError::QueryError(format!(
998 "expected comparison operator at position {}, found '{}'",
999 saved_pos, keyword
1000 )))
1001 }
1002 }
1003 }
1004 _ => Err(DkitError::QueryError(format!(
1005 "expected comparison operator at position {}, found '{}'",
1006 self.pos, c1
1007 ))),
1008 }
1009 }
1010
1011 fn parse_literal_value(&mut self) -> Result<LiteralValue, DkitError> {
1013 match self.peek() {
1014 Some('"') => self.parse_string_literal(),
1015 Some(c) if c.is_ascii_digit() || c == '-' => self.parse_number_literal(),
1016 Some(c) if c.is_alphabetic() => {
1017 let word = self.parse_keyword()?;
1018 match word.as_str() {
1019 "true" => Ok(LiteralValue::Bool(true)),
1020 "false" => Ok(LiteralValue::Bool(false)),
1021 "null" => Ok(LiteralValue::Null),
1022 _ => Err(DkitError::QueryError(format!(
1023 "unexpected value '{}' at position {}",
1024 word,
1025 self.pos - word.len()
1026 ))),
1027 }
1028 }
1029 Some(c) => Err(DkitError::QueryError(format!(
1030 "unexpected character '{}' at position {}",
1031 c, self.pos
1032 ))),
1033 None => Err(DkitError::QueryError(format!(
1034 "expected value at position {}",
1035 self.pos
1036 ))),
1037 }
1038 }
1039
1040 fn parse_literal_list(&mut self) -> Result<Vec<LiteralValue>, DkitError> {
1042 if !self.consume_char('(') {
1043 return Err(DkitError::QueryError(format!(
1044 "expected '(' at position {}",
1045 self.pos
1046 )));
1047 }
1048
1049 let mut values = Vec::new();
1050 self.skip_whitespace();
1051
1052 if self.peek() == Some(')') {
1054 self.advance();
1055 return Ok(values);
1056 }
1057
1058 values.push(self.parse_literal_value()?);
1060
1061 loop {
1062 self.skip_whitespace();
1063 if self.consume_char(')') {
1064 break;
1065 }
1066 if !self.consume_char(',') {
1067 return Err(DkitError::QueryError(format!(
1068 "expected ',' or ')' at position {}",
1069 self.pos
1070 )));
1071 }
1072 self.skip_whitespace();
1073 values.push(self.parse_literal_value()?);
1074 }
1075
1076 Ok(values)
1077 }
1078
1079 fn parse_string_literal(&mut self) -> Result<LiteralValue, DkitError> {
1081 if !self.consume_char('"') {
1082 return Err(DkitError::QueryError(format!(
1083 "expected '\"' at position {}",
1084 self.pos
1085 )));
1086 }
1087 let start = self.pos;
1088 while !self.is_at_end() && self.input[self.pos] != '"' {
1089 self.pos += 1;
1090 }
1091 if self.is_at_end() {
1092 return Err(DkitError::QueryError(format!(
1093 "unterminated string starting at position {}",
1094 start - 1
1095 )));
1096 }
1097 let s: String = self.input[start..self.pos].iter().collect();
1098 self.advance(); Ok(LiteralValue::String(s))
1100 }
1101
1102 fn parse_number_literal(&mut self) -> Result<LiteralValue, DkitError> {
1104 let start = self.pos;
1105 if self.peek() == Some('-') {
1106 self.advance();
1107 }
1108 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1109 self.pos += 1;
1110 }
1111 let mut is_float = false;
1112 if self.peek() == Some('.') {
1113 is_float = true;
1114 self.advance();
1115 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1116 self.pos += 1;
1117 }
1118 }
1119 if self.pos == start || (self.pos == start + 1 && self.input[start] == '-') {
1120 return Err(DkitError::QueryError(format!(
1121 "expected number at position {}",
1122 start
1123 )));
1124 }
1125 let num_str: String = self.input[start..self.pos].iter().collect();
1126 if is_float {
1127 let f: f64 = num_str.parse().map_err(|_| {
1128 DkitError::QueryError(format!(
1129 "invalid number '{}' at position {}",
1130 num_str, start
1131 ))
1132 })?;
1133 Ok(LiteralValue::Float(f))
1134 } else {
1135 let n: i64 = num_str.parse().map_err(|_| {
1136 DkitError::QueryError(format!(
1137 "invalid number '{}' at position {}",
1138 num_str, start
1139 ))
1140 })?;
1141 Ok(LiteralValue::Integer(n))
1142 }
1143 }
1144
1145 fn peek(&self) -> Option<char> {
1148 self.input.get(self.pos).copied()
1149 }
1150
1151 fn peek_is_identifier_start(&self) -> bool {
1152 self.peek().is_some_and(|c| c.is_alphabetic() || c == '_')
1153 }
1154
1155 fn advance(&mut self) {
1156 self.pos += 1;
1157 }
1158
1159 fn consume_char(&mut self, expected: char) -> bool {
1160 if self.peek() == Some(expected) {
1161 self.advance();
1162 true
1163 } else {
1164 false
1165 }
1166 }
1167
1168 fn skip_whitespace(&mut self) {
1169 while self.peek().is_some_and(|c| c.is_whitespace()) {
1170 self.advance();
1171 }
1172 }
1173
1174 fn is_at_end(&self) -> bool {
1175 self.pos >= self.input.len()
1176 }
1177
1178 fn try_parse_identifier(&mut self) -> Option<String> {
1180 if !self.peek_is_identifier_start() {
1181 return None;
1182 }
1183 let saved_pos = self.pos;
1184 match self.parse_identifier() {
1185 Ok(id) => Some(id),
1186 Err(_) => {
1187 self.pos = saved_pos;
1188 None
1189 }
1190 }
1191 }
1192
1193 fn try_consume_keyword(&mut self, keyword: &str) -> bool {
1195 let saved_pos = self.pos;
1196 if let Ok(word) = self.parse_keyword() {
1197 if word == keyword {
1198 return true;
1199 }
1200 }
1201 self.pos = saved_pos;
1202 false
1203 }
1204
1205 fn parse_positive_integer(&mut self) -> Result<usize, DkitError> {
1207 let start = self.pos;
1208 while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
1209 self.pos += 1;
1210 }
1211 if self.pos == start {
1212 return Err(DkitError::QueryError(format!(
1213 "expected positive integer at position {}",
1214 self.pos
1215 )));
1216 }
1217 let num_str: String = self.input[start..self.pos].iter().collect();
1218 num_str.parse().map_err(|_| {
1219 DkitError::QueryError(format!(
1220 "invalid integer '{}' at position {}",
1221 num_str, start
1222 ))
1223 })
1224 }
1225}
1226
1227pub fn parse_query(input: &str) -> Result<Query, DkitError> {
1229 Parser::new(input).parse()
1230}
1231
1232pub fn parse_add_field_expr(input: &str) -> Result<(String, Expr), DkitError> {
1235 let mut parser = Parser::new(input);
1236 parser.skip_whitespace();
1237 let name = parser.parse_identifier().map_err(|_| {
1238 DkitError::QueryError(format!(
1239 "expected field name in --add-field expression: '{input}'"
1240 ))
1241 })?;
1242 parser.skip_whitespace();
1243 if !parser.consume_char('=') {
1244 return Err(DkitError::QueryError(format!(
1245 "expected '=' after field name in --add-field expression: '{input}'"
1246 )));
1247 }
1248 parser.skip_whitespace();
1249 let expr = parser.parse_expr()?;
1250 parser.skip_whitespace();
1251 if parser.pos != parser.input.len() {
1252 return Err(DkitError::QueryError(format!(
1253 "unexpected character '{}' at position {} in --add-field expression",
1254 parser.input[parser.pos], parser.pos
1255 )));
1256 }
1257 Ok((name, expr))
1258}
1259
1260pub fn parse_condition_expr(input: &str) -> Result<Condition, DkitError> {
1263 let mut parser = Parser::new(input);
1264 parser.skip_whitespace();
1265 let condition = parser.parse_condition()?;
1266 parser.skip_whitespace();
1267 if parser.pos != parser.input.len() {
1268 return Err(DkitError::QueryError(format!(
1269 "unexpected character '{}' at position {} in where expression",
1270 parser.input[parser.pos], parser.pos
1271 )));
1272 }
1273 Ok(condition)
1274}
1275
1276#[cfg(test)]
1277mod tests {
1278 use super::*;
1279
1280 #[test]
1283 fn test_root_path() {
1284 let q = parse_query(".").unwrap();
1285 assert!(q.path.segments.is_empty());
1286 }
1287
1288 #[test]
1289 fn test_single_field() {
1290 let q = parse_query(".name").unwrap();
1291 assert_eq!(q.path.segments, vec![Segment::Field("name".to_string())]);
1292 }
1293
1294 #[test]
1295 fn test_nested_fields() {
1296 let q = parse_query(".database.host").unwrap();
1297 assert_eq!(
1298 q.path.segments,
1299 vec![
1300 Segment::Field("database".to_string()),
1301 Segment::Field("host".to_string()),
1302 ]
1303 );
1304 }
1305
1306 #[test]
1307 fn test_deeply_nested_fields() {
1308 let q = parse_query(".a.b.c.d").unwrap();
1309 assert_eq!(q.path.segments.len(), 4);
1310 assert_eq!(q.path.segments[3], Segment::Field("d".to_string()));
1311 }
1312
1313 #[test]
1316 fn test_array_index() {
1317 let q = parse_query(".users[0]").unwrap();
1318 assert_eq!(
1319 q.path.segments,
1320 vec![Segment::Field("users".to_string()), Segment::Index(0),]
1321 );
1322 }
1323
1324 #[test]
1325 fn test_array_negative_index() {
1326 let q = parse_query(".users[-1]").unwrap();
1327 assert_eq!(
1328 q.path.segments,
1329 vec![Segment::Field("users".to_string()), Segment::Index(-1),]
1330 );
1331 }
1332
1333 #[test]
1334 fn test_array_index_with_field_after() {
1335 let q = parse_query(".users[0].name").unwrap();
1336 assert_eq!(
1337 q.path.segments,
1338 vec![
1339 Segment::Field("users".to_string()),
1340 Segment::Index(0),
1341 Segment::Field("name".to_string()),
1342 ]
1343 );
1344 }
1345
1346 #[test]
1347 fn test_large_index() {
1348 let q = parse_query(".items[999]").unwrap();
1349 assert_eq!(
1350 q.path.segments,
1351 vec![Segment::Field("items".to_string()), Segment::Index(999),]
1352 );
1353 }
1354
1355 #[test]
1358 fn test_array_iterate() {
1359 let q = parse_query(".users[]").unwrap();
1360 assert_eq!(
1361 q.path.segments,
1362 vec![Segment::Field("users".to_string()), Segment::Iterate,]
1363 );
1364 }
1365
1366 #[test]
1367 fn test_array_iterate_with_field() {
1368 let q = parse_query(".users[].name").unwrap();
1369 assert_eq!(
1370 q.path.segments,
1371 vec![
1372 Segment::Field("users".to_string()),
1373 Segment::Iterate,
1374 Segment::Field("name".to_string()),
1375 ]
1376 );
1377 }
1378
1379 #[test]
1380 fn test_array_iterate_nested() {
1381 let q = parse_query(".data[].items[].name").unwrap();
1382 assert_eq!(
1383 q.path.segments,
1384 vec![
1385 Segment::Field("data".to_string()),
1386 Segment::Iterate,
1387 Segment::Field("items".to_string()),
1388 Segment::Iterate,
1389 Segment::Field("name".to_string()),
1390 ]
1391 );
1392 }
1393
1394 #[test]
1397 fn test_array_wildcard() {
1398 let q = parse_query(".[*]").unwrap();
1399 assert_eq!(q.path.segments, vec![Segment::Wildcard]);
1400 }
1401
1402 #[test]
1403 fn test_array_wildcard_with_field() {
1404 let q = parse_query(".users[*].name").unwrap();
1405 assert_eq!(
1406 q.path.segments,
1407 vec![
1408 Segment::Field("users".to_string()),
1409 Segment::Wildcard,
1410 Segment::Field("name".to_string()),
1411 ]
1412 );
1413 }
1414
1415 #[test]
1418 fn test_array_slice_basic() {
1419 let q = parse_query(".[0:3]").unwrap();
1420 assert_eq!(
1421 q.path.segments,
1422 vec![Segment::Slice {
1423 start: Some(0),
1424 end: Some(3),
1425 step: None
1426 }]
1427 );
1428 }
1429
1430 #[test]
1431 fn test_array_slice_open_end() {
1432 let q = parse_query(".[1:]").unwrap();
1433 assert_eq!(
1434 q.path.segments,
1435 vec![Segment::Slice {
1436 start: Some(1),
1437 end: None,
1438 step: None
1439 }]
1440 );
1441 }
1442
1443 #[test]
1444 fn test_array_slice_open_start() {
1445 let q = parse_query(".[:3]").unwrap();
1446 assert_eq!(
1447 q.path.segments,
1448 vec![Segment::Slice {
1449 start: None,
1450 end: Some(3),
1451 step: None
1452 }]
1453 );
1454 }
1455
1456 #[test]
1457 fn test_array_slice_negative() {
1458 let q = parse_query(".[-2:]").unwrap();
1459 assert_eq!(
1460 q.path.segments,
1461 vec![Segment::Slice {
1462 start: Some(-2),
1463 end: None,
1464 step: None
1465 }]
1466 );
1467 }
1468
1469 #[test]
1470 fn test_array_slice_with_step() {
1471 let q = parse_query(".[1:5:2]").unwrap();
1472 assert_eq!(
1473 q.path.segments,
1474 vec![Segment::Slice {
1475 start: Some(1),
1476 end: Some(5),
1477 step: Some(2)
1478 }]
1479 );
1480 }
1481
1482 #[test]
1483 fn test_array_slice_full_open() {
1484 let q = parse_query(".[:]").unwrap();
1485 assert_eq!(
1486 q.path.segments,
1487 vec![Segment::Slice {
1488 start: None,
1489 end: None,
1490 step: None
1491 }]
1492 );
1493 }
1494
1495 #[test]
1496 fn test_array_slice_with_field() {
1497 let q = parse_query(".users[0:3].name").unwrap();
1498 assert_eq!(
1499 q.path.segments,
1500 vec![
1501 Segment::Field("users".to_string()),
1502 Segment::Slice {
1503 start: Some(0),
1504 end: Some(3),
1505 step: None
1506 },
1507 Segment::Field("name".to_string()),
1508 ]
1509 );
1510 }
1511
1512 #[test]
1513 fn test_array_slice_reverse_step() {
1514 let q = parse_query(".[::-1]").unwrap();
1515 assert_eq!(
1516 q.path.segments,
1517 vec![Segment::Slice {
1518 start: None,
1519 end: None,
1520 step: Some(-1)
1521 }]
1522 );
1523 }
1524
1525 #[test]
1528 fn test_complex_path() {
1529 let q = parse_query(".data.users[0].address.city").unwrap();
1530 assert_eq!(
1531 q.path.segments,
1532 vec![
1533 Segment::Field("data".to_string()),
1534 Segment::Field("users".to_string()),
1535 Segment::Index(0),
1536 Segment::Field("address".to_string()),
1537 Segment::Field("city".to_string()),
1538 ]
1539 );
1540 }
1541
1542 #[test]
1545 fn test_field_with_underscore() {
1546 let q = parse_query(".user_name").unwrap();
1547 assert_eq!(
1548 q.path.segments,
1549 vec![Segment::Field("user_name".to_string())]
1550 );
1551 }
1552
1553 #[test]
1554 fn test_field_with_hyphen() {
1555 let q = parse_query(".content-type").unwrap();
1556 assert_eq!(
1557 q.path.segments,
1558 vec![Segment::Field("content-type".to_string())]
1559 );
1560 }
1561
1562 #[test]
1563 fn test_field_with_digits() {
1564 let q = parse_query(".field1").unwrap();
1565 assert_eq!(q.path.segments, vec![Segment::Field("field1".to_string())]);
1566 }
1567
1568 #[test]
1571 fn test_error_no_dot() {
1572 let err = parse_query("name").unwrap_err();
1573 assert!(matches!(err, DkitError::QueryError(_)));
1574 }
1575
1576 #[test]
1577 fn test_error_empty() {
1578 let err = parse_query("").unwrap_err();
1579 assert!(matches!(err, DkitError::QueryError(_)));
1580 }
1581
1582 #[test]
1583 fn test_error_unclosed_bracket() {
1584 let err = parse_query(".users[0").unwrap_err();
1585 assert!(matches!(err, DkitError::QueryError(_)));
1586 }
1587
1588 #[test]
1589 fn test_error_invalid_index() {
1590 let err = parse_query(".users[abc]").unwrap_err();
1591 assert!(matches!(err, DkitError::QueryError(_)));
1592 }
1593
1594 #[test]
1595 fn test_error_trailing_garbage() {
1596 let err = parse_query(".name xyz").unwrap_err();
1597 assert!(matches!(err, DkitError::QueryError(_)));
1598 }
1599
1600 #[test]
1603 fn test_whitespace_around() {
1604 let q = parse_query(" .name ").unwrap();
1605 assert_eq!(q.path.segments, vec![Segment::Field("name".to_string())]);
1606 }
1607
1608 #[test]
1611 fn test_root_array_index() {
1612 let q = parse_query(".[0]").unwrap();
1613 assert_eq!(q.path.segments, vec![Segment::Index(0)]);
1614 }
1615
1616 #[test]
1617 fn test_root_array_iterate() {
1618 let q = parse_query(".[]").unwrap();
1619 assert_eq!(q.path.segments, vec![Segment::Iterate]);
1620 }
1621
1622 #[test]
1623 fn test_root_iterate_with_field() {
1624 let q = parse_query(".[].name").unwrap();
1625 assert_eq!(
1626 q.path.segments,
1627 vec![Segment::Iterate, Segment::Field("name".to_string()),]
1628 );
1629 }
1630
1631 #[test]
1634 fn test_where_eq_integer() {
1635 let q = parse_query(".users[] | where age == 30").unwrap();
1636 assert_eq!(
1637 q.path.segments,
1638 vec![Segment::Field("users".to_string()), Segment::Iterate]
1639 );
1640 assert_eq!(q.operations.len(), 1);
1641 assert_eq!(
1642 q.operations[0],
1643 Operation::Where(Condition::Comparison(Comparison {
1644 field: "age".to_string(),
1645 op: CompareOp::Eq,
1646 value: LiteralValue::Integer(30),
1647 }))
1648 );
1649 }
1650
1651 #[test]
1652 fn test_where_ne_string() {
1653 let q = parse_query(".items[] | where status != \"inactive\"").unwrap();
1654 assert_eq!(
1655 q.operations[0],
1656 Operation::Where(Condition::Comparison(Comparison {
1657 field: "status".to_string(),
1658 op: CompareOp::Ne,
1659 value: LiteralValue::String("inactive".to_string()),
1660 }))
1661 );
1662 }
1663
1664 #[test]
1665 fn test_where_gt() {
1666 let q = parse_query(".[] | where age > 25").unwrap();
1667 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1668 panic!("expected Comparison");
1669 };
1670 assert_eq!(cmp.field, "age");
1671 assert_eq!(cmp.op, CompareOp::Gt);
1672 assert_eq!(cmp.value, LiteralValue::Integer(25));
1673 }
1674
1675 #[test]
1676 fn test_where_lt() {
1677 let q = parse_query(".[] | where price < 100").unwrap();
1678 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1679 panic!("expected Comparison");
1680 };
1681 assert_eq!(cmp.op, CompareOp::Lt);
1682 assert_eq!(cmp.value, LiteralValue::Integer(100));
1683 }
1684
1685 #[test]
1686 fn test_where_ge() {
1687 let q = parse_query(".[] | where score >= 80").unwrap();
1688 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1689 panic!("expected Comparison");
1690 };
1691 assert_eq!(cmp.op, CompareOp::Ge);
1692 assert_eq!(cmp.value, LiteralValue::Integer(80));
1693 }
1694
1695 #[test]
1696 fn test_where_le() {
1697 let q = parse_query(".[] | where price <= 1000").unwrap();
1698 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1699 panic!("expected Comparison");
1700 };
1701 assert_eq!(cmp.op, CompareOp::Le);
1702 assert_eq!(cmp.value, LiteralValue::Integer(1000));
1703 }
1704
1705 #[test]
1706 fn test_where_float_literal() {
1707 let q = parse_query(".[] | where score > 3.14").unwrap();
1708 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1709 panic!("expected Comparison");
1710 };
1711 assert_eq!(cmp.value, LiteralValue::Float(3.14));
1712 }
1713
1714 #[test]
1715 fn test_where_negative_number() {
1716 let q = parse_query(".[] | where temp > -10").unwrap();
1717 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1718 panic!("expected Comparison");
1719 };
1720 assert_eq!(cmp.value, LiteralValue::Integer(-10));
1721 }
1722
1723 #[test]
1724 fn test_where_bool_literal() {
1725 let q = parse_query(".[] | where active == true").unwrap();
1726 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1727 panic!("expected Comparison");
1728 };
1729 assert_eq!(cmp.value, LiteralValue::Bool(true));
1730 }
1731
1732 #[test]
1733 fn test_where_null_literal() {
1734 let q = parse_query(".[] | where value == null").unwrap();
1735 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1736 panic!("expected Comparison");
1737 };
1738 assert_eq!(cmp.value, LiteralValue::Null);
1739 }
1740
1741 #[test]
1742 fn test_where_no_operations_for_path_only() {
1743 let q = parse_query(".users[0].name").unwrap();
1744 assert!(q.operations.is_empty());
1745 }
1746
1747 #[test]
1748 fn test_where_with_extra_whitespace() {
1749 let q = parse_query(".[] | where age > 30 ").unwrap();
1750 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1751 panic!("expected Comparison");
1752 };
1753 assert_eq!(cmp.field, "age");
1754 assert_eq!(cmp.op, CompareOp::Gt);
1755 assert_eq!(cmp.value, LiteralValue::Integer(30));
1756 }
1757
1758 #[test]
1761 fn test_error_where_missing_field() {
1762 let err = parse_query(".[] | where == 30").unwrap_err();
1763 assert!(matches!(err, DkitError::QueryError(_)));
1764 }
1765
1766 #[test]
1767 fn test_error_where_missing_operator() {
1768 let err = parse_query(".[] | where age 30").unwrap_err();
1769 assert!(matches!(err, DkitError::QueryError(_)));
1770 }
1771
1772 #[test]
1773 fn test_error_where_missing_value() {
1774 let err = parse_query(".[] | where age >").unwrap_err();
1775 assert!(matches!(err, DkitError::QueryError(_)));
1776 }
1777
1778 #[test]
1779 fn test_error_where_unterminated_string() {
1780 let err = parse_query(".[] | where name == \"hello").unwrap_err();
1781 assert!(matches!(err, DkitError::QueryError(_)));
1782 }
1783
1784 #[test]
1785 fn test_error_unknown_operation() {
1786 let err = parse_query(".[] | foobar age > 30").unwrap_err();
1787 assert!(matches!(err, DkitError::QueryError(_)));
1788 }
1789
1790 #[test]
1793 fn test_where_contains() {
1794 let q = parse_query(".[] | where email contains \"@gmail\"").unwrap();
1795 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1796 panic!("expected Comparison");
1797 };
1798 assert_eq!(cmp.field, "email");
1799 assert_eq!(cmp.op, CompareOp::Contains);
1800 assert_eq!(cmp.value, LiteralValue::String("@gmail".to_string()));
1801 }
1802
1803 #[test]
1804 fn test_where_starts_with() {
1805 let q = parse_query(".[] | where name starts_with \"A\"").unwrap();
1806 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1807 panic!("expected Comparison");
1808 };
1809 assert_eq!(cmp.field, "name");
1810 assert_eq!(cmp.op, CompareOp::StartsWith);
1811 assert_eq!(cmp.value, LiteralValue::String("A".to_string()));
1812 }
1813
1814 #[test]
1815 fn test_where_ends_with() {
1816 let q = parse_query(".[] | where file ends_with \".json\"").unwrap();
1817 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1818 panic!("expected Comparison");
1819 };
1820 assert_eq!(cmp.field, "file");
1821 assert_eq!(cmp.op, CompareOp::EndsWith);
1822 assert_eq!(cmp.value, LiteralValue::String(".json".to_string()));
1823 }
1824
1825 #[test]
1826 fn test_where_matches() {
1827 let q = parse_query(".[] | where email matches \".*@gmail\\.com$\"").unwrap();
1828 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1829 panic!("expected Comparison");
1830 };
1831 assert_eq!(cmp.field, "email");
1832 assert_eq!(cmp.op, CompareOp::Matches);
1833 assert_eq!(
1834 cmp.value,
1835 LiteralValue::String(".*@gmail\\.com$".to_string())
1836 );
1837 }
1838
1839 #[test]
1840 fn test_where_not_matches() {
1841 let q = parse_query(".[] | where name not matches \"^test_\"").unwrap();
1842 let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1843 panic!("expected Comparison");
1844 };
1845 assert_eq!(cmp.field, "name");
1846 assert_eq!(cmp.op, CompareOp::NotMatches);
1847 assert_eq!(cmp.value, LiteralValue::String("^test_".to_string()));
1848 }
1849
1850 #[test]
1853 fn test_where_and() {
1854 let q = parse_query(".[] | where age > 25 and city == \"Seoul\"").unwrap();
1855 let Operation::Where(cond) = &q.operations[0] else {
1856 panic!("expected Where operation");
1857 };
1858 match cond {
1859 Condition::And(left, right) => {
1860 let Condition::Comparison(l) = left.as_ref() else {
1861 panic!("expected left Comparison");
1862 };
1863 assert_eq!(l.field, "age");
1864 assert_eq!(l.op, CompareOp::Gt);
1865 assert_eq!(l.value, LiteralValue::Integer(25));
1866 let Condition::Comparison(r) = right.as_ref() else {
1867 panic!("expected right Comparison");
1868 };
1869 assert_eq!(r.field, "city");
1870 assert_eq!(r.op, CompareOp::Eq);
1871 assert_eq!(r.value, LiteralValue::String("Seoul".to_string()));
1872 }
1873 _ => panic!("expected And condition"),
1874 }
1875 }
1876
1877 #[test]
1878 fn test_where_or() {
1879 let q = parse_query(".[] | where role == \"admin\" or role == \"manager\"").unwrap();
1880 let Operation::Where(cond) = &q.operations[0] else {
1881 panic!("expected Where operation");
1882 };
1883 match cond {
1884 Condition::Or(left, right) => {
1885 let Condition::Comparison(l) = left.as_ref() else {
1886 panic!("expected left Comparison");
1887 };
1888 assert_eq!(l.field, "role");
1889 assert_eq!(l.value, LiteralValue::String("admin".to_string()));
1890 let Condition::Comparison(r) = right.as_ref() else {
1891 panic!("expected right Comparison");
1892 };
1893 assert_eq!(r.field, "role");
1894 assert_eq!(r.value, LiteralValue::String("manager".to_string()));
1895 }
1896 _ => panic!("expected Or condition"),
1897 }
1898 }
1899
1900 #[test]
1901 fn test_where_and_with_string_op() {
1902 let q = parse_query(".[] | where name starts_with \"A\" and age > 20").unwrap();
1903 let Operation::Where(cond) = &q.operations[0] else {
1904 panic!("expected Where operation");
1905 };
1906 assert!(matches!(cond, Condition::And(_, _)));
1907 }
1908
1909 #[test]
1910 fn test_where_chained_and() {
1911 let q = parse_query(".[] | where a == 1 and b == 2 and c == 3").unwrap();
1912 let Operation::Where(cond) = &q.operations[0] else {
1913 panic!("expected Where operation");
1914 };
1915 match cond {
1917 Condition::And(left, right) => {
1918 assert!(matches!(left.as_ref(), Condition::And(_, _)));
1919 assert!(matches!(right.as_ref(), Condition::Comparison(_)));
1920 }
1921 _ => panic!("expected And condition"),
1922 }
1923 }
1924
1925 fn field(name: &str) -> SelectExpr {
1928 SelectExpr {
1929 expr: Expr::Field(name.to_string()),
1930 alias: None,
1931 }
1932 }
1933
1934 fn fields(names: &[&str]) -> Operation {
1935 Operation::Select(names.iter().map(|n| field(n)).collect())
1936 }
1937
1938 #[test]
1939 fn test_select_single_field() {
1940 let q = parse_query(".users[] | select name").unwrap();
1941 assert_eq!(q.operations.len(), 1);
1942 assert_eq!(q.operations[0], fields(&["name"]));
1943 }
1944
1945 #[test]
1946 fn test_select_multiple_fields() {
1947 let q = parse_query(".users[] | select name, email").unwrap();
1948 assert_eq!(q.operations[0], fields(&["name", "email"]));
1949 }
1950
1951 #[test]
1952 fn test_select_three_fields() {
1953 let q = parse_query(".users[] | select name, age, email").unwrap();
1954 assert_eq!(q.operations[0], fields(&["name", "age", "email"]));
1955 }
1956
1957 #[test]
1958 fn test_select_with_extra_whitespace() {
1959 let q = parse_query(".[] | select name , email ").unwrap();
1960 assert_eq!(q.operations[0], fields(&["name", "email"]));
1961 }
1962
1963 #[test]
1964 fn test_select_field_with_underscore() {
1965 let q = parse_query(".[] | select user_name, created_at").unwrap();
1966 assert_eq!(q.operations[0], fields(&["user_name", "created_at"]));
1967 }
1968
1969 #[test]
1970 fn test_select_field_with_hyphen() {
1971 let q = parse_query(".[] | select content-type").unwrap();
1972 assert_eq!(q.operations[0], fields(&["content-type"]));
1973 }
1974
1975 #[test]
1976 fn test_where_then_select() {
1977 let q = parse_query(".users[] | where age > 30 | select name, email").unwrap();
1978 assert_eq!(q.operations.len(), 2);
1979 assert!(matches!(&q.operations[0], Operation::Where(_)));
1980 assert_eq!(q.operations[1], fields(&["name", "email"]));
1981 }
1982
1983 #[test]
1984 fn test_error_select_missing_fields() {
1985 let err = parse_query(".[] | select").unwrap_err();
1986 assert!(matches!(err, DkitError::QueryError(_)));
1987 }
1988
1989 #[test]
1990 fn test_select_func_single() {
1991 let q = parse_query(".[] | select upper(name)").unwrap();
1992 assert_eq!(
1993 q.operations[0],
1994 Operation::Select(vec![SelectExpr {
1995 expr: Expr::FuncCall {
1996 name: "upper".to_string(),
1997 args: vec![Expr::Field("name".to_string())],
1998 },
1999 alias: None,
2000 }])
2001 );
2002 }
2003
2004 #[test]
2005 fn test_select_func_with_alias() {
2006 let q = parse_query(".[] | select upper(name) as NAME").unwrap();
2007 assert_eq!(
2008 q.operations[0],
2009 Operation::Select(vec![SelectExpr {
2010 expr: Expr::FuncCall {
2011 name: "upper".to_string(),
2012 args: vec![Expr::Field("name".to_string())],
2013 },
2014 alias: Some("NAME".to_string()),
2015 }])
2016 );
2017 }
2018
2019 #[test]
2020 fn test_select_func_nested() {
2021 let q = parse_query(".[] | select upper(trim(name))").unwrap();
2022 assert_eq!(
2023 q.operations[0],
2024 Operation::Select(vec![SelectExpr {
2025 expr: Expr::FuncCall {
2026 name: "upper".to_string(),
2027 args: vec![Expr::FuncCall {
2028 name: "trim".to_string(),
2029 args: vec![Expr::Field("name".to_string())],
2030 }],
2031 },
2032 alias: None,
2033 }])
2034 );
2035 }
2036
2037 #[test]
2038 fn test_select_func_with_literal_arg() {
2039 let q = parse_query(".[] | select round(price, 2)").unwrap();
2040 assert_eq!(
2041 q.operations[0],
2042 Operation::Select(vec![SelectExpr {
2043 expr: Expr::FuncCall {
2044 name: "round".to_string(),
2045 args: vec![
2046 Expr::Field("price".to_string()),
2047 Expr::Literal(LiteralValue::Integer(2)),
2048 ],
2049 },
2050 alias: None,
2051 }])
2052 );
2053 }
2054
2055 #[test]
2056 fn test_select_mixed_fields_and_funcs() {
2057 let q = parse_query(".[] | select name, upper(city)").unwrap();
2058 assert_eq!(
2059 q.operations[0],
2060 Operation::Select(vec![
2061 field("name"),
2062 SelectExpr {
2063 expr: Expr::FuncCall {
2064 name: "upper".to_string(),
2065 args: vec![Expr::Field("city".to_string())],
2066 },
2067 alias: None,
2068 }
2069 ])
2070 );
2071 }
2072
2073 #[test]
2076 fn test_sort_asc() {
2077 let q = parse_query(".users[] | sort age").unwrap();
2078 assert_eq!(q.operations.len(), 1);
2079 assert_eq!(
2080 q.operations[0],
2081 Operation::Sort {
2082 field: "age".to_string(),
2083 descending: false,
2084 }
2085 );
2086 }
2087
2088 #[test]
2089 fn test_sort_desc() {
2090 let q = parse_query(".users[] | sort age desc").unwrap();
2091 assert_eq!(q.operations.len(), 1);
2092 assert_eq!(
2093 q.operations[0],
2094 Operation::Sort {
2095 field: "age".to_string(),
2096 descending: true,
2097 }
2098 );
2099 }
2100
2101 #[test]
2102 fn test_sort_with_extra_whitespace() {
2103 let q = parse_query(".[] | sort name ").unwrap();
2104 assert_eq!(
2105 q.operations[0],
2106 Operation::Sort {
2107 field: "name".to_string(),
2108 descending: false,
2109 }
2110 );
2111 }
2112
2113 #[test]
2114 fn test_sort_desc_with_extra_whitespace() {
2115 let q = parse_query(".[] | sort name desc ").unwrap();
2116 assert_eq!(
2117 q.operations[0],
2118 Operation::Sort {
2119 field: "name".to_string(),
2120 descending: true,
2121 }
2122 );
2123 }
2124
2125 #[test]
2126 fn test_sort_field_with_underscore() {
2127 let q = parse_query(".[] | sort created_at").unwrap();
2128 assert_eq!(
2129 q.operations[0],
2130 Operation::Sort {
2131 field: "created_at".to_string(),
2132 descending: false,
2133 }
2134 );
2135 }
2136
2137 #[test]
2138 fn test_error_sort_missing_field() {
2139 let err = parse_query(".[] | sort").unwrap_err();
2140 assert!(matches!(err, DkitError::QueryError(_)));
2141 }
2142
2143 #[test]
2146 fn test_limit() {
2147 let q = parse_query(".users[] | limit 10").unwrap();
2148 assert_eq!(q.operations.len(), 1);
2149 assert_eq!(q.operations[0], Operation::Limit(10));
2150 }
2151
2152 #[test]
2153 fn test_limit_one() {
2154 let q = parse_query(".[] | limit 1").unwrap();
2155 assert_eq!(q.operations[0], Operation::Limit(1));
2156 }
2157
2158 #[test]
2159 fn test_limit_with_extra_whitespace() {
2160 let q = parse_query(".[] | limit 5 ").unwrap();
2161 assert_eq!(q.operations[0], Operation::Limit(5));
2162 }
2163
2164 #[test]
2165 fn test_error_limit_missing_number() {
2166 let err = parse_query(".[] | limit").unwrap_err();
2167 assert!(matches!(err, DkitError::QueryError(_)));
2168 }
2169
2170 #[test]
2171 fn test_error_limit_negative() {
2172 let err = parse_query(".[] | limit -5").unwrap_err();
2173 assert!(matches!(err, DkitError::QueryError(_)));
2174 }
2175
2176 #[test]
2179 fn test_where_sort_limit() {
2180 let q = parse_query(".users[] | where age > 20 | sort age desc | limit 5").unwrap();
2181 assert_eq!(q.operations.len(), 3);
2182 assert!(matches!(&q.operations[0], Operation::Where(_)));
2183 assert_eq!(
2184 q.operations[1],
2185 Operation::Sort {
2186 field: "age".to_string(),
2187 descending: true,
2188 }
2189 );
2190 assert_eq!(q.operations[2], Operation::Limit(5));
2191 }
2192
2193 #[test]
2194 fn test_where_select_sort() {
2195 let q = parse_query(".users[] | where age > 30 | select name, email | sort name").unwrap();
2196 assert_eq!(q.operations.len(), 3);
2197 assert!(matches!(&q.operations[0], Operation::Where(_)));
2198 assert_eq!(q.operations[1], fields(&["name", "email"]));
2199 assert_eq!(
2200 q.operations[2],
2201 Operation::Sort {
2202 field: "name".to_string(),
2203 descending: false,
2204 }
2205 );
2206 }
2207
2208 #[test]
2209 fn test_group_by_single_field() {
2210 let q = parse_query(".[] | group_by category").unwrap();
2211 assert_eq!(q.operations.len(), 1);
2212 match &q.operations[0] {
2213 Operation::GroupBy {
2214 fields,
2215 having,
2216 aggregates,
2217 } => {
2218 assert_eq!(fields, &vec!["category".to_string()]);
2219 assert!(having.is_none());
2220 assert!(aggregates.is_empty());
2221 }
2222 _ => panic!("expected GroupBy"),
2223 }
2224 }
2225
2226 #[test]
2227 fn test_group_by_multiple_fields() {
2228 let q = parse_query(".[] | group_by region, category").unwrap();
2229 match &q.operations[0] {
2230 Operation::GroupBy { fields, .. } => {
2231 assert_eq!(fields, &vec!["region".to_string(), "category".to_string()]);
2232 }
2233 _ => panic!("expected GroupBy"),
2234 }
2235 }
2236
2237 #[test]
2238 fn test_group_by_with_aggregates() {
2239 let q = parse_query(".[] | group_by category count(), sum(price), avg(score)").unwrap();
2240 match &q.operations[0] {
2241 Operation::GroupBy { aggregates, .. } => {
2242 assert_eq!(aggregates.len(), 3);
2243 assert_eq!(aggregates[0].func, AggregateFunc::Count);
2244 assert_eq!(aggregates[0].field, None);
2245 assert_eq!(aggregates[0].alias, "count");
2246 assert_eq!(aggregates[1].func, AggregateFunc::Sum);
2247 assert_eq!(aggregates[1].field, Some("price".to_string()));
2248 assert_eq!(aggregates[1].alias, "sum_price");
2249 assert_eq!(aggregates[2].func, AggregateFunc::Avg);
2250 assert_eq!(aggregates[2].field, Some("score".to_string()));
2251 assert_eq!(aggregates[2].alias, "avg_score");
2252 }
2253 _ => panic!("expected GroupBy"),
2254 }
2255 }
2256
2257 #[test]
2258 fn test_group_by_with_having() {
2259 let q = parse_query(".[] | group_by category count() having count > 5").unwrap();
2260 match &q.operations[0] {
2261 Operation::GroupBy {
2262 fields,
2263 having,
2264 aggregates,
2265 } => {
2266 assert_eq!(fields, &vec!["category".to_string()]);
2267 assert!(having.is_some());
2268 assert_eq!(aggregates.len(), 1);
2269 }
2270 _ => panic!("expected GroupBy"),
2271 }
2272 }
2273
2274 #[test]
2275 fn test_group_by_with_min_max() {
2276 let q = parse_query(".[] | group_by category min(price), max(price)").unwrap();
2277 match &q.operations[0] {
2278 Operation::GroupBy { aggregates, .. } => {
2279 assert_eq!(aggregates.len(), 2);
2280 assert_eq!(aggregates[0].func, AggregateFunc::Min);
2281 assert_eq!(aggregates[1].func, AggregateFunc::Max);
2282 }
2283 _ => panic!("expected GroupBy"),
2284 }
2285 }
2286
2287 #[test]
2288 fn test_group_by_pipeline() {
2289 let q = parse_query(".[] | group_by category count() | sort count desc | limit 5").unwrap();
2290 assert_eq!(q.operations.len(), 3);
2291 assert!(matches!(&q.operations[0], Operation::GroupBy { .. }));
2292 assert!(matches!(
2293 &q.operations[1],
2294 Operation::Sort {
2295 descending: true,
2296 ..
2297 }
2298 ));
2299 assert_eq!(q.operations[2], Operation::Limit(5));
2300 }
2301
2302 #[test]
2305 fn test_add_field_simple_arithmetic() {
2306 let (name, expr) = parse_add_field_expr("total = amount * quantity").unwrap();
2307 assert_eq!(name, "total");
2308 assert!(matches!(
2309 expr,
2310 Expr::BinaryOp {
2311 op: ArithmeticOp::Mul,
2312 ..
2313 }
2314 ));
2315 }
2316
2317 #[test]
2318 fn test_add_field_string_concat() {
2319 let (name, expr) =
2320 parse_add_field_expr("full_name = first_name + \" \" + last_name").unwrap();
2321 assert_eq!(name, "full_name");
2322 assert!(matches!(
2324 expr,
2325 Expr::BinaryOp {
2326 op: ArithmeticOp::Add,
2327 ..
2328 }
2329 ));
2330 }
2331
2332 #[test]
2333 fn test_add_field_with_literal() {
2334 let (name, expr) = parse_add_field_expr("tax = price * 0.1").unwrap();
2335 assert_eq!(name, "tax");
2336 assert!(matches!(
2337 expr,
2338 Expr::BinaryOp {
2339 op: ArithmeticOp::Mul,
2340 ..
2341 }
2342 ));
2343 }
2344
2345 #[test]
2346 fn test_add_field_complex_expr() {
2347 let (name, expr) = parse_add_field_expr("total = price + price * 0.1").unwrap();
2348 assert_eq!(name, "total");
2349 if let Expr::BinaryOp { op, left, right } = &expr {
2351 assert_eq!(*op, ArithmeticOp::Add);
2352 assert!(matches!(left.as_ref(), Expr::Field(f) if f == "price"));
2353 assert!(matches!(
2354 right.as_ref(),
2355 Expr::BinaryOp {
2356 op: ArithmeticOp::Mul,
2357 ..
2358 }
2359 ));
2360 } else {
2361 panic!("expected BinaryOp");
2362 }
2363 }
2364
2365 #[test]
2366 fn test_add_field_with_parens() {
2367 let (name, expr) = parse_add_field_expr("total = (price + tax) * quantity").unwrap();
2368 assert_eq!(name, "total");
2369 if let Expr::BinaryOp { op, left, right } = &expr {
2370 assert_eq!(*op, ArithmeticOp::Mul);
2371 assert!(matches!(
2372 left.as_ref(),
2373 Expr::BinaryOp {
2374 op: ArithmeticOp::Add,
2375 ..
2376 }
2377 ));
2378 assert!(matches!(right.as_ref(), Expr::Field(f) if f == "quantity"));
2379 } else {
2380 panic!("expected BinaryOp");
2381 }
2382 }
2383
2384 #[test]
2385 fn test_add_field_with_function() {
2386 let (name, expr) = parse_add_field_expr("name_upper = upper(name)").unwrap();
2387 assert_eq!(name, "name_upper");
2388 assert!(matches!(expr, Expr::FuncCall { .. }));
2389 }
2390
2391 #[test]
2392 fn test_add_field_missing_equals() {
2393 let result = parse_add_field_expr("total amount * quantity");
2394 assert!(result.is_err());
2395 }
2396
2397 #[test]
2400 fn test_expr_division() {
2401 let q = parse_query(".items[] | select total / count as avg").unwrap();
2402 assert_eq!(q.operations.len(), 1);
2403 }
2404
2405 #[test]
2406 fn test_expr_subtraction() {
2407 let q = parse_query(".items[] | select price - discount as net").unwrap();
2408 assert_eq!(q.operations.len(), 1);
2409 }
2410}