Skip to main content

dkit_core/query/
parser.rs

1use crate::error::DkitError;
2
3/// 쿼리 AST (Abstract Syntax Tree)
4#[derive(Debug, Clone, PartialEq)]
5pub struct Query {
6    /// 경로 접근 (`.users[0].name`)
7    pub path: Path,
8    /// 파이프라인 연산 (`| where ...`)
9    pub operations: Vec<Operation>,
10}
11
12/// 경로: `.` + 세그먼트들
13#[derive(Debug, Clone, PartialEq)]
14pub struct Path {
15    pub segments: Vec<Segment>,
16}
17
18/// A single segment of a navigation path.
19#[derive(Debug, Clone, PartialEq)]
20#[non_exhaustive]
21pub enum Segment {
22    /// 필드 접근 (`.name`)
23    Field(String),
24    /// 배열 인덱스 접근 (`[0]`, `[-1]`)
25    Index(i64),
26    /// 배열 이터레이션 (`[]`)
27    Iterate,
28}
29
30/// Pipeline operation applied after path navigation (e.g., `| where ...`, `| sort ...`).
31#[derive(Debug, Clone, PartialEq)]
32#[non_exhaustive]
33pub enum Operation {
34    /// `where` 필터링
35    Where(Condition),
36    /// `select` 컬럼 선택: `select name, upper(name), round(price, 2)`
37    Select(Vec<SelectExpr>),
38    /// `sort` 정렬: `sort age` (오름차순) / `sort age desc` (내림차순)
39    Sort { field: String, descending: bool },
40    /// `limit` 결과 제한: `limit 10`
41    Limit(usize),
42    /// `count` 전체 카운트 / `count field` 비null 카운트
43    Count { field: Option<String> },
44    /// `sum field` 숫자 필드 합계
45    Sum { field: String },
46    /// `avg field` 숫자 필드 평균
47    Avg { field: String },
48    /// `min field` 최솟값
49    Min { field: String },
50    /// `max field` 최댓값
51    Max { field: String },
52    /// `distinct field` 고유값 목록
53    Distinct { field: String },
54    /// `group_by field1, field2` 그룹별 집계
55    /// 집계 연산: `group_by category | select category, count, sum_price`
56    GroupBy {
57        fields: Vec<String>,
58        having: Option<Condition>,
59        aggregates: Vec<GroupAggregate>,
60    },
61}
62
63/// GROUP BY 집계 연산 정의
64#[derive(Debug, Clone, PartialEq)]
65pub struct GroupAggregate {
66    pub func: AggregateFunc,
67    pub field: Option<String>,
68    pub alias: String,
69}
70
71/// Aggregate function used in `group_by` operations.
72#[derive(Debug, Clone, PartialEq)]
73#[non_exhaustive]
74pub enum AggregateFunc {
75    Count,
76    Sum,
77    Avg,
78    Min,
79    Max,
80}
81
82/// Boolean condition used in `where` clauses.
83#[derive(Debug, Clone, PartialEq)]
84#[non_exhaustive]
85pub enum Condition {
86    /// 단일 비교: `field op value`
87    Comparison(Comparison),
88    /// 논리 AND: `condition and condition`
89    And(Box<Condition>, Box<Condition>),
90    /// 논리 OR: `condition or condition`
91    Or(Box<Condition>, Box<Condition>),
92}
93
94/// 비교식: `IDENTIFIER compare_op value`
95#[derive(Debug, Clone, PartialEq)]
96pub struct Comparison {
97    pub field: String,
98    pub op: CompareOp,
99    pub value: LiteralValue,
100}
101
102/// Comparison operator used in `where` conditions.
103#[derive(Debug, Clone, PartialEq)]
104#[non_exhaustive]
105pub enum CompareOp {
106    Eq,         // ==
107    Ne,         // !=
108    Gt,         // >
109    Lt,         // <
110    Ge,         // >=
111    Le,         // <=
112    Contains,   // contains
113    StartsWith, // starts_with
114    EndsWith,   // ends_with
115}
116
117/// Literal value used as a comparison operand or in expressions.
118#[derive(Debug, Clone, PartialEq)]
119#[non_exhaustive]
120pub enum LiteralValue {
121    String(String),
122    Integer(i64),
123    Float(f64),
124    Bool(bool),
125    Null,
126}
127
128/// Expression used in `select` clauses and function arguments.
129#[derive(Debug, Clone, PartialEq)]
130#[non_exhaustive]
131pub enum Expr {
132    /// 필드 참조: `name`
133    Field(String),
134    /// 리터럴 값: `42`, `"hello"`, `true`
135    Literal(LiteralValue),
136    /// 함수 호출: `upper(name)`, `round(price, 2)`, `upper(trim(name))`
137    FuncCall { name: String, args: Vec<Expr> },
138}
139
140/// SELECT 절의 컬럼 표현식
141#[derive(Debug, Clone, PartialEq)]
142pub struct SelectExpr {
143    pub expr: Expr,
144    /// 출력 키 별칭 (`upper(name) as name_upper` 에서 `name_upper`)
145    pub alias: Option<String>,
146}
147
148/// Internal query string parser.
149///
150/// Use the public [`parse_query`] function instead of constructing this directly.
151pub(crate) struct Parser {
152    input: Vec<char>,
153    pos: usize,
154}
155
156impl Parser {
157    pub(crate) fn new(input: &str) -> Self {
158        Self {
159            input: input.chars().collect(),
160            pos: 0,
161        }
162    }
163
164    /// Parse the query string into a [`Query`] AST.
165    pub(crate) fn parse(&mut self) -> Result<Query, DkitError> {
166        self.skip_whitespace();
167        let path = self.parse_path()?;
168        self.skip_whitespace();
169
170        // 파이프라인 연산 파싱: `| where ...`
171        let mut operations = Vec::new();
172        while self.peek() == Some('|') {
173            self.advance(); // consume '|'
174            self.skip_whitespace();
175            operations.push(self.parse_operation()?);
176            self.skip_whitespace();
177        }
178
179        if self.pos != self.input.len() {
180            return Err(DkitError::QueryError(format!(
181                "unexpected character '{}' at position {}",
182                self.input[self.pos], self.pos
183            )));
184        }
185
186        Ok(Query { path, operations })
187    }
188
189    /// 경로를 파싱: `.` 으로 시작
190    fn parse_path(&mut self) -> Result<Path, DkitError> {
191        if !self.consume_char('.') {
192            return Err(DkitError::QueryError(
193                "query must start with '.'".to_string(),
194            ));
195        }
196
197        let mut segments = Vec::new();
198
199        // `.` 만 있으면 루트 경로 (세그먼트 없음)
200        if self.is_at_end() {
201            return Ok(Path { segments });
202        }
203
204        // 첫 번째 세그먼트: `[` 이면 인덱스/이터레이터, 아니면 필드
205        if self.peek() == Some('[') {
206            segments.push(self.parse_bracket()?);
207        } else if self.peek_is_identifier_start() {
208            segments.push(self.parse_field()?);
209        }
210
211        // 나머지 세그먼트
212        while !self.is_at_end() {
213            self.skip_whitespace();
214            if self.peek() == Some('.') {
215                self.advance(); // consume '.'
216                if self.peek() == Some('[') {
217                    segments.push(self.parse_bracket()?);
218                } else {
219                    segments.push(self.parse_field()?);
220                }
221            } else if self.peek() == Some('[') {
222                segments.push(self.parse_bracket()?);
223            } else {
224                break;
225            }
226        }
227
228        Ok(Path { segments })
229    }
230
231    /// 필드 이름 파싱
232    fn parse_field(&mut self) -> Result<Segment, DkitError> {
233        let start = self.pos;
234        while !self.is_at_end() {
235            let c = self.input[self.pos];
236            if c.is_alphanumeric() || c == '_' || c == '-' {
237                self.pos += 1;
238            } else {
239                break;
240            }
241        }
242
243        if self.pos == start {
244            return Err(DkitError::QueryError(format!(
245                "expected field name at position {}",
246                self.pos
247            )));
248        }
249
250        let name: String = self.input[start..self.pos].iter().collect();
251        Ok(Segment::Field(name))
252    }
253
254    /// `[...]` 파싱: 인덱스 또는 이터레이션
255    fn parse_bracket(&mut self) -> Result<Segment, DkitError> {
256        if !self.consume_char('[') {
257            return Err(DkitError::QueryError(format!(
258                "expected '[' at position {}",
259                self.pos
260            )));
261        }
262
263        self.skip_whitespace();
264
265        // `[]` — 이터레이션
266        if self.peek() == Some(']') {
267            self.advance();
268            return Ok(Segment::Iterate);
269        }
270
271        // `[N]` or `[-N]` — 인덱스
272        let negative = self.consume_char('-');
273        let start = self.pos;
274        while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
275            self.pos += 1;
276        }
277        if self.pos == start {
278            return Err(DkitError::QueryError(format!(
279                "expected integer index at position {}",
280                self.pos
281            )));
282        }
283
284        let num_str: String = self.input[start..self.pos].iter().collect();
285        let index: i64 = num_str.parse().map_err(|_| {
286            DkitError::QueryError(format!("invalid index '{}' at position {}", num_str, start))
287        })?;
288
289        self.skip_whitespace();
290        if !self.consume_char(']') {
291            return Err(DkitError::QueryError(format!(
292                "expected ']' at position {}",
293                self.pos
294            )));
295        }
296
297        Ok(Segment::Index(if negative { -index } else { index }))
298    }
299
300    // --- 파이프라인 연산 파싱 ---
301
302    /// 연산 파싱: `where ...`, `select ...`
303    fn parse_operation(&mut self) -> Result<Operation, DkitError> {
304        let keyword = self.parse_keyword()?;
305        match keyword.as_str() {
306            "where" => {
307                self.skip_whitespace();
308                let condition = self.parse_condition()?;
309                Ok(Operation::Where(condition))
310            }
311            "select" => {
312                self.skip_whitespace();
313                let exprs = self.parse_select_expr_list()?;
314                Ok(Operation::Select(exprs))
315            }
316            "sort" => {
317                self.skip_whitespace();
318                let field = self.parse_identifier()?;
319                self.skip_whitespace();
320                let descending = self.try_consume_keyword("desc");
321                Ok(Operation::Sort { field, descending })
322            }
323            "limit" => {
324                self.skip_whitespace();
325                let n = self.parse_positive_integer()?;
326                Ok(Operation::Limit(n))
327            }
328            "count" => {
329                self.skip_whitespace();
330                let field = self.try_parse_identifier();
331                Ok(Operation::Count { field })
332            }
333            "sum" => {
334                self.skip_whitespace();
335                let field = self.parse_identifier()?;
336                Ok(Operation::Sum { field })
337            }
338            "avg" => {
339                self.skip_whitespace();
340                let field = self.parse_identifier()?;
341                Ok(Operation::Avg { field })
342            }
343            "min" => {
344                self.skip_whitespace();
345                let field = self.parse_identifier()?;
346                Ok(Operation::Min { field })
347            }
348            "max" => {
349                self.skip_whitespace();
350                let field = self.parse_identifier()?;
351                Ok(Operation::Max { field })
352            }
353            "distinct" => {
354                self.skip_whitespace();
355                let field = self.parse_identifier()?;
356                Ok(Operation::Distinct { field })
357            }
358            "group_by" => {
359                self.skip_whitespace();
360                let fields = self.parse_identifier_list()?;
361                self.skip_whitespace();
362
363                // Parse optional aggregate functions
364                let aggregates = self.parse_group_aggregates()?;
365
366                // Parse optional HAVING clause
367                let having = if self.try_consume_keyword("having") {
368                    self.skip_whitespace();
369                    Some(self.parse_condition()?)
370                } else {
371                    None
372                };
373
374                Ok(Operation::GroupBy {
375                    fields,
376                    having,
377                    aggregates,
378                })
379            }
380            _ => Err(DkitError::QueryError(format!(
381                "unknown operation '{}' at position {}",
382                keyword,
383                self.pos - keyword.chars().count()
384            ))),
385        }
386    }
387
388    /// GROUP BY 집계 함수 목록 파싱: `count(), sum(field), avg(field), ...`
389    fn parse_group_aggregates(&mut self) -> Result<Vec<GroupAggregate>, DkitError> {
390        let mut aggregates = Vec::new();
391
392        loop {
393            let saved_pos = self.pos;
394            if let Some(agg) = self.try_parse_single_aggregate()? {
395                aggregates.push(agg);
396                self.skip_whitespace();
397                if !self.consume_char(',') {
398                    // No comma, check if next is "having" or end
399                    break;
400                }
401                self.skip_whitespace();
402            } else {
403                self.pos = saved_pos;
404                break;
405            }
406        }
407
408        Ok(aggregates)
409    }
410
411    /// 단일 집계 함수 파싱: `count()`, `sum(field)`, `avg(field)` 등
412    fn try_parse_single_aggregate(&mut self) -> Result<Option<GroupAggregate>, DkitError> {
413        let saved_pos = self.pos;
414
415        // Try to read a keyword
416        let func_name = match self.parse_keyword() {
417            Ok(name) => name,
418            Err(_) => {
419                self.pos = saved_pos;
420                return Ok(None);
421            }
422        };
423
424        let func = match func_name.as_str() {
425            "count" => AggregateFunc::Count,
426            "sum" => AggregateFunc::Sum,
427            "avg" => AggregateFunc::Avg,
428            "min" => AggregateFunc::Min,
429            "max" => AggregateFunc::Max,
430            _ => {
431                // Not an aggregate function, restore position
432                self.pos = saved_pos;
433                return Ok(None);
434            }
435        };
436
437        self.skip_whitespace();
438
439        // Must have '('
440        if !self.consume_char('(') {
441            self.pos = saved_pos;
442            return Ok(None);
443        }
444
445        self.skip_whitespace();
446
447        // Parse optional field name
448        let field = if self.peek() == Some(')') {
449            None
450        } else {
451            Some(self.parse_identifier()?)
452        };
453
454        self.skip_whitespace();
455
456        if !self.consume_char(')') {
457            return Err(DkitError::QueryError(format!(
458                "expected ')' at position {}",
459                self.pos
460            )));
461        }
462
463        // Generate alias
464        let alias = match &field {
465            Some(f) => format!("{}_{}", func_name, f),
466            None => func_name.clone(),
467        };
468
469        Ok(Some(GroupAggregate { func, field, alias }))
470    }
471
472    /// SELECT 절의 표현식 목록 파싱: `expr [as alias] ( "," expr [as alias] )*`
473    fn parse_select_expr_list(&mut self) -> Result<Vec<SelectExpr>, DkitError> {
474        let mut exprs = vec![self.parse_select_expr()?];
475        loop {
476            self.skip_whitespace();
477            if self.consume_char(',') {
478                self.skip_whitespace();
479                exprs.push(self.parse_select_expr()?);
480            } else {
481                break;
482            }
483        }
484        Ok(exprs)
485    }
486
487    /// 단일 SELECT 표현식 파싱: `expr [as alias]`
488    fn parse_select_expr(&mut self) -> Result<SelectExpr, DkitError> {
489        let expr = self.parse_expr()?;
490        self.skip_whitespace();
491        // Optional alias: `as alias_name`
492        let alias = {
493            let saved = self.pos;
494            if let Ok(keyword) = self.parse_keyword() {
495                if keyword == "as" {
496                    self.skip_whitespace();
497                    Some(self.parse_identifier()?)
498                } else {
499                    self.pos = saved;
500                    None
501                }
502            } else {
503                self.pos = saved;
504                None
505            }
506        };
507        Ok(SelectExpr { expr, alias })
508    }
509
510    /// 표현식 파싱: 필드 참조, 리터럴, 함수 호출
511    fn parse_expr(&mut self) -> Result<Expr, DkitError> {
512        match self.peek() {
513            Some('"') => {
514                let lit = self.parse_string_literal()?;
515                Ok(Expr::Literal(lit))
516            }
517            Some(c) if c.is_ascii_digit() => {
518                let lit = self.parse_number_literal()?;
519                Ok(Expr::Literal(lit))
520            }
521            Some(c) if c.is_alphabetic() || c == '_' => {
522                let name = self.parse_identifier()?;
523                // Check for bool/null literals
524                match name.as_str() {
525                    "true" => return Ok(Expr::Literal(LiteralValue::Bool(true))),
526                    "false" => return Ok(Expr::Literal(LiteralValue::Bool(false))),
527                    "null" => return Ok(Expr::Literal(LiteralValue::Null)),
528                    _ => {}
529                }
530                // Check for function call: name(...)
531                if self.peek() == Some('(') {
532                    self.advance(); // consume '('
533                    self.skip_whitespace();
534                    let mut args = Vec::new();
535                    if self.peek() != Some(')') {
536                        args.push(self.parse_expr()?);
537                        loop {
538                            self.skip_whitespace();
539                            if self.consume_char(',') {
540                                self.skip_whitespace();
541                                args.push(self.parse_expr()?);
542                            } else {
543                                break;
544                            }
545                        }
546                    }
547                    self.skip_whitespace();
548                    if !self.consume_char(')') {
549                        return Err(DkitError::QueryError(format!(
550                            "expected ')' at position {}",
551                            self.pos
552                        )));
553                    }
554                    Ok(Expr::FuncCall { name, args })
555                } else {
556                    Ok(Expr::Field(name))
557                }
558            }
559            Some(c) => Err(DkitError::QueryError(format!(
560                "expected expression at position {}, found '{}'",
561                self.pos, c
562            ))),
563            None => Err(DkitError::QueryError(format!(
564                "expected expression at position {}",
565                self.pos
566            ))),
567        }
568    }
569
570    /// 쉼표로 구분된 식별자 목록 파싱: `IDENTIFIER ( "," IDENTIFIER )*`
571    fn parse_identifier_list(&mut self) -> Result<Vec<String>, DkitError> {
572        let mut fields = vec![self.parse_identifier()?];
573        loop {
574            self.skip_whitespace();
575            if self.consume_char(',') {
576                self.skip_whitespace();
577                fields.push(self.parse_identifier()?);
578            } else {
579                break;
580            }
581        }
582        Ok(fields)
583    }
584
585    /// 키워드 파싱 (알파벳 + 언더스코어)
586    fn parse_keyword(&mut self) -> Result<String, DkitError> {
587        let start = self.pos;
588        while !self.is_at_end() {
589            let c = self.input[self.pos];
590            if c.is_alphabetic() || c == '_' {
591                self.pos += 1;
592            } else {
593                break;
594            }
595        }
596        if self.pos == start {
597            return Err(DkitError::QueryError(format!(
598                "expected operation keyword at position {}",
599                self.pos
600            )));
601        }
602        Ok(self.input[start..self.pos].iter().collect())
603    }
604
605    /// 조건식 파싱: `comparison (and|or comparison)*`
606    fn parse_condition(&mut self) -> Result<Condition, DkitError> {
607        let mut left = Condition::Comparison(self.parse_comparison()?);
608
609        loop {
610            self.skip_whitespace();
611            let saved_pos = self.pos;
612            if let Ok(keyword) = self.parse_keyword() {
613                match keyword.as_str() {
614                    "and" => {
615                        self.skip_whitespace();
616                        let right = Condition::Comparison(self.parse_comparison()?);
617                        left = Condition::And(Box::new(left), Box::new(right));
618                    }
619                    "or" => {
620                        self.skip_whitespace();
621                        let right = Condition::Comparison(self.parse_comparison()?);
622                        left = Condition::Or(Box::new(left), Box::new(right));
623                    }
624                    _ => {
625                        // Not a logical operator, restore position
626                        self.pos = saved_pos;
627                        break;
628                    }
629                }
630            } else {
631                break;
632            }
633        }
634
635        Ok(left)
636    }
637
638    /// 비교식 파싱: `IDENTIFIER compare_op literal_value`
639    fn parse_comparison(&mut self) -> Result<Comparison, DkitError> {
640        // 필드 이름
641        let field = self.parse_identifier()?;
642        self.skip_whitespace();
643
644        // 비교 연산자
645        let op = self.parse_compare_op()?;
646        self.skip_whitespace();
647
648        // 리터럴 값
649        let value = self.parse_literal_value()?;
650
651        Ok(Comparison { field, op, value })
652    }
653
654    /// 식별자 파싱 (필드 이름)
655    fn parse_identifier(&mut self) -> Result<String, DkitError> {
656        let start = self.pos;
657        while !self.is_at_end() {
658            let c = self.input[self.pos];
659            if c.is_alphanumeric() || c == '_' || c == '-' {
660                self.pos += 1;
661            } else {
662                break;
663            }
664        }
665        if self.pos == start {
666            return Err(DkitError::QueryError(format!(
667                "expected field name at position {}",
668                self.pos
669            )));
670        }
671        Ok(self.input[start..self.pos].iter().collect())
672    }
673
674    /// 비교 연산자 파싱: ==, !=, >=, <=, >, <, contains, starts_with, ends_with
675    fn parse_compare_op(&mut self) -> Result<CompareOp, DkitError> {
676        let c1 = self.peek().ok_or_else(|| {
677            DkitError::QueryError(format!(
678                "expected comparison operator at position {}",
679                self.pos
680            ))
681        })?;
682
683        match c1 {
684            '=' => {
685                self.advance();
686                if self.consume_char('=') {
687                    Ok(CompareOp::Eq)
688                } else {
689                    Err(DkitError::QueryError(format!(
690                        "expected '==' at position {}",
691                        self.pos - 1
692                    )))
693                }
694            }
695            '!' => {
696                self.advance();
697                if self.consume_char('=') {
698                    Ok(CompareOp::Ne)
699                } else {
700                    Err(DkitError::QueryError(format!(
701                        "expected '!=' at position {}",
702                        self.pos - 1
703                    )))
704                }
705            }
706            '>' => {
707                self.advance();
708                if self.consume_char('=') {
709                    Ok(CompareOp::Ge)
710                } else {
711                    Ok(CompareOp::Gt)
712                }
713            }
714            '<' => {
715                self.advance();
716                if self.consume_char('=') {
717                    Ok(CompareOp::Le)
718                } else {
719                    Ok(CompareOp::Lt)
720                }
721            }
722            c if c.is_alphabetic() => {
723                let saved_pos = self.pos;
724                let keyword = self.parse_keyword()?;
725                match keyword.as_str() {
726                    "contains" => Ok(CompareOp::Contains),
727                    "starts_with" => Ok(CompareOp::StartsWith),
728                    "ends_with" => Ok(CompareOp::EndsWith),
729                    _ => {
730                        self.pos = saved_pos;
731                        Err(DkitError::QueryError(format!(
732                            "expected comparison operator at position {}, found '{}'",
733                            saved_pos, keyword
734                        )))
735                    }
736                }
737            }
738            _ => Err(DkitError::QueryError(format!(
739                "expected comparison operator at position {}, found '{}'",
740                self.pos, c1
741            ))),
742        }
743    }
744
745    /// 리터럴 값 파싱: 문자열, 숫자, bool, null
746    fn parse_literal_value(&mut self) -> Result<LiteralValue, DkitError> {
747        match self.peek() {
748            Some('"') => self.parse_string_literal(),
749            Some(c) if c.is_ascii_digit() || c == '-' => self.parse_number_literal(),
750            Some(c) if c.is_alphabetic() => {
751                let word = self.parse_keyword()?;
752                match word.as_str() {
753                    "true" => Ok(LiteralValue::Bool(true)),
754                    "false" => Ok(LiteralValue::Bool(false)),
755                    "null" => Ok(LiteralValue::Null),
756                    _ => Err(DkitError::QueryError(format!(
757                        "unexpected value '{}' at position {}",
758                        word,
759                        self.pos - word.len()
760                    ))),
761                }
762            }
763            Some(c) => Err(DkitError::QueryError(format!(
764                "unexpected character '{}' at position {}",
765                c, self.pos
766            ))),
767            None => Err(DkitError::QueryError(format!(
768                "expected value at position {}",
769                self.pos
770            ))),
771        }
772    }
773
774    /// 문자열 리터럴 파싱: `"..."`
775    fn parse_string_literal(&mut self) -> Result<LiteralValue, DkitError> {
776        if !self.consume_char('"') {
777            return Err(DkitError::QueryError(format!(
778                "expected '\"' at position {}",
779                self.pos
780            )));
781        }
782        let start = self.pos;
783        while !self.is_at_end() && self.input[self.pos] != '"' {
784            self.pos += 1;
785        }
786        if self.is_at_end() {
787            return Err(DkitError::QueryError(format!(
788                "unterminated string starting at position {}",
789                start - 1
790            )));
791        }
792        let s: String = self.input[start..self.pos].iter().collect();
793        self.advance(); // consume closing '"'
794        Ok(LiteralValue::String(s))
795    }
796
797    /// 숫자 리터럴 파싱: 정수 또는 부동소수점
798    fn parse_number_literal(&mut self) -> Result<LiteralValue, DkitError> {
799        let start = self.pos;
800        if self.peek() == Some('-') {
801            self.advance();
802        }
803        while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
804            self.pos += 1;
805        }
806        let mut is_float = false;
807        if self.peek() == Some('.') {
808            is_float = true;
809            self.advance();
810            while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
811                self.pos += 1;
812            }
813        }
814        if self.pos == start || (self.pos == start + 1 && self.input[start] == '-') {
815            return Err(DkitError::QueryError(format!(
816                "expected number at position {}",
817                start
818            )));
819        }
820        let num_str: String = self.input[start..self.pos].iter().collect();
821        if is_float {
822            let f: f64 = num_str.parse().map_err(|_| {
823                DkitError::QueryError(format!(
824                    "invalid number '{}' at position {}",
825                    num_str, start
826                ))
827            })?;
828            Ok(LiteralValue::Float(f))
829        } else {
830            let n: i64 = num_str.parse().map_err(|_| {
831                DkitError::QueryError(format!(
832                    "invalid number '{}' at position {}",
833                    num_str, start
834                ))
835            })?;
836            Ok(LiteralValue::Integer(n))
837        }
838    }
839
840    // --- 유틸리티 ---
841
842    fn peek(&self) -> Option<char> {
843        self.input.get(self.pos).copied()
844    }
845
846    fn peek_is_identifier_start(&self) -> bool {
847        self.peek().is_some_and(|c| c.is_alphabetic() || c == '_')
848    }
849
850    fn advance(&mut self) {
851        self.pos += 1;
852    }
853
854    fn consume_char(&mut self, expected: char) -> bool {
855        if self.peek() == Some(expected) {
856            self.advance();
857            true
858        } else {
859            false
860        }
861    }
862
863    fn skip_whitespace(&mut self) {
864        while self.peek().is_some_and(|c| c.is_whitespace()) {
865            self.advance();
866        }
867    }
868
869    fn is_at_end(&self) -> bool {
870        self.pos >= self.input.len()
871    }
872
873    /// 식별자를 시도적으로 파싱: 식별자가 없으면 None 반환 (위치 복원)
874    fn try_parse_identifier(&mut self) -> Option<String> {
875        if !self.peek_is_identifier_start() {
876            return None;
877        }
878        let saved_pos = self.pos;
879        match self.parse_identifier() {
880            Ok(id) => Some(id),
881            Err(_) => {
882                self.pos = saved_pos;
883                None
884            }
885        }
886    }
887
888    /// 키워드를 시도적으로 소비: 매치하면 true, 아니면 위치를 복원하고 false
889    fn try_consume_keyword(&mut self, keyword: &str) -> bool {
890        let saved_pos = self.pos;
891        if let Ok(word) = self.parse_keyword() {
892            if word == keyword {
893                return true;
894            }
895        }
896        self.pos = saved_pos;
897        false
898    }
899
900    /// 양의 정수 파싱 (limit 절용)
901    fn parse_positive_integer(&mut self) -> Result<usize, DkitError> {
902        let start = self.pos;
903        while !self.is_at_end() && self.input[self.pos].is_ascii_digit() {
904            self.pos += 1;
905        }
906        if self.pos == start {
907            return Err(DkitError::QueryError(format!(
908                "expected positive integer at position {}",
909                self.pos
910            )));
911        }
912        let num_str: String = self.input[start..self.pos].iter().collect();
913        num_str.parse().map_err(|_| {
914            DkitError::QueryError(format!(
915                "invalid integer '{}' at position {}",
916                num_str, start
917            ))
918        })
919    }
920}
921
922/// 편의 함수: 쿼리 문자열 → Query
923pub fn parse_query(input: &str) -> Result<Query, DkitError> {
924    Parser::new(input).parse()
925}
926
927/// where 절의 조건식만 파싱하는 편의 함수
928/// 예: "age > 30 and city == \"Seoul\""
929pub fn parse_condition_expr(input: &str) -> Result<Condition, DkitError> {
930    let mut parser = Parser::new(input);
931    parser.skip_whitespace();
932    let condition = parser.parse_condition()?;
933    parser.skip_whitespace();
934    if parser.pos != parser.input.len() {
935        return Err(DkitError::QueryError(format!(
936            "unexpected character '{}' at position {} in where expression",
937            parser.input[parser.pos], parser.pos
938        )));
939    }
940    Ok(condition)
941}
942
943#[cfg(test)]
944mod tests {
945    use super::*;
946
947    // --- 기본 경로 파싱 ---
948
949    #[test]
950    fn test_root_path() {
951        let q = parse_query(".").unwrap();
952        assert!(q.path.segments.is_empty());
953    }
954
955    #[test]
956    fn test_single_field() {
957        let q = parse_query(".name").unwrap();
958        assert_eq!(q.path.segments, vec![Segment::Field("name".to_string())]);
959    }
960
961    #[test]
962    fn test_nested_fields() {
963        let q = parse_query(".database.host").unwrap();
964        assert_eq!(
965            q.path.segments,
966            vec![
967                Segment::Field("database".to_string()),
968                Segment::Field("host".to_string()),
969            ]
970        );
971    }
972
973    #[test]
974    fn test_deeply_nested_fields() {
975        let q = parse_query(".a.b.c.d").unwrap();
976        assert_eq!(q.path.segments.len(), 4);
977        assert_eq!(q.path.segments[3], Segment::Field("d".to_string()));
978    }
979
980    // --- 배열 인덱싱 ---
981
982    #[test]
983    fn test_array_index() {
984        let q = parse_query(".users[0]").unwrap();
985        assert_eq!(
986            q.path.segments,
987            vec![Segment::Field("users".to_string()), Segment::Index(0),]
988        );
989    }
990
991    #[test]
992    fn test_array_negative_index() {
993        let q = parse_query(".users[-1]").unwrap();
994        assert_eq!(
995            q.path.segments,
996            vec![Segment::Field("users".to_string()), Segment::Index(-1),]
997        );
998    }
999
1000    #[test]
1001    fn test_array_index_with_field_after() {
1002        let q = parse_query(".users[0].name").unwrap();
1003        assert_eq!(
1004            q.path.segments,
1005            vec![
1006                Segment::Field("users".to_string()),
1007                Segment::Index(0),
1008                Segment::Field("name".to_string()),
1009            ]
1010        );
1011    }
1012
1013    #[test]
1014    fn test_large_index() {
1015        let q = parse_query(".items[999]").unwrap();
1016        assert_eq!(
1017            q.path.segments,
1018            vec![Segment::Field("items".to_string()), Segment::Index(999),]
1019        );
1020    }
1021
1022    // --- 배열 이터레이션 ---
1023
1024    #[test]
1025    fn test_array_iterate() {
1026        let q = parse_query(".users[]").unwrap();
1027        assert_eq!(
1028            q.path.segments,
1029            vec![Segment::Field("users".to_string()), Segment::Iterate,]
1030        );
1031    }
1032
1033    #[test]
1034    fn test_array_iterate_with_field() {
1035        let q = parse_query(".users[].name").unwrap();
1036        assert_eq!(
1037            q.path.segments,
1038            vec![
1039                Segment::Field("users".to_string()),
1040                Segment::Iterate,
1041                Segment::Field("name".to_string()),
1042            ]
1043        );
1044    }
1045
1046    #[test]
1047    fn test_array_iterate_nested() {
1048        let q = parse_query(".data[].items[].name").unwrap();
1049        assert_eq!(
1050            q.path.segments,
1051            vec![
1052                Segment::Field("data".to_string()),
1053                Segment::Iterate,
1054                Segment::Field("items".to_string()),
1055                Segment::Iterate,
1056                Segment::Field("name".to_string()),
1057            ]
1058        );
1059    }
1060
1061    // --- 복합 경로 ---
1062
1063    #[test]
1064    fn test_complex_path() {
1065        let q = parse_query(".data.users[0].address.city").unwrap();
1066        assert_eq!(
1067            q.path.segments,
1068            vec![
1069                Segment::Field("data".to_string()),
1070                Segment::Field("users".to_string()),
1071                Segment::Index(0),
1072                Segment::Field("address".to_string()),
1073                Segment::Field("city".to_string()),
1074            ]
1075        );
1076    }
1077
1078    // --- 필드 이름에 특수 문자 ---
1079
1080    #[test]
1081    fn test_field_with_underscore() {
1082        let q = parse_query(".user_name").unwrap();
1083        assert_eq!(
1084            q.path.segments,
1085            vec![Segment::Field("user_name".to_string())]
1086        );
1087    }
1088
1089    #[test]
1090    fn test_field_with_hyphen() {
1091        let q = parse_query(".content-type").unwrap();
1092        assert_eq!(
1093            q.path.segments,
1094            vec![Segment::Field("content-type".to_string())]
1095        );
1096    }
1097
1098    #[test]
1099    fn test_field_with_digits() {
1100        let q = parse_query(".field1").unwrap();
1101        assert_eq!(q.path.segments, vec![Segment::Field("field1".to_string())]);
1102    }
1103
1104    // --- 에러 케이스 ---
1105
1106    #[test]
1107    fn test_error_no_dot() {
1108        let err = parse_query("name").unwrap_err();
1109        assert!(matches!(err, DkitError::QueryError(_)));
1110    }
1111
1112    #[test]
1113    fn test_error_empty() {
1114        let err = parse_query("").unwrap_err();
1115        assert!(matches!(err, DkitError::QueryError(_)));
1116    }
1117
1118    #[test]
1119    fn test_error_unclosed_bracket() {
1120        let err = parse_query(".users[0").unwrap_err();
1121        assert!(matches!(err, DkitError::QueryError(_)));
1122    }
1123
1124    #[test]
1125    fn test_error_invalid_index() {
1126        let err = parse_query(".users[abc]").unwrap_err();
1127        assert!(matches!(err, DkitError::QueryError(_)));
1128    }
1129
1130    #[test]
1131    fn test_error_trailing_garbage() {
1132        let err = parse_query(".name xyz").unwrap_err();
1133        assert!(matches!(err, DkitError::QueryError(_)));
1134    }
1135
1136    // --- 공백 처리 ---
1137
1138    #[test]
1139    fn test_whitespace_around() {
1140        let q = parse_query("  .name  ").unwrap();
1141        assert_eq!(q.path.segments, vec![Segment::Field("name".to_string())]);
1142    }
1143
1144    // --- 루트 배열 접근 ---
1145
1146    #[test]
1147    fn test_root_array_index() {
1148        let q = parse_query(".[0]").unwrap();
1149        assert_eq!(q.path.segments, vec![Segment::Index(0)]);
1150    }
1151
1152    #[test]
1153    fn test_root_array_iterate() {
1154        let q = parse_query(".[]").unwrap();
1155        assert_eq!(q.path.segments, vec![Segment::Iterate]);
1156    }
1157
1158    #[test]
1159    fn test_root_iterate_with_field() {
1160        let q = parse_query(".[].name").unwrap();
1161        assert_eq!(
1162            q.path.segments,
1163            vec![Segment::Iterate, Segment::Field("name".to_string()),]
1164        );
1165    }
1166
1167    // --- where 절 파싱 ---
1168
1169    #[test]
1170    fn test_where_eq_integer() {
1171        let q = parse_query(".users[] | where age == 30").unwrap();
1172        assert_eq!(
1173            q.path.segments,
1174            vec![Segment::Field("users".to_string()), Segment::Iterate]
1175        );
1176        assert_eq!(q.operations.len(), 1);
1177        assert_eq!(
1178            q.operations[0],
1179            Operation::Where(Condition::Comparison(Comparison {
1180                field: "age".to_string(),
1181                op: CompareOp::Eq,
1182                value: LiteralValue::Integer(30),
1183            }))
1184        );
1185    }
1186
1187    #[test]
1188    fn test_where_ne_string() {
1189        let q = parse_query(".items[] | where status != \"inactive\"").unwrap();
1190        assert_eq!(
1191            q.operations[0],
1192            Operation::Where(Condition::Comparison(Comparison {
1193                field: "status".to_string(),
1194                op: CompareOp::Ne,
1195                value: LiteralValue::String("inactive".to_string()),
1196            }))
1197        );
1198    }
1199
1200    #[test]
1201    fn test_where_gt() {
1202        let q = parse_query(".[] | where age > 25").unwrap();
1203        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1204            panic!("expected Comparison");
1205        };
1206        assert_eq!(cmp.field, "age");
1207        assert_eq!(cmp.op, CompareOp::Gt);
1208        assert_eq!(cmp.value, LiteralValue::Integer(25));
1209    }
1210
1211    #[test]
1212    fn test_where_lt() {
1213        let q = parse_query(".[] | where price < 100").unwrap();
1214        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1215            panic!("expected Comparison");
1216        };
1217        assert_eq!(cmp.op, CompareOp::Lt);
1218        assert_eq!(cmp.value, LiteralValue::Integer(100));
1219    }
1220
1221    #[test]
1222    fn test_where_ge() {
1223        let q = parse_query(".[] | where score >= 80").unwrap();
1224        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1225            panic!("expected Comparison");
1226        };
1227        assert_eq!(cmp.op, CompareOp::Ge);
1228        assert_eq!(cmp.value, LiteralValue::Integer(80));
1229    }
1230
1231    #[test]
1232    fn test_where_le() {
1233        let q = parse_query(".[] | where price <= 1000").unwrap();
1234        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1235            panic!("expected Comparison");
1236        };
1237        assert_eq!(cmp.op, CompareOp::Le);
1238        assert_eq!(cmp.value, LiteralValue::Integer(1000));
1239    }
1240
1241    #[test]
1242    fn test_where_float_literal() {
1243        let q = parse_query(".[] | where score > 3.14").unwrap();
1244        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1245            panic!("expected Comparison");
1246        };
1247        assert_eq!(cmp.value, LiteralValue::Float(3.14));
1248    }
1249
1250    #[test]
1251    fn test_where_negative_number() {
1252        let q = parse_query(".[] | where temp > -10").unwrap();
1253        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1254            panic!("expected Comparison");
1255        };
1256        assert_eq!(cmp.value, LiteralValue::Integer(-10));
1257    }
1258
1259    #[test]
1260    fn test_where_bool_literal() {
1261        let q = parse_query(".[] | where active == true").unwrap();
1262        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1263            panic!("expected Comparison");
1264        };
1265        assert_eq!(cmp.value, LiteralValue::Bool(true));
1266    }
1267
1268    #[test]
1269    fn test_where_null_literal() {
1270        let q = parse_query(".[] | where value == null").unwrap();
1271        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1272            panic!("expected Comparison");
1273        };
1274        assert_eq!(cmp.value, LiteralValue::Null);
1275    }
1276
1277    #[test]
1278    fn test_where_no_operations_for_path_only() {
1279        let q = parse_query(".users[0].name").unwrap();
1280        assert!(q.operations.is_empty());
1281    }
1282
1283    #[test]
1284    fn test_where_with_extra_whitespace() {
1285        let q = parse_query(".[]  |  where  age  >  30  ").unwrap();
1286        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1287            panic!("expected Comparison");
1288        };
1289        assert_eq!(cmp.field, "age");
1290        assert_eq!(cmp.op, CompareOp::Gt);
1291        assert_eq!(cmp.value, LiteralValue::Integer(30));
1292    }
1293
1294    // --- where 파싱 에러 ---
1295
1296    #[test]
1297    fn test_error_where_missing_field() {
1298        let err = parse_query(".[] | where == 30").unwrap_err();
1299        assert!(matches!(err, DkitError::QueryError(_)));
1300    }
1301
1302    #[test]
1303    fn test_error_where_missing_operator() {
1304        let err = parse_query(".[] | where age 30").unwrap_err();
1305        assert!(matches!(err, DkitError::QueryError(_)));
1306    }
1307
1308    #[test]
1309    fn test_error_where_missing_value() {
1310        let err = parse_query(".[] | where age >").unwrap_err();
1311        assert!(matches!(err, DkitError::QueryError(_)));
1312    }
1313
1314    #[test]
1315    fn test_error_where_unterminated_string() {
1316        let err = parse_query(".[] | where name == \"hello").unwrap_err();
1317        assert!(matches!(err, DkitError::QueryError(_)));
1318    }
1319
1320    #[test]
1321    fn test_error_unknown_operation() {
1322        let err = parse_query(".[] | foobar age > 30").unwrap_err();
1323        assert!(matches!(err, DkitError::QueryError(_)));
1324    }
1325
1326    // --- 문자열 연산자 파싱 ---
1327
1328    #[test]
1329    fn test_where_contains() {
1330        let q = parse_query(".[] | where email contains \"@gmail\"").unwrap();
1331        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1332            panic!("expected Comparison");
1333        };
1334        assert_eq!(cmp.field, "email");
1335        assert_eq!(cmp.op, CompareOp::Contains);
1336        assert_eq!(cmp.value, LiteralValue::String("@gmail".to_string()));
1337    }
1338
1339    #[test]
1340    fn test_where_starts_with() {
1341        let q = parse_query(".[] | where name starts_with \"A\"").unwrap();
1342        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1343            panic!("expected Comparison");
1344        };
1345        assert_eq!(cmp.field, "name");
1346        assert_eq!(cmp.op, CompareOp::StartsWith);
1347        assert_eq!(cmp.value, LiteralValue::String("A".to_string()));
1348    }
1349
1350    #[test]
1351    fn test_where_ends_with() {
1352        let q = parse_query(".[] | where file ends_with \".json\"").unwrap();
1353        let Operation::Where(Condition::Comparison(cmp)) = &q.operations[0] else {
1354            panic!("expected Comparison");
1355        };
1356        assert_eq!(cmp.field, "file");
1357        assert_eq!(cmp.op, CompareOp::EndsWith);
1358        assert_eq!(cmp.value, LiteralValue::String(".json".to_string()));
1359    }
1360
1361    // --- 논리 연산자 파싱 ---
1362
1363    #[test]
1364    fn test_where_and() {
1365        let q = parse_query(".[] | where age > 25 and city == \"Seoul\"").unwrap();
1366        let Operation::Where(cond) = &q.operations[0] else {
1367            panic!("expected Where operation");
1368        };
1369        match cond {
1370            Condition::And(left, right) => {
1371                let Condition::Comparison(l) = left.as_ref() else {
1372                    panic!("expected left Comparison");
1373                };
1374                assert_eq!(l.field, "age");
1375                assert_eq!(l.op, CompareOp::Gt);
1376                assert_eq!(l.value, LiteralValue::Integer(25));
1377                let Condition::Comparison(r) = right.as_ref() else {
1378                    panic!("expected right Comparison");
1379                };
1380                assert_eq!(r.field, "city");
1381                assert_eq!(r.op, CompareOp::Eq);
1382                assert_eq!(r.value, LiteralValue::String("Seoul".to_string()));
1383            }
1384            _ => panic!("expected And condition"),
1385        }
1386    }
1387
1388    #[test]
1389    fn test_where_or() {
1390        let q = parse_query(".[] | where role == \"admin\" or role == \"manager\"").unwrap();
1391        let Operation::Where(cond) = &q.operations[0] else {
1392            panic!("expected Where operation");
1393        };
1394        match cond {
1395            Condition::Or(left, right) => {
1396                let Condition::Comparison(l) = left.as_ref() else {
1397                    panic!("expected left Comparison");
1398                };
1399                assert_eq!(l.field, "role");
1400                assert_eq!(l.value, LiteralValue::String("admin".to_string()));
1401                let Condition::Comparison(r) = right.as_ref() else {
1402                    panic!("expected right Comparison");
1403                };
1404                assert_eq!(r.field, "role");
1405                assert_eq!(r.value, LiteralValue::String("manager".to_string()));
1406            }
1407            _ => panic!("expected Or condition"),
1408        }
1409    }
1410
1411    #[test]
1412    fn test_where_and_with_string_op() {
1413        let q = parse_query(".[] | where name starts_with \"A\" and age > 20").unwrap();
1414        let Operation::Where(cond) = &q.operations[0] else {
1415            panic!("expected Where operation");
1416        };
1417        assert!(matches!(cond, Condition::And(_, _)));
1418    }
1419
1420    #[test]
1421    fn test_where_chained_and() {
1422        let q = parse_query(".[] | where a == 1 and b == 2 and c == 3").unwrap();
1423        let Operation::Where(cond) = &q.operations[0] else {
1424            panic!("expected Where operation");
1425        };
1426        // Left-associative: ((a==1 and b==2) and c==3)
1427        match cond {
1428            Condition::And(left, right) => {
1429                assert!(matches!(left.as_ref(), Condition::And(_, _)));
1430                assert!(matches!(right.as_ref(), Condition::Comparison(_)));
1431            }
1432            _ => panic!("expected And condition"),
1433        }
1434    }
1435
1436    // --- select 절 파싱 ---
1437
1438    fn field(name: &str) -> SelectExpr {
1439        SelectExpr {
1440            expr: Expr::Field(name.to_string()),
1441            alias: None,
1442        }
1443    }
1444
1445    fn fields(names: &[&str]) -> Operation {
1446        Operation::Select(names.iter().map(|n| field(n)).collect())
1447    }
1448
1449    #[test]
1450    fn test_select_single_field() {
1451        let q = parse_query(".users[] | select name").unwrap();
1452        assert_eq!(q.operations.len(), 1);
1453        assert_eq!(q.operations[0], fields(&["name"]));
1454    }
1455
1456    #[test]
1457    fn test_select_multiple_fields() {
1458        let q = parse_query(".users[] | select name, email").unwrap();
1459        assert_eq!(q.operations[0], fields(&["name", "email"]));
1460    }
1461
1462    #[test]
1463    fn test_select_three_fields() {
1464        let q = parse_query(".users[] | select name, age, email").unwrap();
1465        assert_eq!(q.operations[0], fields(&["name", "age", "email"]));
1466    }
1467
1468    #[test]
1469    fn test_select_with_extra_whitespace() {
1470        let q = parse_query(".[]  |  select  name ,  email  ").unwrap();
1471        assert_eq!(q.operations[0], fields(&["name", "email"]));
1472    }
1473
1474    #[test]
1475    fn test_select_field_with_underscore() {
1476        let q = parse_query(".[] | select user_name, created_at").unwrap();
1477        assert_eq!(q.operations[0], fields(&["user_name", "created_at"]));
1478    }
1479
1480    #[test]
1481    fn test_select_field_with_hyphen() {
1482        let q = parse_query(".[] | select content-type").unwrap();
1483        assert_eq!(q.operations[0], fields(&["content-type"]));
1484    }
1485
1486    #[test]
1487    fn test_where_then_select() {
1488        let q = parse_query(".users[] | where age > 30 | select name, email").unwrap();
1489        assert_eq!(q.operations.len(), 2);
1490        assert!(matches!(&q.operations[0], Operation::Where(_)));
1491        assert_eq!(q.operations[1], fields(&["name", "email"]));
1492    }
1493
1494    #[test]
1495    fn test_error_select_missing_fields() {
1496        let err = parse_query(".[] | select").unwrap_err();
1497        assert!(matches!(err, DkitError::QueryError(_)));
1498    }
1499
1500    #[test]
1501    fn test_select_func_single() {
1502        let q = parse_query(".[] | select upper(name)").unwrap();
1503        assert_eq!(
1504            q.operations[0],
1505            Operation::Select(vec![SelectExpr {
1506                expr: Expr::FuncCall {
1507                    name: "upper".to_string(),
1508                    args: vec![Expr::Field("name".to_string())],
1509                },
1510                alias: None,
1511            }])
1512        );
1513    }
1514
1515    #[test]
1516    fn test_select_func_with_alias() {
1517        let q = parse_query(".[] | select upper(name) as NAME").unwrap();
1518        assert_eq!(
1519            q.operations[0],
1520            Operation::Select(vec![SelectExpr {
1521                expr: Expr::FuncCall {
1522                    name: "upper".to_string(),
1523                    args: vec![Expr::Field("name".to_string())],
1524                },
1525                alias: Some("NAME".to_string()),
1526            }])
1527        );
1528    }
1529
1530    #[test]
1531    fn test_select_func_nested() {
1532        let q = parse_query(".[] | select upper(trim(name))").unwrap();
1533        assert_eq!(
1534            q.operations[0],
1535            Operation::Select(vec![SelectExpr {
1536                expr: Expr::FuncCall {
1537                    name: "upper".to_string(),
1538                    args: vec![Expr::FuncCall {
1539                        name: "trim".to_string(),
1540                        args: vec![Expr::Field("name".to_string())],
1541                    }],
1542                },
1543                alias: None,
1544            }])
1545        );
1546    }
1547
1548    #[test]
1549    fn test_select_func_with_literal_arg() {
1550        let q = parse_query(".[] | select round(price, 2)").unwrap();
1551        assert_eq!(
1552            q.operations[0],
1553            Operation::Select(vec![SelectExpr {
1554                expr: Expr::FuncCall {
1555                    name: "round".to_string(),
1556                    args: vec![
1557                        Expr::Field("price".to_string()),
1558                        Expr::Literal(LiteralValue::Integer(2)),
1559                    ],
1560                },
1561                alias: None,
1562            }])
1563        );
1564    }
1565
1566    #[test]
1567    fn test_select_mixed_fields_and_funcs() {
1568        let q = parse_query(".[] | select name, upper(city)").unwrap();
1569        assert_eq!(
1570            q.operations[0],
1571            Operation::Select(vec![
1572                field("name"),
1573                SelectExpr {
1574                    expr: Expr::FuncCall {
1575                        name: "upper".to_string(),
1576                        args: vec![Expr::Field("city".to_string())],
1577                    },
1578                    alias: None,
1579                }
1580            ])
1581        );
1582    }
1583
1584    // --- sort 절 파싱 ---
1585
1586    #[test]
1587    fn test_sort_asc() {
1588        let q = parse_query(".users[] | sort age").unwrap();
1589        assert_eq!(q.operations.len(), 1);
1590        assert_eq!(
1591            q.operations[0],
1592            Operation::Sort {
1593                field: "age".to_string(),
1594                descending: false,
1595            }
1596        );
1597    }
1598
1599    #[test]
1600    fn test_sort_desc() {
1601        let q = parse_query(".users[] | sort age desc").unwrap();
1602        assert_eq!(q.operations.len(), 1);
1603        assert_eq!(
1604            q.operations[0],
1605            Operation::Sort {
1606                field: "age".to_string(),
1607                descending: true,
1608            }
1609        );
1610    }
1611
1612    #[test]
1613    fn test_sort_with_extra_whitespace() {
1614        let q = parse_query(".[]  |  sort  name  ").unwrap();
1615        assert_eq!(
1616            q.operations[0],
1617            Operation::Sort {
1618                field: "name".to_string(),
1619                descending: false,
1620            }
1621        );
1622    }
1623
1624    #[test]
1625    fn test_sort_desc_with_extra_whitespace() {
1626        let q = parse_query(".[]  |  sort  name  desc  ").unwrap();
1627        assert_eq!(
1628            q.operations[0],
1629            Operation::Sort {
1630                field: "name".to_string(),
1631                descending: true,
1632            }
1633        );
1634    }
1635
1636    #[test]
1637    fn test_sort_field_with_underscore() {
1638        let q = parse_query(".[] | sort created_at").unwrap();
1639        assert_eq!(
1640            q.operations[0],
1641            Operation::Sort {
1642                field: "created_at".to_string(),
1643                descending: false,
1644            }
1645        );
1646    }
1647
1648    #[test]
1649    fn test_error_sort_missing_field() {
1650        let err = parse_query(".[] | sort").unwrap_err();
1651        assert!(matches!(err, DkitError::QueryError(_)));
1652    }
1653
1654    // --- limit 절 파싱 ---
1655
1656    #[test]
1657    fn test_limit() {
1658        let q = parse_query(".users[] | limit 10").unwrap();
1659        assert_eq!(q.operations.len(), 1);
1660        assert_eq!(q.operations[0], Operation::Limit(10));
1661    }
1662
1663    #[test]
1664    fn test_limit_one() {
1665        let q = parse_query(".[] | limit 1").unwrap();
1666        assert_eq!(q.operations[0], Operation::Limit(1));
1667    }
1668
1669    #[test]
1670    fn test_limit_with_extra_whitespace() {
1671        let q = parse_query(".[]  |  limit  5  ").unwrap();
1672        assert_eq!(q.operations[0], Operation::Limit(5));
1673    }
1674
1675    #[test]
1676    fn test_error_limit_missing_number() {
1677        let err = parse_query(".[] | limit").unwrap_err();
1678        assert!(matches!(err, DkitError::QueryError(_)));
1679    }
1680
1681    #[test]
1682    fn test_error_limit_negative() {
1683        let err = parse_query(".[] | limit -5").unwrap_err();
1684        assert!(matches!(err, DkitError::QueryError(_)));
1685    }
1686
1687    // --- 복합 파이프라인 ---
1688
1689    #[test]
1690    fn test_where_sort_limit() {
1691        let q = parse_query(".users[] | where age > 20 | sort age desc | limit 5").unwrap();
1692        assert_eq!(q.operations.len(), 3);
1693        assert!(matches!(&q.operations[0], Operation::Where(_)));
1694        assert_eq!(
1695            q.operations[1],
1696            Operation::Sort {
1697                field: "age".to_string(),
1698                descending: true,
1699            }
1700        );
1701        assert_eq!(q.operations[2], Operation::Limit(5));
1702    }
1703
1704    #[test]
1705    fn test_where_select_sort() {
1706        let q = parse_query(".users[] | where age > 30 | select name, email | sort name").unwrap();
1707        assert_eq!(q.operations.len(), 3);
1708        assert!(matches!(&q.operations[0], Operation::Where(_)));
1709        assert_eq!(q.operations[1], fields(&["name", "email"]));
1710        assert_eq!(
1711            q.operations[2],
1712            Operation::Sort {
1713                field: "name".to_string(),
1714                descending: false,
1715            }
1716        );
1717    }
1718
1719    #[test]
1720    fn test_group_by_single_field() {
1721        let q = parse_query(".[] | group_by category").unwrap();
1722        assert_eq!(q.operations.len(), 1);
1723        match &q.operations[0] {
1724            Operation::GroupBy {
1725                fields,
1726                having,
1727                aggregates,
1728            } => {
1729                assert_eq!(fields, &vec!["category".to_string()]);
1730                assert!(having.is_none());
1731                assert!(aggregates.is_empty());
1732            }
1733            _ => panic!("expected GroupBy"),
1734        }
1735    }
1736
1737    #[test]
1738    fn test_group_by_multiple_fields() {
1739        let q = parse_query(".[] | group_by region, category").unwrap();
1740        match &q.operations[0] {
1741            Operation::GroupBy { fields, .. } => {
1742                assert_eq!(fields, &vec!["region".to_string(), "category".to_string()]);
1743            }
1744            _ => panic!("expected GroupBy"),
1745        }
1746    }
1747
1748    #[test]
1749    fn test_group_by_with_aggregates() {
1750        let q = parse_query(".[] | group_by category count(), sum(price), avg(score)").unwrap();
1751        match &q.operations[0] {
1752            Operation::GroupBy { aggregates, .. } => {
1753                assert_eq!(aggregates.len(), 3);
1754                assert_eq!(aggregates[0].func, AggregateFunc::Count);
1755                assert_eq!(aggregates[0].field, None);
1756                assert_eq!(aggregates[0].alias, "count");
1757                assert_eq!(aggregates[1].func, AggregateFunc::Sum);
1758                assert_eq!(aggregates[1].field, Some("price".to_string()));
1759                assert_eq!(aggregates[1].alias, "sum_price");
1760                assert_eq!(aggregates[2].func, AggregateFunc::Avg);
1761                assert_eq!(aggregates[2].field, Some("score".to_string()));
1762                assert_eq!(aggregates[2].alias, "avg_score");
1763            }
1764            _ => panic!("expected GroupBy"),
1765        }
1766    }
1767
1768    #[test]
1769    fn test_group_by_with_having() {
1770        let q = parse_query(".[] | group_by category count() having count > 5").unwrap();
1771        match &q.operations[0] {
1772            Operation::GroupBy {
1773                fields,
1774                having,
1775                aggregates,
1776            } => {
1777                assert_eq!(fields, &vec!["category".to_string()]);
1778                assert!(having.is_some());
1779                assert_eq!(aggregates.len(), 1);
1780            }
1781            _ => panic!("expected GroupBy"),
1782        }
1783    }
1784
1785    #[test]
1786    fn test_group_by_with_min_max() {
1787        let q = parse_query(".[] | group_by category min(price), max(price)").unwrap();
1788        match &q.operations[0] {
1789            Operation::GroupBy { aggregates, .. } => {
1790                assert_eq!(aggregates.len(), 2);
1791                assert_eq!(aggregates[0].func, AggregateFunc::Min);
1792                assert_eq!(aggregates[1].func, AggregateFunc::Max);
1793            }
1794            _ => panic!("expected GroupBy"),
1795        }
1796    }
1797
1798    #[test]
1799    fn test_group_by_pipeline() {
1800        let q = parse_query(".[] | group_by category count() | sort count desc | limit 5").unwrap();
1801        assert_eq!(q.operations.len(), 3);
1802        assert!(matches!(&q.operations[0], Operation::GroupBy { .. }));
1803        assert!(matches!(
1804            &q.operations[1],
1805            Operation::Sort {
1806                descending: true,
1807                ..
1808            }
1809        ));
1810        assert_eq!(q.operations[2], Operation::Limit(5));
1811    }
1812}