Skip to main content

pick/selector/
parser.rs

1use crate::error::PickError;
2use super::types::*;
3
4// ──────────────────────────────────────────────
5// Expression / Pipeline / Stage parsing
6// ──────────────────────────────────────────────
7
8impl Expression {
9    /// Parse a full expression: `pipeline (',' pipeline)*`
10    pub fn parse(input: &str) -> Result<Self, PickError> {
11        if input.is_empty() {
12            return Ok(Expression {
13                pipelines: vec![Pipeline {
14                    stages: vec![PipeStage::Path(Selector { segments: vec![] })],
15                }],
16            });
17        }
18
19        let parts = split_top_level(input, ',');
20        let mut pipelines = Vec::new();
21        for part in parts {
22            let trimmed = part.trim();
23            if trimmed.is_empty() {
24                return Err(PickError::InvalidSelector(
25                    "empty selector in comma-separated list".into(),
26                ));
27            }
28            pipelines.push(Pipeline::parse(trimmed)?);
29        }
30
31        Ok(Expression { pipelines })
32    }
33}
34
35impl Pipeline {
36    /// Parse a pipeline: `stage ('|' stage)*`
37    pub fn parse(input: &str) -> Result<Self, PickError> {
38        let parts = split_top_level(input, '|');
39        let mut stages = Vec::new();
40        for part in parts {
41            let trimmed = part.trim();
42            if trimmed.is_empty() {
43                return Err(PickError::InvalidSelector(
44                    "empty stage in pipeline".into(),
45                ));
46            }
47            stages.push(parse_pipe_stage(trimmed)?);
48        }
49        Ok(Pipeline { stages })
50    }
51}
52
53/// Parse a single pipe stage.
54fn parse_pipe_stage(input: &str) -> Result<PipeStage, PickError> {
55    // select(...)
56    if let Some(rest) = input.strip_prefix("select(") {
57        let inner = rest
58            .strip_suffix(')')
59            .ok_or_else(|| PickError::InvalidSelector("unterminated select()".into()))?;
60        let expr = parse_filter_expr(inner.trim())?;
61        return Ok(PipeStage::Select(expr));
62    }
63
64    // set(.path, value)
65    if let Some(rest) = input.strip_prefix("set(") {
66        let inner = rest
67            .strip_suffix(')')
68            .ok_or_else(|| PickError::InvalidSelector("unterminated set()".into()))?;
69        let (path, value) = parse_set_args(inner.trim())?;
70        return Ok(PipeStage::Set { path, value });
71    }
72
73    // del(.path)
74    if let Some(rest) = input.strip_prefix("del(") {
75        let inner = rest
76            .strip_suffix(')')
77            .ok_or_else(|| PickError::InvalidSelector("unterminated del()".into()))?;
78        let path = parse_filter_path(inner.trim())?;
79        return Ok(PipeStage::Del(path));
80    }
81
82    // Standalone builtin: keys(), values(), length()
83    if let Some(builtin) = try_parse_standalone_builtin(input) {
84        return Ok(PipeStage::Builtin(builtin));
85    }
86
87    // Default: path expression
88    Ok(PipeStage::Path(Selector::parse(input)?))
89}
90
91fn try_parse_standalone_builtin(input: &str) -> Option<Builtin> {
92    match input {
93        "keys()" => Some(Builtin::Keys),
94        "values()" => Some(Builtin::Values),
95        "length()" => Some(Builtin::Length),
96        _ => None,
97    }
98}
99
100// ──────────────────────────────────────────────
101// Selector / Segment / Key / Index parsing
102// ──────────────────────────────────────────────
103
104impl Selector {
105    /// Parse a dot-separated path: `segment ('.' segment)*`
106    ///
107    /// Supports recursive descent via `..`: `foo..bar` finds `bar` anywhere
108    /// under `foo`.
109    pub fn parse(input: &str) -> Result<Self, PickError> {
110        if input.is_empty() {
111            return Ok(Selector { segments: vec![] });
112        }
113
114        let mut segments = Vec::new();
115        let mut remaining = input;
116        let mut next_recursive = false;
117
118        // Leading `..` means recursive from root
119        if remaining.starts_with("..") {
120            next_recursive = true;
121            remaining = &remaining[2..];
122            if remaining.is_empty() {
123                return Err(PickError::InvalidSelector(
124                    "trailing '..' in selector".into(),
125                ));
126            }
127        }
128
129        while !remaining.is_empty() {
130            let (mut segment, rest) = parse_segment(remaining)?;
131            segment.recursive = next_recursive;
132            next_recursive = false;
133            segments.push(segment);
134            remaining = rest;
135
136            if remaining.starts_with("..") {
137                next_recursive = true;
138                remaining = &remaining[2..];
139                if remaining.is_empty() {
140                    return Err(PickError::InvalidSelector(
141                        "trailing '..' in selector".into(),
142                    ));
143                }
144            } else if remaining.starts_with('.') {
145                remaining = &remaining[1..];
146                if remaining.is_empty() {
147                    return Err(PickError::InvalidSelector(
148                        "trailing dot in selector".into(),
149                    ));
150                }
151            }
152        }
153
154        Ok(Selector { segments })
155    }
156}
157
158fn parse_segment(input: &str) -> Result<(Segment, &str), PickError> {
159    let (key, remaining) = parse_key(input)?;
160
161    // Check for builtin syntax: keys(), values(), length()
162    if let Some(ref k) = key {
163        if let Some(builtin) = recognize_builtin(k) {
164            if remaining.starts_with("()") {
165                return Ok((
166                    Segment {
167                        key: None,
168                        indices: vec![],
169                        recursive: false,
170                        builtin: Some(builtin),
171                    },
172                    &remaining[2..],
173                ));
174            }
175        }
176    }
177
178    let (indices, remaining) = parse_indices(remaining)?;
179
180    if key.is_none() && indices.is_empty() {
181        return Err(PickError::InvalidSelector(format!(
182            "unexpected character: '{}'",
183            input.chars().next().unwrap_or('?')
184        )));
185    }
186
187    Ok((
188        Segment {
189            key,
190            indices,
191            recursive: false,
192            builtin: None,
193        },
194        remaining,
195    ))
196}
197
198fn parse_key(input: &str) -> Result<(Option<String>, &str), PickError> {
199    if input.is_empty() {
200        return Ok((None, input));
201    }
202
203    let first = input.as_bytes()[0];
204
205    if first == b'"' {
206        let rest = &input[1..];
207        let mut key = String::new();
208        let mut chars = rest.chars();
209        let mut consumed = 0;
210        loop {
211            match chars.next() {
212                None => {
213                    return Err(PickError::InvalidSelector(
214                        "unterminated quoted key".into(),
215                    ));
216                }
217                Some('"') => {
218                    consumed += 1;
219                    break;
220                }
221                Some('\\') => {
222                    consumed += 1;
223                    match chars.next() {
224                        Some('"') => {
225                            key.push('"');
226                            consumed += 1;
227                        }
228                        Some('\\') => {
229                            key.push('\\');
230                            consumed += 1;
231                        }
232                        Some(c) => {
233                            key.push('\\');
234                            key.push(c);
235                            consumed += c.len_utf8();
236                        }
237                        None => {
238                            return Err(PickError::InvalidSelector(
239                                "unterminated quoted key".into(),
240                            ));
241                        }
242                    }
243                }
244                Some(c) => {
245                    key.push(c);
246                    consumed += c.len_utf8();
247                }
248            }
249        }
250        Ok((Some(key), &rest[consumed..]))
251    } else if first == b'[' {
252        Ok((None, input))
253    } else if first.is_ascii_alphanumeric() || first == b'_' {
254        let end = input
255            .find(|c: char| !c.is_ascii_alphanumeric() && c != '_' && c != '-')
256            .unwrap_or(input.len());
257        let key = &input[..end];
258        Ok((Some(key.to_string()), &input[end..]))
259    } else {
260        Err(PickError::InvalidSelector(format!(
261            "unexpected character: '{}'",
262            first as char
263        )))
264    }
265}
266
267fn parse_indices(input: &str) -> Result<(Vec<Index>, &str), PickError> {
268    let mut indices = Vec::new();
269    let mut remaining = input;
270
271    while remaining.starts_with('[') {
272        remaining = &remaining[1..]; // consume [
273
274        let bracket_end = remaining
275            .find(']')
276            .ok_or_else(|| PickError::InvalidSelector("unterminated index bracket".into()))?;
277        let content = &remaining[..bracket_end];
278
279        if content == "*" {
280            indices.push(Index::Wildcard);
281        } else if content.is_empty() {
282            return Err(PickError::InvalidSelector(
283                "empty index bracket".into(),
284            ));
285        } else if let Some(colon_pos) = content.find(':') {
286            let start_str = content[..colon_pos].trim();
287            let end_str = content[colon_pos + 1..].trim();
288
289            let start = if start_str.is_empty() {
290                None
291            } else {
292                Some(start_str.parse::<i64>().map_err(|_| {
293                    PickError::InvalidSelector(format!("invalid slice start: '{start_str}'"))
294                })?)
295            };
296
297            let end = if end_str.is_empty() {
298                None
299            } else {
300                Some(end_str.parse::<i64>().map_err(|_| {
301                    PickError::InvalidSelector(format!("invalid slice end: '{end_str}'"))
302                })?)
303            };
304
305            indices.push(Index::Slice { start, end });
306        } else {
307            let n: i64 = content.parse().map_err(|_| {
308                PickError::InvalidSelector(format!("invalid index: '{content}'"))
309            })?;
310            indices.push(Index::Number(n));
311        }
312
313        remaining = &remaining[bracket_end + 1..]; // skip past ]
314    }
315
316    Ok((indices, remaining))
317}
318
319fn recognize_builtin(name: &str) -> Option<Builtin> {
320    match name {
321        "keys" => Some(Builtin::Keys),
322        "values" => Some(Builtin::Values),
323        "length" => Some(Builtin::Length),
324        _ => None,
325    }
326}
327
328// ──────────────────────────────────────────────
329// Filter expression parsing
330// ──────────────────────────────────────────────
331
332/// Parse a complete filter expression with `and`/`or` logic.
333fn parse_filter_expr(input: &str) -> Result<FilterExpr, PickError> {
334    let (expr, remaining) = parse_or_expr(input)?;
335    let remaining = remaining.trim();
336    if !remaining.is_empty() {
337        return Err(PickError::InvalidSelector(format!(
338            "unexpected trailing content in filter: '{remaining}'"
339        )));
340    }
341    Ok(expr)
342}
343
344fn parse_or_expr(input: &str) -> Result<(FilterExpr, &str), PickError> {
345    let (mut left, mut remaining) = parse_and_expr(input)?;
346    loop {
347        let trimmed = remaining.trim_start();
348        if let Some(rest) = trimmed.strip_prefix("or ") {
349            let (right, rest) = parse_and_expr(rest)?;
350            left = FilterExpr::Or(Box::new(left), Box::new(right));
351            remaining = rest;
352        } else {
353            break;
354        }
355    }
356    Ok((left, remaining))
357}
358
359fn parse_and_expr(input: &str) -> Result<(FilterExpr, &str), PickError> {
360    let (mut left, mut remaining) = parse_atom(input)?;
361    loop {
362        let trimmed = remaining.trim_start();
363        if let Some(rest) = trimmed.strip_prefix("and ") {
364            let (right, rest) = parse_atom(rest)?;
365            left = FilterExpr::And(Box::new(left), Box::new(right));
366            remaining = rest;
367        } else {
368            break;
369        }
370    }
371    Ok((left, remaining))
372}
373
374fn parse_atom(input: &str) -> Result<(FilterExpr, &str), PickError> {
375    let input = input.trim_start();
376
377    // not <atom>
378    if let Some(rest) = input.strip_prefix("not ") {
379        let (inner, remaining) = parse_atom(rest)?;
380        return Ok((FilterExpr::Not(Box::new(inner)), remaining));
381    }
382
383    // Must start with a path (.)
384    if !input.starts_with('.') {
385        return Err(PickError::InvalidSelector(
386            "filter condition must start with '.' (path)".into(),
387        ));
388    }
389
390    let (path, after_path) = parse_filter_path_with_remaining(input)?;
391    let after_path_trimmed = after_path.trim_start();
392
393    // Try to parse a comparison operator
394    if let Some((op, after_op)) = try_parse_compare_op(after_path_trimmed) {
395        let (value, remaining) = parse_literal(after_op.trim_start())?;
396        Ok((
397            FilterExpr::Condition(Condition { path, op, value }),
398            remaining,
399        ))
400    } else {
401        // Truthiness test: select(.active)
402        Ok((FilterExpr::Truthy(path), after_path))
403    }
404}
405
406/// Parse a filter path starting with `.` and return remaining input.
407fn parse_filter_path_with_remaining(input: &str) -> Result<(Selector, &str), PickError> {
408    debug_assert!(input.starts_with('.'));
409    let after_dot = &input[1..];
410
411    if after_dot.is_empty()
412        || after_dot.starts_with(' ')
413        || after_dot.starts_with(')')
414        || after_dot.starts_with('=')
415        || after_dot.starts_with('!')
416        || after_dot.starts_with('>')
417        || after_dot.starts_with('<')
418        || after_dot.starts_with('~')
419    {
420        // Identity path (just `.`)
421        return Ok((Selector { segments: vec![] }, after_dot));
422    }
423
424    // Find where the path ends: at whitespace, operator, or closing paren
425    let mut bracket_depth: i32 = 0;
426    let mut in_quotes = false;
427    let bytes = after_dot.as_bytes();
428    let mut i = 0;
429
430    while i < bytes.len() {
431        if in_quotes {
432            if bytes[i] == b'\\' && i + 1 < bytes.len() {
433                i += 2;
434                continue;
435            }
436            if bytes[i] == b'"' {
437                in_quotes = false;
438            }
439            i += 1;
440            continue;
441        }
442
443        match bytes[i] {
444            b'"' => in_quotes = true,
445            b'[' => bracket_depth += 1,
446            b']' => bracket_depth -= 1,
447            b' ' | b')' if bracket_depth == 0 => break,
448            b'=' | b'!' | b'>' | b'<' | b'~' if bracket_depth == 0 => break,
449            _ => {}
450        }
451        i += 1;
452    }
453    let end = i;
454
455    let path_str = &after_dot[..end];
456    let remaining = &after_dot[end..];
457    let selector = Selector::parse(path_str)?;
458    Ok((selector, remaining))
459}
460
461/// Parse a filter path (no remaining). Used for del() and set() paths.
462fn parse_filter_path(input: &str) -> Result<Selector, PickError> {
463    if !input.starts_with('.') {
464        return Err(PickError::InvalidSelector(
465            "path must start with '.'".into(),
466        ));
467    }
468    let after_dot = &input[1..];
469    if after_dot.is_empty() {
470        return Ok(Selector { segments: vec![] });
471    }
472    Selector::parse(after_dot)
473}
474
475fn try_parse_compare_op(input: &str) -> Option<(CompareOp, &str)> {
476    // Two-char operators first
477    if let Some(rest) = input.strip_prefix("==") {
478        return Some((CompareOp::Eq, rest));
479    }
480    if let Some(rest) = input.strip_prefix("!=") {
481        return Some((CompareOp::Ne, rest));
482    }
483    if let Some(rest) = input.strip_prefix(">=") {
484        return Some((CompareOp::Gte, rest));
485    }
486    if let Some(rest) = input.strip_prefix("<=") {
487        return Some((CompareOp::Lte, rest));
488    }
489    // Single-char operators
490    if let Some(rest) = input.strip_prefix('>') {
491        return Some((CompareOp::Gt, rest));
492    }
493    if let Some(rest) = input.strip_prefix('<') {
494        return Some((CompareOp::Lt, rest));
495    }
496    if let Some(rest) = input.strip_prefix('~') {
497        return Some((CompareOp::Match, rest));
498    }
499    None
500}
501
502fn parse_literal(input: &str) -> Result<(LiteralValue, &str), PickError> {
503    let input = input.trim_start();
504
505    // null
506    if let Some(rest) = input.strip_prefix("null") {
507        if rest.is_empty() || !rest.as_bytes()[0].is_ascii_alphanumeric() {
508            return Ok((LiteralValue::Null, rest));
509        }
510    }
511
512    // booleans
513    if let Some(rest) = input.strip_prefix("true") {
514        if rest.is_empty() || !rest.as_bytes()[0].is_ascii_alphanumeric() {
515            return Ok((LiteralValue::Bool(true), rest));
516        }
517    }
518    if let Some(rest) = input.strip_prefix("false") {
519        if rest.is_empty() || !rest.as_bytes()[0].is_ascii_alphanumeric() {
520            return Ok((LiteralValue::Bool(false), rest));
521        }
522    }
523
524    // quoted string
525    if input.starts_with('"') {
526        let rest = &input[1..];
527        let mut value = String::new();
528        let mut chars = rest.chars();
529        let mut consumed = 0;
530        loop {
531            match chars.next() {
532                None => {
533                    return Err(PickError::InvalidSelector(
534                        "unterminated string literal".into(),
535                    ));
536                }
537                Some('"') => {
538                    consumed += 1;
539                    break;
540                }
541                Some('\\') => {
542                    consumed += 1;
543                    match chars.next() {
544                        Some('"') => {
545                            value.push('"');
546                            consumed += 1;
547                        }
548                        Some('\\') => {
549                            value.push('\\');
550                            consumed += 1;
551                        }
552                        Some('n') => {
553                            value.push('\n');
554                            consumed += 1;
555                        }
556                        Some('t') => {
557                            value.push('\t');
558                            consumed += 1;
559                        }
560                        Some(c) => {
561                            value.push(c);
562                            consumed += c.len_utf8();
563                        }
564                        None => {
565                            return Err(PickError::InvalidSelector(
566                                "unterminated string literal".into(),
567                            ));
568                        }
569                    }
570                }
571                Some(c) => {
572                    value.push(c);
573                    consumed += c.len_utf8();
574                }
575            }
576        }
577        return Ok((LiteralValue::String(value), &rest[consumed..]));
578    }
579
580    // number (integer or float, possibly negative)
581    let num_end = input
582        .find(|c: char| !c.is_ascii_digit() && c != '.' && c != '-' && c != '+' && c != 'e' && c != 'E')
583        .unwrap_or(input.len());
584
585    if num_end == 0 {
586        return Err(PickError::InvalidSelector(format!(
587            "expected literal value, got: '{}'",
588            &input[..input.len().min(20)]
589        )));
590    }
591
592    let num_str = &input[..num_end];
593    let n: f64 = num_str.parse().map_err(|_| {
594        PickError::InvalidSelector(format!("invalid number: '{num_str}'"))
595    })?;
596    Ok((LiteralValue::Number(n), &input[num_end..]))
597}
598
599/// Parse set() arguments: `.path, value`
600fn parse_set_args(input: &str) -> Result<(Selector, LiteralValue), PickError> {
601    let comma_pos = find_top_level_comma(input).ok_or_else(|| {
602        PickError::InvalidSelector("set() requires two arguments: set(.path, value)".into())
603    })?;
604
605    let path_str = input[..comma_pos].trim();
606    let value_str = input[comma_pos + 1..].trim();
607
608    let path = parse_filter_path(path_str)?;
609    let (value, remaining) = parse_literal(value_str)?;
610
611    if !remaining.trim().is_empty() {
612        return Err(PickError::InvalidSelector(format!(
613            "unexpected content after set() value: '{}'",
614            remaining.trim()
615        )));
616    }
617
618    Ok((path, value))
619}
620
621fn find_top_level_comma(input: &str) -> Option<usize> {
622    let mut depth = 0;
623    let mut in_quotes = false;
624    for (i, b) in input.bytes().enumerate() {
625        if in_quotes {
626            if b == b'\\' {
627                continue; // next char is escaped
628            }
629            if b == b'"' {
630                in_quotes = false;
631            }
632            continue;
633        }
634        match b {
635            b'"' => in_quotes = true,
636            b'(' | b'[' => depth += 1,
637            b')' | b']' => depth -= 1,
638            b',' if depth == 0 => return Some(i),
639            _ => {}
640        }
641    }
642    None
643}
644
645// ──────────────────────────────────────────────
646// Top-level splitting utilities
647// ──────────────────────────────────────────────
648
649/// Split `input` on `delimiter` at the top level, respecting brackets,
650/// parentheses, and quoted strings.
651fn split_top_level(input: &str, delimiter: char) -> Vec<&str> {
652    let delim_byte = delimiter as u8;
653    let mut parts = Vec::new();
654    let mut depth: i32 = 0;
655    let mut in_quotes = false;
656    let mut start = 0;
657    let bytes = input.as_bytes();
658    let mut i = 0;
659
660    while i < bytes.len() {
661        if in_quotes {
662            if bytes[i] == b'\\' && i + 1 < bytes.len() {
663                i += 2;
664                continue;
665            }
666            if bytes[i] == b'"' {
667                in_quotes = false;
668            }
669            i += 1;
670            continue;
671        }
672
673        match bytes[i] {
674            b'"' => in_quotes = true,
675            b'[' | b'(' => depth += 1,
676            b']' | b')' => depth = (depth - 1).max(0),
677            b if b == delim_byte && depth == 0 => {
678                parts.push(&input[start..i]);
679                start = i + 1;
680            }
681            _ => {}
682        }
683        i += 1;
684    }
685    parts.push(&input[start..]);
686    parts
687}
688
689// ──────────────────────────────────────────────
690// Tests
691// ──────────────────────────────────────────────
692
693#[cfg(test)]
694mod tests {
695    use super::*;
696
697    // ── Selector parsing (existing behavior preserved) ──
698
699    #[test]
700    fn parse_empty_selector() {
701        let sel = Selector::parse("").unwrap();
702        assert!(sel.segments.is_empty());
703    }
704
705    #[test]
706    fn parse_simple_key() {
707        let sel = Selector::parse("foo").unwrap();
708        assert_eq!(sel.segments.len(), 1);
709        assert_eq!(sel.segments[0].key, Some("foo".into()));
710        assert!(sel.segments[0].indices.is_empty());
711    }
712
713    #[test]
714    fn parse_nested_keys() {
715        let sel = Selector::parse("foo.bar.baz").unwrap();
716        assert_eq!(sel.segments.len(), 3);
717        assert_eq!(sel.segments[0].key, Some("foo".into()));
718        assert_eq!(sel.segments[1].key, Some("bar".into()));
719        assert_eq!(sel.segments[2].key, Some("baz".into()));
720    }
721
722    #[test]
723    fn parse_array_index() {
724        let sel = Selector::parse("items[0]").unwrap();
725        assert_eq!(sel.segments.len(), 1);
726        assert_eq!(sel.segments[0].key, Some("items".into()));
727        assert_eq!(sel.segments[0].indices, vec![Index::Number(0)]);
728    }
729
730    #[test]
731    fn parse_nested_with_index() {
732        let sel = Selector::parse("foo.bar[0].baz").unwrap();
733        assert_eq!(sel.segments.len(), 3);
734        assert_eq!(sel.segments[1].key, Some("bar".into()));
735        assert_eq!(sel.segments[1].indices, vec![Index::Number(0)]);
736    }
737
738    #[test]
739    fn parse_wildcard() {
740        let sel = Selector::parse("items[*]").unwrap();
741        assert_eq!(sel.segments[0].indices, vec![Index::Wildcard]);
742    }
743
744    #[test]
745    fn parse_multiple_indices() {
746        let sel = Selector::parse("matrix[0][1]").unwrap();
747        assert_eq!(
748            sel.segments[0].indices,
749            vec![Index::Number(0), Index::Number(1)]
750        );
751    }
752
753    #[test]
754    fn parse_negative_index() {
755        let sel = Selector::parse("items[-1]").unwrap();
756        assert_eq!(sel.segments[0].indices, vec![Index::Number(-1)]);
757    }
758
759    #[test]
760    fn parse_quoted_key() {
761        let sel = Selector::parse("\"foo.bar\".baz").unwrap();
762        assert_eq!(sel.segments.len(), 2);
763        assert_eq!(sel.segments[0].key, Some("foo.bar".into()));
764        assert_eq!(sel.segments[1].key, Some("baz".into()));
765    }
766
767    #[test]
768    fn parse_key_with_hyphens() {
769        let sel = Selector::parse("content-type").unwrap();
770        assert_eq!(sel.segments[0].key, Some("content-type".into()));
771    }
772
773    #[test]
774    fn parse_key_with_numbers() {
775        let sel = Selector::parse("item1.value2").unwrap();
776        assert_eq!(sel.segments[0].key, Some("item1".into()));
777        assert_eq!(sel.segments[1].key, Some("value2".into()));
778    }
779
780    #[test]
781    fn parse_leading_index() {
782        let sel = Selector::parse("[0].name").unwrap();
783        assert_eq!(sel.segments.len(), 2);
784        assert_eq!(sel.segments[0].key, None);
785        assert_eq!(sel.segments[0].indices, vec![Index::Number(0)]);
786        assert_eq!(sel.segments[1].key, Some("name".into()));
787    }
788
789    #[test]
790    fn parse_only_index() {
791        let sel = Selector::parse("[0]").unwrap();
792        assert_eq!(sel.segments.len(), 1);
793        assert_eq!(sel.segments[0].key, None);
794        assert_eq!(sel.segments[0].indices, vec![Index::Number(0)]);
795    }
796
797    #[test]
798    fn parse_only_wildcard() {
799        let sel = Selector::parse("[*]").unwrap();
800        assert_eq!(sel.segments.len(), 1);
801        assert_eq!(sel.segments[0].indices, vec![Index::Wildcard]);
802    }
803
804    #[test]
805    fn parse_trailing_dot_error() {
806        assert!(Selector::parse("foo.").is_err());
807    }
808
809    #[test]
810    fn parse_double_dot_error_old_style() {
811        // `foo..bar` is now valid: recursive descent
812        let sel = Selector::parse("foo..bar").unwrap();
813        assert_eq!(sel.segments.len(), 2);
814        assert!(!sel.segments[0].recursive);
815        assert!(sel.segments[1].recursive);
816    }
817
818    #[test]
819    fn parse_unterminated_bracket_error() {
820        assert!(Selector::parse("foo[0").is_err());
821    }
822
823    #[test]
824    fn parse_empty_bracket_error() {
825        assert!(Selector::parse("foo[]").is_err());
826    }
827
828    #[test]
829    fn parse_invalid_index_error() {
830        assert!(Selector::parse("foo[abc]").is_err());
831    }
832
833    #[test]
834    fn parse_unterminated_quote_error() {
835        assert!(Selector::parse("\"foo").is_err());
836    }
837
838    #[test]
839    fn parse_wildcard_then_index() {
840        let sel = Selector::parse("[*][0]").unwrap();
841        assert_eq!(
842            sel.segments[0].indices,
843            vec![Index::Wildcard, Index::Number(0)]
844        );
845    }
846
847    // ── Phase 1: Slice parsing ──
848
849    #[test]
850    fn parse_slice_full() {
851        let sel = Selector::parse("items[1:3]").unwrap();
852        assert_eq!(
853            sel.segments[0].indices,
854            vec![Index::Slice {
855                start: Some(1),
856                end: Some(3)
857            }]
858        );
859    }
860
861    #[test]
862    fn parse_slice_open_end() {
863        let sel = Selector::parse("items[2:]").unwrap();
864        assert_eq!(
865            sel.segments[0].indices,
866            vec![Index::Slice {
867                start: Some(2),
868                end: None
869            }]
870        );
871    }
872
873    #[test]
874    fn parse_slice_open_start() {
875        let sel = Selector::parse("items[:3]").unwrap();
876        assert_eq!(
877            sel.segments[0].indices,
878            vec![Index::Slice {
879                start: None,
880                end: Some(3)
881            }]
882        );
883    }
884
885    #[test]
886    fn parse_slice_both_open() {
887        let sel = Selector::parse("items[:]").unwrap();
888        assert_eq!(
889            sel.segments[0].indices,
890            vec![Index::Slice {
891                start: None,
892                end: None
893            }]
894        );
895    }
896
897    #[test]
898    fn parse_slice_negative_start() {
899        let sel = Selector::parse("items[-2:]").unwrap();
900        assert_eq!(
901            sel.segments[0].indices,
902            vec![Index::Slice {
903                start: Some(-2),
904                end: None
905            }]
906        );
907    }
908
909    #[test]
910    fn parse_slice_negative_end() {
911        let sel = Selector::parse("items[:-1]").unwrap();
912        assert_eq!(
913            sel.segments[0].indices,
914            vec![Index::Slice {
915                start: None,
916                end: Some(-1)
917            }]
918        );
919    }
920
921    #[test]
922    fn parse_slice_both_negative() {
923        let sel = Selector::parse("items[-3:-1]").unwrap();
924        assert_eq!(
925            sel.segments[0].indices,
926            vec![Index::Slice {
927                start: Some(-3),
928                end: Some(-1)
929            }]
930        );
931    }
932
933    #[test]
934    fn parse_slice_invalid_start() {
935        assert!(Selector::parse("items[abc:]").is_err());
936    }
937
938    #[test]
939    fn parse_slice_invalid_end() {
940        assert!(Selector::parse("items[:abc]").is_err());
941    }
942
943    #[test]
944    fn parse_slice_then_index() {
945        let sel = Selector::parse("matrix[0:2][0]").unwrap();
946        assert_eq!(sel.segments[0].indices.len(), 2);
947        assert_eq!(
948            sel.segments[0].indices[0],
949            Index::Slice {
950                start: Some(0),
951                end: Some(2)
952            }
953        );
954        assert_eq!(sel.segments[0].indices[1], Index::Number(0));
955    }
956
957    // ── Phase 1: Builtin parsing ──
958
959    #[test]
960    fn parse_builtin_keys() {
961        let sel = Selector::parse("keys()").unwrap();
962        assert_eq!(sel.segments.len(), 1);
963        assert_eq!(sel.segments[0].key, None);
964        assert_eq!(sel.segments[0].builtin, Some(Builtin::Keys));
965    }
966
967    #[test]
968    fn parse_builtin_values() {
969        let sel = Selector::parse("values()").unwrap();
970        assert_eq!(sel.segments[0].builtin, Some(Builtin::Values));
971    }
972
973    #[test]
974    fn parse_builtin_length() {
975        let sel = Selector::parse("length()").unwrap();
976        assert_eq!(sel.segments[0].builtin, Some(Builtin::Length));
977    }
978
979    #[test]
980    fn parse_builtin_after_key() {
981        let sel = Selector::parse("foo.keys()").unwrap();
982        assert_eq!(sel.segments.len(), 2);
983        assert_eq!(sel.segments[0].key, Some("foo".into()));
984        assert_eq!(sel.segments[1].builtin, Some(Builtin::Keys));
985    }
986
987    #[test]
988    fn parse_keys_as_key_without_parens() {
989        // "keys" without () is a key lookup, not a builtin
990        let sel = Selector::parse("keys").unwrap();
991        assert_eq!(sel.segments[0].key, Some("keys".into()));
992        assert_eq!(sel.segments[0].builtin, None);
993    }
994
995    #[test]
996    fn parse_builtin_after_index() {
997        let sel = Selector::parse("items[0].keys()").unwrap();
998        assert_eq!(sel.segments.len(), 2);
999        assert_eq!(sel.segments[0].key, Some("items".into()));
1000        assert_eq!(sel.segments[1].builtin, Some(Builtin::Keys));
1001    }
1002
1003    // ── Phase 1: Recursive descent parsing ──
1004
1005    #[test]
1006    fn parse_recursive_simple() {
1007        let sel = Selector::parse("..name").unwrap();
1008        assert_eq!(sel.segments.len(), 1);
1009        assert!(sel.segments[0].recursive);
1010        assert_eq!(sel.segments[0].key, Some("name".into()));
1011    }
1012
1013    #[test]
1014    fn parse_recursive_after_key() {
1015        let sel = Selector::parse("foo..bar").unwrap();
1016        assert_eq!(sel.segments.len(), 2);
1017        assert!(!sel.segments[0].recursive);
1018        assert_eq!(sel.segments[0].key, Some("foo".into()));
1019        assert!(sel.segments[1].recursive);
1020        assert_eq!(sel.segments[1].key, Some("bar".into()));
1021    }
1022
1023    #[test]
1024    fn parse_recursive_with_index() {
1025        let sel = Selector::parse("..items[0]").unwrap();
1026        assert_eq!(sel.segments.len(), 1);
1027        assert!(sel.segments[0].recursive);
1028        assert_eq!(sel.segments[0].key, Some("items".into()));
1029        assert_eq!(sel.segments[0].indices, vec![Index::Number(0)]);
1030    }
1031
1032    #[test]
1033    fn parse_recursive_chained() {
1034        let sel = Selector::parse("a..b..c").unwrap();
1035        assert_eq!(sel.segments.len(), 3);
1036        assert!(!sel.segments[0].recursive);
1037        assert!(sel.segments[1].recursive);
1038        assert!(sel.segments[2].recursive);
1039    }
1040
1041    #[test]
1042    fn parse_trailing_double_dot_error() {
1043        assert!(Selector::parse("foo..").is_err());
1044    }
1045
1046    #[test]
1047    fn parse_only_double_dot_error() {
1048        assert!(Selector::parse("..").is_err());
1049    }
1050
1051    // ── Phase 1: Expression (multiple selectors) ──
1052
1053    #[test]
1054    fn parse_expression_single() {
1055        let expr = Expression::parse("name").unwrap();
1056        assert_eq!(expr.pipelines.len(), 1);
1057        assert_eq!(expr.pipelines[0].stages.len(), 1);
1058    }
1059
1060    #[test]
1061    fn parse_expression_multiple() {
1062        let expr = Expression::parse("name, age").unwrap();
1063        assert_eq!(expr.pipelines.len(), 2);
1064    }
1065
1066    #[test]
1067    fn parse_expression_three() {
1068        let expr = Expression::parse("name, age, email").unwrap();
1069        assert_eq!(expr.pipelines.len(), 3);
1070    }
1071
1072    #[test]
1073    fn parse_expression_empty_part_error() {
1074        assert!(Expression::parse("name,").is_err());
1075    }
1076
1077    #[test]
1078    fn parse_expression_empty() {
1079        let expr = Expression::parse("").unwrap();
1080        assert_eq!(expr.pipelines.len(), 1);
1081    }
1082
1083    // ── Phase 2: Pipeline parsing ──
1084
1085    #[test]
1086    fn parse_pipeline_single_stage() {
1087        let p = Pipeline::parse("name").unwrap();
1088        assert_eq!(p.stages.len(), 1);
1089        assert!(matches!(p.stages[0], PipeStage::Path(_)));
1090    }
1091
1092    #[test]
1093    fn parse_pipeline_two_stages() {
1094        let p = Pipeline::parse("items[*] | name").unwrap();
1095        assert_eq!(p.stages.len(), 2);
1096    }
1097
1098    #[test]
1099    fn parse_pipeline_builtin_stage() {
1100        let p = Pipeline::parse("foo | keys()").unwrap();
1101        assert_eq!(p.stages.len(), 2);
1102        assert!(matches!(p.stages[1], PipeStage::Builtin(Builtin::Keys)));
1103    }
1104
1105    #[test]
1106    fn parse_pipeline_select_stage() {
1107        let p = Pipeline::parse("items[*] | select(.price > 100)").unwrap();
1108        assert_eq!(p.stages.len(), 2);
1109        assert!(matches!(p.stages[1], PipeStage::Select(_)));
1110    }
1111
1112    #[test]
1113    fn parse_pipeline_empty_stage_error() {
1114        assert!(Pipeline::parse("foo | ").is_err());
1115    }
1116
1117    // ── Phase 2: Filter expression parsing ──
1118
1119    #[test]
1120    fn parse_filter_eq_string() {
1121        let expr = parse_filter_expr(".name == \"Alice\"").unwrap();
1122        match expr {
1123            FilterExpr::Condition(c) => {
1124                assert_eq!(c.op, CompareOp::Eq);
1125                assert_eq!(c.value, LiteralValue::String("Alice".into()));
1126            }
1127            _ => panic!("expected Condition"),
1128        }
1129    }
1130
1131    #[test]
1132    fn parse_filter_gt_number() {
1133        let expr = parse_filter_expr(".price > 100").unwrap();
1134        match expr {
1135            FilterExpr::Condition(c) => {
1136                assert_eq!(c.op, CompareOp::Gt);
1137                assert_eq!(c.value, LiteralValue::Number(100.0));
1138            }
1139            _ => panic!("expected Condition"),
1140        }
1141    }
1142
1143    #[test]
1144    fn parse_filter_ne_bool() {
1145        let expr = parse_filter_expr(".active != false").unwrap();
1146        match expr {
1147            FilterExpr::Condition(c) => {
1148                assert_eq!(c.op, CompareOp::Ne);
1149                assert_eq!(c.value, LiteralValue::Bool(false));
1150            }
1151            _ => panic!("expected Condition"),
1152        }
1153    }
1154
1155    #[test]
1156    fn parse_filter_eq_null() {
1157        let expr = parse_filter_expr(".deleted == null").unwrap();
1158        match expr {
1159            FilterExpr::Condition(c) => {
1160                assert_eq!(c.op, CompareOp::Eq);
1161                assert_eq!(c.value, LiteralValue::Null);
1162            }
1163            _ => panic!("expected Condition"),
1164        }
1165    }
1166
1167    #[test]
1168    fn parse_filter_regex() {
1169        let expr = parse_filter_expr(".name ~ \"^A\"").unwrap();
1170        match expr {
1171            FilterExpr::Condition(c) => {
1172                assert_eq!(c.op, CompareOp::Match);
1173                assert_eq!(c.value, LiteralValue::String("^A".into()));
1174            }
1175            _ => panic!("expected Condition"),
1176        }
1177    }
1178
1179    #[test]
1180    fn parse_filter_and() {
1181        let expr = parse_filter_expr(".price > 10 and .stock > 0").unwrap();
1182        assert!(matches!(expr, FilterExpr::And(_, _)));
1183    }
1184
1185    #[test]
1186    fn parse_filter_or() {
1187        let expr = parse_filter_expr(".price < 10 or .sale == true").unwrap();
1188        assert!(matches!(expr, FilterExpr::Or(_, _)));
1189    }
1190
1191    #[test]
1192    fn parse_filter_not() {
1193        let expr = parse_filter_expr("not .deleted == true").unwrap();
1194        assert!(matches!(expr, FilterExpr::Not(_)));
1195    }
1196
1197    #[test]
1198    fn parse_filter_truthy() {
1199        let expr = parse_filter_expr(".active").unwrap();
1200        assert!(matches!(expr, FilterExpr::Truthy(_)));
1201    }
1202
1203    #[test]
1204    fn parse_filter_truthy_not() {
1205        let expr = parse_filter_expr("not .hidden").unwrap();
1206        match expr {
1207            FilterExpr::Not(inner) => assert!(matches!(*inner, FilterExpr::Truthy(_))),
1208            _ => panic!("expected Not(Truthy)"),
1209        }
1210    }
1211
1212    #[test]
1213    fn parse_filter_nested_path() {
1214        let expr = parse_filter_expr(".user.age >= 18").unwrap();
1215        match expr {
1216            FilterExpr::Condition(c) => {
1217                assert_eq!(c.path.segments.len(), 2);
1218                assert_eq!(c.op, CompareOp::Gte);
1219            }
1220            _ => panic!("expected Condition"),
1221        }
1222    }
1223
1224    #[test]
1225    fn parse_filter_identity_comparison() {
1226        let expr = parse_filter_expr(". > 5").unwrap();
1227        match expr {
1228            FilterExpr::Condition(c) => {
1229                assert!(c.path.segments.is_empty()); // identity
1230                assert_eq!(c.op, CompareOp::Gt);
1231            }
1232            _ => panic!("expected Condition"),
1233        }
1234    }
1235
1236    #[test]
1237    fn parse_filter_lte() {
1238        let expr = parse_filter_expr(".count <= 50").unwrap();
1239        match expr {
1240            FilterExpr::Condition(c) => assert_eq!(c.op, CompareOp::Lte),
1241            _ => panic!("expected Condition"),
1242        }
1243    }
1244
1245    #[test]
1246    fn parse_filter_lt() {
1247        let expr = parse_filter_expr(".count < 50").unwrap();
1248        match expr {
1249            FilterExpr::Condition(c) => assert_eq!(c.op, CompareOp::Lt),
1250            _ => panic!("expected Condition"),
1251        }
1252    }
1253
1254    #[test]
1255    fn parse_filter_float_literal() {
1256        let expr = parse_filter_expr(".score > 3.14").unwrap();
1257        match expr {
1258            FilterExpr::Condition(c) => assert_eq!(c.value, LiteralValue::Number(3.14)),
1259            _ => panic!("expected Condition"),
1260        }
1261    }
1262
1263    #[test]
1264    fn parse_filter_negative_number() {
1265        let expr = parse_filter_expr(".temp > -10").unwrap();
1266        match expr {
1267            FilterExpr::Condition(c) => assert_eq!(c.value, LiteralValue::Number(-10.0)),
1268            _ => panic!("expected Condition"),
1269        }
1270    }
1271
1272    #[test]
1273    fn parse_filter_and_or_precedence() {
1274        // `a or b and c` should parse as `a or (b and c)`
1275        let expr = parse_filter_expr(".a == 1 or .b == 2 and .c == 3").unwrap();
1276        match expr {
1277            FilterExpr::Or(left, right) => {
1278                assert!(matches!(*left, FilterExpr::Condition(_)));
1279                assert!(matches!(*right, FilterExpr::And(_, _)));
1280            }
1281            _ => panic!("expected Or at top level"),
1282        }
1283    }
1284
1285    #[test]
1286    fn parse_filter_escaped_string() {
1287        let expr = parse_filter_expr(".name == \"hello\\\"world\"").unwrap();
1288        match expr {
1289            FilterExpr::Condition(c) => {
1290                assert_eq!(c.value, LiteralValue::String("hello\"world".into()));
1291            }
1292            _ => panic!("expected Condition"),
1293        }
1294    }
1295
1296    // ── Phase 3: set / del parsing ──
1297
1298    #[test]
1299    fn parse_set_string() {
1300        let p = Pipeline::parse("set(.name, \"Alice\")").unwrap();
1301        match &p.stages[0] {
1302            PipeStage::Set { path, value } => {
1303                assert_eq!(path.segments[0].key, Some("name".into()));
1304                assert_eq!(*value, LiteralValue::String("Alice".into()));
1305            }
1306            _ => panic!("expected Set"),
1307        }
1308    }
1309
1310    #[test]
1311    fn parse_set_number() {
1312        let p = Pipeline::parse("set(.count, 42)").unwrap();
1313        match &p.stages[0] {
1314            PipeStage::Set { path, value } => {
1315                assert_eq!(*value, LiteralValue::Number(42.0));
1316            }
1317            _ => panic!("expected Set"),
1318        }
1319    }
1320
1321    #[test]
1322    fn parse_set_bool() {
1323        let p = Pipeline::parse("set(.active, true)").unwrap();
1324        match &p.stages[0] {
1325            PipeStage::Set { path, value } => {
1326                assert_eq!(*value, LiteralValue::Bool(true));
1327            }
1328            _ => panic!("expected Set"),
1329        }
1330    }
1331
1332    #[test]
1333    fn parse_set_null() {
1334        let p = Pipeline::parse("set(.deleted, null)").unwrap();
1335        match &p.stages[0] {
1336            PipeStage::Set { path, value } => {
1337                assert_eq!(*value, LiteralValue::Null);
1338            }
1339            _ => panic!("expected Set"),
1340        }
1341    }
1342
1343    #[test]
1344    fn parse_set_nested_path() {
1345        let p = Pipeline::parse("set(.user.name, \"Bob\")").unwrap();
1346        match &p.stages[0] {
1347            PipeStage::Set { path, .. } => {
1348                assert_eq!(path.segments.len(), 2);
1349            }
1350            _ => panic!("expected Set"),
1351        }
1352    }
1353
1354    #[test]
1355    fn parse_del_simple() {
1356        let p = Pipeline::parse("del(.temp)").unwrap();
1357        match &p.stages[0] {
1358            PipeStage::Del(path) => {
1359                assert_eq!(path.segments[0].key, Some("temp".into()));
1360            }
1361            _ => panic!("expected Del"),
1362        }
1363    }
1364
1365    #[test]
1366    fn parse_del_nested() {
1367        let p = Pipeline::parse("del(.metadata.temp)").unwrap();
1368        match &p.stages[0] {
1369            PipeStage::Del(path) => {
1370                assert_eq!(path.segments.len(), 2);
1371            }
1372            _ => panic!("expected Del"),
1373        }
1374    }
1375
1376    #[test]
1377    fn parse_set_unterminated_error() {
1378        assert!(Pipeline::parse("set(.name, \"Alice\"").is_err());
1379    }
1380
1381    #[test]
1382    fn parse_del_unterminated_error() {
1383        assert!(Pipeline::parse("del(.name").is_err());
1384    }
1385
1386    // ── Utility: split_top_level ──
1387
1388    #[test]
1389    fn split_simple_comma() {
1390        let parts = split_top_level("a, b, c", ',');
1391        assert_eq!(parts, vec!["a", " b", " c"]);
1392    }
1393
1394    #[test]
1395    fn split_respects_brackets() {
1396        let parts = split_top_level("items[0,1], name", ',');
1397        assert_eq!(parts, vec!["items[0,1]", " name"]);
1398    }
1399
1400    #[test]
1401    fn split_respects_parens() {
1402        let parts = split_top_level("select(.a, .b) | name", '|');
1403        assert_eq!(parts, vec!["select(.a, .b) ", " name"]);
1404    }
1405
1406    #[test]
1407    fn split_respects_quotes() {
1408        let parts = split_top_level("\"a,b\", c", ',');
1409        assert_eq!(parts, vec!["\"a,b\"", " c"]);
1410    }
1411
1412    #[test]
1413    fn split_pipe() {
1414        let parts = split_top_level("items[*] | select(.x > 1)", '|');
1415        assert_eq!(parts, vec!["items[*] ", " select(.x > 1)"]);
1416    }
1417
1418    // ── Literal parsing ──
1419
1420    #[test]
1421    fn parse_literal_string() {
1422        let (v, r) = parse_literal("\"hello\"").unwrap();
1423        assert_eq!(v, LiteralValue::String("hello".into()));
1424        assert_eq!(r, "");
1425    }
1426
1427    #[test]
1428    fn parse_literal_number() {
1429        let (v, r) = parse_literal("42").unwrap();
1430        assert_eq!(v, LiteralValue::Number(42.0));
1431        assert_eq!(r, "");
1432    }
1433
1434    #[test]
1435    fn parse_literal_negative() {
1436        let (v, r) = parse_literal("-3.5").unwrap();
1437        assert_eq!(v, LiteralValue::Number(-3.5));
1438        assert_eq!(r, "");
1439    }
1440
1441    #[test]
1442    fn parse_literal_true() {
1443        let (v, _) = parse_literal("true").unwrap();
1444        assert_eq!(v, LiteralValue::Bool(true));
1445    }
1446
1447    #[test]
1448    fn parse_literal_false() {
1449        let (v, _) = parse_literal("false").unwrap();
1450        assert_eq!(v, LiteralValue::Bool(false));
1451    }
1452
1453    #[test]
1454    fn parse_literal_null() {
1455        let (v, _) = parse_literal("null").unwrap();
1456        assert_eq!(v, LiteralValue::Null);
1457    }
1458
1459    #[test]
1460    fn parse_literal_escaped_string() {
1461        let (v, _) = parse_literal("\"a\\\"b\"").unwrap();
1462        assert_eq!(v, LiteralValue::String("a\"b".into()));
1463    }
1464
1465    #[test]
1466    fn parse_literal_number_with_remainder() {
1467        let (v, r) = parse_literal("42 and").unwrap();
1468        assert_eq!(v, LiteralValue::Number(42.0));
1469        assert_eq!(r, " and");
1470    }
1471
1472    // ══════════════════════════════════════════════
1473    // Additional coverage tests
1474    // ══════════════════════════════════════════════
1475
1476    // ── Slice parsing edge cases ──
1477
1478    #[test]
1479    fn parse_slice_in_nested_path() {
1480        let sel = Selector::parse("data[0].items[1:3]").unwrap();
1481        assert_eq!(sel.segments.len(), 2);
1482        assert_eq!(sel.segments[0].key, Some("data".into()));
1483        assert_eq!(sel.segments[0].indices, vec![Index::Number(0)]);
1484        assert_eq!(sel.segments[1].key, Some("items".into()));
1485        assert_eq!(
1486            sel.segments[1].indices,
1487            vec![Index::Slice { start: Some(1), end: Some(3) }]
1488        );
1489    }
1490
1491    #[test]
1492    fn parse_slice_wildcard_then_slice() {
1493        let sel = Selector::parse("items[*][0:2]").unwrap();
1494        assert_eq!(sel.segments[0].indices.len(), 2);
1495        assert_eq!(sel.segments[0].indices[0], Index::Wildcard);
1496        assert_eq!(
1497            sel.segments[0].indices[1],
1498            Index::Slice { start: Some(0), end: Some(2) }
1499        );
1500    }
1501
1502    #[test]
1503    fn parse_slice_then_wildcard() {
1504        let sel = Selector::parse("items[0:3][*]").unwrap();
1505        assert_eq!(sel.segments[0].indices.len(), 2);
1506        assert_eq!(
1507            sel.segments[0].indices[0],
1508            Index::Slice { start: Some(0), end: Some(3) }
1509        );
1510        assert_eq!(sel.segments[0].indices[1], Index::Wildcard);
1511    }
1512
1513    #[test]
1514    fn parse_triple_index_chain() {
1515        let sel = Selector::parse("a[0][1][2]").unwrap();
1516        assert_eq!(sel.segments[0].indices.len(), 3);
1517        assert_eq!(sel.segments[0].indices[0], Index::Number(0));
1518        assert_eq!(sel.segments[0].indices[1], Index::Number(1));
1519        assert_eq!(sel.segments[0].indices[2], Index::Number(2));
1520    }
1521
1522    #[test]
1523    fn parse_slice_zero_to_zero() {
1524        let sel = Selector::parse("items[0:0]").unwrap();
1525        assert_eq!(
1526            sel.segments[0].indices,
1527            vec![Index::Slice { start: Some(0), end: Some(0) }]
1528        );
1529    }
1530
1531    // ── Builtin parsing edge cases ──
1532
1533    #[test]
1534    fn parse_builtin_length_after_nested() {
1535        let sel = Selector::parse("a.b.length()").unwrap();
1536        assert_eq!(sel.segments.len(), 3);
1537        assert_eq!(sel.segments[2].builtin, Some(Builtin::Length));
1538    }
1539
1540    #[test]
1541    fn parse_builtin_values_after_key() {
1542        let sel = Selector::parse("data.values()").unwrap();
1543        assert_eq!(sel.segments.len(), 2);
1544        assert_eq!(sel.segments[1].builtin, Some(Builtin::Values));
1545    }
1546
1547    // ── Recursive descent parsing edge cases ──
1548
1549    #[test]
1550    fn parse_recursive_with_slice() {
1551        let sel = Selector::parse("..items[1:3]").unwrap();
1552        assert!(sel.segments[0].recursive);
1553        assert_eq!(sel.segments[0].key, Some("items".into()));
1554        assert_eq!(
1555            sel.segments[0].indices,
1556            vec![Index::Slice { start: Some(1), end: Some(3) }]
1557        );
1558    }
1559
1560    #[test]
1561    fn parse_recursive_with_wildcard() {
1562        let sel = Selector::parse("..items[*]").unwrap();
1563        assert!(sel.segments[0].recursive);
1564        assert_eq!(sel.segments[0].indices, vec![Index::Wildcard]);
1565    }
1566
1567    #[test]
1568    fn parse_recursive_then_builtin() {
1569        // ..name should be a recursive selector; builtin after is separate
1570        let p = Pipeline::parse("..items | length()").unwrap();
1571        assert_eq!(p.stages.len(), 2);
1572    }
1573
1574    // ── Pipeline parsing edge cases ──
1575
1576    #[test]
1577    fn parse_pipeline_three_stages() {
1578        let p = Pipeline::parse("items[*] | select(.price > 100) | name").unwrap();
1579        assert_eq!(p.stages.len(), 3);
1580        assert!(matches!(p.stages[0], PipeStage::Path(_)));
1581        assert!(matches!(p.stages[1], PipeStage::Select(_)));
1582        assert!(matches!(p.stages[2], PipeStage::Path(_)));
1583    }
1584
1585    #[test]
1586    fn parse_pipeline_four_stages() {
1587        let p = Pipeline::parse("items[*] | select(.x > 0) | name | length()").unwrap();
1588        assert_eq!(p.stages.len(), 4);
1589        assert!(matches!(p.stages[3], PipeStage::Builtin(Builtin::Length)));
1590    }
1591
1592    #[test]
1593    fn parse_pipeline_set_then_path() {
1594        let p = Pipeline::parse("set(.name, \"Bob\") | name").unwrap();
1595        assert_eq!(p.stages.len(), 2);
1596        assert!(matches!(p.stages[0], PipeStage::Set { .. }));
1597        assert!(matches!(p.stages[1], PipeStage::Path(_)));
1598    }
1599
1600    #[test]
1601    fn parse_pipeline_del_then_builtin() {
1602        let p = Pipeline::parse("del(.x) | keys()").unwrap();
1603        assert_eq!(p.stages.len(), 2);
1604        assert!(matches!(p.stages[0], PipeStage::Del(_)));
1605        assert!(matches!(p.stages[1], PipeStage::Builtin(Builtin::Keys)));
1606    }
1607
1608    #[test]
1609    fn parse_pipeline_set_then_set() {
1610        let p = Pipeline::parse("set(.a, 1) | set(.b, 2)").unwrap();
1611        assert_eq!(p.stages.len(), 2);
1612        assert!(matches!(p.stages[0], PipeStage::Set { .. }));
1613        assert!(matches!(p.stages[1], PipeStage::Set { .. }));
1614    }
1615
1616    #[test]
1617    fn parse_pipeline_del_then_del() {
1618        let p = Pipeline::parse("del(.a) | del(.b)").unwrap();
1619        assert_eq!(p.stages.len(), 2);
1620        assert!(matches!(p.stages[0], PipeStage::Del(_)));
1621        assert!(matches!(p.stages[1], PipeStage::Del(_)));
1622    }
1623
1624    // ── Filter parsing edge cases ──
1625
1626    #[test]
1627    fn parse_filter_triple_and() {
1628        let expr = parse_filter_expr(".a > 1 and .b > 2 and .c > 3").unwrap();
1629        // Should nest as ((.a > 1) and (.b > 2)) and (.c > 3) — left-associative
1630        assert!(matches!(expr, FilterExpr::And(_, _)));
1631    }
1632
1633    #[test]
1634    fn parse_filter_triple_or() {
1635        let expr = parse_filter_expr(".a > 1 or .b > 2 or .c > 3").unwrap();
1636        assert!(matches!(expr, FilterExpr::Or(_, _)));
1637    }
1638
1639    #[test]
1640    fn parse_filter_not_condition() {
1641        let expr = parse_filter_expr("not .x == 0").unwrap();
1642        match expr {
1643            FilterExpr::Not(inner) => assert!(matches!(*inner, FilterExpr::Condition(_))),
1644            _ => panic!("expected Not(Condition)"),
1645        }
1646    }
1647
1648    #[test]
1649    fn parse_filter_deep_nested_path() {
1650        let expr = parse_filter_expr(".a.b.c.d > 0").unwrap();
1651        match expr {
1652            FilterExpr::Condition(c) => assert_eq!(c.path.segments.len(), 4),
1653            _ => panic!("expected Condition"),
1654        }
1655    }
1656
1657    #[test]
1658    fn parse_filter_empty_string_literal() {
1659        let expr = parse_filter_expr(".name == \"\"").unwrap();
1660        match expr {
1661            FilterExpr::Condition(c) => assert_eq!(c.value, LiteralValue::String("".into())),
1662            _ => panic!("expected Condition"),
1663        }
1664    }
1665
1666    #[test]
1667    fn parse_filter_zero_literal() {
1668        let expr = parse_filter_expr(".count == 0").unwrap();
1669        match expr {
1670            FilterExpr::Condition(c) => assert_eq!(c.value, LiteralValue::Number(0.0)),
1671            _ => panic!("expected Condition"),
1672        }
1673    }
1674
1675    #[test]
1676    fn parse_filter_with_index_in_path() {
1677        let expr = parse_filter_expr(".items[0].name == \"first\"").unwrap();
1678        match expr {
1679            FilterExpr::Condition(c) => {
1680                assert_eq!(c.path.segments.len(), 2);
1681                assert_eq!(c.path.segments[0].key, Some("items".into()));
1682                assert_eq!(c.path.segments[0].indices, vec![Index::Number(0)]);
1683            }
1684            _ => panic!("expected Condition"),
1685        }
1686    }
1687
1688    // ── set/del parsing edge cases ──
1689
1690    #[test]
1691    fn parse_set_with_array_index() {
1692        let p = Pipeline::parse("set(.items[0], 99)").unwrap();
1693        match &p.stages[0] {
1694            PipeStage::Set { path, value } => {
1695                assert_eq!(path.segments[0].key, Some("items".into()));
1696                assert_eq!(path.segments[0].indices, vec![Index::Number(0)]);
1697                assert_eq!(*value, LiteralValue::Number(99.0));
1698            }
1699            _ => panic!("expected Set"),
1700        }
1701    }
1702
1703    #[test]
1704    fn parse_set_float_value() {
1705        let p = Pipeline::parse("set(.score, 3.14)").unwrap();
1706        match &p.stages[0] {
1707            PipeStage::Set { value, .. } => assert_eq!(*value, LiteralValue::Number(3.14)),
1708            _ => panic!("expected Set"),
1709        }
1710    }
1711
1712    #[test]
1713    fn parse_set_negative_value() {
1714        let p = Pipeline::parse("set(.temp, -5)").unwrap();
1715        match &p.stages[0] {
1716            PipeStage::Set { value, .. } => assert_eq!(*value, LiteralValue::Number(-5.0)),
1717            _ => panic!("expected Set"),
1718        }
1719    }
1720
1721    #[test]
1722    fn parse_set_false_value() {
1723        let p = Pipeline::parse("set(.active, false)").unwrap();
1724        match &p.stages[0] {
1725            PipeStage::Set { value, .. } => assert_eq!(*value, LiteralValue::Bool(false)),
1726            _ => panic!("expected Set"),
1727        }
1728    }
1729
1730    #[test]
1731    fn parse_del_with_array_index() {
1732        let p = Pipeline::parse("del(.items[0])").unwrap();
1733        match &p.stages[0] {
1734            PipeStage::Del(path) => {
1735                assert_eq!(path.segments[0].key, Some("items".into()));
1736                assert_eq!(path.segments[0].indices, vec![Index::Number(0)]);
1737            }
1738            _ => panic!("expected Del"),
1739        }
1740    }
1741
1742    #[test]
1743    fn parse_del_deeply_nested() {
1744        let p = Pipeline::parse("del(.a.b.c)").unwrap();
1745        match &p.stages[0] {
1746            PipeStage::Del(path) => assert_eq!(path.segments.len(), 3),
1747            _ => panic!("expected Del"),
1748        }
1749    }
1750
1751    // ── Expression/multi-selector edge cases ──
1752
1753    #[test]
1754    fn parse_expression_with_pipelines() {
1755        let expr = Expression::parse("name, items | length()").unwrap();
1756        assert_eq!(expr.pipelines.len(), 2);
1757        assert_eq!(expr.pipelines[1].stages.len(), 2);
1758    }
1759
1760    #[test]
1761    fn parse_expression_with_builtins() {
1762        let expr = Expression::parse("keys(), values()").unwrap();
1763        assert_eq!(expr.pipelines.len(), 2);
1764    }
1765
1766    #[test]
1767    fn parse_expression_with_recursive() {
1768        let expr = Expression::parse("..name, ..id").unwrap();
1769        assert_eq!(expr.pipelines.len(), 2);
1770    }
1771
1772    // ── Whitespace handling ──
1773
1774    #[test]
1775    fn parse_pipeline_extra_whitespace() {
1776        let p = Pipeline::parse("  items[*]  |  select(.price > 100)  |  name  ").unwrap();
1777        assert_eq!(p.stages.len(), 3);
1778    }
1779
1780    #[test]
1781    fn parse_filter_whitespace_around_ops() {
1782        let expr = parse_filter_expr(".price  >  100").unwrap();
1783        assert!(matches!(expr, FilterExpr::Condition(_)));
1784    }
1785
1786    // ── Literal parsing edge cases ──
1787
1788    #[test]
1789    fn parse_literal_large_number() {
1790        let (v, _) = parse_literal("999999999").unwrap();
1791        assert_eq!(v, LiteralValue::Number(999999999.0));
1792    }
1793
1794    #[test]
1795    fn parse_literal_zero() {
1796        let (v, _) = parse_literal("0").unwrap();
1797        assert_eq!(v, LiteralValue::Number(0.0));
1798    }
1799
1800    #[test]
1801    fn parse_literal_negative_zero() {
1802        let (v, _) = parse_literal("-0").unwrap();
1803        assert_eq!(v, LiteralValue::Number(0.0)); // -0.0 == 0.0
1804    }
1805
1806    #[test]
1807    fn parse_literal_string_with_spaces() {
1808        let (v, _) = parse_literal("\"hello world\"").unwrap();
1809        assert_eq!(v, LiteralValue::String("hello world".into()));
1810    }
1811
1812    #[test]
1813    fn parse_literal_string_with_special_chars() {
1814        let (v, _) = parse_literal("\"foo@bar.com\"").unwrap();
1815        assert_eq!(v, LiteralValue::String("foo@bar.com".into()));
1816    }
1817}