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