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