1use crate::error::PickError;
2use super::types::*;
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(
44 "empty stage in pipeline".into(),
45 ));
46 }
47 stages.push(parse_pipe_stage(trimmed)?);
48 }
49 Ok(Pipeline { stages })
50 }
51}
52
53fn parse_pipe_stage(input: &str) -> Result<PipeStage, PickError> {
55 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 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 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 if let Some(builtin) = try_parse_standalone_builtin(input) {
84 return Ok(PipeStage::Builtin(builtin));
85 }
86
87 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
100impl Selector {
105 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 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 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..]; 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..]; }
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
328fn 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 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 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 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 Ok((FilterExpr::Truthy(path), after_path))
403 }
404}
405
406fn 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 return Ok((Selector { segments: vec![] }, after_dot));
422 }
423
424 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
461fn 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 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 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 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 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 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 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
599fn 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; }
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
645fn 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#[cfg(test)]
694mod tests {
695 use super::*;
696
697 #[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 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 #[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 #[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 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 #[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 #[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 #[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 #[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()); 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 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 #[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 #[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 #[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 #[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 #[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 #[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 let p = Pipeline::parse("..items | length()").unwrap();
1571 assert_eq!(p.stages.len(), 2);
1572 }
1573
1574 #[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 #[test]
1627 fn parse_filter_triple_and() {
1628 let expr = parse_filter_expr(".a > 1 and .b > 2 and .c > 3").unwrap();
1629 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 #[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 #[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 #[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 #[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)); }
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}