1use std::fmt;
24
25#[derive(Debug, Clone)]
27pub struct Query {
28 segments: Vec<QuerySegment>,
30 has_recursive: bool,
32 has_wildcard: bool,
34 original: String,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum QuerySegment {
41 Field(String),
43 Index(usize),
45 Wildcard,
47 Recursive(String),
49}
50
51#[derive(Debug, Clone)]
53pub enum QueryError {
54 Empty,
56 UnexpectedChar(char, usize),
58 UnclosedBracket,
60 UnclosedQuote,
62 InvalidIndex(String),
64 ExpectedField,
66}
67
68impl fmt::Display for QueryError {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 match self {
71 Self::Empty => write!(f, "empty query"),
72 Self::UnexpectedChar(c, pos) => {
73 write!(f, "unexpected character '{c}' at position {pos}")
74 }
75 Self::UnclosedBracket => write!(f, "unclosed bracket"),
76 Self::UnclosedQuote => write!(f, "unclosed quote"),
77 Self::InvalidIndex(s) => write!(f, "invalid index: {s}"),
78 Self::ExpectedField => write!(f, "expected field name"),
79 }
80 }
81}
82
83impl std::error::Error for QueryError {}
84
85impl Query {
86 pub fn parse(query: &str) -> Result<Self, QueryError> {
91 if query.is_empty() {
92 return Err(QueryError::Empty);
93 }
94
95 let mut segments = Vec::new();
96 let bytes = query.as_bytes();
97 let mut i = 0;
98
99 if bytes[0] != b'.' && bytes[0] != b'[' {
102 while i < bytes.len() && bytes[i] != b'.' && bytes[i] != b'[' {
104 i += 1;
105 }
106 }
107
108 while i < bytes.len() {
109 match bytes[i] {
110 b'.' => {
111 i += 1;
112 if i >= bytes.len() {
113 return Err(QueryError::ExpectedField);
114 }
115
116 if bytes[i] == b'.' {
118 i += 1;
119 let (field, consumed) = parse_identifier(&bytes[i..])?;
120 segments.push(QuerySegment::Recursive(field));
121 i += consumed;
122 } else {
123 let (field, consumed) = parse_identifier(&bytes[i..])?;
124 segments.push(QuerySegment::Field(field));
125 i += consumed;
126 }
127 }
128 b'[' => {
129 i += 1;
130 if i >= bytes.len() {
131 return Err(QueryError::UnclosedBracket);
132 }
133
134 match bytes[i] {
135 b'*' => {
136 i += 1;
138 if i >= bytes.len() || bytes[i] != b']' {
139 return Err(QueryError::UnclosedBracket);
140 }
141 i += 1;
142 segments.push(QuerySegment::Wildcard);
143 }
144 b'"' => {
145 i += 1;
147 let (field, consumed) = parse_quoted_string(&bytes[i..])?;
148 i += consumed;
149 if i >= bytes.len() || bytes[i] != b']' {
150 return Err(QueryError::UnclosedBracket);
151 }
152 i += 1;
153 segments.push(QuerySegment::Field(field));
154 }
155 b'0'..=b'9' => {
156 let start = i;
158 while i < bytes.len() && bytes[i].is_ascii_digit() {
159 i += 1;
160 }
161 let index_str =
162 std::str::from_utf8(&bytes[start..i]).map_err(|_| {
163 QueryError::InvalidIndex("invalid utf8".to_string())
164 })?;
165 let index: usize = index_str
166 .parse()
167 .map_err(|_| QueryError::InvalidIndex(index_str.to_string()))?;
168
169 if i >= bytes.len() || bytes[i] != b']' {
170 return Err(QueryError::UnclosedBracket);
171 }
172 i += 1;
173 segments.push(QuerySegment::Index(index));
174 }
175 c => {
176 return Err(QueryError::UnexpectedChar(c as char, i));
177 }
178 }
179 }
180 c => {
181 return Err(QueryError::UnexpectedChar(c as char, i));
182 }
183 }
184 }
185
186 let has_recursive = segments
187 .iter()
188 .any(|s| matches!(s, QuerySegment::Recursive(_)));
189 let has_wildcard = segments.iter().any(|s| matches!(s, QuerySegment::Wildcard));
190
191 Ok(Self {
192 segments,
193 has_recursive,
194 has_wildcard,
195 original: query.to_string(),
196 })
197 }
198
199 #[must_use]
203 pub fn matches(&self, path: &str) -> bool {
204 let path_segments = parse_path_segments(path);
205 self.matches_segments(&path_segments, 0, 0)
206 }
207
208 #[must_use]
215 pub fn match_potential(&self, path: &str) -> MatchPotential {
216 let path_segments = parse_path_segments(path);
217 self.check_potential(&path_segments, 0, 0)
218 }
219
220 #[must_use]
222 pub fn original(&self) -> &str {
223 &self.original
224 }
225
226 #[must_use]
228 pub const fn has_recursive(&self) -> bool {
229 self.has_recursive
230 }
231
232 #[must_use]
234 pub const fn has_wildcard(&self) -> bool {
235 self.has_wildcard
236 }
237
238 fn matches_segments(
239 &self,
240 path_segments: &[PathSegment<'_>],
241 query_idx: usize,
242 path_idx: usize,
243 ) -> bool {
244 if query_idx >= self.segments.len() {
246 return true;
247 }
248
249 if path_idx >= path_segments.len() {
251 return false;
252 }
253
254 let query_seg = &self.segments[query_idx];
255 let path_seg = &path_segments[path_idx];
256
257 match query_seg {
258 QuerySegment::Field(expected) => {
259 if let PathSegment::Field(actual) = path_seg
260 && *actual == expected
261 {
262 return self.matches_segments(path_segments, query_idx + 1, path_idx + 1);
263 }
264 false
265 }
266 QuerySegment::Index(expected) => {
267 if let PathSegment::Index(actual) = path_seg
268 && actual == expected
269 {
270 return self.matches_segments(path_segments, query_idx + 1, path_idx + 1);
271 }
272 false
273 }
274 QuerySegment::Wildcard => {
275 self.matches_segments(path_segments, query_idx + 1, path_idx + 1)
277 }
278 QuerySegment::Recursive(expected) => {
279 for i in path_idx..path_segments.len() {
281 if let PathSegment::Field(actual) = &path_segments[i]
282 && *actual == expected
283 && self.matches_segments(path_segments, query_idx + 1, i + 1)
284 {
285 return true;
286 }
287 }
288 false
289 }
290 }
291 }
292
293 fn check_potential(
294 &self,
295 path_segments: &[PathSegment<'_>],
296 query_idx: usize,
297 path_idx: usize,
298 ) -> MatchPotential {
299 if query_idx >= self.segments.len() {
301 return MatchPotential::Matches;
302 }
303
304 if path_idx >= path_segments.len() {
306 return MatchPotential::Partial;
307 }
308
309 let query_seg = &self.segments[query_idx];
310 let path_seg = &path_segments[path_idx];
311
312 match query_seg {
313 QuerySegment::Field(expected) => {
314 if let PathSegment::Field(actual) = path_seg
315 && *actual == expected
316 {
317 return self.check_potential(path_segments, query_idx + 1, path_idx + 1);
318 }
319 MatchPotential::NoMatch
321 }
322 QuerySegment::Index(expected) => {
323 if let PathSegment::Index(actual) = path_seg
324 && actual == expected
325 {
326 return self.check_potential(path_segments, query_idx + 1, path_idx + 1);
327 }
328 MatchPotential::NoMatch
329 }
330 QuerySegment::Wildcard => {
331 self.check_potential(path_segments, query_idx + 1, path_idx + 1)
333 }
334 QuerySegment::Recursive(_) => {
335 MatchPotential::Partial
337 }
338 }
339 }
340}
341
342#[derive(Debug, Clone, Copy, PartialEq, Eq)]
344pub enum MatchPotential {
345 Matches,
347 Partial,
349 NoMatch,
351}
352
353#[derive(Debug, Clone, PartialEq, Eq)]
355enum PathSegment<'a> {
356 Field(&'a str),
357 Index(usize),
358}
359
360fn parse_path_segments(path: &str) -> Vec<PathSegment<'_>> {
362 let mut segments = Vec::new();
363 let bytes = path.as_bytes();
364 let mut i = 0;
365
366 while i < bytes.len() && bytes[i] != b'.' && bytes[i] != b'[' {
368 i += 1;
369 }
370
371 while i < bytes.len() {
372 match bytes[i] {
373 b'.' => {
374 i += 1;
375 let start = i;
376 while i < bytes.len() && bytes[i] != b'.' && bytes[i] != b'[' {
377 i += 1;
378 }
379 if i > start {
380 let field = unsafe { std::str::from_utf8_unchecked(&bytes[start..i]) };
381 segments.push(PathSegment::Field(field));
382 }
383 }
384 b'[' => {
385 i += 1;
386 if i < bytes.len() && bytes[i] == b'"' {
387 i += 1;
389 let start = i;
390 while i < bytes.len() && bytes[i] != b'"' {
391 if bytes[i] == b'\\' && i + 1 < bytes.len() {
392 i += 2;
393 } else {
394 i += 1;
395 }
396 }
397 let field = unsafe { std::str::from_utf8_unchecked(&bytes[start..i]) };
398 segments.push(PathSegment::Field(field));
399 i += 1; } else {
401 let start = i;
403 while i < bytes.len() && bytes[i].is_ascii_digit() {
404 i += 1;
405 }
406 if i > start {
407 let index_str = unsafe { std::str::from_utf8_unchecked(&bytes[start..i]) };
408 if let Ok(index) = index_str.parse() {
409 segments.push(PathSegment::Index(index));
410 }
411 }
412 }
413 if i < bytes.len() && bytes[i] == b']' {
415 i += 1;
416 }
417 }
418 _ => {
419 i += 1;
420 }
421 }
422 }
423
424 segments
425}
426
427fn parse_identifier(bytes: &[u8]) -> Result<(String, usize), QueryError> {
429 let mut i = 0;
430 while i < bytes.len() {
431 let b = bytes[i];
432 if b == b'.' || b == b'[' {
433 break;
434 }
435 if !b.is_ascii_alphanumeric() && b != b'_' && b != b'-' {
436 break;
437 }
438 i += 1;
439 }
440 if i == 0 {
441 return Err(QueryError::ExpectedField);
442 }
443 let field = std::str::from_utf8(&bytes[..i])
444 .map_err(|_| QueryError::ExpectedField)?
445 .to_string();
446 Ok((field, i))
447}
448
449fn parse_quoted_string(bytes: &[u8]) -> Result<(String, usize), QueryError> {
451 let mut result = String::new();
452 let mut i = 0;
453
454 while i < bytes.len() {
455 match bytes[i] {
456 b'"' => {
457 return Ok((result, i + 1));
458 }
459 b'\\' if i + 1 < bytes.len() => {
460 i += 1;
461 match bytes[i] {
462 b'"' => result.push('"'),
463 b'\\' => result.push('\\'),
464 b'n' => result.push('\n'),
465 b't' => result.push('\t'),
466 other => {
467 result.push('\\');
468 result.push(other as char);
469 }
470 }
471 i += 1;
472 }
473 b => {
474 result.push(b as char);
475 i += 1;
476 }
477 }
478 }
479
480 Err(QueryError::UnclosedQuote)
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486
487 #[test]
492 fn test_parse_simple_field() {
493 let query = Query::parse(".name").unwrap();
494 assert_eq!(query.segments.len(), 1);
495 assert_eq!(query.segments[0], QuerySegment::Field("name".to_string()));
496 }
497
498 #[test]
499 fn test_parse_nested_fields() {
500 let query = Query::parse(".users.name").unwrap();
501 assert_eq!(query.segments.len(), 2);
502 assert_eq!(query.segments[0], QuerySegment::Field("users".to_string()));
503 assert_eq!(query.segments[1], QuerySegment::Field("name".to_string()));
504 }
505
506 #[test]
507 fn test_parse_array_index() {
508 let query = Query::parse(".items[0]").unwrap();
509 assert_eq!(query.segments.len(), 2);
510 assert_eq!(query.segments[0], QuerySegment::Field("items".to_string()));
511 assert_eq!(query.segments[1], QuerySegment::Index(0));
512 }
513
514 #[test]
515 fn test_parse_wildcard() {
516 let query = Query::parse(".users[*].name").unwrap();
517 assert_eq!(query.segments.len(), 3);
518 assert_eq!(query.segments[0], QuerySegment::Field("users".to_string()));
519 assert_eq!(query.segments[1], QuerySegment::Wildcard);
520 assert_eq!(query.segments[2], QuerySegment::Field("name".to_string()));
521 assert!(query.has_wildcard());
522 }
523
524 #[test]
525 fn test_parse_recursive() {
526 let query = Query::parse("..error").unwrap();
527 assert_eq!(query.segments.len(), 1);
528 assert_eq!(
529 query.segments[0],
530 QuerySegment::Recursive("error".to_string())
531 );
532 assert!(query.has_recursive());
533 }
534
535 #[test]
536 fn test_parse_bracket_notation() {
537 let query = Query::parse("[\"field.name\"]").unwrap();
538 assert_eq!(query.segments.len(), 1);
539 assert_eq!(
540 query.segments[0],
541 QuerySegment::Field("field.name".to_string())
542 );
543 }
544
545 #[test]
546 fn test_parse_with_root() {
547 let query = Query::parse("json.users[0]").unwrap();
548 assert_eq!(query.segments.len(), 2);
549 assert_eq!(query.segments[0], QuerySegment::Field("users".to_string()));
550 assert_eq!(query.segments[1], QuerySegment::Index(0));
551 }
552
553 #[test]
558 fn test_matches_simple() {
559 let query = Query::parse(".name").unwrap();
560 assert!(query.matches("json.name"));
561 assert!(!query.matches("json.age"));
562 assert!(!query.matches("json.user.name"));
563 }
564
565 #[test]
566 fn test_matches_nested() {
567 let query = Query::parse(".user.name").unwrap();
568 assert!(query.matches("json.user.name"));
569 assert!(!query.matches("json.user"));
570 assert!(!query.matches("json.user.age"));
571 }
572
573 #[test]
574 fn test_matches_array_index() {
575 let query = Query::parse(".items[0]").unwrap();
576 assert!(query.matches("json.items[0]"));
577 assert!(!query.matches("json.items[1]"));
578 assert!(!query.matches("json.items"));
579 }
580
581 #[test]
582 fn test_matches_wildcard() {
583 let query = Query::parse(".users[*].name").unwrap();
584 assert!(query.matches("json.users[0].name"));
585 assert!(query.matches("json.users[99].name"));
586 assert!(!query.matches("json.users[0].age"));
587 assert!(!query.matches("json.users[0]"));
588 }
589
590 #[test]
591 fn test_matches_recursive() {
592 let query = Query::parse("..error").unwrap();
593 assert!(query.matches("json.error"));
594 assert!(query.matches("json.nested.error"));
595 assert!(query.matches("json.deeply.nested.path.error"));
596 assert!(!query.matches("json.message"));
597 }
598
599 #[test]
600 fn test_match_potential_exact() {
601 let query = Query::parse(".users.name").unwrap();
602
603 assert_eq!(
604 query.match_potential("json.users.name"),
605 MatchPotential::Matches
606 );
607 assert_eq!(query.match_potential("json.users"), MatchPotential::Partial);
608 assert_eq!(query.match_potential("json.items"), MatchPotential::NoMatch);
609 }
610
611 #[test]
612 fn test_match_potential_wildcard() {
613 let query = Query::parse(".users[*].name").unwrap();
614
615 assert_eq!(
616 query.match_potential("json.users[0].name"),
617 MatchPotential::Matches
618 );
619 assert_eq!(
620 query.match_potential("json.users[0]"),
621 MatchPotential::Partial
622 );
623 assert_eq!(query.match_potential("json.users"), MatchPotential::Partial);
624 }
625
626 #[test]
627 fn test_complex_query() {
628 let query = Query::parse(".data[*].users[0].email").unwrap();
629
630 assert!(query.matches("json.data[5].users[0].email"));
631 assert!(!query.matches("json.data[5].users[1].email"));
632 assert!(!query.matches("json.data[5].users[0].name"));
633 }
634
635 #[test]
640 fn test_parse_error_empty() {
641 assert!(matches!(Query::parse(""), Err(QueryError::Empty)));
642 }
643
644 #[test]
645 fn test_parse_error_unclosed_bracket() {
646 assert!(matches!(
647 Query::parse("[0"),
648 Err(QueryError::UnclosedBracket)
649 ));
650 }
651
652 #[test]
653 fn test_parse_error_unclosed_quote() {
654 assert!(matches!(
655 Query::parse("[\"field"),
656 Err(QueryError::UnclosedQuote)
657 ));
658 }
659
660 #[test]
665 fn test_query_error_display_empty() {
666 let err = QueryError::Empty;
667 assert_eq!(err.to_string(), "empty query");
668 }
669
670 #[test]
671 fn test_query_error_display_unexpected_char() {
672 let err = QueryError::UnexpectedChar('$', 5);
673 let msg = err.to_string();
674 assert!(msg.contains("unexpected character"));
675 assert!(msg.contains('$'));
676 assert!(msg.contains('5'));
677 }
678
679 #[test]
680 fn test_query_error_display_unclosed_bracket() {
681 let err = QueryError::UnclosedBracket;
682 assert_eq!(err.to_string(), "unclosed bracket");
683 }
684
685 #[test]
686 fn test_query_error_display_unclosed_quote() {
687 let err = QueryError::UnclosedQuote;
688 assert_eq!(err.to_string(), "unclosed quote");
689 }
690
691 #[test]
692 fn test_query_error_display_invalid_index() {
693 let err = QueryError::InvalidIndex("abc".to_string());
694 let msg = err.to_string();
695 assert!(msg.contains("invalid index"));
696 assert!(msg.contains("abc"));
697 }
698
699 #[test]
700 fn test_query_error_display_expected_field() {
701 let err = QueryError::ExpectedField;
702 assert_eq!(err.to_string(), "expected field name");
703 }
704
705 #[test]
706 fn test_query_error_is_std_error() {
707 let err: Box<dyn std::error::Error> = Box::new(QueryError::Empty);
708 assert!(!err.to_string().is_empty());
709 }
710
711 #[test]
716 fn test_query_original() {
717 let query = Query::parse(".users[0].name").unwrap();
718 assert_eq!(query.original(), ".users[0].name");
719 }
720
721 #[test]
722 fn test_query_has_recursive_false() {
723 let query = Query::parse(".users.name").unwrap();
724 assert!(!query.has_recursive());
725 }
726
727 #[test]
728 fn test_query_has_recursive_true() {
729 let query = Query::parse("..name").unwrap();
730 assert!(query.has_recursive());
731 }
732
733 #[test]
734 fn test_query_has_wildcard_false() {
735 let query = Query::parse(".users[0]").unwrap();
736 assert!(!query.has_wildcard());
737 }
738
739 #[test]
740 fn test_query_has_wildcard_true() {
741 let query = Query::parse(".users[*]").unwrap();
742 assert!(query.has_wildcard());
743 }
744
745 #[test]
750 fn test_parse_error_expected_field_after_dot() {
751 let result = Query::parse(".");
752 assert!(matches!(result, Err(QueryError::ExpectedField)));
753 }
754
755 #[test]
756 fn test_parse_error_expected_field_after_recursive() {
757 let result = Query::parse("..");
758 assert!(matches!(result, Err(QueryError::ExpectedField)));
759 }
760
761 #[test]
762 fn test_parse_error_unexpected_char_in_bracket() {
763 let result = Query::parse("[abc]");
764 assert!(matches!(result, Err(QueryError::UnexpectedChar(_, _))));
765 }
766
767 #[test]
768 fn test_parse_error_unexpected_char_outside() {
769 let result = Query::parse(".field@more");
772 assert!(matches!(result, Err(QueryError::UnexpectedChar('@', _))));
773 }
774
775 #[test]
776 fn test_parse_wildcard_unclosed() {
777 let result = Query::parse("[*");
778 assert!(matches!(result, Err(QueryError::UnclosedBracket)));
779 }
780
781 #[test]
782 fn test_parse_quoted_field_no_close_bracket() {
783 let result = Query::parse("[\"field\"");
784 assert!(matches!(result, Err(QueryError::UnclosedBracket)));
785 }
786
787 #[test]
788 fn test_parse_numeric_index_no_close_bracket() {
789 let result = Query::parse("[123");
790 assert!(matches!(result, Err(QueryError::UnclosedBracket)));
791 }
792
793 #[test]
794 fn test_parse_empty_bracket() {
795 let result = Query::parse("[");
796 assert!(matches!(result, Err(QueryError::UnclosedBracket)));
797 }
798
799 #[test]
804 fn test_query_segment_equality() {
805 assert_eq!(
806 QuerySegment::Field("a".to_string()),
807 QuerySegment::Field("a".to_string())
808 );
809 assert_ne!(
810 QuerySegment::Field("a".to_string()),
811 QuerySegment::Field("b".to_string())
812 );
813 assert_eq!(QuerySegment::Index(0), QuerySegment::Index(0));
814 assert_ne!(QuerySegment::Index(0), QuerySegment::Index(1));
815 assert_eq!(QuerySegment::Wildcard, QuerySegment::Wildcard);
816 assert_eq!(
817 QuerySegment::Recursive("x".to_string()),
818 QuerySegment::Recursive("x".to_string())
819 );
820 }
821
822 #[test]
823 fn test_query_segment_clone() {
824 let seg = QuerySegment::Field("test".to_string());
825 let cloned = seg.clone();
826 assert_eq!(seg, cloned);
827 }
828
829 #[test]
830 fn test_query_segment_debug() {
831 let seg = QuerySegment::Wildcard;
832 let debug = format!("{seg:?}");
833 assert!(debug.contains("Wildcard"));
834 }
835
836 #[test]
841 fn test_match_potential_equality() {
842 assert_eq!(MatchPotential::Matches, MatchPotential::Matches);
843 assert_eq!(MatchPotential::Partial, MatchPotential::Partial);
844 assert_eq!(MatchPotential::NoMatch, MatchPotential::NoMatch);
845 assert_ne!(MatchPotential::Matches, MatchPotential::NoMatch);
846 }
847
848 #[test]
849 fn test_match_potential_clone_copy() {
850 let mp = MatchPotential::Partial;
851 let cloned = mp;
852 let copied = mp;
853 assert_eq!(mp, cloned);
854 assert_eq!(mp, copied);
855 }
856
857 #[test]
858 fn test_match_potential_debug() {
859 let mp = MatchPotential::Matches;
860 let debug = format!("{mp:?}");
861 assert!(debug.contains("Matches"));
862 }
863
864 #[test]
869 fn test_query_clone() {
870 let query = Query::parse(".users[*].name").unwrap();
871 let cloned = query.clone();
872 assert_eq!(query.original(), cloned.original());
873 assert_eq!(query.has_recursive(), cloned.has_recursive());
874 assert_eq!(query.has_wildcard(), cloned.has_wildcard());
875 }
876
877 #[test]
878 fn test_query_debug() {
879 let query = Query::parse(".name").unwrap();
880 let debug = format!("{query:?}");
881 assert!(debug.contains("Query"));
882 }
883
884 #[test]
889 fn test_parse_quoted_escape_quote() {
890 let query = Query::parse("[\"field\\\"name\"]").unwrap();
891 assert_eq!(
892 query.segments[0],
893 QuerySegment::Field("field\"name".to_string())
894 );
895 }
896
897 #[test]
898 fn test_parse_quoted_escape_backslash() {
899 let query = Query::parse("[\"path\\\\to\"]").unwrap();
900 assert_eq!(
901 query.segments[0],
902 QuerySegment::Field("path\\to".to_string())
903 );
904 }
905
906 #[test]
907 fn test_parse_quoted_escape_n() {
908 let query = Query::parse("[\"line\\nbreak\"]").unwrap();
909 assert_eq!(
910 query.segments[0],
911 QuerySegment::Field("line\nbreak".to_string())
912 );
913 }
914
915 #[test]
916 fn test_parse_quoted_escape_t() {
917 let query = Query::parse("[\"tab\\there\"]").unwrap();
918 assert_eq!(
919 query.segments[0],
920 QuerySegment::Field("tab\there".to_string())
921 );
922 }
923
924 #[test]
925 fn test_parse_quoted_unknown_escape() {
926 let query = Query::parse("[\"test\\xvalue\"]").unwrap();
927 assert_eq!(
929 query.segments[0],
930 QuerySegment::Field("test\\xvalue".to_string())
931 );
932 }
933
934 #[test]
939 fn test_matches_with_quoted_path_field() {
940 let query = Query::parse("[\"special-field\"]").unwrap();
941 assert!(query.matches("json[\"special-field\"]"));
943 }
944
945 #[test]
946 fn test_matches_path_with_escape() {
947 let query = Query::parse(".field").unwrap();
948 assert!(query.matches("json.field"));
950 }
951
952 #[test]
957 fn test_recursive_no_match_if_field_not_found() {
958 let query = Query::parse("..notfound").unwrap();
959 assert!(!query.matches("json.a.b.c"));
960 }
961
962 #[test]
963 fn test_recursive_match_at_various_depths() {
964 let query = Query::parse("..target").unwrap();
965 assert!(query.matches("json.target"));
966 assert!(query.matches("json.a.target"));
967 assert!(query.matches("json.a.b.target"));
968 assert!(query.matches("json.a.b.c.d.target"));
969 }
970
971 #[test]
972 fn test_recursive_with_continuation() {
973 let query = Query::parse("..error.message").unwrap();
974 assert!(query.matches("json.error.message"));
975 assert!(query.matches("json.nested.error.message"));
976 assert!(!query.matches("json.nested.error"));
977 }
978
979 #[test]
984 fn test_match_potential_recursive() {
985 let query = Query::parse("..field").unwrap();
986 assert_eq!(query.match_potential("json.other"), MatchPotential::Partial);
988 }
989
990 #[test]
991 fn test_match_potential_index_mismatch() {
992 let query = Query::parse(".items[0]").unwrap();
993 assert_eq!(
994 query.match_potential("json.items[1]"),
995 MatchPotential::NoMatch
996 );
997 }
998
999 #[test]
1000 fn test_match_potential_field_index_type_mismatch() {
1001 let query = Query::parse(".items.name").unwrap();
1002 assert_eq!(
1004 query.match_potential("json.items[0]"),
1005 MatchPotential::NoMatch
1006 );
1007 }
1008
1009 #[test]
1014 fn test_parse_large_index() {
1015 let query = Query::parse("[999999]").unwrap();
1016 assert_eq!(query.segments[0], QuerySegment::Index(999_999));
1017 }
1018
1019 #[test]
1020 fn test_matches_large_index() {
1021 let query = Query::parse(".items[12345]").unwrap();
1022 assert!(query.matches("json.items[12345]"));
1023 assert!(!query.matches("json.items[12346]"));
1024 }
1025
1026 #[test]
1031 fn test_multiple_wildcards() {
1032 let query = Query::parse("[*][*]").unwrap();
1033 assert!(query.matches("json[0][0]"));
1034 assert!(query.matches("json[5][10]"));
1035 }
1036
1037 #[test]
1038 fn test_wildcard_then_field() {
1039 let query = Query::parse("[*].name").unwrap();
1040 assert!(query.matches("json[0].name"));
1041 assert!(!query.matches("json[0].age"));
1042 }
1043
1044 #[test]
1049 fn test_parse_path_empty_field() {
1050 let segments = parse_path_segments("json..field");
1051 assert!(!segments.is_empty());
1053 }
1054
1055 #[test]
1056 fn test_parse_path_only_root() {
1057 let segments = parse_path_segments("json");
1058 assert!(segments.is_empty());
1059 }
1060
1061 #[test]
1062 fn test_parse_path_trailing_dot() {
1063 let segments = parse_path_segments("json.field.");
1064 assert_eq!(segments.len(), 1);
1066 }
1067
1068 #[test]
1073 fn test_parse_identifier_with_underscore() {
1074 let query = Query::parse(".my_field").unwrap();
1075 assert_eq!(
1076 query.segments[0],
1077 QuerySegment::Field("my_field".to_string())
1078 );
1079 }
1080
1081 #[test]
1082 fn test_parse_identifier_with_hyphen() {
1083 let query = Query::parse(".my-field").unwrap();
1084 assert_eq!(
1085 query.segments[0],
1086 QuerySegment::Field("my-field".to_string())
1087 );
1088 }
1089
1090 #[test]
1091 fn test_parse_identifier_with_numbers() {
1092 let query = Query::parse(".field123").unwrap();
1093 assert_eq!(
1094 query.segments[0],
1095 QuerySegment::Field("field123".to_string())
1096 );
1097 }
1098}