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