1pub mod decode;
3pub mod parser;
4pub(crate) mod schema;
5pub(crate) mod walk;
6pub mod write;
7
8#[cfg(test)]
9mod test_parser;
10
11use std::{
12 collections::BTreeMap,
13 fmt::{self, Write as _},
14 rc::Rc,
15 sync::Arc,
16};
17
18use serde::Serialize;
19use tracing::{trace, Level};
20
21use crate::{warning, Verdict};
22use parser::ErrorKind;
23use parser::{Parser, Span};
24
25#[doc(inline)]
26pub use parser::{line_col, Error, ErrorReport, LineCol};
27pub(crate) use parser::{parse, RawStr};
28
29const PATH_SEPARATOR: char = '.';
30const PATH_ROOT: &str = "$";
31
32pub(crate) trait FromJson<'elem, 'buf>: Sized {
34 type WarningKind: warning::Kind;
35
36 fn from_json(elem: &'elem Element<'buf>) -> Verdict<Self, Self::WarningKind>;
38}
39
40#[derive(Debug, Eq, PartialEq)]
44pub struct Element<'buf> {
45 id: ElemId,
47
48 path_node: PathNodeRef<'buf>,
50
51 span: Span,
55
56 value: Value<'buf>,
58}
59
60#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
64pub struct ElemId(usize);
65
66impl fmt::Display for ElemId {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 fmt::Display::fmt(&self.0, f)
69 }
70}
71
72impl<'buf> Element<'buf> {
73 fn new(id: ElemId, path: PathNodeRef<'buf>, span: Span, value: Value<'buf>) -> Element<'buf> {
75 Element {
76 id,
77 path_node: path,
78 span,
79 value,
80 }
81 }
82
83 pub(crate) const fn id(&self) -> ElemId {
85 self.id
86 }
87
88 pub fn path(&self) -> PathRef<'buf> {
90 PathRef(self.path_node())
91 }
92
93 pub(crate) fn path_node(&self) -> PathNodeRef<'buf> {
95 Rc::clone(&self.path_node)
96 }
97
98 pub fn source_json(&self, source_json: &'buf str) -> SourceStr<'buf> {
112 if let PathNode::Object { key, .. } = *self.path_node {
113 let span = Span {
115 start: key.span().start,
116 end: self.span.end,
118 };
119 let field_str = &source_json
120 .get(span.start..span.end)
121 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR");
122 let field = RawStr::from_str(field_str, span);
123 let (key, value) = field_str
124 .split_once(':')
125 .expect("An objects field always contains a delimiting `:`");
126
127 SourceStr::Field { field, key, value }
128 } else {
129 let span = self.span;
130 let s = source_json
131 .get(span.start..span.end)
132 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR");
133 SourceStr::Value(RawStr::from_str(s, span))
134 }
135 }
136
137 pub fn source_json_value(&self, source_json: &'buf str) -> &'buf str {
150 source_json
151 .get(self.span.start..self.span.end)
152 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR")
153 }
154
155 pub(crate) fn value(&self) -> &Value<'buf> {
157 &self.value
158 }
159
160 pub(crate) fn as_value(&self) -> &Value<'buf> {
162 &self.value
163 }
164
165 pub(crate) fn as_raw_str(&self) -> Option<&RawStr<'buf>> {
167 self.value.as_raw_str()
168 }
169
170 pub(crate) fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
172 self.value.as_object_fields()
173 }
174
175 pub(crate) fn as_array(&self) -> Option<&[Element<'buf>]> {
176 self.value.as_array()
177 }
178
179 pub fn as_number_str(&self) -> Option<&str> {
180 self.value.as_number()
181 }
182}
183
184#[derive(Debug)]
185pub enum SourceStr<'buf> {
186 Value(RawStr<'buf>),
188
189 Field {
191 field: RawStr<'buf>,
193
194 key: &'buf str,
196
197 value: &'buf str,
199 },
200}
201
202impl fmt::Display for SourceStr<'_> {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 match self {
205 SourceStr::Value(s) => f.write_str(s.as_raw()),
206 SourceStr::Field { field, .. } => f.write_str(field.as_raw()),
207 }
208 }
209}
210
211impl PartialEq<&str> for SourceStr<'_> {
213 fn eq(&self, other: &&str) -> bool {
214 match self {
215 SourceStr::Value(s) => s.as_raw() == *other,
216 SourceStr::Field { field, .. } => field.as_raw() == *other,
217 }
218 }
219}
220
221impl PartialEq<String> for SourceStr<'_> {
223 fn eq(&self, other: &String) -> bool {
224 self.eq(&&**other)
225 }
226}
227
228impl PartialOrd for Element<'_> {
229 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
230 Some(self.cmp(other))
231 }
232}
233
234impl Ord for Element<'_> {
235 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
236 self.path_node.cmp(&other.path_node)
237 }
238}
239
240impl fmt::Display for Element<'_> {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 write!(f, "{} = {}", self.path_node, self.value)
243 }
244}
245
246#[derive(Debug, Eq, PartialEq)]
248pub(crate) struct Field<'buf>(Element<'buf>);
249
250impl<'buf> Field<'buf> {
251 #[expect(
252 clippy::unreachable,
253 reason = "A Field is created by the parser when the type is an Object."
254 )]
255 pub(crate) fn key(&self) -> RawStr<'buf> {
256 let PathNode::Object { key, .. } = *self.0.path_node else {
257 unreachable!();
258 };
259
260 key
261 }
262
263 #[expect(dead_code, reason = "pending use in `tariff::lint`")]
265 pub(crate) fn into_element(self) -> Element<'buf> {
266 self.0
267 }
268
269 pub(crate) fn element(&self) -> &Element<'buf> {
271 &self.0
272 }
273}
274
275#[derive(Debug, Eq, PartialEq)]
277pub(crate) enum Value<'buf> {
278 Null,
280
281 True,
283
284 False,
286
287 String(RawStr<'buf>),
289
290 Number(&'buf str),
295
296 Array(Vec<Element<'buf>>),
301
302 Object(Vec<Field<'buf>>),
308}
309
310impl<'buf> Value<'buf> {
311 pub(crate) fn kind(&self) -> ValueKind {
312 match self {
313 Value::Null => ValueKind::Null,
314 Value::True | Value::False => ValueKind::Bool,
315 Value::String(_) => ValueKind::String,
316 Value::Number(_) => ValueKind::Number,
317 Value::Array(_) => ValueKind::Array,
318 Value::Object(_) => ValueKind::Object,
319 }
320 }
321
322 pub(crate) fn is_scalar(&self) -> bool {
324 matches!(
325 self,
326 Value::Null | Value::True | Value::False | Value::String(_) | Value::Number(_)
327 )
328 }
329
330 pub(crate) fn as_array(&self) -> Option<&[Element<'buf>]> {
331 match self {
332 Value::Array(elems) => Some(elems),
333 _ => None,
334 }
335 }
336
337 pub(crate) fn as_number(&self) -> Option<&str> {
338 match self {
339 Value::Number(s) => Some(s),
340 _ => None,
341 }
342 }
343
344 pub(crate) fn as_raw_str(&self) -> Option<&RawStr<'buf>> {
346 match self {
347 Value::String(s) => Some(s),
348 _ => None,
349 }
350 }
351
352 pub(crate) fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
354 match self {
355 Value::Object(fields) => Some(fields),
356 _ => None,
357 }
358 }
359}
360
361impl fmt::Display for Value<'_> {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 match self {
364 Self::Null => write!(f, "null"),
365 Self::True => write!(f, "true"),
366 Self::False => write!(f, "false"),
367 Self::String(s) => write!(f, "{s}"),
368 Self::Number(s) => write!(f, "{s}"),
369 Self::Array(..) => f.write_str("[...]"),
370 Self::Object(..) => f.write_str("{...}"),
371 }
372 }
373}
374
375#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize)]
377pub enum ValueKind {
378 Null,
379 Bool,
380 Number,
381 String,
382 Array,
383 Object,
384}
385
386impl fmt::Display for ValueKind {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 match self {
389 ValueKind::Null => write!(f, "null"),
390 ValueKind::Bool => write!(f, "bool"),
391 ValueKind::Number => write!(f, "number"),
392 ValueKind::String => write!(f, "string"),
393 ValueKind::Array => write!(f, "array"),
394 ValueKind::Object => write!(f, "object"),
395 }
396 }
397}
398
399#[derive(Copy, Clone, Debug, Eq, PartialEq)]
403pub(crate) enum ObjectKind {
404 Object,
405 Array,
406}
407
408type RawMap<'buf> = BTreeMap<RawStr<'buf>, Element<'buf>>;
409type RawRefMap<'a, 'buf> = BTreeMap<RawStr<'buf>, &'a Element<'buf>>;
410
411#[expect(dead_code, reason = "pending use in `tariff::lint`")]
412pub(crate) trait FieldsIntoExt<'buf> {
413 fn into_map(self) -> RawMap<'buf>;
414}
415
416pub(crate) trait FieldsAsExt<'buf> {
417 fn as_raw_map(&self) -> RawRefMap<'_, 'buf>;
418 fn find_field(&self, key: &str) -> Option<&Field<'buf>>;
419}
420
421impl<'buf> FieldsIntoExt<'buf> for Vec<Field<'buf>> {
422 fn into_map(self) -> RawMap<'buf> {
423 self.into_iter()
424 .map(|field| (field.key(), field.into_element()))
425 .collect()
426 }
427}
428
429impl<'buf> FieldsAsExt<'buf> for Vec<Field<'buf>> {
430 fn as_raw_map(&self) -> RawRefMap<'_, 'buf> {
431 self.iter()
432 .map(|field| (field.key(), field.element()))
433 .collect()
434 }
435
436 fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
437 self.iter().find(|field| field.key().as_raw() == key)
438 }
439}
440
441impl<'buf> FieldsAsExt<'buf> for [Field<'buf>] {
442 fn as_raw_map(&self) -> RawRefMap<'_, 'buf> {
443 self.iter()
444 .map(|field| (field.key(), field.element()))
445 .collect()
446 }
447
448 fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
449 self.iter().find(|field| field.key().as_raw() == key)
450 }
451}
452
453pub(crate) type RawValue = serde_json::value::RawValue;
466
467#[derive(Clone, Debug)]
470pub struct UnexpectedFields<'buf>(Vec<PathNodeRef<'buf>>);
471
472impl fmt::Display for UnexpectedFields<'_> {
473 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474 if f.alternate() {
475 f.write_str("[\n")?;
477 for entry in &self.0 {
478 writeln!(f, "\t\"{entry}\",")?;
479 }
480 f.write_str("]\n")?;
481 } else {
482 f.write_char('[')?;
484 for entry in &self.0 {
485 write!(f, "{entry},")?;
486 }
487 f.write_char(']')?;
488 }
489
490 Ok(())
491 }
492}
493
494impl<'buf> UnexpectedFields<'buf> {
495 pub(crate) fn empty() -> Self {
497 Self(vec![])
498 }
499
500 pub(crate) fn from_vec(v: Vec<PathNodeRef<'buf>>) -> Self {
502 Self(v)
503 }
504
505 pub fn to_strings(&self) -> Vec<String> {
507 self.0.iter().map(ToString::to_string).collect()
508 }
509
510 pub fn into_strings(self) -> Vec<String> {
512 self.0.into_iter().map(|path| path.to_string()).collect()
513 }
514
515 pub fn is_empty(&self) -> bool {
517 self.0.is_empty()
518 }
519
520 pub fn len(&self) -> usize {
522 self.0.len()
523 }
524
525 pub fn iter<'a>(&'a self) -> UnexpectedFieldsIter<'a, 'buf> {
527 UnexpectedFieldsIter(self.0.iter())
528 }
529}
530
531impl<'buf> IntoIterator for UnexpectedFields<'buf> {
532 type Item = PathRef<'buf>;
533
534 type IntoIter = UnexpectedFieldsIntoIter<'buf>;
535
536 fn into_iter(self) -> Self::IntoIter {
537 UnexpectedFieldsIntoIter(self.0.into_iter())
538 }
539}
540
541pub struct UnexpectedFieldsIntoIter<'buf>(std::vec::IntoIter<PathNodeRef<'buf>>);
542
543impl<'buf> Iterator for UnexpectedFieldsIntoIter<'buf> {
544 type Item = PathRef<'buf>;
545
546 fn next(&mut self) -> Option<Self::Item> {
547 let path_node = self.0.next()?;
548
549 Some(PathRef(path_node))
550 }
551}
552
553impl<'a, 'buf> IntoIterator for &'a UnexpectedFields<'buf> {
554 type Item = PathRef<'buf>;
555
556 type IntoIter = UnexpectedFieldsIter<'a, 'buf>;
557
558 fn into_iter(self) -> Self::IntoIter {
559 self.iter()
560 }
561}
562
563pub struct UnexpectedFieldsIter<'a, 'buf>(std::slice::Iter<'a, PathNodeRef<'buf>>);
565
566impl<'buf> Iterator for UnexpectedFieldsIter<'_, 'buf> {
567 type Item = PathRef<'buf>;
568
569 fn next(&mut self) -> Option<Self::Item> {
570 let path_node = self.0.next()?;
571
572 Some(PathRef(Rc::clone(path_node)))
573 }
574}
575
576pub struct PathRef<'buf>(PathNodeRef<'buf>);
584
585impl fmt::Debug for PathRef<'_> {
586 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587 write!(f, "{self}")
588 }
589}
590
591impl<'buf> PathRef<'buf> {
592 pub fn components(&self) -> PathComponents<'buf> {
594 PathComponents(PathIter::new(Rc::clone(&self.0)))
595 }
596}
597
598pub struct PathComponents<'buf>(PathIter<'buf>);
600
601impl<'buf> Iterator for PathComponents<'buf> {
602 type Item = PathComponent<'buf>;
603
604 fn next(&mut self) -> Option<Self::Item> {
605 let path_node = self.0.next()?;
606 Some(PathComponent(path_node))
607 }
608}
609
610impl PartialEq<&str> for PathRef<'_> {
612 fn eq(&self, other: &&str) -> bool {
613 match_path_node(&self.0, other, |_| false)
614 }
615}
616
617impl PartialEq<String> for PathRef<'_> {
619 fn eq(&self, other: &String) -> bool {
620 match_path_node(&self.0, other, |_| false)
621 }
622}
623
624impl fmt::Display for PathRef<'_> {
625 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
626 fmt::Display::fmt(&self.0, f)
627 }
628}
629
630#[cfg(test)]
631mod test_path_node_matches_str {
632 use std::rc::Rc;
633
634 use crate::test;
635
636 use super::PathNode;
637
638 #[test]
639 fn should_match_path() {
640 test::setup();
641
642 let root = Rc::new(PathNode::Root);
643 let path_a = Rc::new(PathNode::Array {
644 parent: Rc::clone(&root),
645 index: 1,
646 });
647 let path_b = Rc::new(PathNode::Object {
648 parent: Rc::clone(&path_a),
649 key: r#""name""#.into(),
650 });
651 let path_c = PathNode::Object {
652 parent: Rc::clone(&path_b),
653 key: r#""gene""#.into(),
654 };
655
656 assert_eq!(*root, "$");
657 assert_eq!(*path_a, "$.1");
658 assert_eq!(*path_b, "$.1.name");
659 assert_eq!(path_c, "$.1.name.gene");
660 }
661}
662
663pub(crate) type PathNodeRef<'buf> = Rc<PathNode<'buf>>;
665
666#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
676pub(crate) enum PathNode<'buf> {
677 #[default]
679 Root,
680 Array {
682 parent: PathNodeRef<'buf>,
683 index: usize,
684 },
685 Object {
687 parent: PathNodeRef<'buf>,
688 key: RawStr<'buf>,
689 },
690}
691
692pub enum PathNodeKind {
695 Root,
697 Array,
699 Object,
701}
702
703#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
712pub struct Path(Vec<PathPiece>);
713
714impl Path {
715 const fn root() -> Self {
717 Self(vec![])
718 }
719
720 fn from_node(path: PathNodeRef<'_>) -> Self {
722 let paths: Vec<_> = PathIter::new(path).collect();
723
724 let pieces = paths
725 .into_iter()
726 .rev()
727 .filter_map(|path_node| match *path_node {
728 PathNode::Root => None,
729 PathNode::Array { index, .. } => Some(PathPiece::Array(index)),
730 PathNode::Object { key, .. } => Some(PathPiece::Object(key.to_string())),
731 })
732 .collect();
733
734 Self(pieces)
735 }
736}
737
738impl PartialEq<&str> for Path {
740 fn eq(&self, other: &&str) -> bool {
741 match_path(self, other)
742 }
743}
744
745impl PartialEq<String> for Path {
747 fn eq(&self, other: &String) -> bool {
748 match_path(self, other)
749 }
750}
751
752impl fmt::Display for Path {
753 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
754 let iter = self.0.iter();
755
756 write!(f, "$")?;
757
758 for path in iter {
759 write!(f, ".{path}")?;
760 }
761
762 Ok(())
763 }
764}
765
766#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
770enum PathPiece {
771 Array(usize),
773 Object(String),
775}
776
777impl fmt::Display for PathPiece {
778 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
779 match self {
780 PathPiece::Array(index) => write!(f, "{index}"),
781 PathPiece::Object(key) => write!(f, "{key}"),
782 }
783 }
784}
785
786pub struct PathComponent<'buf>(PathNodeRef<'buf>);
788
789impl PathComponent<'_> {
790 pub fn kind(&self) -> PathNodeKind {
792 match *self.0 {
793 PathNode::Root => PathNodeKind::Root,
794 PathNode::Array { .. } => PathNodeKind::Array,
795 PathNode::Object { .. } => PathNodeKind::Object,
796 }
797 }
798}
799
800impl fmt::Display for PathComponent<'_> {
801 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
802 match *self.0 {
803 PathNode::Root => f.write_str(PATH_ROOT),
804 PathNode::Array { index, .. } => write!(f, "{index}"),
805 PathNode::Object { key, .. } => write!(f, "{key}"),
806 }
807 }
808}
809
810impl fmt::Display for PathNode<'_> {
811 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
812 let paths: Vec<_> = PathIter::new(Rc::new(self.clone())).collect();
813 let mut iter = paths.into_iter().rev();
814
815 if f.alternate() {
816 for path in iter {
818 match *path {
819 PathNode::Root => f.write_str("")?,
820 PathNode::Array { .. } | PathNode::Object { .. } => f.write_str("...|")?,
821 }
822 }
823 } else {
824 if let Some(path) = iter.next() {
825 write!(f, "{}", PathComponent(path))?;
826 }
827
828 for path in iter {
829 write!(f, ".{}", PathComponent(path))?;
830 }
831 }
832 Ok(())
833 }
834}
835
836impl<'buf> PathNode<'buf> {
837 pub(crate) fn is_root(&self) -> bool {
839 matches!(self, PathNode::Root)
840 }
841
842 pub(crate) fn is_array(&self) -> bool {
844 matches!(self, PathNode::Array { .. })
845 }
846
847 pub(crate) fn as_object_key(&self) -> Option<&RawStr<'buf>> {
849 match self {
850 PathNode::Object { key, .. } => Some(key),
851 PathNode::Root | PathNode::Array { .. } => None,
852 }
853 }
854}
855
856fn match_path_node<F>(path: &PathNode<'_>, s: &str, mut skip: F) -> bool
862where
863 F: FnMut(&str) -> bool,
864{
865 let mut parts = s.rsplit(PATH_SEPARATOR);
866 let mut paths_iter = PathIter::new(Rc::new(path.clone()));
867
868 loop {
869 let node_segment = paths_iter.next();
870 let str_segment = parts.next();
871
872 let (node_segment, str_segment) = match (node_segment, str_segment) {
873 (None, None) => return true,
875 (None, Some(_)) | (Some(_), None) => return false,
877 (Some(a), Some(b)) => (a, b),
879 };
880
881 if skip(str_segment) {
883 continue;
884 }
885
886 let yip = match *node_segment {
887 PathNode::Root => str_segment == PATH_ROOT,
888 PathNode::Array { index, .. } => {
889 let Ok(b) = str_segment.parse::<usize>() else {
890 return false;
891 };
892
893 index == b
894 }
895 PathNode::Object { key, .. } => key.as_raw() == str_segment,
896 };
897
898 if !yip {
900 return false;
901 }
902 }
903}
904
905fn match_path(path: &Path, s: &str) -> bool {
907 let mut parts = s.split(PATH_SEPARATOR);
908 let mut paths_iter = path.0.iter();
909
910 let Some(str_segment) = parts.next() else {
911 return false;
912 };
913
914 if str_segment != PATH_ROOT {
917 return false;
918 }
919
920 loop {
921 let node_segment = paths_iter.next();
922 let str_segment = parts.next();
923
924 let (node_segment, str_segment) = match (node_segment, str_segment) {
925 (None, None) => return true,
927 (None, Some(_)) | (Some(_), None) => return false,
929 (Some(a), Some(b)) => (a, b),
931 };
932
933 let yip = match node_segment {
934 PathPiece::Array(index) => {
935 let Ok(b) = str_segment.parse::<usize>() else {
936 return false;
937 };
938
939 *index == b
940 }
941 PathPiece::Object(key) => key == str_segment,
942 };
943
944 if !yip {
946 return false;
947 }
948 }
949}
950
951impl PartialEq<&str> for PathNode<'_> {
952 fn eq(&self, other: &&str) -> bool {
953 match_path_node(self, other, |_| false)
954 }
955}
956
957impl PartialEq<String> for PathNode<'_> {
958 fn eq(&self, other: &String) -> bool {
959 match_path_node(self, other, |_| false)
960 }
961}
962
963struct PathIter<'buf> {
965 complete: bool,
967 path: PathNodeRef<'buf>,
969}
970
971impl<'buf> PathIter<'buf> {
972 fn new(path: PathNodeRef<'buf>) -> Self {
974 Self {
975 complete: false,
976 path,
977 }
978 }
979}
980
981impl<'buf> Iterator for PathIter<'buf> {
982 type Item = PathNodeRef<'buf>;
983
984 fn next(&mut self) -> Option<Self::Item> {
985 if self.complete {
986 return None;
987 }
988
989 match &*self.path {
990 PathNode::Root => {
991 self.complete = true;
993 Some(Rc::clone(&self.path))
994 }
995 PathNode::Array { parent, .. } | PathNode::Object { parent, .. } => {
996 let next = Rc::clone(&self.path);
997 self.path = Rc::clone(parent);
998 Some(next)
999 }
1000 }
1001 }
1002}
1003
1004struct DisplayExpectStack<'a>(&'a [schema::Expect]);
1006
1007impl fmt::Display for DisplayExpectStack<'_> {
1008 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1009 let mut iter = self.0.iter().rev();
1010 let last = iter.next();
1011
1012 f.write_str("~")?;
1014
1015 for _ in iter {
1016 f.write_str("...~")?;
1017 }
1018
1019 if let Some(exp) = last {
1020 match exp {
1021 schema::Expect::Scalar => f.write_str("~")?,
1022 schema::Expect::Array(element) => match &**element {
1023 schema::Element::Scalar => f.write_str("~")?,
1024 schema::Element::Array(element) => write!(f, "[{element:?}]")?,
1025 schema::Element::Object(fields) => {
1026 write!(f, "[{{{:}}}])", DisplayExpectFields(&**fields))?;
1027 }
1028 },
1029 schema::Expect::Object(fields) => {
1030 write!(f, "{{{:}}}", DisplayExpectFields(&**fields))?;
1031 }
1032 schema::Expect::UnmatchedScalar => write!(f, "unmatched(scalar)")?,
1033 schema::Expect::UnmatchedArray => write!(f, "unmatched(array)")?,
1034 schema::Expect::UnmatchedObject => write!(f, "unmatched(object)")?,
1035 schema::Expect::OutOfSchema => write!(f, "no_schema")?,
1036 }
1037 }
1038
1039 Ok(())
1040 }
1041}
1042
1043struct DisplayExpectFields<'a, V>(&'a BTreeMap<&'a str, V>);
1045
1046impl<V> fmt::Display for DisplayExpectFields<'_, V> {
1047 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1048 const MAX_FIELDS: usize = 8;
1049
1050 let mut count = 0;
1051 let mut iter = self.0.keys().peekable();
1052
1053 loop {
1054 if count >= MAX_FIELDS {
1055 f.write_str("...")?;
1056 break;
1057 }
1058
1059 let Some(field) = iter.next() else {
1060 break;
1061 };
1062
1063 count += 1;
1064 write!(f, "{field}")?;
1065
1066 let Some(_) = iter.peek() else {
1067 break;
1068 };
1069
1070 f.write_str(", ")?;
1071 }
1072
1073 Ok(())
1074 }
1075}
1076
1077#[derive(Debug)]
1078struct UnbalancedExpectStack;
1079
1080impl fmt::Display for UnbalancedExpectStack {
1081 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1082 f.write_str("unbalanced expectation stack")
1083 }
1084}
1085
1086impl std::error::Error for UnbalancedExpectStack {}
1087
1088pub(crate) fn parse_with_schema<'buf>(
1091 json: &'buf str,
1092 schema: &schema::Element,
1093) -> Result<ParseReport<'buf>, Error> {
1094 let parser = Parser::new(json);
1095 let mut unexpected_fields = vec![];
1096 let mut expectation_stack = vec![schema.to_expectation()];
1098
1099 for event in parser {
1100 match event? {
1101 parser::Event::Open { kind, parent_path } => {
1102 let Some(expectation) = expectation_stack.pop() else {
1105 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1106 .into_partial_error_without_token()
1107 .with_root_path());
1108 };
1109
1110 if tracing::enabled!(Level::DEBUG) {
1111 match kind {
1112 ObjectKind::Array => {
1113 trace!("{parent_path} [ {}", DisplayExpectStack(&expectation_stack));
1114 }
1115 ObjectKind::Object => trace!(
1116 "{parent_path} {{ {}",
1117 DisplayExpectStack(&expectation_stack)
1118 ),
1119 }
1120 }
1121
1122 match expectation {
1123 schema::Expect::Array(elem) => {
1124 if parent_path.is_root() {
1127 let next = match kind {
1128 ObjectKind::Array => schema::Expect::Array(elem),
1129 ObjectKind::Object => schema::Expect::UnmatchedArray,
1130 };
1131
1132 expectation_stack.push(next);
1133 trace!("{}", DisplayExpectStack(&expectation_stack));
1134 continue;
1135 }
1136
1137 if !parent_path.is_array() {
1138 expectation_stack.push(schema::Expect::UnmatchedArray);
1139 trace!("{}", DisplayExpectStack(&expectation_stack));
1140 continue;
1141 }
1142
1143 expectation_stack.push(schema::Expect::Array(Arc::clone(&elem)));
1144 expectation_stack.push(elem.to_expectation());
1146 }
1147 schema::Expect::Object(fields) => {
1148 if parent_path.is_root() {
1151 let next = match kind {
1152 ObjectKind::Array => schema::Expect::UnmatchedObject,
1153 ObjectKind::Object => schema::Expect::Object(fields),
1154 };
1155
1156 expectation_stack.push(next);
1157 trace!("{}", DisplayExpectStack(&expectation_stack));
1158 continue;
1159 }
1160 let Some(key) = parent_path.as_object_key() else {
1161 expectation_stack.push(schema::Expect::UnmatchedObject);
1162 trace!("{}", DisplayExpectStack(&expectation_stack));
1163 continue;
1164 };
1165
1166 let next = if let Some(elem) = fields.get(key.as_raw()) {
1167 open_object(kind, elem.as_ref())
1168 } else {
1169 unexpected_fields.push(parent_path);
1170 schema::Expect::OutOfSchema
1171 };
1172
1173 expectation_stack.push(schema::Expect::Object(fields));
1174 expectation_stack.push(next);
1175 }
1176 schema::Expect::OutOfSchema => {
1177 expectation_stack.push(expectation);
1185 expectation_stack.push(schema::Expect::OutOfSchema);
1186 }
1187 schema::Expect::UnmatchedArray | schema::Expect::UnmatchedObject => {
1188 expectation_stack.push(expectation);
1189 expectation_stack.push(schema::Expect::OutOfSchema);
1190 }
1191 _ => {
1192 expectation_stack.push(expectation);
1193 }
1194 }
1195
1196 trace!("{}", DisplayExpectStack(&expectation_stack));
1197 }
1198 parser::Event::Element { kind, parent_path } => {
1199 let Some(expectation) = expectation_stack.pop() else {
1202 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1203 .into_partial_error_without_token()
1204 .with_root_path());
1205 };
1206
1207 if let ValueKind::Array | ValueKind::Object = kind {
1210 if tracing::enabled!(Level::DEBUG) {
1211 match kind {
1212 ValueKind::Array => {
1213 trace!(
1214 "{parent_path} ] {}",
1215 DisplayExpectStack(&expectation_stack)
1216 );
1217 }
1218 ValueKind::Object => trace!(
1219 "{parent_path} }} {}",
1220 DisplayExpectStack(&expectation_stack)
1221 ),
1222 _ => (),
1223 }
1224 }
1225 continue;
1226 }
1227
1228 match expectation {
1229 #[expect(
1230 clippy::unreachable,
1231 reason = "The parser only emits an `Event::Complete` for a scalar object at the root"
1232 )]
1233 schema::Expect::Object(fields) => match &*parent_path {
1234 PathNode::Root => unreachable!(),
1235 PathNode::Array { .. } => {
1236 expectation_stack.push(schema::Expect::UnmatchedObject);
1237 }
1238 PathNode::Object { parent, key } => {
1239 trace!("{parent:#}.{key}");
1240
1241 if !fields.contains_key(key.as_raw()) {
1242 unexpected_fields.push(parent_path);
1243 }
1244
1245 expectation_stack.push(schema::Expect::Object(fields));
1246 }
1247 },
1248 schema::Expect::OutOfSchema => {
1249 unexpected_fields.push(parent_path);
1250 expectation_stack.push(expectation);
1251 }
1252 _ => {
1253 expectation_stack.push(expectation);
1254 }
1255 }
1256 }
1257 parser::Event::Complete(element) => {
1258 if element.value().is_scalar() {
1259 unexpected_fields.push(element.path_node());
1260 }
1261
1262 return Ok(ParseReport {
1265 element,
1266 unexpected_fields: UnexpectedFields::from_vec(unexpected_fields),
1267 });
1268 }
1269 }
1270 }
1271
1272 Err(ErrorKind::UnexpectedEOF
1273 .into_partial_error_without_token()
1274 .with_root_path())
1275}
1276
1277fn open_object(kind: ObjectKind, elem: Option<&Arc<schema::Element>>) -> schema::Expect {
1278 let Some(schema) = elem else {
1279 return schema::Expect::OutOfSchema;
1280 };
1281
1282 match (kind, &**schema) {
1283 (ObjectKind::Object | ObjectKind::Array, schema::Element::Scalar) => {
1284 schema::Expect::UnmatchedScalar
1285 }
1286 (ObjectKind::Object, schema::Element::Array(_)) => schema::Expect::UnmatchedArray,
1287 (ObjectKind::Object, schema::Element::Object(fields)) => {
1288 schema::Expect::Object(Arc::clone(fields))
1289 }
1290 (ObjectKind::Array, schema::Element::Array(element)) => {
1291 schema::Expect::Array(Arc::clone(element))
1292 }
1293 (ObjectKind::Array, schema::Element::Object(_)) => schema::Expect::UnmatchedObject,
1294 }
1295}
1296
1297#[derive(Debug)]
1300pub(crate) struct ParseReport<'buf> {
1301 pub element: Element<'buf>,
1303
1304 pub unexpected_fields: UnexpectedFields<'buf>,
1306}
1307
1308#[cfg(test)]
1309pub mod test {
1310 #![allow(clippy::missing_panics_doc, reason = "tests are allowed to panic")]
1311 #![allow(clippy::panic, reason = "tests are allowed panic")]
1312
1313 use std::borrow::Cow;
1314
1315 use crate::json::match_path_node;
1316
1317 use super::{
1318 parser::Span, walk::DepthFirst, ElemId, Element, Field, FieldsAsExt as _, PathNode,
1319 PathNodeRef, PathRef, RawStr, UnexpectedFields, Value,
1320 };
1321
1322 impl<'buf> Element<'buf> {
1323 pub fn span(&self) -> Span {
1325 self.span
1326 }
1327
1328 pub(crate) fn into_value(self) -> Value<'buf> {
1330 self.value
1331 }
1332
1333 pub(crate) fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1335 let Self {
1336 id,
1337 path_node: path,
1338 span,
1339 value,
1340 } = self;
1341 (id, path, span, value)
1342 }
1343
1344 pub(crate) fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
1345 self.as_object_fields()
1346 .and_then(|fields| fields.find_field(key))
1347 }
1348 }
1349
1350 impl<'buf> Value<'buf> {
1351 pub(crate) fn is_array(&self) -> bool {
1353 matches!(self, Value::Array(_))
1354 }
1355
1356 pub(crate) fn is_object(&self) -> bool {
1358 matches!(self, Value::Object(_))
1359 }
1360
1361 pub(crate) fn as_string(&self) -> Option<&RawStr<'buf>> {
1362 match self {
1363 Value::String(s) => Some(s),
1364 _ => None,
1365 }
1366 }
1367 }
1368
1369 impl<'buf> Field<'buf> {
1370 pub fn id(&self) -> ElemId {
1371 self.0.id()
1372 }
1373
1374 pub fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1375 self.0.into_parts()
1376 }
1377 }
1378
1379 impl<'buf> UnexpectedFields<'buf> {
1380 pub(crate) fn into_inner(self) -> Vec<PathNodeRef<'buf>> {
1382 self.0
1383 }
1384
1385 pub(crate) fn filter_matches(&mut self, glob: &PathGlob<'_>) {
1387 self.0.retain(|path| !glob.matches(path));
1388 }
1389 }
1390
1391 #[derive(Debug)]
1394 pub(crate) struct PathGlob<'a>(Cow<'a, str>);
1395
1396 impl PathGlob<'_> {
1397 pub(crate) fn matches(&self, path: &PathNode<'_>) -> bool {
1399 const WILDCARD: &str = "*";
1400
1401 match_path_node(path, &self.0, |s| {
1402 s == WILDCARD
1404 })
1405 }
1406 }
1407
1408 impl From<usize> for ElemId {
1410 fn from(value: usize) -> Self {
1411 Self(value)
1412 }
1413 }
1414
1415 impl<'a> From<&'a str> for PathGlob<'a> {
1416 fn from(s: &'a str) -> Self {
1417 Self(s.into())
1418 }
1419 }
1420
1421 impl From<String> for PathGlob<'_> {
1422 fn from(s: String) -> Self {
1423 Self(s.into())
1424 }
1425 }
1426
1427 impl<'de, 'buf> serde::Deserialize<'de> for PathGlob<'buf> {
1428 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1429 where
1430 D: ::serde::Deserializer<'de>,
1431 {
1432 let s = <Cow<'buf, str> as ::serde::Deserialize>::deserialize(deserializer)?;
1433 Ok(Self(s))
1434 }
1435 }
1436
1437 pub struct ElementMap<'a, 'bin>(Vec<&'a Element<'bin>>);
1439
1440 impl<'a, 'bin> ElementMap<'a, 'bin> {
1441 pub fn for_elem(root: &'a Element<'bin>) -> Self {
1443 let walker = DepthFirst::new(root);
1445 Self(walker.collect())
1446 }
1447
1448 pub fn get(&self, id: ElemId) -> &Element<'bin> {
1450 self.0.get(id.0).map(|e| &**e).unwrap()
1451 }
1452
1453 pub fn path(&self, id: ElemId) -> PathRef<'bin> {
1455 self.0.get(id.0).map(|elem| elem.path()).unwrap()
1456 }
1457 }
1458
1459 #[cfg(test)]
1460 mod test_path_matches_glob {
1461 use std::rc::Rc;
1462
1463 use crate::test;
1464
1465 use super::{PathGlob, PathNode};
1466
1467 #[test]
1468 fn should_match_path() {
1469 test::setup();
1470
1471 let root = Rc::new(PathNode::Root);
1472 let path_a = Rc::new(PathNode::Array {
1473 parent: Rc::clone(&root),
1474 index: 1,
1475 });
1476 let path_b = Rc::new(PathNode::Object {
1477 parent: Rc::clone(&path_a),
1478 key: r#""name""#.into(),
1479 });
1480 let path_c = PathNode::Object {
1481 parent: Rc::clone(&path_b),
1482 key: r#""gene""#.into(),
1483 };
1484
1485 assert!(PathGlob::from("$").matches(&root));
1486 assert!(PathGlob::from("*").matches(&root));
1487
1488 assert!(!PathGlob::from("*").matches(&path_a));
1489 assert!(PathGlob::from("*.*").matches(&path_a));
1490 assert!(PathGlob::from("$.*").matches(&path_a));
1491 assert!(PathGlob::from("$.1").matches(&path_a));
1492
1493 assert!(!PathGlob::from("*").matches(&path_b));
1494 assert!(!PathGlob::from("*.*").matches(&path_b));
1495 assert!(PathGlob::from("*.*.*").matches(&path_b));
1496 assert!(PathGlob::from("$.*.*").matches(&path_b));
1497 assert!(PathGlob::from("$.1.*").matches(&path_b));
1498 assert!(PathGlob::from("$.*.name").matches(&path_b));
1499 assert!(PathGlob::from("$.1.name").matches(&path_b));
1500
1501 assert!(PathGlob::from("$.1.name.gene").matches(&path_c));
1502 }
1503 }
1504}
1505
1506#[cfg(test)]
1507mod test_path {
1508 use super::{Path, PathPiece};
1509
1510 #[test]
1511 fn path_should_cmp_with_str() {
1512 assert_ne!(Path::root(), "");
1513 assert_eq!(Path::root(), "$");
1514 assert_eq!(Path(vec![PathPiece::Object("field_a".into())]), "$.field_a");
1515 assert_eq!(Path(vec![PathPiece::Array(1)]), "$.1");
1516 assert_eq!(
1517 Path(vec![
1518 PathPiece::Object("field_a".into()),
1519 PathPiece::Array(1)
1520 ]),
1521 "$.field_a.1"
1522 );
1523 }
1524
1525 #[test]
1526 fn path_should_display() {
1527 assert_eq!(Path::root().to_string(), "$");
1528 assert_eq!(
1529 Path(vec![PathPiece::Object("field_a".into())]).to_string(),
1530 "$.field_a"
1531 );
1532 assert_eq!(Path(vec![PathPiece::Array(1)]).to_string(), "$.1");
1533 assert_eq!(
1534 Path(vec![
1535 PathPiece::Object("field_a".into()),
1536 PathPiece::Array(1)
1537 ])
1538 .to_string(),
1539 "$.field_a.1"
1540 );
1541 }
1542}
1543
1544#[cfg(test)]
1545mod test_parse_with_schema {
1546 use crate::{json_schema, test};
1547
1548 use super::{parse_with_schema, ParseReport};
1549
1550 #[test]
1551 fn should_report_unexpected_fields_for_root_element() {
1552 const JSON: &str = "null";
1553
1554 test::setup();
1555
1556 let schema = json_schema!({
1557 "id",
1558 "currency",
1559 });
1560
1561 let report = parse_with_schema(JSON, &schema).unwrap();
1562 let ParseReport {
1563 element: _,
1564 unexpected_fields,
1565 } = report;
1566
1567 {
1568 let [field_a] = unexpected_fields.into_inner().try_into().unwrap();
1569 assert_eq!(*field_a, "$");
1570 }
1571 }
1572
1573 #[test]
1574 fn should_report_unexpected_fields_in_flat_object() {
1575 const JSON: &str = r#"{
1576 "id": "tariff_id",
1577 "currency": "EUR",
1578 "name": "Barry",
1579 "address": "Barrystown"
1580}"#;
1581
1582 test::setup();
1583
1584 let schema = json_schema!({
1585 "id",
1586 "currency",
1587 });
1588
1589 let report = parse_with_schema(JSON, &schema).unwrap();
1590 let ParseReport {
1591 element: _,
1592 unexpected_fields,
1593 } = report;
1594
1595 {
1596 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1597 assert_eq!(*field_a, "$.name");
1598 assert_eq!(*field_b, "$.address");
1599 }
1600 }
1601
1602 #[test]
1603 fn should_report_unexpected_fields_in_nested_object() {
1604 const JSON: &str = r#"{
1605 "id": "tariff_id",
1606 "currency": "EUR",
1607 "owner": {
1608 "id": "456856",
1609 "subscription_id": "tedi4568",
1610 "name": "Barry",
1611 "address": "Barrystown"
1612 }
1613}"#;
1614
1615 test::setup();
1616
1617 let schema = json_schema!({
1618 "id",
1619 "currency",
1620 "owner": {
1621 "id",
1622 "subscription_id"
1623 }
1624 });
1625
1626 let report = parse_with_schema(JSON, &schema).unwrap();
1627 let ParseReport {
1628 element: _,
1629 unexpected_fields,
1630 } = report;
1631
1632 {
1633 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1634 assert_eq!(*field_a, "$.owner.name");
1635 assert_eq!(*field_b, "$.owner.address");
1636 }
1637 }
1638
1639 #[test]
1640 fn should_parse_nested_object_out_of_schema() {
1641 const JSON: &str = r#"{
1642 "id": "tariff_id",
1643 "owner": {
1644 "id": "456856",
1645 "subscription_id": "tedi4568",
1646 "name": "Barry",
1647 "address": {
1648 "city": "Barrystown",
1649 "street": "Barrysstreet"
1650 }
1651 },
1652 "currency": "EUR",
1653 "country": "NL"
1654}"#;
1655
1656 test::setup();
1657
1658 let schema = json_schema!({
1659 "id",
1660 "currency",
1661 "owner"
1662 });
1663
1664 let report = parse_with_schema(JSON, &schema).unwrap();
1665 let ParseReport {
1666 element: _,
1667 unexpected_fields,
1668 } = report;
1669
1670 {
1671 let [field_a, field_b, field_c, field_d, field_e, field_f] =
1672 unexpected_fields.into_inner().try_into().unwrap();
1673 assert_eq!(*field_a, "$.owner.id");
1674 assert_eq!(*field_b, "$.owner.subscription_id");
1675 assert_eq!(*field_c, "$.owner.name");
1676 assert_eq!(*field_d, "$.owner.address.city");
1677 assert_eq!(*field_e, "$.owner.address.street");
1678 assert_eq!(*field_f, "$.country");
1679 }
1680 }
1681
1682 #[test]
1683 fn should_report_unexpected_fields_in_array_with_nested_object() {
1684 const JSON: &str = r#"{
1685 "id": "tariff_id",
1686 "currency": "EUR",
1687 "elements": [{
1688 "id": "456856",
1689 "subscription_id": "tedi4568",
1690 "name": "Barry",
1691 "address": "Barrystown"
1692 }]
1693}"#;
1694
1695 test::setup();
1696
1697 let schema = json_schema!({
1698 "id",
1699 "currency",
1700 "elements": [{
1701 "id",
1702 "subscription_id"
1703 }]
1704 });
1705
1706 let report = parse_with_schema(JSON, &schema).unwrap();
1707 let ParseReport {
1708 element: _,
1709 unexpected_fields,
1710 } = report;
1711
1712 {
1713 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1714 assert_eq!(*field_a, "$.elements.0.name");
1715 assert_eq!(*field_b, "$.elements.0.address");
1716 }
1717 }
1718
1719 #[test]
1720 fn should_report_unexpected_fields_in_array_of_nested_objects() {
1721 const JSON: &str = r#"{
1722 "id": "tariff_id",
1723 "currency": "EUR",
1724 "elements": [
1725 {
1726 "id": "456856",
1727 "subscription_id": "tedi4568",
1728 "name": "Barry",
1729 "address": "Barrystown"
1730 },
1731 {
1732 "id": "8746we",
1733 "subscription_id": "dfr345",
1734 "name": "Gerry",
1735 "address": "Gerrystown"
1736 }
1737 ]
1738}"#;
1739
1740 test::setup();
1741
1742 let schema = json_schema!({
1743 "id",
1744 "currency",
1745 "elements": [{
1746 "id",
1747 "subscription_id"
1748 }]
1749 });
1750
1751 let report = parse_with_schema(JSON, &schema).unwrap();
1752 let ParseReport {
1753 element: _,
1754 unexpected_fields,
1755 } = report;
1756
1757 {
1758 let [field_a, field_b, field_c, field_d] =
1759 unexpected_fields.into_inner().try_into().unwrap();
1760 assert_eq!(*field_a, "$.elements.0.name");
1761 assert_eq!(*field_b, "$.elements.0.address");
1762 assert_eq!(*field_c, "$.elements.1.name");
1763 assert_eq!(*field_d, "$.elements.1.address");
1764 }
1765 }
1766
1767 #[test]
1768 fn should_report_unexpected_fields_in_array_of_objects() {
1769 const JSON: &str = r#"[
1770 {
1771 "id": "456856",
1772 "subscription_id": "tedi4568",
1773 "name": "Barry",
1774 "address": "Barrystown"
1775 },
1776 {
1777 "id": "8746we",
1778 "subscription_id": "dfr345",
1779 "name": "Gerry",
1780 "address": "Gerrystown"
1781 }
1782]"#;
1783
1784 test::setup();
1785
1786 let schema = json_schema!([
1787 {
1788 "id",
1789 "subscription_id"
1790 }
1791 ]);
1792
1793 let report = parse_with_schema(JSON, &schema).unwrap();
1794 let ParseReport {
1795 element: _,
1796 unexpected_fields,
1797 } = report;
1798
1799 {
1800 let [field_a, field_b, field_c, field_d] =
1801 unexpected_fields.into_inner().try_into().unwrap();
1802 assert_eq!(*field_a, "$.0.name");
1803 assert_eq!(*field_b, "$.0.address");
1804 assert_eq!(*field_c, "$.1.name");
1805 assert_eq!(*field_d, "$.1.address");
1806 }
1807 }
1808}
1809
1810#[cfg(test)]
1811mod test_source_json {
1812 use super::{parse, walk};
1813
1814 #[test]
1815 fn should_resolve_to_source_json() {
1816 const JSON: &str = r#"{
1817 "name": "David Byrne",
1818 "hobbies": ["song writing", "thinking about society"]
1819}"#;
1820
1821 let element = parse(JSON).unwrap();
1822
1823 let mut walk = walk::DepthFirst::new(&element);
1824
1825 let root = walk.next().unwrap();
1826 assert_eq!(root.source_json(JSON), JSON);
1827
1828 let field_name = walk.next().unwrap();
1829 assert_eq!(field_name.source_json(JSON), r#""name": "David Byrne""#);
1830 assert_eq!(field_name.source_json_value(JSON), r#""David Byrne""#);
1831
1832 let field_hobbies = walk.next().unwrap();
1833 assert_eq!(
1834 field_hobbies.source_json(JSON),
1835 r#""hobbies": ["song writing", "thinking about society"]"#
1836 );
1837 assert_eq!(
1838 field_hobbies.source_json_value(JSON),
1839 r#"["song writing", "thinking about society"]"#
1840 );
1841
1842 let hobbies_one = walk.next().unwrap();
1843 assert_eq!(hobbies_one.source_json(JSON), r#""song writing""#);
1844 assert_eq!(hobbies_one.source_json_value(JSON), r#""song writing""#);
1845
1846 let hobbies_two = walk.next().unwrap();
1847 assert_eq!(hobbies_two.source_json(JSON), r#""thinking about society""#);
1848 assert_eq!(
1849 hobbies_two.source_json_value(JSON),
1850 r#""thinking about society""#
1851 );
1852 }
1853}
1854
1855#[cfg(test)]
1856mod test_path_node {
1857 use std::rc::Rc;
1858
1859 use super::{
1860 parser::{RawStr, Token, TokenType},
1861 PathNode, Span,
1862 };
1863
1864 #[test]
1865 fn should_display_path() {
1866 let root = Rc::new(PathNode::Root);
1867 let path_a = Rc::new(PathNode::Array {
1868 parent: Rc::clone(&root),
1869 index: 1,
1870 });
1871 let path_b = Rc::new(PathNode::Object {
1872 parent: Rc::clone(&path_a),
1873 key: r#""name""#.into(),
1874 });
1875 let path_c = Rc::new(PathNode::Object {
1876 parent: Rc::clone(&path_b),
1877 key: r#""gene""#.into(),
1878 });
1879
1880 assert_eq!(*root, "$");
1881 assert_eq!(*path_a, "$.1");
1882 assert_eq!(*path_b, "$.1.name");
1883 assert_eq!(*path_c, "$.1.name.gene");
1884 }
1885
1886 impl<'buf> From<&'buf str> for RawStr<'buf> {
1887 #[track_caller]
1888 fn from(s: &'buf str) -> Self {
1889 RawStr::from_quoted_str(
1890 s,
1891 Token {
1892 kind: TokenType::String,
1893 span: Span::default(),
1894 },
1895 )
1896 .unwrap()
1897 }
1898 }
1899}
1900
1901#[cfg(test)]
1902mod test_raw_json {
1903 use std::{borrow::Cow, rc::Rc};
1904
1905 use serde::{de::IntoDeserializer, Deserialize};
1906
1907 use super::{ElemId, Element, PathNode, RawValue, Span, Value, ValueKind};
1908 use crate::json::decode::unescape_str;
1909
1910 const TITLE: &str = "همّا مين واحنا مين (Who Are They and Who Are We?)";
1911
1912 pub(crate) trait RawValueExt {
1918 fn kind(&self) -> ValueKind;
1919
1920 fn is_string(&self) -> bool;
1921
1922 fn as_str(&self) -> Option<Cow<'_, str>>;
1926 }
1927
1928 impl RawValueExt for RawValue {
1929 fn kind(&self) -> ValueKind {
1930 let s = self.get();
1931 let first = s
1932 .as_bytes()
1933 .first()
1934 .expect("A RawValue can`t be an empty string, it has to contain a JSON value");
1935
1936 match *first {
1945 b'n' => ValueKind::Null,
1946 b't' | b'f' => ValueKind::Bool,
1947 b'"' => ValueKind::String,
1948 b'[' => ValueKind::Array,
1949 b'{' => ValueKind::Object,
1950 _ => ValueKind::Number,
1953 }
1954 }
1955
1956 fn is_string(&self) -> bool {
1957 matches!(self.kind(), ValueKind::String)
1958 }
1959
1960 fn as_str(&self) -> Option<Cow<'_, str>> {
1961 if !self.is_string() {
1962 return None;
1963 }
1964
1965 let s = self.get().trim_matches('"');
1966 let elem = Element {
1969 id: ElemId(0),
1970 path_node: Rc::new(PathNode::Root),
1971 span: Span::default(),
1972 value: Value::Null,
1973 };
1974 let (s, _warnings) = unescape_str(s, &elem).into_parts();
1975 Some(s)
1976 }
1977 }
1978
1979 #[test]
1980 fn should_fail_to_parse_whitespace_only_string_as_json() {
1981 const JSON: &str = " ";
1982 let err = serde_json::from_str::<&RawValue>(JSON).unwrap_err();
1983
1984 assert_eq!(
1985 err.classify(),
1986 serde_json::error::Category::Eof,
1987 "A JSON string can't be empty"
1988 );
1989
1990 let err = RawValue::from_string(JSON.to_string()).unwrap_err();
1991
1992 assert_eq!(
1993 err.classify(),
1994 serde_json::error::Category::Eof,
1995 "A JSON string can't be empty"
1996 );
1997 }
1998
1999 #[test]
2000 fn should_validate_json_without_allocating_for_each_token() {
2001 #[derive(Deserialize)]
2002 struct Song {
2003 title: String,
2004 }
2005
2006 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2007
2008 let json: Box<RawValue> = serde_json::from_str(&json).unwrap();
2018
2019 let song = Song::deserialize(json.into_deserializer()).unwrap();
2024
2025 assert_eq!(song.title, TITLE);
2026 }
2027
2028 #[test]
2029 fn should_compare_raw_title_correctly() {
2030 #[derive(Deserialize)]
2031 struct Song<'a> {
2032 #[serde(borrow)]
2033 title: &'a RawValue,
2034 }
2035
2036 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2037 let song: Song<'_> = serde_json::from_str(&json).unwrap();
2038
2039 assert_ne!(
2040 song.title.get(),
2041 TITLE,
2042 "The raw `title` field contains the delimiting '\"' and so is technically not directly equal to the `TITLE` const"
2043 );
2044
2045 let title = song.title.as_str().unwrap();
2046
2047 assert_eq!(
2048 title, TITLE,
2049 "When the quotes are removed the `title` field is the same as the `TITLE` const"
2050 );
2051 }
2052
2053 #[test]
2054 fn should_fail_to_parse_invalid_json() {
2055 const JSON: &str = r#"{ "title": }"#;
2056
2057 let err = serde_json::from_str::<Box<RawValue>>(JSON).unwrap_err();
2058
2059 assert_eq!(
2060 err.classify(),
2061 serde_json::error::Category::Syntax,
2062 "The bytes contained within `RawValue` are valid JSON"
2063 );
2064 }
2065
2066 #[test]
2067 fn should_parse_raw_json_to_rust_struct() {
2068 #[derive(Deserialize)]
2070 struct Song {
2071 title: String,
2072 }
2073
2074 struct SongMessage {
2076 song: Song,
2077 original: Box<RawValue>,
2078 }
2079
2080 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2082
2083 let raw_json: Box<RawValue> = serde_json::from_str(&json)
2085 .expect("Typically we want to parse some JSON from an endpoint first");
2086 let song = Song::deserialize(raw_json.into_deserializer()).expect("And then introspect it");
2088
2089 let message = SongMessage {
2090 song,
2091 original: raw_json,
2092 };
2093
2094 assert_eq!(
2095 message.song.title, TITLE,
2096 "The title is a normal `String` and so can be directly compared"
2097 );
2098 assert_eq!(
2099 message.original.get(),
2100 &json,
2101 "The original has not been modified in any way"
2102 );
2103 }
2104
2105 #[test]
2106 fn should_parse_borrowed_raw_json_to_rust_struct() {
2107 #[derive(Deserialize)]
2109 struct Song<'a> {
2110 title: &'a str,
2111 }
2112
2113 struct SongMessage<'a> {
2117 song: Song<'a>,
2118 original: &'a RawValue,
2119 }
2120
2121 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2123
2124 let raw_json: &RawValue = serde_json::from_str(&json)
2126 .expect("Typically we want to parse some JSON from an endpoint first");
2127 let song =
2129 Song::<'_>::deserialize(raw_json.into_deserializer()).expect("And then introspect it");
2130 let message = SongMessage {
2132 song,
2133 original: raw_json,
2134 };
2135
2136 assert_eq!(
2137 message.song.title, TITLE,
2138 "The title is a normal `&str` and so can be directly compared"
2139 );
2140 assert_eq!(
2141 message.original.get(),
2142 &json,
2143 "The original has not been modified in any way"
2144 );
2145 }
2146
2147 #[test]
2148 fn should_deser_number_as_i64() {
2149 const JSON: &str = "123";
2150 let json: &RawValue = serde_json::from_str(JSON).unwrap();
2151
2152 let n = i64::deserialize(json.into_deserializer()).unwrap();
2153
2154 assert_eq!(n, 123);
2155 }
2156
2157 #[test]
2158 fn should_convert_json_string_to_str() {
2159 #[derive(Deserialize)]
2161 struct Song<'a> {
2162 title: &'a str,
2163 }
2164
2165 struct SongMessage<'a> {
2169 song: Song<'a>,
2170 original: &'a RawValue,
2171 }
2172
2173 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2175
2176 let raw_json: &RawValue = serde_json::from_str(&json)
2178 .expect("Typically we want to parse some JSON from an endpoint first");
2179 let song =
2181 Song::<'_>::deserialize(raw_json.into_deserializer()).expect("And then introspect it");
2182 let message = SongMessage {
2184 song,
2185 original: raw_json,
2186 };
2187
2188 assert_eq!(
2189 message.song.title, TITLE,
2190 "The title is a normal `&str` and so can be directly compared"
2191 );
2192 assert_eq!(
2193 message.original.get(),
2194 &json,
2195 "The original has not been modified in any way"
2196 );
2197 }
2198}