1use crate::config::buffers::{max_predicates, max_query_length};
7use crate::query::error::ParseError;
8use crate::query::lexer::{Token, TokenType};
9use crate::query::types::{
10 Condition, Expr, Field, JoinEdgeKind, JoinExpr, Operator, PipelineQuery, PipelineStage, Query,
11 RegexFlags, RegexValue, Span, Value,
12};
13
14pub struct Parser {
16 tokens: Vec<Token>,
18 position: usize,
20}
21
22impl Parser {
23 #[must_use]
25 pub fn new(tokens: Vec<Token>) -> Self {
26 Self {
27 tokens,
28 position: 0,
29 }
30 }
31
32 pub fn parse_query(input: &str) -> Result<Query, crate::query::error::QueryError> {
41 use crate::query::lexer::with_lexer;
42
43 let trimmed = input.trim();
44 if trimmed.is_empty() {
45 return Err(ParseError::EmptyQuery.into());
46 }
47
48 let max_len = max_query_length();
49 let input_len = input.len();
50 if input_len > max_len {
51 return Err(ParseError::InvalidSyntax {
52 message: format!(
53 "Query too long: {input_len} bytes exceeds {max_len} byte limit. \
54 Adjust SQRY_MAX_QUERY_LENGTH environment variable if needed."
55 ),
56 span: Span::new(0, 0),
57 }
58 .into());
59 }
60
61 let tokens = with_lexer(input, |batch| Ok(batch.into_vec()))?;
62
63 let mut parser = Parser::new(tokens);
64 let query = parser.parse()?;
65
66 let max_preds = max_predicates();
67 let predicate_count = count_conditions(&query.root);
68 if predicate_count > max_preds {
69 return Err(ParseError::InvalidSyntax {
70 message: format!(
71 "Too many predicates: {predicate_count} exceeds {max_preds} limit. \
72 Adjust SQRY_MAX_PREDICATES environment variable if needed."
73 ),
74 span: Span::new(0, 0),
75 }
76 .into());
77 }
78
79 Ok(query)
80 }
81
82 pub fn parse_pipeline_query(
89 input: &str,
90 ) -> Result<Option<PipelineQuery>, crate::query::error::QueryError> {
91 use crate::query::lexer::with_lexer;
92
93 let trimmed = input.trim();
94 if trimmed.is_empty() {
95 return Err(ParseError::EmptyQuery.into());
96 }
97
98 if !trimmed.contains('|') {
100 return Ok(None);
101 }
102
103 let tokens = with_lexer(input, |batch| Ok(batch.into_vec()))?;
104
105 let mut parser = Parser::new(tokens);
106 let pipeline = parser.parse_pipeline()?;
107 Ok(pipeline)
108 }
109
110 pub fn parse(&mut self) -> Result<Query, ParseError> {
116 if self.is_at_end() {
117 return Err(ParseError::EmptyQuery);
118 }
119
120 let start_span = self.peek().span.clone();
121 let root = self.parse_join()?;
122
123 if !self.is_at_end() && !matches!(self.peek().token_type, TokenType::Pipe) {
125 let token = self.peek();
126 return Err(ParseError::UnexpectedToken {
127 token: token.clone(),
128 expected: "end of query".to_string(),
129 });
130 }
131
132 let end_span = self.tokens[self.position - 1].span.clone();
133 let span = start_span.merge(&end_span);
134
135 Ok(Query { root, span })
136 }
137
138 fn parse_pipeline(&mut self) -> Result<Option<PipelineQuery>, ParseError> {
142 if self.is_at_end() {
143 return Err(ParseError::EmptyQuery);
144 }
145
146 let start_span = self.peek().span.clone();
147 let query = self.parse()?;
148
149 if !self.match_token(&TokenType::Pipe) {
151 return Ok(None);
152 }
153
154 let mut stages = Vec::new();
155 stages.push(self.parse_pipeline_stage()?);
156
157 while self.match_token(&TokenType::Pipe) {
158 stages.push(self.parse_pipeline_stage()?);
159 }
160
161 if !self.is_at_end() {
162 let token = self.peek();
163 return Err(ParseError::UnexpectedToken {
164 token: token.clone(),
165 expected: "end of query or '|'".to_string(),
166 });
167 }
168
169 let end_span = self.tokens[self.position - 1].span.clone();
170 let span = start_span.merge(&end_span);
171
172 Ok(Some(PipelineQuery {
173 query,
174 stages,
175 span,
176 }))
177 }
178
179 fn parse_pipeline_stage(&mut self) -> Result<PipelineStage, ParseError> {
181 let token = self.advance().clone();
182 match &token.token_type {
183 TokenType::Word(w) | TokenType::Identifier(w) => match w.to_lowercase().as_str() {
184 "count" => Ok(PipelineStage::Count),
185 "stats" => Ok(PipelineStage::Stats),
186 "group_by" => {
187 let field_token = self.advance().clone();
188 let field_name = match &field_token.token_type {
189 TokenType::Word(f) | TokenType::Identifier(f) => f.clone(),
190 _ => {
191 return Err(ParseError::InvalidSyntax {
192 message: "Expected field name after 'group_by'".to_string(),
193 span: field_token.span,
194 });
195 }
196 };
197 Ok(PipelineStage::GroupBy {
198 field: Field::new(field_name),
199 })
200 }
201 "top" => {
202 let n_token = self.advance().clone();
203 let n = match &n_token.token_type {
204 TokenType::NumberLiteral(n) => {
205 usize::try_from(*n).map_err(|_| ParseError::InvalidSyntax {
206 message: format!("Invalid count for 'top': {n}"),
207 span: n_token.span.clone(),
208 })?
209 }
210 _ => {
211 return Err(ParseError::InvalidSyntax {
212 message: "Expected number after 'top'".to_string(),
213 span: n_token.span,
214 });
215 }
216 };
217 let field_token = self.advance().clone();
218 let field_name = match &field_token.token_type {
219 TokenType::Word(f) | TokenType::Identifier(f) => f.clone(),
220 _ => {
221 return Err(ParseError::InvalidSyntax {
222 message: "Expected field name after 'top <N>'".to_string(),
223 span: field_token.span,
224 });
225 }
226 };
227 Ok(PipelineStage::Top {
228 n,
229 field: Field::new(field_name),
230 })
231 }
232 other => Err(ParseError::InvalidSyntax {
233 message: format!(
234 "Unknown pipeline stage: '{other}'. Expected: count, group_by, top, stats"
235 ),
236 span: token.span,
237 }),
238 },
239 _ => Err(ParseError::InvalidSyntax {
240 message: "Expected pipeline stage name after '|'".to_string(),
241 span: token.span,
242 }),
243 }
244 }
245
246 fn parse_join(&mut self) -> Result<Expr, ParseError> {
249 let left = self.parse_or()?;
250
251 let join_edge = match &self.peek().token_type {
253 TokenType::Word(w) => match w.to_uppercase().as_str() {
254 "CALLS" => Some(JoinEdgeKind::Calls),
255 "IMPORTS" => Some(JoinEdgeKind::Imports),
256 "INHERITS" => Some(JoinEdgeKind::Inherits),
257 "IMPLEMENTS" => Some(JoinEdgeKind::Implements),
258 _ => None,
259 },
260 _ => None,
261 };
262
263 if let Some(edge) = join_edge {
264 let join_token = self.advance().clone();
265 let right = self.parse_or()?;
266
267 let span = if let Some(left_span) = expr_span(&left) {
268 left_span.merge(&join_token.span)
269 } else {
270 join_token.span
271 };
272
273 Ok(Expr::Join(JoinExpr {
274 left: Box::new(left),
275 edge,
276 right: Box::new(right),
277 span,
278 }))
279 } else {
280 Ok(left)
281 }
282 }
283
284 fn parse_or(&mut self) -> Result<Expr, ParseError> {
287 let mut operands = vec![self.parse_and()?];
288
289 while self.match_token(&TokenType::Or) {
290 operands.push(self.parse_and()?);
291 }
292
293 if operands.len() == 1 {
294 operands
295 .into_iter()
296 .next()
297 .ok_or_else(|| ParseError::InvalidSyntax {
298 message: "Internal parser error: OR expression has no operands (this should never happen)".to_string(),
299 span: Span::new(0, 0),
301 })
302 } else {
303 Ok(Expr::Or(operands))
304 }
305 }
306
307 fn parse_and(&mut self) -> Result<Expr, ParseError> {
316 let mut operands = vec![self.parse_not()?];
317
318 loop {
319 if self.match_token(&TokenType::And) {
320 operands.push(self.parse_not()?);
322 } else if !self.is_at_end()
323 && !matches!(
324 self.peek().token_type,
325 TokenType::Or | TokenType::RParen | TokenType::Pipe
326 )
327 && !self.is_join_keyword()
328 {
329 operands.push(self.parse_not()?);
331 } else {
332 break;
333 }
334 }
335
336 if operands.len() == 1 {
337 operands
338 .into_iter()
339 .next()
340 .ok_or_else(|| ParseError::InvalidSyntax {
341 message: "Internal parser error: AND expression has no operands (this should never happen)".to_string(),
342 span: Span::new(0, 0),
344 })
345 } else {
346 Ok(Expr::And(operands))
347 }
348 }
349
350 fn is_join_keyword(&self) -> bool {
357 matches!(
358 &self.peek().token_type,
359 TokenType::Word(w)
360 if w.eq_ignore_ascii_case("CALLS")
361 || w.eq_ignore_ascii_case("IMPORTS")
362 || w.eq_ignore_ascii_case("INHERITS")
363 || w.eq_ignore_ascii_case("IMPLEMENTS")
364 )
365 }
366
367 fn parse_not(&mut self) -> Result<Expr, ParseError> {
370 if self.match_token(&TokenType::Not) {
371 let operand = self.parse_not()?; Ok(Expr::Not(Box::new(operand)))
373 } else {
374 self.parse_primary()
375 }
376 }
377
378 fn parse_primary(&mut self) -> Result<Expr, ParseError> {
381 if self.match_token(&TokenType::LParen) {
383 let lparen_span = self.tokens[self.position - 1].span.clone();
384 let expr = self.parse_or()?;
385
386 if !self.match_token(&TokenType::RParen) {
387 return Err(ParseError::UnmatchedParen {
388 open_span: lparen_span,
389 eof: self.is_at_end(),
390 });
391 }
392
393 return Ok(expr);
394 }
395
396 let token_type = self.peek().token_type.clone();
398 match token_type {
399 TokenType::Word(word) => {
400 let token = self.advance().clone();
401 Ok(Expr::Condition(Condition {
402 field: Field::new("name"),
403 operator: Operator::Regex,
404 value: Value::Regex(RegexValue {
405 pattern: word,
406 flags: RegexFlags::default(),
407 }),
408 span: token.span,
409 }))
410 }
411 TokenType::StringLiteral(value) => {
412 let token = self.advance().clone();
413 Ok(Expr::Condition(Condition {
414 field: Field::new("name"),
415 operator: Operator::Equal,
416 value: Value::String(value),
417 span: token.span,
418 }))
419 }
420 TokenType::RegexLiteral { pattern, flags } => {
421 let token = self.advance().clone();
422 Ok(Expr::Condition(Condition {
423 field: Field::new("name"),
424 operator: Operator::Regex,
425 value: Value::Regex(RegexValue { pattern, flags }),
426 span: token.span,
427 }))
428 }
429 TokenType::Variable(name) => {
430 let token = self.advance().clone();
431 Ok(Expr::Condition(Condition {
432 field: Field::new("name"),
433 operator: Operator::Equal,
434 value: Value::Variable(name),
435 span: token.span,
436 }))
437 }
438 _ => self.parse_condition(),
439 }
440 }
441
442 fn parse_condition(&mut self) -> Result<Expr, ParseError> {
448 let field_token = self.advance().clone();
450 let field = match &field_token.token_type {
451 TokenType::Identifier(name) => Field::new(name.clone()),
452 _ => {
453 return Err(ParseError::ExpectedIdentifier { token: field_token });
454 }
455 };
456
457 let operator_token = self.advance().clone();
459 let mut operator = match &operator_token.token_type {
460 TokenType::Colon => Operator::Equal,
461 TokenType::RegexOp => Operator::Regex,
462 TokenType::Greater => Operator::Greater,
463 TokenType::Less => Operator::Less,
464 TokenType::GreaterEq => Operator::GreaterEq,
465 TokenType::LessEq => Operator::LessEq,
466 _ => {
467 return Err(ParseError::ExpectedOperator {
468 token: operator_token,
469 });
470 }
471 };
472
473 let is_relation = crate::query::types::is_relation_field(field.as_str());
475 if is_relation
476 && operator == Operator::Equal
477 && matches!(self.peek().token_type, TokenType::LParen)
478 {
479 let lparen_span = self.peek().span.clone();
480 self.advance(); let inner_expr = self.parse_or()?;
482 if !self.match_token(&TokenType::RParen) {
483 return Err(ParseError::UnmatchedParen {
484 open_span: lparen_span,
485 eof: self.is_at_end(),
486 });
487 }
488
489 let end_span = self.tokens[self.position - 1].span.clone();
490 let span = field_token.span.merge(&end_span);
491
492 return Ok(Expr::Condition(Condition {
493 field,
494 operator,
495 value: Value::Subquery(Box::new(inner_expr)),
496 span,
497 }));
498 }
499
500 let value_token = self.advance().clone();
502 let mut value = match &value_token.token_type {
503 TokenType::StringLiteral(s) | TokenType::Word(s) => Value::String(s.clone()),
504 TokenType::RegexLiteral { pattern, flags } => Value::Regex(RegexValue {
505 pattern: pattern.clone(),
506 flags: flags.clone(),
507 }),
508 TokenType::NumberLiteral(n) => Value::Number(*n),
509 TokenType::BooleanLiteral(b) => Value::Boolean(*b),
510 TokenType::Variable(name) => Value::Variable(name.clone()),
511 _ => {
512 return Err(ParseError::ExpectedValue { token: value_token });
513 }
514 };
515
516 if operator == Operator::Regex {
517 if let Value::String(pattern) = &value {
518 value = Value::Regex(RegexValue {
519 pattern: pattern.clone(),
520 flags: RegexFlags::default(),
521 });
522 }
523 } else if operator == Operator::Equal && matches!(value, Value::Regex(_)) {
524 operator = Operator::Regex;
525 }
526
527 let span = field_token.span.merge(&value_token.span);
528
529 Ok(Expr::Condition(Condition {
530 field,
531 operator,
532 value,
533 span,
534 }))
535 }
536
537 fn match_token(&mut self, token_type: &TokenType) -> bool {
539 if self.check(token_type) {
540 self.advance();
541 true
542 } else {
543 false
544 }
545 }
546
547 fn check(&self, token_type: &TokenType) -> bool {
549 if self.is_at_end() {
550 return false;
551 }
552
553 std::mem::discriminant(&self.peek().token_type) == std::mem::discriminant(token_type)
554 }
555
556 fn advance(&mut self) -> &Token {
558 if !self.is_at_end() {
559 self.position += 1;
560 }
561 &self.tokens[self.position - 1]
562 }
563
564 fn is_at_end(&self) -> bool {
566 matches!(self.peek().token_type, TokenType::Eof)
567 }
568
569 fn peek(&self) -> &Token {
571 &self.tokens[self.position]
572 }
573}
574
575fn count_conditions(expr: &Expr) -> usize {
576 match expr {
577 Expr::And(operands) | Expr::Or(operands) => operands.iter().map(count_conditions).sum(),
578 Expr::Not(operand) => count_conditions(operand),
579 Expr::Condition(cond) => {
580 if let Value::Subquery(inner) = &cond.value {
582 1 + count_conditions(inner)
583 } else {
584 1
585 }
586 }
587 Expr::Join(join) => count_conditions(&join.left) + count_conditions(&join.right),
588 }
589}
590
591fn expr_span(expr: &Expr) -> Option<Span> {
593 match expr {
594 Expr::Condition(c) => Some(c.span.clone()),
595 Expr::Join(j) => Some(j.span.clone()),
596 Expr::And(ops) | Expr::Or(ops) => {
597 let first = ops.first().and_then(expr_span)?;
598 let last = ops.last().and_then(expr_span)?;
599 Some(first.merge(&last))
600 }
601 Expr::Not(inner) => expr_span(inner),
602 }
603}
604
605#[cfg(test)]
606mod tests {
607 use super::*;
608 use crate::query::lexer::with_lexer;
609 use crate::query::types::RegexFlags;
610
611 fn parse(input: &str) -> Result<Query, ParseError> {
612 let tokens = with_lexer(input, |batch| Ok(batch.into_vec())).unwrap();
613 let mut parser = Parser::new(tokens);
614 parser.parse()
615 }
616
617 #[test]
618 fn parse_query_reuses_thread_local_pool() {
619 crate::query::lexer::configure_pool_for_tests(
620 4,
621 crate::query::lexer::ShrinkPolicy::default(),
622 );
623
624 let (before_stash, before_in_flight, _) = crate::query::lexer::pool_stats_for_tests();
625 assert_eq!(before_stash, 0);
626 assert_eq!(before_in_flight, 0);
627
628 parse("kind:function").unwrap();
629 parse("name:test").unwrap();
630
631 let (after_stash, after_in_flight, max_size) = crate::query::lexer::pool_stats_for_tests();
632 assert!(max_size >= 1);
633 assert!(after_stash >= 1);
634 assert_eq!(after_in_flight, 0);
635
636 crate::query::lexer::reset_pool_to_default_for_tests();
637 }
638
639 #[test]
640 fn test_parse_simple_condition() {
641 let query = parse("kind:function").unwrap();
642
643 match query.root {
644 Expr::Condition(cond) => {
645 assert_eq!(cond.field.as_str(), "kind");
646 assert_eq!(cond.operator, Operator::Equal);
647 assert!(matches!(cond.value, Value::String(ref s) if s == "function"));
648 }
649 _ => panic!("Expected Condition"),
650 }
651 }
652
653 #[test]
654 fn test_parse_and() {
655 let query = parse("kind:function AND async:true").unwrap();
656
657 match query.root {
658 Expr::And(operands) => {
659 assert_eq!(operands.len(), 2);
660
661 match &operands[0] {
662 Expr::Condition(cond) => {
663 assert_eq!(cond.field.as_str(), "kind");
664 }
665 _ => panic!("Expected Condition"),
666 }
667
668 match &operands[1] {
669 Expr::Condition(cond) => {
670 assert_eq!(cond.field.as_str(), "async");
671 }
672 _ => panic!("Expected Condition"),
673 }
674 }
675 _ => panic!("Expected And"),
676 }
677 }
678
679 #[test]
680 fn test_parse_or() {
681 let query = parse("kind:function OR kind:class").unwrap();
682
683 match query.root {
684 Expr::Or(operands) => {
685 assert_eq!(operands.len(), 2);
686 }
687 _ => panic!("Expected Or"),
688 }
689 }
690
691 #[test]
692 fn test_parse_not() {
693 let query = parse("NOT kind:function").unwrap();
694
695 match query.root {
696 Expr::Not(operand) => match *operand {
697 Expr::Condition(cond) => {
698 assert_eq!(cond.field.as_str(), "kind");
699 }
700 _ => panic!("Expected Condition inside Not"),
701 },
702 _ => panic!("Expected Not"),
703 }
704 }
705
706 #[test]
707 fn test_precedence_or_and() {
708 let query = parse("a:1 OR b:2 AND c:3").unwrap();
710
711 match query.root {
712 Expr::Or(operands) => {
713 assert_eq!(operands.len(), 2);
714
715 assert!(matches!(operands[0], Expr::Condition(_)));
717
718 match &operands[1] {
720 Expr::And(and_operands) => {
721 assert_eq!(and_operands.len(), 2);
722 }
723 _ => panic!("Expected And as second operand of Or"),
724 }
725 }
726 _ => panic!("Expected Or"),
727 }
728 }
729
730 #[test]
731 fn test_precedence_not_and() {
732 let query = parse("NOT a:1 AND b:2").unwrap();
734
735 match query.root {
736 Expr::And(operands) => {
737 assert_eq!(operands.len(), 2);
738
739 assert!(matches!(operands[0], Expr::Not(_)));
741
742 assert!(matches!(operands[1], Expr::Condition(_)));
744 }
745 _ => panic!("Expected And"),
746 }
747 }
748
749 #[test]
750 fn test_parentheses_simple() {
751 let query = parse("(kind:function)").unwrap();
752
753 match query.root {
754 Expr::Condition(cond) => {
755 assert_eq!(cond.field.as_str(), "kind");
756 }
757 _ => panic!("Expected Condition"),
758 }
759 }
760
761 #[test]
762 fn test_parentheses_override_precedence() {
763 let query = parse("(a:1 OR b:2) AND c:3").unwrap();
765
766 match query.root {
767 Expr::And(operands) => {
768 assert_eq!(operands.len(), 2);
769
770 match &operands[0] {
772 Expr::Or(or_operands) => {
773 assert_eq!(or_operands.len(), 2);
774 }
775 _ => panic!("Expected Or as first operand"),
776 }
777
778 assert!(matches!(operands[1], Expr::Condition(_)));
780 }
781 _ => panic!("Expected And"),
782 }
783 }
784
785 #[test]
786 fn test_nested_parentheses() {
787 let query = parse("((a:1))").unwrap();
788
789 match query.root {
790 Expr::Condition(cond) => {
791 assert_eq!(cond.field.as_str(), "a");
792 }
793 _ => panic!("Expected Condition"),
794 }
795 }
796
797 #[test]
798 fn test_complex_query() {
799 let query =
800 parse("(kind:function OR kind:method) AND async:true AND NOT name~=/test/").unwrap();
801
802 match query.root {
803 Expr::And(operands) => {
804 assert_eq!(operands.len(), 3);
805
806 assert!(matches!(operands[0], Expr::Or(_)));
808
809 assert!(matches!(operands[1], Expr::Condition(_)));
811
812 assert!(matches!(operands[2], Expr::Not(_)));
814 }
815 _ => panic!("Expected And"),
816 }
817 }
818
819 #[test]
820 fn test_chained_and() {
821 let query = parse("a:1 AND b:2 AND c:3 AND d:4").unwrap();
822
823 match query.root {
824 Expr::And(operands) => {
825 assert_eq!(operands.len(), 4);
826 }
827 _ => panic!("Expected And"),
828 }
829 }
830
831 #[test]
832 fn test_chained_or() {
833 let query = parse("a:1 OR b:2 OR c:3 OR d:4").unwrap();
834
835 match query.root {
836 Expr::Or(operands) => {
837 assert_eq!(operands.len(), 4);
838 }
839 _ => panic!("Expected Or"),
840 }
841 }
842
843 #[test]
844 fn test_double_not() {
845 let query = parse("NOT NOT kind:function").unwrap();
846
847 match query.root {
848 Expr::Not(operand) => match *operand {
849 Expr::Not(inner) => match *inner {
850 Expr::Condition(_) => {}
851 _ => panic!("Expected Condition"),
852 },
853 _ => panic!("Expected Not"),
854 },
855 _ => panic!("Expected Not"),
856 }
857 }
858
859 #[test]
860 fn test_triple_not() {
861 let query = parse("NOT NOT NOT kind:function").unwrap();
862
863 match query.root {
864 Expr::Not(operand1) => match *operand1 {
865 Expr::Not(operand2) => match *operand2 {
866 Expr::Not(operand3) => match *operand3 {
867 Expr::Condition(_) => {}
868 _ => panic!("Expected Condition"),
869 },
870 _ => panic!("Expected Not"),
871 },
872 _ => panic!("Expected Not"),
873 },
874 _ => panic!("Expected Not"),
875 }
876 }
877
878 #[test]
879 fn test_operators_all() {
880 let _ = parse("a:b").unwrap(); let _ = parse("a~=/b/").unwrap(); let _ = parse("a>1").unwrap(); let _ = parse("a<1").unwrap(); let _ = parse("a>=1").unwrap(); let _ = parse("a<=1").unwrap(); }
888
889 #[test]
890 fn test_value_types() {
891 let query = parse(r#"name:"hello world""#).unwrap();
893 match query.root {
894 Expr::Condition(cond) => {
895 assert!(matches!(cond.value, Value::String(ref s) if s == "hello world"));
896 }
897 _ => panic!("Expected Condition"),
898 }
899
900 let query = parse("lines:42").unwrap();
902 match query.root {
903 Expr::Condition(cond) => {
904 assert!(matches!(cond.value, Value::Number(42)));
905 }
906 _ => panic!("Expected Condition"),
907 }
908
909 let query = parse("async:true").unwrap();
911 match query.root {
912 Expr::Condition(cond) => {
913 assert!(matches!(cond.value, Value::Boolean(true)));
914 }
915 _ => panic!("Expected Condition"),
916 }
917
918 let query = parse("name~=/^test_/i").unwrap();
920 match query.root {
921 Expr::Condition(cond) => match &cond.value {
922 Value::Regex(regex) => {
923 assert_eq!(regex.pattern, "^test_");
924 assert!(regex.flags.case_insensitive);
925 }
926 _ => panic!("Expected Regex value"),
927 },
928 _ => panic!("Expected Condition"),
929 }
930 }
931
932 #[test]
933 fn test_plain_word_defaults_to_name_regex() {
934 let query = parse("Error").unwrap();
935
936 match query.root {
937 Expr::Condition(cond) => {
938 assert_eq!(cond.field.as_str(), "name");
939 assert_eq!(cond.operator, Operator::Regex);
940 match cond.value {
941 Value::Regex(regex) => {
942 assert_eq!(regex.pattern, "Error");
943 assert_eq!(regex.flags, RegexFlags::default());
944 }
945 _ => panic!("Expected regex value"),
946 }
947 }
948 _ => panic!("Expected Condition"),
949 }
950 }
951
952 #[test]
953 fn test_plain_string_literal_defaults_to_name_equal() {
954 let query = parse(r#""Error""#).unwrap();
955
956 match query.root {
957 Expr::Condition(cond) => {
958 assert_eq!(cond.field.as_str(), "name");
959 assert_eq!(cond.operator, Operator::Equal);
960 match cond.value {
961 Value::String(value) => assert_eq!(value, "Error"),
962 _ => panic!("Expected string value"),
963 }
964 }
965 _ => panic!("Expected Condition"),
966 }
967 }
968
969 #[test]
970 fn test_error_empty_query() {
971 let result = parse("");
972 assert!(matches!(result, Err(ParseError::EmptyQuery)));
973 }
974
975 #[test]
976 fn test_error_unmatched_lparen() {
977 let result = parse("(kind:function");
978 assert!(matches!(result, Err(ParseError::UnmatchedParen { .. })));
979 }
980
981 #[test]
982 fn test_error_unmatched_rparen() {
983 let result = parse("kind:function)");
984 assert!(matches!(result, Err(ParseError::UnexpectedToken { .. })));
985 }
986
987 #[test]
988 fn test_error_missing_value() {
989 let result = parse("kind:");
990 assert!(matches!(result, Err(ParseError::ExpectedValue { .. })));
991 }
992
993 #[test]
994 fn test_error_incomplete_and() {
995 let result = parse("kind:function AND");
996 assert!(matches!(result, Err(ParseError::ExpectedIdentifier { .. })));
997 }
998
999 #[test]
1000 fn test_error_incomplete_or() {
1001 let result = parse("kind:function OR");
1002 assert!(matches!(result, Err(ParseError::ExpectedIdentifier { .. })));
1003 }
1004
1005 #[test]
1006 fn test_error_incomplete_not() {
1007 let result = parse("NOT");
1008 assert!(matches!(result, Err(ParseError::ExpectedIdentifier { .. })));
1009 }
1010
1011 #[test]
1012 fn test_bare_word_implicit_and_two_words() {
1013 let query = parse("kind function").unwrap();
1016 match query.root {
1017 Expr::And(operands) => {
1018 assert_eq!(operands.len(), 2);
1019 match &operands[0] {
1020 Expr::Condition(cond) => {
1021 assert_eq!(cond.field.as_str(), "name");
1022 assert_eq!(cond.operator, Operator::Regex);
1023 assert!(
1024 matches!(&cond.value, Value::Regex(r) if r.pattern == "kind"),
1025 "Expected regex pattern 'kind', got {:?}",
1026 cond.value
1027 );
1028 }
1029 other => panic!("Expected Condition for first bare word, got {other:?}"),
1030 }
1031 match &operands[1] {
1032 Expr::Condition(cond) => {
1033 assert_eq!(cond.field.as_str(), "name");
1034 assert_eq!(cond.operator, Operator::Regex);
1035 assert!(
1036 matches!(&cond.value, Value::Regex(r) if r.pattern == "function"),
1037 "Expected regex pattern 'function', got {:?}",
1038 cond.value
1039 );
1040 }
1041 other => panic!("Expected Condition for second bare word, got {other:?}"),
1042 }
1043 }
1044 other => panic!("Expected And from two bare words, got {other:?}"),
1045 }
1046 }
1047
1048 #[test]
1049 fn test_precedence_complex_1() {
1050 let query = parse("a:1 OR b:2 AND c:3 OR d:4").unwrap();
1052
1053 match query.root {
1054 Expr::Or(operands) => {
1055 assert_eq!(operands.len(), 3);
1056
1057 assert!(matches!(operands[0], Expr::Condition(_)));
1059
1060 assert!(matches!(operands[1], Expr::And(_)));
1062
1063 assert!(matches!(operands[2], Expr::Condition(_)));
1065 }
1066 _ => panic!("Expected Or"),
1067 }
1068 }
1069
1070 #[test]
1071 fn test_precedence_complex_2() {
1072 let query = parse("NOT a:1 OR b:2 AND c:3").unwrap();
1074
1075 match query.root {
1076 Expr::Or(operands) => {
1077 assert_eq!(operands.len(), 2);
1078
1079 assert!(matches!(operands[0], Expr::Not(_)));
1081
1082 assert!(matches!(operands[1], Expr::And(_)));
1084 }
1085 _ => panic!("Expected Or"),
1086 }
1087 }
1088
1089 #[test]
1090 fn test_multiple_groups() {
1091 let query = parse("(a:1 OR b:2) AND (c:3 OR d:4)").unwrap();
1093
1094 match query.root {
1095 Expr::And(operands) => {
1096 assert_eq!(operands.len(), 2);
1097
1098 assert!(matches!(operands[0], Expr::Or(_)));
1100 assert!(matches!(operands[1], Expr::Or(_)));
1101 }
1102 _ => panic!("Expected And"),
1103 }
1104 }
1105
1106 #[test]
1107 fn test_not_with_parentheses() {
1108 let query = parse("NOT (a:1 OR b:2)").unwrap();
1110
1111 match query.root {
1112 Expr::Not(operand) => match *operand {
1113 Expr::Or(or_operands) => {
1114 assert_eq!(or_operands.len(), 2);
1115 }
1116 _ => panic!("Expected Or inside Not"),
1117 },
1118 _ => panic!("Expected Not"),
1119 }
1120 }
1121
1122 #[test]
1123 fn test_deeply_nested() {
1124 let query = parse("((a:1 AND b:2) OR (c:3 AND d:4)) AND e:5").unwrap();
1126
1127 match query.root {
1128 Expr::And(operands) => {
1129 assert_eq!(operands.len(), 2);
1130
1131 match &operands[0] {
1133 Expr::Or(or_operands) => {
1134 assert_eq!(or_operands.len(), 2);
1135 assert!(matches!(or_operands[0], Expr::And(_)));
1136 assert!(matches!(or_operands[1], Expr::And(_)));
1137 }
1138 _ => panic!("Expected Or"),
1139 }
1140
1141 assert!(matches!(operands[1], Expr::Condition(_)));
1143 }
1144 _ => panic!("Expected And"),
1145 }
1146 }
1147
1148 #[test]
1151 fn test_parse_scope_type() {
1152 let query = parse("scope.type:class").unwrap();
1153
1154 match query.root {
1155 Expr::Condition(cond) => {
1156 assert_eq!(cond.field.as_str(), "scope.type");
1157 assert_eq!(cond.operator, Operator::Equal);
1158 assert!(matches!(cond.value, Value::String(ref s) if s == "class"));
1159 }
1160 _ => panic!("Expected Condition"),
1161 }
1162 }
1163
1164 #[test]
1165 fn test_parse_scope_name() {
1166 let query = parse("scope.name:UserService").unwrap();
1167
1168 match query.root {
1169 Expr::Condition(cond) => {
1170 assert_eq!(cond.field.as_str(), "scope.name");
1171 assert_eq!(cond.operator, Operator::Equal);
1172 assert!(matches!(cond.value, Value::String(ref s) if s == "UserService"));
1173 }
1174 _ => panic!("Expected Condition"),
1175 }
1176 }
1177
1178 #[test]
1179 fn test_parse_scope_parent() {
1180 let query = parse("scope.parent:Database").unwrap();
1181
1182 match query.root {
1183 Expr::Condition(cond) => {
1184 assert_eq!(cond.field.as_str(), "scope.parent");
1185 assert_eq!(cond.operator, Operator::Equal);
1186 assert!(matches!(cond.value, Value::String(ref s) if s == "Database"));
1187 }
1188 _ => panic!("Expected Condition"),
1189 }
1190 }
1191
1192 #[test]
1193 fn test_parse_scope_ancestor() {
1194 let query = parse("scope.ancestor:UserModule").unwrap();
1195
1196 match query.root {
1197 Expr::Condition(cond) => {
1198 assert_eq!(cond.field.as_str(), "scope.ancestor");
1199 assert_eq!(cond.operator, Operator::Equal);
1200 assert!(matches!(cond.value, Value::String(ref s) if s == "UserModule"));
1201 }
1202 _ => panic!("Expected Condition"),
1203 }
1204 }
1205
1206 #[test]
1207 fn test_parse_scope_type_with_regex() {
1208 let query = parse("scope.type~=/class|function/").unwrap();
1209
1210 match query.root {
1211 Expr::Condition(cond) => {
1212 assert_eq!(cond.field.as_str(), "scope.type");
1213 assert_eq!(cond.operator, Operator::Regex);
1214 assert!(matches!(cond.value, Value::Regex(_)));
1215 }
1216 _ => panic!("Expected Condition"),
1217 }
1218 }
1219
1220 #[test]
1221 fn test_parse_scope_and_kind() {
1222 let query = parse("scope.type:class AND name:connect").unwrap();
1223
1224 match query.root {
1225 Expr::And(operands) => {
1226 assert_eq!(operands.len(), 2);
1227
1228 match &operands[0] {
1229 Expr::Condition(cond) => {
1230 assert_eq!(cond.field.as_str(), "scope.type");
1231 }
1232 _ => panic!("Expected Condition"),
1233 }
1234
1235 match &operands[1] {
1236 Expr::Condition(cond) => {
1237 assert_eq!(cond.field.as_str(), "name");
1238 }
1239 _ => panic!("Expected Condition"),
1240 }
1241 }
1242 _ => panic!("Expected And"),
1243 }
1244 }
1245
1246 #[test]
1247 fn test_parse_scope_with_and_composition() {
1248 let query = parse("name:connect AND scope.ancestor:UserModule").unwrap();
1250
1251 match query.root {
1252 Expr::And(operands) => {
1253 assert_eq!(operands.len(), 2);
1254
1255 match &operands[0] {
1256 Expr::Condition(cond) => {
1257 assert_eq!(cond.field.as_str(), "name");
1258 assert!(matches!(cond.value, Value::String(ref s) if s == "connect"));
1259 }
1260 _ => panic!("Expected Condition"),
1261 }
1262
1263 match &operands[1] {
1264 Expr::Condition(cond) => {
1265 assert_eq!(cond.field.as_str(), "scope.ancestor");
1266 assert!(matches!(cond.value, Value::String(ref s) if s == "UserModule"));
1267 }
1268 _ => panic!("Expected Condition"),
1269 }
1270 }
1271 _ => panic!("Expected And"),
1272 }
1273 }
1274
1275 #[test]
1276 fn test_parse_not_scope_type() {
1277 let query = parse("NOT scope.type:test").unwrap();
1279
1280 match query.root {
1281 Expr::Not(operand) => match *operand {
1282 Expr::Condition(cond) => {
1283 assert_eq!(cond.field.as_str(), "scope.type");
1284 assert!(matches!(cond.value, Value::String(ref s) if s == "test"));
1285 }
1286 _ => panic!("Expected Condition inside Not"),
1287 },
1288 _ => panic!("Expected Not"),
1289 }
1290 }
1291
1292 #[test]
1293 fn test_parse_complex_scope_composition() {
1294 let query = parse("scope.type:class AND (name:connect OR name:disconnect)").unwrap();
1296
1297 match query.root {
1298 Expr::And(operands) => {
1299 assert_eq!(operands.len(), 2);
1300
1301 match &operands[0] {
1303 Expr::Condition(cond) => {
1304 assert_eq!(cond.field.as_str(), "scope.type");
1305 }
1306 _ => panic!("Expected Condition for scope.type"),
1307 }
1308
1309 match &operands[1] {
1311 Expr::Or(or_operands) => {
1312 assert_eq!(or_operands.len(), 2);
1313 match &or_operands[0] {
1314 Expr::Condition(cond) => {
1315 assert_eq!(cond.field.as_str(), "name");
1316 assert!(
1317 matches!(cond.value, Value::String(ref s) if s == "connect")
1318 );
1319 }
1320 _ => panic!("Expected name:connect"),
1321 }
1322 match &or_operands[1] {
1323 Expr::Condition(cond) => {
1324 assert_eq!(cond.field.as_str(), "name");
1325 assert!(
1326 matches!(cond.value, Value::String(ref s) if s == "disconnect")
1327 );
1328 }
1329 _ => panic!("Expected name:disconnect"),
1330 }
1331 }
1332 _ => panic!("Expected Or for name conditions"),
1333 }
1334 }
1335 _ => panic!("Expected And"),
1336 }
1337 }
1338
1339 #[test]
1340 fn test_parse_multiple_scope_filters() {
1341 let query =
1343 parse("scope.type:function AND scope.parent:Database AND scope.ancestor:UserModule")
1344 .unwrap();
1345
1346 match query.root {
1347 Expr::And(operands) => {
1348 assert_eq!(operands.len(), 3);
1349
1350 match &operands[0] {
1351 Expr::Condition(cond) => {
1352 assert_eq!(cond.field.as_str(), "scope.type");
1353 }
1354 _ => panic!("Expected scope.type condition"),
1355 }
1356
1357 match &operands[1] {
1358 Expr::Condition(cond) => {
1359 assert_eq!(cond.field.as_str(), "scope.parent");
1360 }
1361 _ => panic!("Expected scope.parent condition"),
1362 }
1363
1364 match &operands[2] {
1365 Expr::Condition(cond) => {
1366 assert_eq!(cond.field.as_str(), "scope.ancestor");
1367 }
1368 _ => panic!("Expected scope.ancestor condition"),
1369 }
1370 }
1371 _ => panic!("Expected And"),
1372 }
1373 }
1374
1375 #[test]
1379 fn test_single_operand_or_not_wrapped() {
1380 let query = parse("kind:function").unwrap();
1382
1383 match query.root {
1384 Expr::Condition(_) => {
1385 }
1387 Expr::Or(_) => panic!("Single operand should NOT be wrapped in Or expression"),
1388 _ => panic!("Expected Condition expression"),
1389 }
1390 }
1391
1392 #[test]
1393 fn test_multiple_operands_or_wrapped() {
1394 let query = parse("kind:function OR kind:class").unwrap();
1396
1397 match query.root {
1398 Expr::Or(operands) => {
1399 assert_eq!(
1400 operands.len(),
1401 2,
1402 "OR expression should have exactly 2 operands"
1403 );
1404 }
1405 _ => panic!("Multiple operands with OR should create Or expression"),
1406 }
1407 }
1408
1409 #[test]
1410 fn test_single_operand_and_not_wrapped() {
1411 let query = parse("name:test").unwrap();
1413
1414 match query.root {
1415 Expr::Condition(_) => {
1416 }
1418 Expr::And(_) => panic!("Single operand should NOT be wrapped in And expression"),
1419 _ => panic!("Expected Condition expression"),
1420 }
1421 }
1422
1423 #[test]
1424 fn test_multiple_operands_and_wrapped() {
1425 let query = parse("kind:function AND async:true").unwrap();
1427
1428 match query.root {
1429 Expr::And(operands) => {
1430 assert_eq!(
1431 operands.len(),
1432 2,
1433 "AND expression should have exactly 2 operands"
1434 );
1435 }
1436 _ => panic!("Multiple operands with AND should create And expression"),
1437 }
1438 }
1439
1440 #[test]
1443 fn test_parse_variable_value() {
1444 let query = parse("kind:$type").unwrap();
1445 match query.root {
1446 Expr::Condition(cond) => {
1447 assert_eq!(cond.field.as_str(), "kind");
1448 assert_eq!(cond.operator, Operator::Equal);
1449 assert!(matches!(cond.value, Value::Variable(ref n) if n == "type"));
1450 }
1451 _ => panic!("Expected Condition"),
1452 }
1453 }
1454
1455 #[test]
1456 fn test_parse_subquery() {
1457 let query = parse("callers:(kind:function)").unwrap();
1458 match query.root {
1459 Expr::Condition(cond) => {
1460 assert_eq!(cond.field.as_str(), "callers");
1461 assert_eq!(cond.operator, Operator::Equal);
1462 match cond.value {
1463 Value::Subquery(inner) => match *inner {
1464 Expr::Condition(inner_cond) => {
1465 assert_eq!(inner_cond.field.as_str(), "kind");
1466 assert!(
1467 matches!(inner_cond.value, Value::String(ref s) if s == "function")
1468 );
1469 }
1470 _ => panic!("Expected inner Condition"),
1471 },
1472 _ => panic!("Expected Subquery value"),
1473 }
1474 }
1475 _ => panic!("Expected Condition"),
1476 }
1477 }
1478
1479 #[test]
1480 fn test_parse_non_relation_field_not_subquery() {
1481 let query = parse("(kind:function)").unwrap();
1483 match query.root {
1484 Expr::Condition(cond) => {
1485 assert_eq!(cond.field.as_str(), "kind");
1486 assert!(matches!(cond.value, Value::String(_)));
1488 }
1489 _ => panic!("Expected Condition (grouping), not a subquery"),
1490 }
1491 }
1492
1493 #[test]
1494 fn test_parse_join_calls() {
1495 let query = parse("(kind:function) CALLS (kind:function)").unwrap();
1496 match query.root {
1497 Expr::Join(join) => {
1498 assert_eq!(join.edge, JoinEdgeKind::Calls);
1499 match *join.left {
1500 Expr::Condition(ref cond) => assert_eq!(cond.field.as_str(), "kind"),
1501 _ => panic!("Expected left Condition"),
1502 }
1503 match *join.right {
1504 Expr::Condition(ref cond) => assert_eq!(cond.field.as_str(), "kind"),
1505 _ => panic!("Expected right Condition"),
1506 }
1507 }
1508 _ => panic!("Expected Join expression"),
1509 }
1510 }
1511
1512 #[test]
1513 fn test_parse_join_imports() {
1514 let query = parse("(kind:function) IMPORTS (kind:module)").unwrap();
1515 match query.root {
1516 Expr::Join(join) => {
1517 assert_eq!(join.edge, JoinEdgeKind::Imports);
1518 match *join.right {
1519 Expr::Condition(ref cond) => {
1520 assert!(matches!(cond.value, Value::String(ref s) if s == "module"));
1521 }
1522 _ => panic!("Expected right Condition"),
1523 }
1524 }
1525 _ => panic!("Expected Join expression"),
1526 }
1527 }
1528
1529 #[test]
1530 fn test_parse_pipeline_count() {
1531 let pipeline = Parser::parse_pipeline_query("kind:function | count")
1532 .unwrap()
1533 .expect("Expected Some(PipelineQuery)");
1534 assert_eq!(pipeline.stages.len(), 1);
1535 assert!(matches!(pipeline.stages[0], PipelineStage::Count));
1536 }
1537
1538 #[test]
1539 fn test_parse_pipeline_group_by() {
1540 let pipeline = Parser::parse_pipeline_query("kind:function | group_by lang")
1541 .unwrap()
1542 .expect("Expected Some(PipelineQuery)");
1543 assert_eq!(pipeline.stages.len(), 1);
1544 match &pipeline.stages[0] {
1545 PipelineStage::GroupBy { field } => assert_eq!(field.as_str(), "lang"),
1546 other => panic!("Expected GroupBy, got {other:?}"),
1547 }
1548 }
1549
1550 #[test]
1551 fn test_parse_pipeline_top() {
1552 let pipeline = Parser::parse_pipeline_query("kind:function | top 10 lang")
1553 .unwrap()
1554 .expect("Expected Some(PipelineQuery)");
1555 assert_eq!(pipeline.stages.len(), 1);
1556 match &pipeline.stages[0] {
1557 PipelineStage::Top { n, field } => {
1558 assert_eq!(*n, 10);
1559 assert_eq!(field.as_str(), "lang");
1560 }
1561 other => panic!("Expected Top, got {other:?}"),
1562 }
1563 }
1564
1565 #[test]
1566 fn test_parse_pipeline_stats() {
1567 let pipeline = Parser::parse_pipeline_query("kind:function | stats")
1568 .unwrap()
1569 .expect("Expected Some(PipelineQuery)");
1570 assert_eq!(pipeline.stages.len(), 1);
1571 assert!(matches!(pipeline.stages[0], PipelineStage::Stats));
1572 }
1573
1574 #[test]
1577 fn test_implicit_and_three_conditions() {
1578 let query = parse("kind:function name~=/smb2_/ lang:c").unwrap();
1580 match query.root {
1581 Expr::And(ref operands) => assert_eq!(operands.len(), 3),
1582 other => panic!("Expected And with 3 operands, got {other:?}"),
1583 }
1584 }
1585
1586 #[test]
1587 fn test_implicit_and_bare_word() {
1588 let query = parse("kind:function smb2_open").unwrap();
1590 match query.root {
1591 Expr::And(ref operands) => {
1592 assert_eq!(operands.len(), 2);
1593 match &operands[0] {
1594 Expr::Condition(c) => assert_eq!(c.field.as_str(), "kind"),
1595 other => panic!("Expected kind condition, got {other:?}"),
1596 }
1597 match &operands[1] {
1598 Expr::Condition(c) => {
1599 assert_eq!(c.field.as_str(), "name");
1600 assert_eq!(c.operator, Operator::Regex);
1601 assert!(
1602 matches!(&c.value, Value::Regex(r) if r.pattern == "smb2_open"),
1603 "Expected bare word promoted to name regex"
1604 );
1605 }
1606 other => panic!("Expected bare-word condition, got {other:?}"),
1607 }
1608 }
1609 other => panic!("Expected And, got {other:?}"),
1610 }
1611 }
1612
1613 #[test]
1614 fn test_implicit_and_or_precedence() {
1615 let query = parse("kind:function OR lang:c name:main").unwrap();
1618 match query.root {
1619 Expr::Or(ref operands) => {
1620 assert_eq!(operands.len(), 2, "Expected 2 OR operands");
1621 match &operands[1] {
1622 Expr::And(and_ops) => assert_eq!(and_ops.len(), 2),
1623 other => panic!("Expected And on right side of OR, got {other:?}"),
1624 }
1625 }
1626 other => panic!("Expected Or expression, got {other:?}"),
1627 }
1628 }
1629
1630 #[test]
1631 fn test_implicit_and_before_not() {
1632 let query = parse("kind:function NOT name:test").unwrap();
1635 match query.root {
1636 Expr::And(ref operands) => {
1637 assert_eq!(operands.len(), 2);
1638 assert!(
1639 matches!(&operands[1], Expr::Not(_)),
1640 "Expected Not as second operand"
1641 );
1642 }
1643 other => panic!("Expected And expression, got {other:?}"),
1644 }
1645 }
1646
1647 #[test]
1648 fn test_implicit_and_before_paren_group() {
1649 let query = parse("lang:rust (kind:function OR kind:method)").unwrap();
1651 match query.root {
1652 Expr::And(ref operands) => {
1653 assert_eq!(operands.len(), 2);
1654 assert!(
1655 matches!(&operands[1], Expr::Or(_)),
1656 "Expected Or as second operand"
1657 );
1658 }
1659 other => panic!("Expected And expression, got {other:?}"),
1660 }
1661 }
1662
1663 #[test]
1664 fn test_implicit_and_stops_at_rparen() {
1665 let query = parse("(kind:function lang:c) OR kind:method").unwrap();
1667 match query.root {
1668 Expr::Or(ref operands) => {
1669 assert_eq!(operands.len(), 2);
1670 match &operands[0] {
1671 Expr::And(and_ops) => assert_eq!(and_ops.len(), 2),
1672 other => panic!("Expected And inside parens, got {other:?}"),
1673 }
1674 }
1675 other => panic!("Expected Or expression, got {other:?}"),
1676 }
1677 }
1678
1679 #[test]
1680 fn test_implicit_and_stops_at_pipe() {
1681 let pipeline = Parser::parse_pipeline_query("kind:function lang:c | count")
1683 .unwrap()
1684 .expect("Expected Some(PipelineQuery)");
1685 match &pipeline.query.root {
1687 Expr::And(ops) => assert_eq!(ops.len(), 2),
1688 other => panic!("Expected And filter, got {other:?}"),
1689 }
1690 assert_eq!(pipeline.stages.len(), 1);
1691 assert!(matches!(pipeline.stages[0], PipelineStage::Count));
1692 }
1693
1694 #[test]
1695 fn test_implicit_and_within_pipe_stage() {
1696 let pipeline = Parser::parse_pipeline_query("kind:function lang:c | count")
1698 .unwrap()
1699 .expect("Expected Some(PipelineQuery)");
1700 assert!(matches!(pipeline.stages[0], PipelineStage::Count));
1701 match &pipeline.query.root {
1702 Expr::And(ops) => assert_eq!(ops.len(), 2),
1703 other => panic!("Expected And, got {other:?}"),
1704 }
1705 let inner = parse("kind:method lang:rust").unwrap();
1707 assert!(matches!(inner.root, Expr::And(_)));
1708 }
1709
1710 #[test]
1711 fn test_implicit_and_after_relation_subquery() {
1712 let query = parse("callers:(kind:function name:main) lang:rust").unwrap();
1715 match query.root {
1716 Expr::And(ref ops) => {
1717 assert_eq!(ops.len(), 2, "Expected And with 2 operands");
1718 }
1719 other => panic!("Expected And, got {other:?}"),
1720 }
1721 }
1722
1723 #[test]
1724 fn test_error_and_and() {
1725 let result = parse("kind:function AND AND name:test");
1727 assert!(
1728 result.is_err(),
1729 "Expected parse error for consecutive AND keywords"
1730 );
1731 }
1732
1733 #[test]
1734 fn test_error_trailing_and() {
1735 let result = parse("kind:function AND");
1737 assert!(
1738 result.is_err(),
1739 "Expected parse error for trailing AND keyword"
1740 );
1741 }
1742
1743 #[test]
1744 fn test_error_colon_colon() {
1745 let result = parse(": :");
1747 assert!(result.is_err(), "Expected parse error for ':: '");
1748 }
1749
1750 #[test]
1751 fn test_implicit_and_regex_bare_token() {
1752 let query = parse("kind:function /test_/").unwrap();
1754 match query.root {
1755 Expr::And(ref ops) => {
1756 assert_eq!(ops.len(), 2);
1757 match &ops[1] {
1758 Expr::Condition(c) => {
1759 assert_eq!(c.field.as_str(), "name");
1760 assert_eq!(c.operator, Operator::Regex);
1761 }
1762 other => panic!("Expected Condition for regex bare token, got {other:?}"),
1763 }
1764 }
1765 other => panic!("Expected And, got {other:?}"),
1766 }
1767 }
1768
1769 #[test]
1770 fn test_implicit_and_string_literal_bare_token() {
1771 let query = parse(r#"kind:function "my_func""#).unwrap();
1773 match query.root {
1774 Expr::And(ref ops) => {
1775 assert_eq!(ops.len(), 2);
1776 match &ops[1] {
1777 Expr::Condition(c) => {
1778 assert_eq!(c.field.as_str(), "name");
1779 }
1780 other => panic!("Expected Condition for string literal, got {other:?}"),
1781 }
1782 }
1783 other => panic!("Expected And, got {other:?}"),
1784 }
1785 }
1786
1787 #[test]
1788 fn test_implicit_and_multiple_bare_words() {
1789 let query = parse("foo bar baz").unwrap();
1791 match query.root {
1792 Expr::And(ref ops) => assert_eq!(ops.len(), 3),
1793 other => panic!("Expected And with 3 operands, got {other:?}"),
1794 }
1795 }
1796
1797 #[test]
1798 fn test_implicit_and_does_not_affect_explicit_or() {
1799 let query = parse("kind:function OR kind:method").unwrap();
1801 assert!(
1802 matches!(query.root, Expr::Or(_)),
1803 "Expected Or, implicit AND must not absorb OR"
1804 );
1805 }
1806
1807 #[test]
1808 fn test_implicit_and_same_ast_as_explicit_and() {
1809 let implicit = parse("kind:function lang:rust").unwrap();
1812 let explicit = parse("kind:function AND lang:rust").unwrap();
1813
1814 fn assert_string_cond(expr: &Expr, expected_field: &str, expected_value: &str) {
1816 match expr {
1817 Expr::Condition(c) => {
1818 assert_eq!(
1819 c.field.as_str(),
1820 expected_field,
1821 "Expected field '{expected_field}'"
1822 );
1823 assert_eq!(c.operator, Operator::Equal, "Expected Equals operator");
1824 assert!(
1825 matches!(&c.value, Value::String(s) if s == expected_value),
1826 "Expected value '{expected_value}', got {:?}",
1827 c.value
1828 );
1829 }
1830 other => panic!("Expected Condition, got {other:?}"),
1831 }
1832 }
1833
1834 match (&implicit.root, &explicit.root) {
1835 (Expr::And(imp_ops), Expr::And(exp_ops)) => {
1836 assert_eq!(imp_ops.len(), 2, "Expected 2 implicit AND operands");
1837 assert_eq!(exp_ops.len(), 2, "Expected 2 explicit AND operands");
1838 assert_string_cond(&imp_ops[0], "kind", "function");
1840 assert_string_cond(&exp_ops[0], "kind", "function");
1841 assert_string_cond(&imp_ops[1], "lang", "rust");
1843 assert_string_cond(&exp_ops[1], "lang", "rust");
1844 }
1845 _ => panic!("Both queries must parse to And with 2 operands"),
1846 }
1847 }
1848
1849 #[test]
1850 fn test_implicit_and_with_leading_not() {
1851 let query = parse("NOT kind:function lang:rust").unwrap();
1854 match query.root {
1855 Expr::And(ref ops) => {
1856 assert_eq!(ops.len(), 2);
1857 assert!(
1858 matches!(&ops[0], Expr::Not(_)),
1859 "First operand should be Not"
1860 );
1861 }
1862 other => panic!("Expected And, got {other:?}"),
1863 }
1864 }
1865
1866 #[test]
1867 fn test_join_keyword_stops_implicit_and() {
1868 let query = parse("(kind:function) CALLS (kind:function)").unwrap();
1871 match query.root {
1874 Expr::Join(join) => {
1875 assert_eq!(
1876 join.edge,
1877 JoinEdgeKind::Calls,
1878 "Expected JoinEdgeKind::Calls"
1879 );
1880 match *join.left {
1881 Expr::Condition(ref c) => assert_eq!(c.field.as_str(), "kind"),
1882 other => panic!("Expected kind Condition on left, got {other:?}"),
1883 }
1884 match *join.right {
1885 Expr::Condition(ref c) => assert_eq!(c.field.as_str(), "kind"),
1886 other => panic!("Expected kind Condition on right, got {other:?}"),
1887 }
1888 }
1889 other => panic!("Expected Join expression, got {other:?}"),
1890 }
1891 }
1892}