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> {
506 self.0.iter().map(ToString::to_string).collect()
507 }
508
509 pub fn into_strings(self) -> Vec<String> {
510 self.0.into_iter().map(|path| path.to_string()).collect()
511 }
512
513 pub fn is_empty(&self) -> bool {
514 self.0.is_empty()
515 }
516
517 pub fn len(&self) -> usize {
518 self.0.len()
519 }
520
521 pub fn iter<'a>(&'a self) -> UnexpectedFieldsIter<'a, 'buf> {
522 UnexpectedFieldsIter(self.0.iter())
523 }
524}
525
526impl<'buf> IntoIterator for UnexpectedFields<'buf> {
527 type Item = PathRef<'buf>;
528
529 type IntoIter = UnexpectedFieldsIntoIter<'buf>;
530
531 fn into_iter(self) -> Self::IntoIter {
532 UnexpectedFieldsIntoIter(self.0.into_iter())
533 }
534}
535
536pub struct UnexpectedFieldsIntoIter<'buf>(std::vec::IntoIter<PathNodeRef<'buf>>);
537
538impl<'buf> Iterator for UnexpectedFieldsIntoIter<'buf> {
539 type Item = PathRef<'buf>;
540
541 fn next(&mut self) -> Option<Self::Item> {
542 let path_node = self.0.next()?;
543
544 Some(PathRef(path_node))
545 }
546}
547
548impl<'a, 'buf> IntoIterator for &'a UnexpectedFields<'buf> {
549 type Item = PathRef<'buf>;
550
551 type IntoIter = UnexpectedFieldsIter<'a, 'buf>;
552
553 fn into_iter(self) -> Self::IntoIter {
554 self.iter()
555 }
556}
557
558pub struct UnexpectedFieldsIter<'a, 'buf>(std::slice::Iter<'a, PathNodeRef<'buf>>);
559
560impl<'buf> Iterator for UnexpectedFieldsIter<'_, 'buf> {
561 type Item = PathRef<'buf>;
562
563 fn next(&mut self) -> Option<Self::Item> {
564 let path_node = self.0.next()?;
565
566 Some(PathRef(Rc::clone(path_node)))
567 }
568}
569
570pub struct PathRef<'buf>(PathNodeRef<'buf>);
578
579impl fmt::Debug for PathRef<'_> {
580 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
581 write!(f, "{self}")
582 }
583}
584
585impl<'buf> PathRef<'buf> {
586 pub fn components(&self) -> PathComponents<'buf> {
588 PathComponents(PathIter::new(Rc::clone(&self.0)))
589 }
590}
591
592pub struct PathComponents<'buf>(PathIter<'buf>);
594
595impl<'buf> Iterator for PathComponents<'buf> {
596 type Item = PathComponent<'buf>;
597
598 fn next(&mut self) -> Option<Self::Item> {
599 let path_node = self.0.next()?;
600 Some(PathComponent(path_node))
601 }
602}
603
604impl PartialEq<&str> for PathRef<'_> {
606 fn eq(&self, other: &&str) -> bool {
607 match_path_node(&self.0, other, |_| false)
608 }
609}
610
611impl PartialEq<String> for PathRef<'_> {
613 fn eq(&self, other: &String) -> bool {
614 match_path_node(&self.0, other, |_| false)
615 }
616}
617
618impl fmt::Display for PathRef<'_> {
619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620 fmt::Display::fmt(&self.0, f)
621 }
622}
623
624#[cfg(test)]
625mod test_path_node_matches_str {
626 use std::rc::Rc;
627
628 use crate::test;
629
630 use super::PathNode;
631
632 #[test]
633 fn should_match_path() {
634 test::setup();
635
636 let root = Rc::new(PathNode::Root);
637 let path_a = Rc::new(PathNode::Array {
638 parent: Rc::clone(&root),
639 index: 1,
640 });
641 let path_b = Rc::new(PathNode::Object {
642 parent: Rc::clone(&path_a),
643 key: r#""name""#.into(),
644 });
645 let path_c = PathNode::Object {
646 parent: Rc::clone(&path_b),
647 key: r#""gene""#.into(),
648 };
649
650 assert_eq!(*root, "$");
651 assert_eq!(*path_a, "$.1");
652 assert_eq!(*path_b, "$.1.name");
653 assert_eq!(path_c, "$.1.name.gene");
654 }
655}
656
657pub(crate) type PathNodeRef<'buf> = Rc<PathNode<'buf>>;
659
660#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
670pub(crate) enum PathNode<'buf> {
671 #[default]
673 Root,
674 Array {
676 parent: PathNodeRef<'buf>,
677 index: usize,
678 },
679 Object {
681 parent: PathNodeRef<'buf>,
682 key: RawStr<'buf>,
683 },
684}
685
686pub enum PathNodeKind {
689 Root,
691 Array,
693 Object,
695}
696
697#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
706pub struct Path(Vec<PathPiece>);
707
708impl Path {
709 const fn root() -> Self {
711 Self(vec![])
712 }
713
714 fn from_node(path: PathNodeRef<'_>) -> Self {
716 let paths: Vec<_> = PathIter::new(path).collect();
717
718 let pieces = paths
719 .into_iter()
720 .rev()
721 .filter_map(|path_node| match *path_node {
722 PathNode::Root => None,
723 PathNode::Array { index, .. } => Some(PathPiece::Array(index)),
724 PathNode::Object { key, .. } => Some(PathPiece::Object(key.to_string())),
725 })
726 .collect();
727
728 Self(pieces)
729 }
730}
731
732impl PartialEq<&str> for Path {
734 fn eq(&self, other: &&str) -> bool {
735 match_path(self, other)
736 }
737}
738
739impl PartialEq<String> for Path {
741 fn eq(&self, other: &String) -> bool {
742 match_path(self, other)
743 }
744}
745
746impl fmt::Display for Path {
747 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
748 let iter = self.0.iter();
749
750 write!(f, "$")?;
751
752 for path in iter {
753 write!(f, ".{path}")?;
754 }
755
756 Ok(())
757 }
758}
759
760#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
764enum PathPiece {
765 Array(usize),
767 Object(String),
769}
770
771impl fmt::Display for PathPiece {
772 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
773 match self {
774 PathPiece::Array(index) => write!(f, "{index}"),
775 PathPiece::Object(key) => write!(f, "{key}"),
776 }
777 }
778}
779
780pub struct PathComponent<'buf>(PathNodeRef<'buf>);
782
783impl PathComponent<'_> {
784 pub fn kind(&self) -> PathNodeKind {
786 match *self.0 {
787 PathNode::Root => PathNodeKind::Root,
788 PathNode::Array { .. } => PathNodeKind::Array,
789 PathNode::Object { .. } => PathNodeKind::Object,
790 }
791 }
792}
793
794impl fmt::Display for PathComponent<'_> {
795 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
796 match *self.0 {
797 PathNode::Root => f.write_str(PATH_ROOT),
798 PathNode::Array { index, .. } => write!(f, "{index}"),
799 PathNode::Object { key, .. } => write!(f, "{key}"),
800 }
801 }
802}
803
804impl fmt::Display for PathNode<'_> {
805 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
806 let paths: Vec<_> = PathIter::new(Rc::new(self.clone())).collect();
807 let mut iter = paths.into_iter().rev();
808
809 if f.alternate() {
810 for path in iter {
812 match *path {
813 PathNode::Root => f.write_str("")?,
814 PathNode::Array { .. } | PathNode::Object { .. } => f.write_str("...|")?,
815 }
816 }
817 } else {
818 if let Some(path) = iter.next() {
819 write!(f, "{}", PathComponent(path))?;
820 }
821
822 for path in iter {
823 write!(f, ".{}", PathComponent(path))?;
824 }
825 }
826 Ok(())
827 }
828}
829
830impl<'buf> PathNode<'buf> {
831 pub(crate) fn is_root(&self) -> bool {
833 matches!(self, PathNode::Root)
834 }
835
836 pub(crate) fn is_array(&self) -> bool {
838 matches!(self, PathNode::Array { .. })
839 }
840
841 pub(crate) fn as_object_key(&self) -> Option<&RawStr<'buf>> {
843 match self {
844 PathNode::Object { key, .. } => Some(key),
845 PathNode::Root | PathNode::Array { .. } => None,
846 }
847 }
848}
849
850fn match_path_node<F>(path: &PathNode<'_>, s: &str, mut skip: F) -> bool
856where
857 F: FnMut(&str) -> bool,
858{
859 let mut parts = s.rsplit(PATH_SEPARATOR);
860 let mut paths_iter = PathIter::new(Rc::new(path.clone()));
861
862 loop {
863 let node_segment = paths_iter.next();
864 let str_segment = parts.next();
865
866 let (node_segment, str_segment) = match (node_segment, str_segment) {
867 (None, None) => return true,
869 (None, Some(_)) | (Some(_), None) => return false,
871 (Some(a), Some(b)) => (a, b),
873 };
874
875 if skip(str_segment) {
877 continue;
878 }
879
880 let yip = match *node_segment {
881 PathNode::Root => str_segment == PATH_ROOT,
882 PathNode::Array { index, .. } => {
883 let Ok(b) = str_segment.parse::<usize>() else {
884 return false;
885 };
886
887 index == b
888 }
889 PathNode::Object { key, .. } => key.as_raw() == str_segment,
890 };
891
892 if !yip {
894 return false;
895 }
896 }
897}
898
899fn match_path(path: &Path, s: &str) -> bool {
901 let mut parts = s.split(PATH_SEPARATOR);
902 let mut paths_iter = path.0.iter();
903
904 let Some(str_segment) = parts.next() else {
905 return false;
906 };
907
908 if str_segment != PATH_ROOT {
911 return false;
912 }
913
914 loop {
915 let node_segment = paths_iter.next();
916 let str_segment = parts.next();
917
918 let (node_segment, str_segment) = match (node_segment, str_segment) {
919 (None, None) => return true,
921 (None, Some(_)) | (Some(_), None) => return false,
923 (Some(a), Some(b)) => (a, b),
925 };
926
927 let yip = match node_segment {
928 PathPiece::Array(index) => {
929 let Ok(b) = str_segment.parse::<usize>() else {
930 return false;
931 };
932
933 *index == b
934 }
935 PathPiece::Object(key) => key == str_segment,
936 };
937
938 if !yip {
940 return false;
941 }
942 }
943}
944
945impl PartialEq<&str> for PathNode<'_> {
946 fn eq(&self, other: &&str) -> bool {
947 match_path_node(self, other, |_| false)
948 }
949}
950
951impl PartialEq<String> for PathNode<'_> {
952 fn eq(&self, other: &String) -> bool {
953 match_path_node(self, other, |_| false)
954 }
955}
956
957struct PathIter<'buf> {
959 complete: bool,
961 path: PathNodeRef<'buf>,
963}
964
965impl<'buf> PathIter<'buf> {
966 fn new(path: PathNodeRef<'buf>) -> Self {
968 Self {
969 complete: false,
970 path,
971 }
972 }
973}
974
975impl<'buf> Iterator for PathIter<'buf> {
976 type Item = PathNodeRef<'buf>;
977
978 fn next(&mut self) -> Option<Self::Item> {
979 if self.complete {
980 return None;
981 }
982
983 match &*self.path {
984 PathNode::Root => {
985 self.complete = true;
987 Some(Rc::clone(&self.path))
988 }
989 PathNode::Array { parent, .. } | PathNode::Object { parent, .. } => {
990 let next = Rc::clone(&self.path);
991 self.path = Rc::clone(parent);
992 Some(next)
993 }
994 }
995 }
996}
997
998struct DisplayExpectStack<'a>(&'a [schema::Expect]);
1000
1001impl fmt::Display for DisplayExpectStack<'_> {
1002 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1003 let mut iter = self.0.iter().rev();
1004 let last = iter.next();
1005
1006 f.write_str("~")?;
1008
1009 for _ in iter {
1010 f.write_str("...~")?;
1011 }
1012
1013 if let Some(exp) = last {
1014 match exp {
1015 schema::Expect::Scalar => f.write_str("~")?,
1016 schema::Expect::Array(element) => match &**element {
1017 schema::Element::Scalar => f.write_str("~")?,
1018 schema::Element::Array(element) => write!(f, "[{element:?}]")?,
1019 schema::Element::Object(fields) => {
1020 write!(f, "[{{{:}}}])", DisplayExpectFields(&**fields))?;
1021 }
1022 },
1023 schema::Expect::Object(fields) => {
1024 write!(f, "{{{:}}}", DisplayExpectFields(&**fields))?;
1025 }
1026 schema::Expect::UnmatchedScalar => write!(f, "unmatched(scalar)")?,
1027 schema::Expect::UnmatchedArray => write!(f, "unmatched(array)")?,
1028 schema::Expect::UnmatchedObject => write!(f, "unmatched(object)")?,
1029 schema::Expect::OutOfSchema => write!(f, "no_schema")?,
1030 }
1031 }
1032
1033 Ok(())
1034 }
1035}
1036
1037struct DisplayExpectFields<'a, V>(&'a BTreeMap<&'a str, V>);
1039
1040impl<V> fmt::Display for DisplayExpectFields<'_, V> {
1041 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1042 const MAX_FIELDS: usize = 8;
1043
1044 let mut count = 0;
1045 let mut iter = self.0.keys().peekable();
1046
1047 loop {
1048 if count >= MAX_FIELDS {
1049 f.write_str("...")?;
1050 break;
1051 }
1052
1053 let Some(field) = iter.next() else {
1054 break;
1055 };
1056
1057 count += 1;
1058 write!(f, "{field}")?;
1059
1060 let Some(_) = iter.peek() else {
1061 break;
1062 };
1063
1064 f.write_str(", ")?;
1065 }
1066
1067 Ok(())
1068 }
1069}
1070
1071#[derive(Debug)]
1072struct UnbalancedExpectStack;
1073
1074impl fmt::Display for UnbalancedExpectStack {
1075 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1076 f.write_str("unbalanced expectation stack")
1077 }
1078}
1079
1080impl std::error::Error for UnbalancedExpectStack {}
1081
1082pub(crate) fn parse_with_schema<'buf>(
1084 json: &'buf str,
1085 schema: &schema::Element,
1086) -> Result<Report<'buf>, Error> {
1087 let parser = Parser::new(json);
1088 let mut unexpected_fields = vec![];
1089 let mut expectation_stack = vec![schema.to_expectation()];
1091
1092 for event in parser {
1093 match event? {
1094 parser::Event::Open { kind, parent_path } => {
1095 let Some(expectation) = expectation_stack.pop() else {
1098 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1099 .into_partial_error_without_token()
1100 .with_root_path());
1101 };
1102
1103 if tracing::enabled!(Level::DEBUG) {
1104 match kind {
1105 ObjectKind::Array => {
1106 trace!("{parent_path} [ {}", DisplayExpectStack(&expectation_stack));
1107 }
1108 ObjectKind::Object => trace!(
1109 "{parent_path} {{ {}",
1110 DisplayExpectStack(&expectation_stack)
1111 ),
1112 }
1113 }
1114
1115 match expectation {
1116 schema::Expect::Array(elem) => {
1117 if parent_path.is_root() {
1120 let next = match kind {
1121 ObjectKind::Array => schema::Expect::Array(elem),
1122 ObjectKind::Object => schema::Expect::UnmatchedArray,
1123 };
1124
1125 expectation_stack.push(next);
1126 trace!("{}", DisplayExpectStack(&expectation_stack));
1127 continue;
1128 }
1129
1130 if !parent_path.is_array() {
1131 expectation_stack.push(schema::Expect::UnmatchedArray);
1132 trace!("{}", DisplayExpectStack(&expectation_stack));
1133 continue;
1134 }
1135
1136 expectation_stack.push(schema::Expect::Array(Arc::clone(&elem)));
1137 expectation_stack.push(elem.to_expectation());
1139 }
1140 schema::Expect::Object(fields) => {
1141 if parent_path.is_root() {
1144 let next = match kind {
1145 ObjectKind::Array => schema::Expect::UnmatchedObject,
1146 ObjectKind::Object => schema::Expect::Object(fields),
1147 };
1148
1149 expectation_stack.push(next);
1150 trace!("{}", DisplayExpectStack(&expectation_stack));
1151 continue;
1152 }
1153 let Some(key) = parent_path.as_object_key() else {
1154 expectation_stack.push(schema::Expect::UnmatchedObject);
1155 trace!("{}", DisplayExpectStack(&expectation_stack));
1156 continue;
1157 };
1158
1159 let next = if let Some(elem) = fields.get(key.as_raw()) {
1160 open_object(kind, elem.as_ref())
1161 } else {
1162 unexpected_fields.push(parent_path);
1163 schema::Expect::OutOfSchema
1164 };
1165
1166 expectation_stack.push(schema::Expect::Object(fields));
1167 expectation_stack.push(next);
1168 }
1169 schema::Expect::OutOfSchema => {
1170 expectation_stack.push(expectation);
1178 expectation_stack.push(schema::Expect::OutOfSchema);
1179 }
1180 schema::Expect::UnmatchedArray | schema::Expect::UnmatchedObject => {
1181 expectation_stack.push(expectation);
1182 expectation_stack.push(schema::Expect::OutOfSchema);
1183 }
1184 _ => {
1185 expectation_stack.push(expectation);
1186 }
1187 }
1188
1189 trace!("{}", DisplayExpectStack(&expectation_stack));
1190 }
1191 parser::Event::Element { kind, parent_path } => {
1192 let Some(expectation) = expectation_stack.pop() else {
1195 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1196 .into_partial_error_without_token()
1197 .with_root_path());
1198 };
1199
1200 if let ValueKind::Array | ValueKind::Object = kind {
1203 if tracing::enabled!(Level::DEBUG) {
1204 match kind {
1205 ValueKind::Array => {
1206 trace!(
1207 "{parent_path} ] {}",
1208 DisplayExpectStack(&expectation_stack)
1209 );
1210 }
1211 ValueKind::Object => trace!(
1212 "{parent_path} }} {}",
1213 DisplayExpectStack(&expectation_stack)
1214 ),
1215 _ => (),
1216 }
1217 }
1218 continue;
1219 }
1220
1221 match expectation {
1222 #[expect(
1223 clippy::unreachable,
1224 reason = "The parser only emits an `Event::Complete` for a scalar object at the root"
1225 )]
1226 schema::Expect::Object(fields) => match &*parent_path {
1227 PathNode::Root => unreachable!(),
1228 PathNode::Array { .. } => {
1229 expectation_stack.push(schema::Expect::UnmatchedObject);
1230 }
1231 PathNode::Object { parent, key } => {
1232 trace!("{parent:#}.{key}");
1233
1234 if !fields.contains_key(key.as_raw()) {
1235 unexpected_fields.push(parent_path);
1236 }
1237
1238 expectation_stack.push(schema::Expect::Object(fields));
1239 }
1240 },
1241 schema::Expect::OutOfSchema => {
1242 unexpected_fields.push(parent_path);
1243 expectation_stack.push(expectation);
1244 }
1245 _ => {
1246 expectation_stack.push(expectation);
1247 }
1248 }
1249 }
1250 parser::Event::Complete(element) => {
1251 if element.value().is_scalar() {
1252 unexpected_fields.push(element.path_node());
1253 }
1254
1255 return Ok(Report {
1258 element,
1259 unexpected_fields: UnexpectedFields::from_vec(unexpected_fields),
1260 });
1261 }
1262 }
1263 }
1264
1265 Err(ErrorKind::UnexpectedEOF
1266 .into_partial_error_without_token()
1267 .with_root_path())
1268}
1269
1270fn open_object(kind: ObjectKind, elem: Option<&Arc<schema::Element>>) -> schema::Expect {
1271 let Some(schema) = elem else {
1272 return schema::Expect::OutOfSchema;
1273 };
1274
1275 match (kind, &**schema) {
1276 (ObjectKind::Object | ObjectKind::Array, schema::Element::Scalar) => {
1277 schema::Expect::UnmatchedScalar
1278 }
1279 (ObjectKind::Object, schema::Element::Array(_)) => schema::Expect::UnmatchedArray,
1280 (ObjectKind::Object, schema::Element::Object(fields)) => {
1281 schema::Expect::Object(Arc::clone(fields))
1282 }
1283 (ObjectKind::Array, schema::Element::Array(element)) => {
1284 schema::Expect::Array(Arc::clone(element))
1285 }
1286 (ObjectKind::Array, schema::Element::Object(_)) => schema::Expect::UnmatchedObject,
1287 }
1288}
1289
1290#[derive(Debug)]
1293pub(crate) struct Report<'buf> {
1294 pub element: Element<'buf>,
1296
1297 pub unexpected_fields: UnexpectedFields<'buf>,
1299}
1300
1301#[cfg(test)]
1302pub mod test {
1303 #![allow(clippy::missing_panics_doc, reason = "tests are allowed to panic")]
1304 #![allow(clippy::panic, reason = "tests are allowed panic")]
1305
1306 use std::borrow::Cow;
1307
1308 use crate::json::match_path_node;
1309
1310 use super::{
1311 parser::Span, walk::DepthFirst, ElemId, Element, Field, FieldsAsExt as _, PathNode,
1312 PathNodeRef, PathRef, RawStr, UnexpectedFields, Value,
1313 };
1314
1315 impl<'buf> Element<'buf> {
1316 pub fn span(&self) -> Span {
1318 self.span
1319 }
1320
1321 pub(crate) fn into_value(self) -> Value<'buf> {
1323 self.value
1324 }
1325
1326 pub(crate) fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1328 let Self {
1329 id,
1330 path_node: path,
1331 span,
1332 value,
1333 } = self;
1334 (id, path, span, value)
1335 }
1336
1337 pub(crate) fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
1338 self.as_object_fields()
1339 .and_then(|fields| fields.find_field(key))
1340 }
1341 }
1342
1343 impl<'buf> Value<'buf> {
1344 pub(crate) fn is_array(&self) -> bool {
1346 matches!(self, Value::Array(_))
1347 }
1348
1349 pub(crate) fn is_object(&self) -> bool {
1351 matches!(self, Value::Object(_))
1352 }
1353
1354 pub(crate) fn as_string(&self) -> Option<&RawStr<'buf>> {
1355 match self {
1356 Value::String(s) => Some(s),
1357 _ => None,
1358 }
1359 }
1360 }
1361
1362 impl<'buf> Field<'buf> {
1363 pub fn id(&self) -> ElemId {
1364 self.0.id()
1365 }
1366
1367 pub fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1368 self.0.into_parts()
1369 }
1370 }
1371
1372 impl<'buf> UnexpectedFields<'buf> {
1373 pub(crate) fn into_inner(self) -> Vec<PathNodeRef<'buf>> {
1375 self.0
1376 }
1377
1378 pub(crate) fn filter_matches(&mut self, glob: &PathGlob<'_>) {
1380 self.0.retain(|path| !glob.matches(path));
1381 }
1382 }
1383
1384 #[derive(Debug)]
1387 pub(crate) struct PathGlob<'a>(Cow<'a, str>);
1388
1389 impl PathGlob<'_> {
1390 pub(crate) fn matches(&self, path: &PathNode<'_>) -> bool {
1392 const WILDCARD: &str = "*";
1393
1394 match_path_node(path, &self.0, |s| {
1395 s == WILDCARD
1397 })
1398 }
1399 }
1400
1401 impl From<usize> for ElemId {
1403 fn from(value: usize) -> Self {
1404 Self(value)
1405 }
1406 }
1407
1408 impl<'a> From<&'a str> for PathGlob<'a> {
1409 fn from(s: &'a str) -> Self {
1410 Self(s.into())
1411 }
1412 }
1413
1414 impl From<String> for PathGlob<'_> {
1415 fn from(s: String) -> Self {
1416 Self(s.into())
1417 }
1418 }
1419
1420 impl<'de, 'buf> serde::Deserialize<'de> for PathGlob<'buf> {
1421 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1422 where
1423 D: ::serde::Deserializer<'de>,
1424 {
1425 let s = <Cow<'buf, str> as ::serde::Deserialize>::deserialize(deserializer)?;
1426 Ok(Self(s))
1427 }
1428 }
1429
1430 pub struct ElementMap<'a, 'bin>(Vec<&'a Element<'bin>>);
1432
1433 impl<'a, 'bin> ElementMap<'a, 'bin> {
1434 pub fn for_elem(root: &'a Element<'bin>) -> Self {
1436 let walker = DepthFirst::new(root);
1438 Self(walker.collect())
1439 }
1440
1441 pub fn get(&self, id: ElemId) -> &Element<'bin> {
1443 self.0.get(id.0).map(|e| &**e).unwrap()
1444 }
1445
1446 pub fn path(&self, id: ElemId) -> PathRef<'bin> {
1448 self.0.get(id.0).map(|elem| elem.path()).unwrap()
1449 }
1450 }
1451
1452 #[cfg(test)]
1453 mod test_path_matches_glob {
1454 use std::rc::Rc;
1455
1456 use crate::test;
1457
1458 use super::{PathGlob, PathNode};
1459
1460 #[test]
1461 fn should_match_path() {
1462 test::setup();
1463
1464 let root = Rc::new(PathNode::Root);
1465 let path_a = Rc::new(PathNode::Array {
1466 parent: Rc::clone(&root),
1467 index: 1,
1468 });
1469 let path_b = Rc::new(PathNode::Object {
1470 parent: Rc::clone(&path_a),
1471 key: r#""name""#.into(),
1472 });
1473 let path_c = PathNode::Object {
1474 parent: Rc::clone(&path_b),
1475 key: r#""gene""#.into(),
1476 };
1477
1478 assert!(PathGlob::from("$").matches(&root));
1479 assert!(PathGlob::from("*").matches(&root));
1480
1481 assert!(!PathGlob::from("*").matches(&path_a));
1482 assert!(PathGlob::from("*.*").matches(&path_a));
1483 assert!(PathGlob::from("$.*").matches(&path_a));
1484 assert!(PathGlob::from("$.1").matches(&path_a));
1485
1486 assert!(!PathGlob::from("*").matches(&path_b));
1487 assert!(!PathGlob::from("*.*").matches(&path_b));
1488 assert!(PathGlob::from("*.*.*").matches(&path_b));
1489 assert!(PathGlob::from("$.*.*").matches(&path_b));
1490 assert!(PathGlob::from("$.1.*").matches(&path_b));
1491 assert!(PathGlob::from("$.*.name").matches(&path_b));
1492 assert!(PathGlob::from("$.1.name").matches(&path_b));
1493
1494 assert!(PathGlob::from("$.1.name.gene").matches(&path_c));
1495 }
1496 }
1497}
1498
1499#[cfg(test)]
1500mod test_path {
1501 use super::{Path, PathPiece};
1502
1503 #[test]
1504 fn path_should_cmp_with_str() {
1505 assert_ne!(Path::root(), "");
1506 assert_eq!(Path::root(), "$");
1507 assert_eq!(Path(vec![PathPiece::Object("field_a".into())]), "$.field_a");
1508 assert_eq!(Path(vec![PathPiece::Array(1)]), "$.1");
1509 assert_eq!(
1510 Path(vec![
1511 PathPiece::Object("field_a".into()),
1512 PathPiece::Array(1)
1513 ]),
1514 "$.field_a.1"
1515 );
1516 }
1517
1518 #[test]
1519 fn path_should_display() {
1520 assert_eq!(Path::root().to_string(), "$");
1521 assert_eq!(
1522 Path(vec![PathPiece::Object("field_a".into())]).to_string(),
1523 "$.field_a"
1524 );
1525 assert_eq!(Path(vec![PathPiece::Array(1)]).to_string(), "$.1");
1526 assert_eq!(
1527 Path(vec![
1528 PathPiece::Object("field_a".into()),
1529 PathPiece::Array(1)
1530 ])
1531 .to_string(),
1532 "$.field_a.1"
1533 );
1534 }
1535}
1536
1537#[cfg(test)]
1538mod test_parse_with_schema {
1539 use crate::{json_schema, test};
1540
1541 use super::{parse_with_schema, Report};
1542
1543 #[test]
1544 fn should_report_unexpected_fields_for_root_element() {
1545 const JSON: &str = "null";
1546
1547 test::setup();
1548
1549 let schema = json_schema!({
1550 "id",
1551 "currency",
1552 });
1553
1554 let report = parse_with_schema(JSON, &schema).unwrap();
1555 let Report {
1556 element: _,
1557 unexpected_fields,
1558 } = report;
1559
1560 {
1561 let [field_a] = unexpected_fields.into_inner().try_into().unwrap();
1562 assert_eq!(*field_a, "$");
1563 }
1564 }
1565
1566 #[test]
1567 fn should_report_unexpected_fields_in_flat_object() {
1568 const JSON: &str = r#"{
1569 "id": "tariff_id",
1570 "currency": "EUR",
1571 "name": "Barry",
1572 "address": "Barrystown"
1573}"#;
1574
1575 test::setup();
1576
1577 let schema = json_schema!({
1578 "id",
1579 "currency",
1580 });
1581
1582 let report = parse_with_schema(JSON, &schema).unwrap();
1583 let Report {
1584 element: _,
1585 unexpected_fields,
1586 } = report;
1587
1588 {
1589 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1590 assert_eq!(*field_a, "$.name");
1591 assert_eq!(*field_b, "$.address");
1592 }
1593 }
1594
1595 #[test]
1596 fn should_report_unexpected_fields_in_nested_object() {
1597 const JSON: &str = r#"{
1598 "id": "tariff_id",
1599 "currency": "EUR",
1600 "owner": {
1601 "id": "456856",
1602 "subscription_id": "tedi4568",
1603 "name": "Barry",
1604 "address": "Barrystown"
1605 }
1606}"#;
1607
1608 test::setup();
1609
1610 let schema = json_schema!({
1611 "id",
1612 "currency",
1613 "owner": {
1614 "id",
1615 "subscription_id"
1616 }
1617 });
1618
1619 let report = parse_with_schema(JSON, &schema).unwrap();
1620 let Report {
1621 element: _,
1622 unexpected_fields,
1623 } = report;
1624
1625 {
1626 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1627 assert_eq!(*field_a, "$.owner.name");
1628 assert_eq!(*field_b, "$.owner.address");
1629 }
1630 }
1631
1632 #[test]
1633 fn should_parse_nested_object_out_of_schema() {
1634 const JSON: &str = r#"{
1635 "id": "tariff_id",
1636 "owner": {
1637 "id": "456856",
1638 "subscription_id": "tedi4568",
1639 "name": "Barry",
1640 "address": {
1641 "city": "Barrystown",
1642 "street": "Barrysstreet"
1643 }
1644 },
1645 "currency": "EUR",
1646 "country": "NL"
1647}"#;
1648
1649 test::setup();
1650
1651 let schema = json_schema!({
1652 "id",
1653 "currency",
1654 "owner"
1655 });
1656
1657 let report = parse_with_schema(JSON, &schema).unwrap();
1658 let Report {
1659 element: _,
1660 unexpected_fields,
1661 } = report;
1662
1663 {
1664 let [field_a, field_b, field_c, field_d, field_e, field_f] =
1665 unexpected_fields.into_inner().try_into().unwrap();
1666 assert_eq!(*field_a, "$.owner.id");
1667 assert_eq!(*field_b, "$.owner.subscription_id");
1668 assert_eq!(*field_c, "$.owner.name");
1669 assert_eq!(*field_d, "$.owner.address.city");
1670 assert_eq!(*field_e, "$.owner.address.street");
1671 assert_eq!(*field_f, "$.country");
1672 }
1673 }
1674
1675 #[test]
1676 fn should_report_unexpected_fields_in_array_with_nested_object() {
1677 const JSON: &str = r#"{
1678 "id": "tariff_id",
1679 "currency": "EUR",
1680 "elements": [{
1681 "id": "456856",
1682 "subscription_id": "tedi4568",
1683 "name": "Barry",
1684 "address": "Barrystown"
1685 }]
1686}"#;
1687
1688 test::setup();
1689
1690 let schema = json_schema!({
1691 "id",
1692 "currency",
1693 "elements": [{
1694 "id",
1695 "subscription_id"
1696 }]
1697 });
1698
1699 let report = parse_with_schema(JSON, &schema).unwrap();
1700 let Report {
1701 element: _,
1702 unexpected_fields,
1703 } = report;
1704
1705 {
1706 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1707 assert_eq!(*field_a, "$.elements.0.name");
1708 assert_eq!(*field_b, "$.elements.0.address");
1709 }
1710 }
1711
1712 #[test]
1713 fn should_report_unexpected_fields_in_array_of_nested_objects() {
1714 const JSON: &str = r#"{
1715 "id": "tariff_id",
1716 "currency": "EUR",
1717 "elements": [
1718 {
1719 "id": "456856",
1720 "subscription_id": "tedi4568",
1721 "name": "Barry",
1722 "address": "Barrystown"
1723 },
1724 {
1725 "id": "8746we",
1726 "subscription_id": "dfr345",
1727 "name": "Gerry",
1728 "address": "Gerrystown"
1729 }
1730 ]
1731}"#;
1732
1733 test::setup();
1734
1735 let schema = json_schema!({
1736 "id",
1737 "currency",
1738 "elements": [{
1739 "id",
1740 "subscription_id"
1741 }]
1742 });
1743
1744 let report = parse_with_schema(JSON, &schema).unwrap();
1745 let Report {
1746 element: _,
1747 unexpected_fields,
1748 } = report;
1749
1750 {
1751 let [field_a, field_b, field_c, field_d] =
1752 unexpected_fields.into_inner().try_into().unwrap();
1753 assert_eq!(*field_a, "$.elements.0.name");
1754 assert_eq!(*field_b, "$.elements.0.address");
1755 assert_eq!(*field_c, "$.elements.1.name");
1756 assert_eq!(*field_d, "$.elements.1.address");
1757 }
1758 }
1759
1760 #[test]
1761 fn should_report_unexpected_fields_in_array_of_objects() {
1762 const JSON: &str = r#"[
1763 {
1764 "id": "456856",
1765 "subscription_id": "tedi4568",
1766 "name": "Barry",
1767 "address": "Barrystown"
1768 },
1769 {
1770 "id": "8746we",
1771 "subscription_id": "dfr345",
1772 "name": "Gerry",
1773 "address": "Gerrystown"
1774 }
1775]"#;
1776
1777 test::setup();
1778
1779 let schema = json_schema!([
1780 {
1781 "id",
1782 "subscription_id"
1783 }
1784 ]);
1785
1786 let report = parse_with_schema(JSON, &schema).unwrap();
1787 let Report {
1788 element: _,
1789 unexpected_fields,
1790 } = report;
1791
1792 {
1793 let [field_a, field_b, field_c, field_d] =
1794 unexpected_fields.into_inner().try_into().unwrap();
1795 assert_eq!(*field_a, "$.0.name");
1796 assert_eq!(*field_b, "$.0.address");
1797 assert_eq!(*field_c, "$.1.name");
1798 assert_eq!(*field_d, "$.1.address");
1799 }
1800 }
1801}
1802
1803#[cfg(test)]
1804mod test_source_json {
1805 use super::{parse, walk};
1806
1807 #[test]
1808 fn should_resolve_to_source_json() {
1809 const JSON: &str = r#"{
1810 "name": "David Byrne",
1811 "hobbies": ["song writing", "thinking about society"]
1812}"#;
1813
1814 let element = parse(JSON).unwrap();
1815
1816 let mut walk = walk::DepthFirst::new(&element);
1817
1818 let root = walk.next().unwrap();
1819 assert_eq!(root.source_json(JSON), JSON);
1820
1821 let field_name = walk.next().unwrap();
1822 assert_eq!(field_name.source_json(JSON), r#""name": "David Byrne""#);
1823 assert_eq!(field_name.source_json_value(JSON), r#""David Byrne""#);
1824
1825 let field_hobbies = walk.next().unwrap();
1826 assert_eq!(
1827 field_hobbies.source_json(JSON),
1828 r#""hobbies": ["song writing", "thinking about society"]"#
1829 );
1830 assert_eq!(
1831 field_hobbies.source_json_value(JSON),
1832 r#"["song writing", "thinking about society"]"#
1833 );
1834
1835 let hobbies_one = walk.next().unwrap();
1836 assert_eq!(hobbies_one.source_json(JSON), r#""song writing""#);
1837 assert_eq!(hobbies_one.source_json_value(JSON), r#""song writing""#);
1838
1839 let hobbies_two = walk.next().unwrap();
1840 assert_eq!(hobbies_two.source_json(JSON), r#""thinking about society""#);
1841 assert_eq!(
1842 hobbies_two.source_json_value(JSON),
1843 r#""thinking about society""#
1844 );
1845 }
1846}
1847
1848#[cfg(test)]
1849mod test_path_node {
1850 use std::rc::Rc;
1851
1852 use super::{
1853 parser::{RawStr, Token, TokenType},
1854 PathNode, Span,
1855 };
1856
1857 #[test]
1858 fn should_display_path() {
1859 let root = Rc::new(PathNode::Root);
1860 let path_a = Rc::new(PathNode::Array {
1861 parent: Rc::clone(&root),
1862 index: 1,
1863 });
1864 let path_b = Rc::new(PathNode::Object {
1865 parent: Rc::clone(&path_a),
1866 key: r#""name""#.into(),
1867 });
1868 let path_c = Rc::new(PathNode::Object {
1869 parent: Rc::clone(&path_b),
1870 key: r#""gene""#.into(),
1871 });
1872
1873 assert_eq!(*root, "$");
1874 assert_eq!(*path_a, "$.1");
1875 assert_eq!(*path_b, "$.1.name");
1876 assert_eq!(*path_c, "$.1.name.gene");
1877 }
1878
1879 impl<'buf> From<&'buf str> for RawStr<'buf> {
1880 #[track_caller]
1881 fn from(s: &'buf str) -> Self {
1882 RawStr::from_quoted_str(
1883 s,
1884 Token {
1885 kind: TokenType::String,
1886 span: Span::default(),
1887 },
1888 )
1889 .unwrap()
1890 }
1891 }
1892}
1893
1894#[cfg(test)]
1895mod test_raw_json {
1896 use std::{borrow::Cow, rc::Rc};
1897
1898 use serde::{de::IntoDeserializer, Deserialize};
1899
1900 use super::{ElemId, Element, PathNode, RawValue, Span, Value, ValueKind};
1901 use crate::json::decode::unescape_str;
1902
1903 const TITLE: &str = "همّا مين واحنا مين (Who Are They and Who Are We?)";
1904
1905 pub(crate) trait RawValueExt {
1911 fn kind(&self) -> ValueKind;
1912
1913 fn is_string(&self) -> bool;
1914
1915 fn as_str(&self) -> Option<Cow<'_, str>>;
1919 }
1920
1921 impl RawValueExt for RawValue {
1922 fn kind(&self) -> ValueKind {
1923 let s = self.get();
1924 let first = s
1925 .as_bytes()
1926 .first()
1927 .expect("A RawValue can`t be an empty string, it has to contain a JSON value");
1928
1929 match *first {
1938 b'n' => ValueKind::Null,
1939 b't' | b'f' => ValueKind::Bool,
1940 b'"' => ValueKind::String,
1941 b'[' => ValueKind::Array,
1942 b'{' => ValueKind::Object,
1943 _ => ValueKind::Number,
1946 }
1947 }
1948
1949 fn is_string(&self) -> bool {
1950 matches!(self.kind(), ValueKind::String)
1951 }
1952
1953 fn as_str(&self) -> Option<Cow<'_, str>> {
1954 if !self.is_string() {
1955 return None;
1956 }
1957
1958 let s = self.get().trim_matches('"');
1959 let elem = Element {
1962 id: ElemId(0),
1963 path_node: Rc::new(PathNode::Root),
1964 span: Span::default(),
1965 value: Value::Null,
1966 };
1967 let (s, _warnings) = unescape_str(s, &elem).into_parts();
1968 Some(s)
1969 }
1970 }
1971
1972 #[test]
1973 fn should_fail_to_parse_whitespace_only_string_as_json() {
1974 const JSON: &str = " ";
1975 let err = serde_json::from_str::<&RawValue>(JSON).unwrap_err();
1976
1977 assert_eq!(
1978 err.classify(),
1979 serde_json::error::Category::Eof,
1980 "A JSON string can't be empty"
1981 );
1982
1983 let err = RawValue::from_string(JSON.to_string()).unwrap_err();
1984
1985 assert_eq!(
1986 err.classify(),
1987 serde_json::error::Category::Eof,
1988 "A JSON string can't be empty"
1989 );
1990 }
1991
1992 #[test]
1993 fn should_validate_json_without_allocating_for_each_token() {
1994 #[derive(Deserialize)]
1995 struct Song {
1996 title: String,
1997 }
1998
1999 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2000
2001 let json: Box<RawValue> = serde_json::from_str(&json).unwrap();
2011
2012 let song = Song::deserialize(json.into_deserializer()).unwrap();
2017
2018 assert_eq!(song.title, TITLE);
2019 }
2020
2021 #[test]
2022 fn should_compare_raw_title_correctly() {
2023 #[derive(Deserialize)]
2024 struct Song<'a> {
2025 #[serde(borrow)]
2026 title: &'a RawValue,
2027 }
2028
2029 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2030 let song: Song<'_> = serde_json::from_str(&json).unwrap();
2031
2032 assert_ne!(
2033 song.title.get(),
2034 TITLE,
2035 "The raw `title` field contains the delimiting '\"' and so is technically not directly equal to the `TITLE` const"
2036 );
2037
2038 let title = song.title.as_str().unwrap();
2039
2040 assert_eq!(
2041 title, TITLE,
2042 "When the quotes are removed the `title` field is the same as the `TITLE` const"
2043 );
2044 }
2045
2046 #[test]
2047 fn should_fail_to_parse_invalid_json() {
2048 const JSON: &str = r#"{ "title": }"#;
2049
2050 let err = serde_json::from_str::<Box<RawValue>>(JSON).unwrap_err();
2051
2052 assert_eq!(
2053 err.classify(),
2054 serde_json::error::Category::Syntax,
2055 "The bytes contained within `RawValue` are valid JSON"
2056 );
2057 }
2058
2059 #[test]
2060 fn should_parse_raw_json_to_rust_struct() {
2061 #[derive(Deserialize)]
2063 struct Song {
2064 title: String,
2065 }
2066
2067 struct SongMessage {
2069 song: Song,
2070 original: Box<RawValue>,
2071 }
2072
2073 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2075
2076 let raw_json: Box<RawValue> = serde_json::from_str(&json)
2078 .expect("Typically we want to parse some JSON from an endpoint first");
2079 let song = Song::deserialize(raw_json.into_deserializer()).expect("And then introspect it");
2081
2082 let message = SongMessage {
2083 song,
2084 original: raw_json,
2085 };
2086
2087 assert_eq!(
2088 message.song.title, TITLE,
2089 "The title is a normal `String` and so can be directly compared"
2090 );
2091 assert_eq!(
2092 message.original.get(),
2093 &json,
2094 "The original has not been modified in any way"
2095 );
2096 }
2097
2098 #[test]
2099 fn should_parse_borrowed_raw_json_to_rust_struct() {
2100 #[derive(Deserialize)]
2102 struct Song<'a> {
2103 title: &'a str,
2104 }
2105
2106 struct SongMessage<'a> {
2110 song: Song<'a>,
2111 original: &'a RawValue,
2112 }
2113
2114 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2116
2117 let raw_json: &RawValue = serde_json::from_str(&json)
2119 .expect("Typically we want to parse some JSON from an endpoint first");
2120 let song =
2122 Song::<'_>::deserialize(raw_json.into_deserializer()).expect("And then introspect it");
2123 let message = SongMessage {
2125 song,
2126 original: raw_json,
2127 };
2128
2129 assert_eq!(
2130 message.song.title, TITLE,
2131 "The title is a normal `&str` and so can be directly compared"
2132 );
2133 assert_eq!(
2134 message.original.get(),
2135 &json,
2136 "The original has not been modified in any way"
2137 );
2138 }
2139
2140 #[test]
2141 fn should_deser_number_as_i64() {
2142 const JSON: &str = "123";
2143 let json: &RawValue = serde_json::from_str(JSON).unwrap();
2144
2145 let n = i64::deserialize(json.into_deserializer()).unwrap();
2146
2147 assert_eq!(n, 123);
2148 }
2149
2150 #[test]
2151 fn should_convert_json_string_to_str() {
2152 #[derive(Deserialize)]
2154 struct Song<'a> {
2155 title: &'a str,
2156 }
2157
2158 struct SongMessage<'a> {
2162 song: Song<'a>,
2163 original: &'a RawValue,
2164 }
2165
2166 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2168
2169 let raw_json: &RawValue = serde_json::from_str(&json)
2171 .expect("Typically we want to parse some JSON from an endpoint first");
2172 let song =
2174 Song::<'_>::deserialize(raw_json.into_deserializer()).expect("And then introspect it");
2175 let message = SongMessage {
2177 song,
2178 original: raw_json,
2179 };
2180
2181 assert_eq!(
2182 message.song.title, TITLE,
2183 "The title is a normal `&str` and so can be directly compared"
2184 );
2185 assert_eq!(
2186 message.original.get(),
2187 &json,
2188 "The original has not been modified in any way"
2189 );
2190 }
2191}