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 sync::Arc,
15};
16
17use tracing::{trace, Level};
18
19use crate::{
20 warning::{self, IntoCaveat},
21 Verdict,
22};
23use parser::ErrorKind;
24use parser::{Parser, Span};
25
26#[doc(inline)]
27pub use parser::{line_col, Error, ErrorReport, LineCol};
28pub(crate) use parser::{parse, RawStr};
29
30const PATH_SEPARATOR: char = '.';
31const PATH_ROOT: &str = "$";
32
33#[doc(hidden)]
36#[macro_export]
37macro_rules! required_field_or_bail {
38 ($elem:expr, $fields:expr, $field_name:literal, $warnings:expr) => {
39 match $fields.get($field_name) {
40 Some(field_elem) => field_elem,
41 None => {
42 $warnings.with_elem(
43 WarningKind::FieldRequired {
44 field_name: $field_name.into(),
45 },
46 $elem,
47 );
48 return Err($warnings);
49 }
50 }
51 };
52}
53
54#[doc(hidden)]
57#[macro_export]
58macro_rules! required_field {
59 ($elem:expr, $fields:expr, $field_name:literal, $warnings:expr) => {{
60 let field = $fields.get($field_name);
61
62 if field.is_none() {
63 $warnings.with_elem(
64 WarningKind::FieldRequired {
65 field_name: $field_name.into(),
66 },
67 $elem,
68 );
69 }
70
71 field
72 }};
73}
74
75#[doc(hidden)]
78#[macro_export]
79macro_rules! expect_object_or_bail {
80 ($elem:expr, $warnings:expr) => {
81 match $elem.as_object_fields() {
82 Some(fields) => fields,
83 None => {
84 $warnings.with_elem(
85 WarningKind::FieldInvalidType {
86 expected_type: json::ValueKind::Object,
87 },
88 $elem,
89 );
90 return Err($warnings);
91 }
92 }
93 };
94}
95
96#[doc(hidden)]
99#[macro_export]
100macro_rules! expect_array_or_bail {
101 ($elem:expr, $warnings:expr) => {
102 match $elem.as_array() {
103 Some(fields) => fields,
104 None => {
105 $warnings.with_elem(
106 WarningKind::FieldInvalidType {
107 expected_type: json::ValueKind::Array,
108 },
109 $elem,
110 );
111 return Err($warnings);
112 }
113 }
114 };
115}
116
117#[doc(hidden)]
122#[macro_export]
123macro_rules! expect_string_or_bail {
124 ($elem:expr, $warnings:expr) => {{
125 use $crate::warning::GatherWarnings as _;
126
127 match $elem.as_raw_str() {
128 Some(s) => s.decode_escapes($elem).gather_warnings_into(&mut $warnings),
129 None => {
130 $warnings.with_elem(
131 WarningKind::FieldInvalidType {
132 expected_type: json::ValueKind::String,
133 },
134 $elem,
135 );
136 return Err($warnings);
137 }
138 }
139 }};
140}
141
142#[doc(hidden)]
146#[macro_export]
147macro_rules! parse_required_or_bail {
148 ($elem:expr, $fields:expr, $elem_name:literal, $target:ty, $warnings:expr) => {{
149 #[allow(
150 unused,
151 reason = "the macro uses the import but maybe the outside scope does too."
152 )]
153 use $crate::json::FromJson;
154 use $crate::warning::GatherWarnings as _;
155
156 let elem = $crate::required_field_or_bail!($elem, $fields, $elem_name, $warnings);
157 <$target as FromJson>::from_json(elem)?.gather_warnings_into(&mut $warnings)
158 }};
159}
160
161#[doc(hidden)]
165#[macro_export]
166macro_rules! parse_required {
167 ($elem:expr, $fields:expr, $elem_name:literal, $target:ty, $warnings:expr) => {{
168 #[allow(
169 unused,
170 reason = "the macro uses the import but maybe the outside scope does too."
171 )]
172 use $crate::json::FromJson;
173 use $crate::warning::GatherWarnings as _;
174
175 let elem = $crate::required_field!($elem, $fields, $elem_name, $warnings);
176
177 if let Some(elem) = elem {
178 let value =
179 <$target as FromJson>::from_json(elem)?.gather_warnings_into(&mut $warnings);
180 Some(value)
181 } else {
182 None
183 }
184 }};
185}
186
187#[doc(hidden)]
190#[macro_export]
191macro_rules! parse_nullable_or_bail {
192 ($fields:expr, $elem_name:literal, $target:ty, $warnings:expr) => {{
193 #[allow(
194 unused,
195 reason = "the macro uses the import but maybe the outside scope does too."
196 )]
197 use $crate::json::FromJson as _;
198 use $crate::warning::GatherWarnings as _;
199
200 match $fields.get($elem_name) {
201 Some(elem) => Option::<$target>::from_json(elem)?.gather_warnings_into(&mut $warnings),
202 None => None,
203 }
204 }};
205}
206
207pub(crate) trait FromJson<'elem, 'buf>: Sized {
209 type WarningKind: warning::Kind;
210
211 fn from_json(elem: &'elem Element<'buf>) -> Verdict<Self, Self::WarningKind>;
213}
214
215impl<'a, 'b, T> FromJson<'a, 'b> for Option<T>
219where
220 T: FromJson<'a, 'b> + IntoCaveat,
221{
222 type WarningKind = T::WarningKind;
223
224 fn from_json(elem: &'a Element<'b>) -> Verdict<Self, Self::WarningKind> {
225 let value = elem.as_value();
226
227 if value.is_null() {
228 Ok(None.into_caveat(warning::Set::new()))
229 } else {
230 let v = T::from_json(elem)?;
231 Ok(v.map(Some))
232 }
233 }
234}
235
236#[derive(Clone, Debug, Eq, PartialEq)]
240pub struct Element<'buf> {
241 id: ElemId,
243
244 path_node: PathNodeRef<'buf>,
246
247 span: Span,
251
252 value: Value<'buf>,
254}
255
256#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
260pub struct ElemId(usize);
261
262impl fmt::Display for ElemId {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264 fmt::Display::fmt(&self.0, f)
265 }
266}
267
268impl<'buf> Element<'buf> {
269 fn new(id: ElemId, path: PathNodeRef<'buf>, span: Span, value: Value<'buf>) -> Element<'buf> {
271 Element {
272 id,
273 path_node: path,
274 span,
275 value,
276 }
277 }
278
279 pub(crate) const fn id(&self) -> ElemId {
281 self.id
282 }
283
284 pub fn path(&self) -> PathRef<'buf> {
286 PathRef(self.path_node())
287 }
288
289 pub(crate) fn path_node(&self) -> PathNodeRef<'buf> {
291 Arc::clone(&self.path_node)
292 }
293
294 pub fn source_json(&self, source_json: &'buf str) -> SourceStr<'buf> {
308 if let PathNode::Object { key, .. } = *self.path_node {
309 let span = Span {
311 start: key.span().start,
312 end: self.span.end,
314 };
315 let field_str = &source_json
316 .get(span.start..span.end)
317 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR");
318 let field = RawStr::from_str(field_str, span);
319 let (key, value) = field_str
320 .split_once(':')
321 .expect("An objects field always contains a delimiting `:`");
322
323 SourceStr::Field { field, key, value }
324 } else {
325 let span = self.span;
326 let s = source_json
327 .get(span.start..span.end)
328 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR");
329 SourceStr::Value(RawStr::from_str(s, span))
330 }
331 }
332
333 pub fn source_json_value(&self, source_json: &'buf str) -> &'buf str {
346 source_json
347 .get(self.span.start..self.span.end)
348 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR")
349 }
350
351 pub(crate) fn value(&self) -> &Value<'buf> {
353 &self.value
354 }
355
356 pub(crate) fn as_value(&self) -> &Value<'buf> {
358 &self.value
359 }
360
361 pub(crate) fn as_raw_str(&self) -> Option<&RawStr<'buf>> {
363 self.value.as_raw_str()
364 }
365
366 pub(crate) fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
368 self.value.as_object_fields()
369 }
370
371 pub(crate) fn as_array(&self) -> Option<&[Element<'buf>]> {
372 self.value.as_array()
373 }
374
375 pub fn as_number_str(&self) -> Option<&str> {
376 self.value.as_number()
377 }
378}
379
380#[derive(Debug)]
381pub enum SourceStr<'buf> {
382 Value(RawStr<'buf>),
384
385 Field {
387 field: RawStr<'buf>,
389
390 key: &'buf str,
392
393 value: &'buf str,
395 },
396}
397
398impl fmt::Display for SourceStr<'_> {
399 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400 match self {
401 SourceStr::Value(s) => f.write_str(s.as_raw()),
402 SourceStr::Field { field, .. } => f.write_str(field.as_raw()),
403 }
404 }
405}
406
407impl PartialEq<&str> for SourceStr<'_> {
409 fn eq(&self, other: &&str) -> bool {
410 match self {
411 SourceStr::Value(s) => s.as_raw() == *other,
412 SourceStr::Field { field, .. } => field.as_raw() == *other,
413 }
414 }
415}
416
417impl PartialEq<String> for SourceStr<'_> {
419 fn eq(&self, other: &String) -> bool {
420 self.eq(&&**other)
421 }
422}
423
424impl PartialOrd for Element<'_> {
425 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
426 Some(self.cmp(other))
427 }
428}
429
430impl Ord for Element<'_> {
431 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
432 self.path_node.cmp(&other.path_node)
433 }
434}
435
436impl fmt::Display for Element<'_> {
437 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438 write!(f, "{} = {}", self.path_node, self.value)
439 }
440}
441
442#[derive(Clone, Debug, Eq, PartialEq)]
444pub(crate) struct Field<'buf>(Element<'buf>);
445
446impl<'buf> Field<'buf> {
447 #[expect(
448 clippy::unreachable,
449 reason = "A Field is created by the parser when the type is an Object."
450 )]
451 pub(crate) fn key(&self) -> RawStr<'buf> {
452 let PathNode::Object { key, .. } = *self.0.path_node else {
453 unreachable!();
454 };
455
456 key
457 }
458
459 #[expect(dead_code, reason = "pending use in `tariff::lint`")]
461 pub(crate) fn into_element(self) -> Element<'buf> {
462 self.0
463 }
464
465 pub(crate) fn element(&self) -> &Element<'buf> {
467 &self.0
468 }
469}
470
471#[derive(Clone, Debug, Eq, PartialEq)]
473pub(crate) enum Value<'buf> {
474 Null,
476
477 True,
479
480 False,
482
483 String(RawStr<'buf>),
485
486 Number(&'buf str),
491
492 Array(Vec<Element<'buf>>),
497
498 Object(Vec<Field<'buf>>),
504}
505
506impl<'buf> Value<'buf> {
507 pub(crate) fn kind(&self) -> ValueKind {
508 match self {
509 Value::Null => ValueKind::Null,
510 Value::True | Value::False => ValueKind::Bool,
511 Value::String(_) => ValueKind::String,
512 Value::Number(_) => ValueKind::Number,
513 Value::Array(_) => ValueKind::Array,
514 Value::Object(_) => ValueKind::Object,
515 }
516 }
517
518 pub(crate) fn is_null(&self) -> bool {
519 matches!(self, Value::Null)
520 }
521
522 pub(crate) fn is_scalar(&self) -> bool {
524 matches!(
525 self,
526 Value::Null | Value::True | Value::False | Value::String(_) | Value::Number(_)
527 )
528 }
529
530 pub(crate) fn as_array(&self) -> Option<&[Element<'buf>]> {
531 match self {
532 Value::Array(elems) => Some(elems),
533 _ => None,
534 }
535 }
536
537 pub(crate) fn as_number(&self) -> Option<&str> {
538 match self {
539 Value::Number(s) => Some(s),
540 _ => None,
541 }
542 }
543
544 pub(crate) fn as_raw_str(&self) -> Option<&RawStr<'buf>> {
546 match self {
547 Value::String(s) => Some(s),
548 _ => None,
549 }
550 }
551
552 pub(crate) fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
554 match self {
555 Value::Object(fields) => Some(fields),
556 _ => None,
557 }
558 }
559}
560
561impl fmt::Display for Value<'_> {
562 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
563 match self {
564 Self::Null => write!(f, "null"),
565 Self::True => write!(f, "true"),
566 Self::False => write!(f, "false"),
567 Self::String(s) => write!(f, "{s}"),
568 Self::Number(s) => write!(f, "{s}"),
569 Self::Array(..) => f.write_str("[...]"),
570 Self::Object(..) => f.write_str("{...}"),
571 }
572 }
573}
574
575#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
577pub enum ValueKind {
578 Null,
579 Bool,
580 Number,
581 String,
582 Array,
583 Object,
584}
585
586impl fmt::Display for ValueKind {
587 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
588 match self {
589 ValueKind::Null => write!(f, "null"),
590 ValueKind::Bool => write!(f, "bool"),
591 ValueKind::Number => write!(f, "number"),
592 ValueKind::String => write!(f, "string"),
593 ValueKind::Array => write!(f, "array"),
594 ValueKind::Object => write!(f, "object"),
595 }
596 }
597}
598
599#[derive(Copy, Clone, Debug, Eq, PartialEq)]
603pub(crate) enum ObjectKind {
604 Object,
605 Array,
606}
607
608pub type RawMap<'buf> = BTreeMap<RawStr<'buf>, Element<'buf>>;
609pub type RawRefMap<'a, 'buf> = BTreeMap<RawStr<'buf>, &'a Element<'buf>>;
610
611#[expect(dead_code, reason = "pending use in `tariff::lint`")]
612pub(crate) trait FieldsIntoExt<'buf> {
613 fn into_map(self) -> RawMap<'buf>;
614}
615
616pub(crate) trait FieldsAsExt<'buf> {
617 fn as_raw_map(&self) -> RawRefMap<'_, 'buf>;
618 fn find_field(&self, key: &str) -> Option<&Field<'buf>>;
619}
620
621impl<'buf> FieldsIntoExt<'buf> for Vec<Field<'buf>> {
622 fn into_map(self) -> RawMap<'buf> {
623 self.into_iter()
624 .map(|field| (field.key(), field.into_element()))
625 .collect()
626 }
627}
628
629impl<'buf> FieldsAsExt<'buf> for Vec<Field<'buf>> {
630 fn as_raw_map(&self) -> RawRefMap<'_, 'buf> {
631 self.iter()
632 .map(|field| (field.key(), field.element()))
633 .collect()
634 }
635
636 fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
637 self.iter().find(|field| field.key().as_raw() == key)
638 }
639}
640
641impl<'buf> FieldsAsExt<'buf> for [Field<'buf>] {
642 fn as_raw_map(&self) -> RawRefMap<'_, 'buf> {
643 self.iter()
644 .map(|field| (field.key(), field.element()))
645 .collect()
646 }
647
648 fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
649 self.iter().find(|field| field.key().as_raw() == key)
650 }
651}
652
653#[derive(Clone, Debug)]
656pub struct UnexpectedFields<'buf>(Vec<PathNodeRef<'buf>>);
657
658impl fmt::Display for UnexpectedFields<'_> {
659 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
660 if f.alternate() {
661 f.write_str("[\n")?;
663 for entry in &self.0 {
664 writeln!(f, "\t\"{entry}\",")?;
665 }
666 f.write_str("]\n")?;
667 } else {
668 f.write_char('[')?;
670 for entry in &self.0 {
671 write!(f, "{entry},")?;
672 }
673 f.write_char(']')?;
674 }
675
676 Ok(())
677 }
678}
679
680impl<'buf> UnexpectedFields<'buf> {
681 pub(crate) fn empty() -> Self {
683 Self(vec![])
684 }
685
686 pub(crate) fn from_vec(v: Vec<PathNodeRef<'buf>>) -> Self {
688 Self(v)
689 }
690
691 pub fn to_strings(&self) -> Vec<String> {
693 self.0.iter().map(ToString::to_string).collect()
694 }
695
696 pub fn into_strings(self) -> Vec<String> {
698 self.0.into_iter().map(|path| path.to_string()).collect()
699 }
700
701 pub fn is_empty(&self) -> bool {
703 self.0.is_empty()
704 }
705
706 pub fn len(&self) -> usize {
708 self.0.len()
709 }
710
711 pub fn iter<'a>(&'a self) -> UnexpectedFieldsIter<'a, 'buf> {
713 UnexpectedFieldsIter(self.0.iter())
714 }
715}
716
717impl<'buf> IntoIterator for UnexpectedFields<'buf> {
718 type Item = PathRef<'buf>;
719
720 type IntoIter = UnexpectedFieldsIntoIter<'buf>;
721
722 fn into_iter(self) -> Self::IntoIter {
723 UnexpectedFieldsIntoIter(self.0.into_iter())
724 }
725}
726
727pub struct UnexpectedFieldsIntoIter<'buf>(std::vec::IntoIter<PathNodeRef<'buf>>);
728
729impl<'buf> Iterator for UnexpectedFieldsIntoIter<'buf> {
730 type Item = PathRef<'buf>;
731
732 fn next(&mut self) -> Option<Self::Item> {
733 let path_node = self.0.next()?;
734
735 Some(PathRef(path_node))
736 }
737}
738
739impl<'a, 'buf> IntoIterator for &'a UnexpectedFields<'buf> {
740 type Item = PathRef<'buf>;
741
742 type IntoIter = UnexpectedFieldsIter<'a, 'buf>;
743
744 fn into_iter(self) -> Self::IntoIter {
745 self.iter()
746 }
747}
748
749pub struct UnexpectedFieldsIter<'a, 'buf>(std::slice::Iter<'a, PathNodeRef<'buf>>);
751
752impl<'buf> Iterator for UnexpectedFieldsIter<'_, 'buf> {
753 type Item = PathRef<'buf>;
754
755 fn next(&mut self) -> Option<Self::Item> {
756 let path_node = self.0.next()?;
757
758 Some(PathRef(Arc::clone(path_node)))
759 }
760}
761
762pub struct PathRef<'buf>(PathNodeRef<'buf>);
770
771impl fmt::Debug for PathRef<'_> {
772 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
773 write!(f, "{self}")
774 }
775}
776
777impl<'buf> PathRef<'buf> {
778 pub fn components(&self) -> PathComponents<'buf> {
780 PathComponents(PathIter::new(Arc::clone(&self.0)))
781 }
782}
783
784pub struct PathComponents<'buf>(PathIter<'buf>);
786
787impl<'buf> Iterator for PathComponents<'buf> {
788 type Item = PathComponent<'buf>;
789
790 fn next(&mut self) -> Option<Self::Item> {
791 let path_node = self.0.next()?;
792 Some(PathComponent(path_node))
793 }
794}
795
796impl PartialEq<&str> for PathRef<'_> {
798 fn eq(&self, other: &&str) -> bool {
799 match_path_node(&self.0, other, |_| false)
800 }
801}
802
803impl PartialEq<String> for PathRef<'_> {
805 fn eq(&self, other: &String) -> bool {
806 match_path_node(&self.0, other, |_| false)
807 }
808}
809
810impl fmt::Display for PathRef<'_> {
811 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
812 fmt::Display::fmt(&self.0, f)
813 }
814}
815
816#[cfg(test)]
817mod test_path_node_matches_str {
818 use std::sync::Arc;
819
820 use crate::test;
821
822 use super::PathNode;
823
824 #[test]
825 fn should_match_path() {
826 test::setup();
827
828 let root = Arc::new(PathNode::Root);
829 let path_a = Arc::new(PathNode::Array {
830 parent: Arc::clone(&root),
831 index: 1,
832 });
833 let path_b = Arc::new(PathNode::Object {
834 parent: Arc::clone(&path_a),
835 key: r#""name""#.into(),
836 });
837 let path_c = PathNode::Object {
838 parent: Arc::clone(&path_b),
839 key: r#""gene""#.into(),
840 };
841
842 assert_eq!(*root, "$");
843 assert_eq!(*path_a, "$.1");
844 assert_eq!(*path_b, "$.1.name");
845 assert_eq!(path_c, "$.1.name.gene");
846 }
847}
848
849pub(crate) type PathNodeRef<'buf> = Arc<PathNode<'buf>>;
851
852#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
862pub(crate) enum PathNode<'buf> {
863 #[default]
865 Root,
866 Array {
868 parent: PathNodeRef<'buf>,
869 index: usize,
870 },
871 Object {
873 parent: PathNodeRef<'buf>,
874 key: RawStr<'buf>,
875 },
876}
877
878pub enum PathNodeKind {
881 Root,
883 Array,
885 Object,
887}
888
889#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
898pub struct Path(Vec<PathPiece>);
899
900impl Path {
901 const fn root() -> Self {
903 Self(vec![])
904 }
905
906 fn from_node(path: PathNodeRef<'_>) -> Self {
908 let paths: Vec<_> = PathIter::new(path).collect();
909
910 let pieces = paths
911 .into_iter()
912 .rev()
913 .filter_map(|path_node| match *path_node {
914 PathNode::Root => None,
915 PathNode::Array { index, .. } => Some(PathPiece::Array(index)),
916 PathNode::Object { key, .. } => Some(PathPiece::Object(key.to_string())),
917 })
918 .collect();
919
920 Self(pieces)
921 }
922}
923
924impl PartialEq<&str> for Path {
926 fn eq(&self, other: &&str) -> bool {
927 match_path(self, other)
928 }
929}
930
931impl PartialEq<String> for Path {
933 fn eq(&self, other: &String) -> bool {
934 match_path(self, other)
935 }
936}
937
938impl fmt::Display for Path {
939 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
940 let iter = self.0.iter();
941
942 write!(f, "$")?;
943
944 for path in iter {
945 write!(f, ".{path}")?;
946 }
947
948 Ok(())
949 }
950}
951
952#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
956enum PathPiece {
957 Array(usize),
959 Object(String),
961}
962
963impl fmt::Display for PathPiece {
964 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
965 match self {
966 PathPiece::Array(index) => write!(f, "{index}"),
967 PathPiece::Object(key) => write!(f, "{key}"),
968 }
969 }
970}
971
972pub struct PathComponent<'buf>(PathNodeRef<'buf>);
974
975impl PathComponent<'_> {
976 pub fn kind(&self) -> PathNodeKind {
978 match *self.0 {
979 PathNode::Root => PathNodeKind::Root,
980 PathNode::Array { .. } => PathNodeKind::Array,
981 PathNode::Object { .. } => PathNodeKind::Object,
982 }
983 }
984}
985
986impl fmt::Display for PathComponent<'_> {
987 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
988 match *self.0 {
989 PathNode::Root => f.write_str(PATH_ROOT),
990 PathNode::Array { index, .. } => write!(f, "{index}"),
991 PathNode::Object { key, .. } => write!(f, "{key}"),
992 }
993 }
994}
995
996impl fmt::Display for PathNode<'_> {
997 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
998 let paths: Vec<_> = PathIter::new(Arc::new(self.clone())).collect();
999 let mut iter = paths.into_iter().rev();
1000
1001 if f.alternate() {
1002 for path in iter {
1004 match *path {
1005 PathNode::Root => f.write_str("")?,
1006 PathNode::Array { .. } | PathNode::Object { .. } => f.write_str("...|")?,
1007 }
1008 }
1009 } else {
1010 if let Some(path) = iter.next() {
1011 write!(f, "{}", PathComponent(path))?;
1012 }
1013
1014 for path in iter {
1015 write!(f, ".{}", PathComponent(path))?;
1016 }
1017 }
1018 Ok(())
1019 }
1020}
1021
1022impl<'buf> PathNode<'buf> {
1023 pub(crate) fn is_root(&self) -> bool {
1025 matches!(self, PathNode::Root)
1026 }
1027
1028 pub(crate) fn is_array(&self) -> bool {
1030 matches!(self, PathNode::Array { .. })
1031 }
1032
1033 pub(crate) fn as_object_key(&self) -> Option<&RawStr<'buf>> {
1035 match self {
1036 PathNode::Object { key, .. } => Some(key),
1037 PathNode::Root | PathNode::Array { .. } => None,
1038 }
1039 }
1040}
1041
1042fn match_path_node<F>(path: &PathNode<'_>, s: &str, mut skip: F) -> bool
1048where
1049 F: FnMut(&str) -> bool,
1050{
1051 let mut parts = s.rsplit(PATH_SEPARATOR);
1052 let mut paths_iter = PathIter::new(Arc::new(path.clone()));
1053
1054 loop {
1055 let node_segment = paths_iter.next();
1056 let str_segment = parts.next();
1057
1058 let (node_segment, str_segment) = match (node_segment, str_segment) {
1059 (None, None) => return true,
1061 (None, Some(_)) | (Some(_), None) => return false,
1063 (Some(a), Some(b)) => (a, b),
1065 };
1066
1067 if skip(str_segment) {
1069 continue;
1070 }
1071
1072 let yip = match *node_segment {
1073 PathNode::Root => str_segment == PATH_ROOT,
1074 PathNode::Array { index, .. } => {
1075 let Ok(b) = str_segment.parse::<usize>() else {
1076 return false;
1077 };
1078
1079 index == b
1080 }
1081 PathNode::Object { key, .. } => key.as_raw() == str_segment,
1082 };
1083
1084 if !yip {
1086 return false;
1087 }
1088 }
1089}
1090
1091fn match_path(path: &Path, s: &str) -> bool {
1093 let mut parts = s.split(PATH_SEPARATOR);
1094 let mut paths_iter = path.0.iter();
1095
1096 let Some(str_segment) = parts.next() else {
1097 return false;
1098 };
1099
1100 if str_segment != PATH_ROOT {
1103 return false;
1104 }
1105
1106 loop {
1107 let node_segment = paths_iter.next();
1108 let str_segment = parts.next();
1109
1110 let (node_segment, str_segment) = match (node_segment, str_segment) {
1111 (None, None) => return true,
1113 (None, Some(_)) | (Some(_), None) => return false,
1115 (Some(a), Some(b)) => (a, b),
1117 };
1118
1119 let yip = match node_segment {
1120 PathPiece::Array(index) => {
1121 let Ok(b) = str_segment.parse::<usize>() else {
1122 return false;
1123 };
1124
1125 *index == b
1126 }
1127 PathPiece::Object(key) => key == str_segment,
1128 };
1129
1130 if !yip {
1132 return false;
1133 }
1134 }
1135}
1136
1137impl PartialEq<&str> for PathNode<'_> {
1138 fn eq(&self, other: &&str) -> bool {
1139 match_path_node(self, other, |_| false)
1140 }
1141}
1142
1143impl PartialEq<String> for PathNode<'_> {
1144 fn eq(&self, other: &String) -> bool {
1145 match_path_node(self, other, |_| false)
1146 }
1147}
1148
1149struct PathIter<'buf> {
1151 complete: bool,
1153 path: PathNodeRef<'buf>,
1155}
1156
1157impl<'buf> PathIter<'buf> {
1158 fn new(path: PathNodeRef<'buf>) -> Self {
1160 Self {
1161 complete: false,
1162 path,
1163 }
1164 }
1165}
1166
1167impl<'buf> Iterator for PathIter<'buf> {
1168 type Item = PathNodeRef<'buf>;
1169
1170 fn next(&mut self) -> Option<Self::Item> {
1171 if self.complete {
1172 return None;
1173 }
1174
1175 match &*self.path {
1176 PathNode::Root => {
1177 self.complete = true;
1179 Some(Arc::clone(&self.path))
1180 }
1181 PathNode::Array { parent, .. } | PathNode::Object { parent, .. } => {
1182 let next = Arc::clone(&self.path);
1183 self.path = Arc::clone(parent);
1184 Some(next)
1185 }
1186 }
1187 }
1188}
1189
1190struct DisplayExpectStack<'a>(&'a [schema::Expect]);
1192
1193impl fmt::Display for DisplayExpectStack<'_> {
1194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1195 let mut iter = self.0.iter().rev();
1196 let last = iter.next();
1197
1198 f.write_str("~")?;
1200
1201 for _ in iter {
1202 f.write_str("...~")?;
1203 }
1204
1205 if let Some(exp) = last {
1206 match exp {
1207 schema::Expect::Scalar => f.write_str("~")?,
1208 schema::Expect::Array(element) => match &**element {
1209 schema::Element::Scalar => f.write_str("~")?,
1210 schema::Element::Array(element) => write!(f, "[{element:?}]")?,
1211 schema::Element::Object(fields) => {
1212 write!(f, "[{{{:}}}])", DisplayExpectFields(&**fields))?;
1213 }
1214 },
1215 schema::Expect::Object(fields) => {
1216 write!(f, "{{{:}}}", DisplayExpectFields(&**fields))?;
1217 }
1218 schema::Expect::UnmatchedScalar => write!(f, "unmatched(scalar)")?,
1219 schema::Expect::UnmatchedArray => write!(f, "unmatched(array)")?,
1220 schema::Expect::UnmatchedObject => write!(f, "unmatched(object)")?,
1221 schema::Expect::OutOfSchema => write!(f, "no_schema")?,
1222 }
1223 }
1224
1225 Ok(())
1226 }
1227}
1228
1229struct DisplayExpectFields<'a, V>(&'a BTreeMap<&'a str, V>);
1231
1232impl<V> fmt::Display for DisplayExpectFields<'_, V> {
1233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1234 const MAX_FIELDS: usize = 8;
1235
1236 let mut count = 0;
1237 let mut iter = self.0.keys().peekable();
1238
1239 loop {
1240 if count >= MAX_FIELDS {
1241 f.write_str("...")?;
1242 break;
1243 }
1244
1245 let Some(field) = iter.next() else {
1246 break;
1247 };
1248
1249 count += 1;
1250 write!(f, "{field}")?;
1251
1252 let Some(_) = iter.peek() else {
1253 break;
1254 };
1255
1256 f.write_str(", ")?;
1257 }
1258
1259 Ok(())
1260 }
1261}
1262
1263#[derive(Debug)]
1264struct UnbalancedExpectStack;
1265
1266impl fmt::Display for UnbalancedExpectStack {
1267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1268 f.write_str("unbalanced expectation stack")
1269 }
1270}
1271
1272impl std::error::Error for UnbalancedExpectStack {}
1273
1274pub(crate) fn parse_with_schema<'buf>(
1277 json: &'buf str,
1278 schema: &schema::Element,
1279) -> Result<ParseReport<'buf>, Error> {
1280 let parser = Parser::new(json);
1281 let mut unexpected_fields = vec![];
1282 let mut expectation_stack = vec![schema.to_expectation()];
1284
1285 for event in parser {
1286 match event? {
1287 parser::Event::Open { kind, parent_path } => {
1288 let Some(expectation) = expectation_stack.pop() else {
1291 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1292 .into_partial_error_without_token()
1293 .with_root_path());
1294 };
1295
1296 if tracing::enabled!(Level::DEBUG) {
1297 match kind {
1298 ObjectKind::Array => {
1299 trace!("{parent_path} [ {}", DisplayExpectStack(&expectation_stack));
1300 }
1301 ObjectKind::Object => trace!(
1302 "{parent_path} {{ {}",
1303 DisplayExpectStack(&expectation_stack)
1304 ),
1305 }
1306 }
1307
1308 match expectation {
1309 schema::Expect::Array(elem) => {
1310 if parent_path.is_root() {
1313 let next = match kind {
1314 ObjectKind::Array => schema::Expect::Array(elem),
1315 ObjectKind::Object => schema::Expect::UnmatchedArray,
1316 };
1317
1318 expectation_stack.push(next);
1319 trace!("{}", DisplayExpectStack(&expectation_stack));
1320 continue;
1321 }
1322
1323 if !parent_path.is_array() {
1324 expectation_stack.push(schema::Expect::UnmatchedArray);
1325 trace!("{}", DisplayExpectStack(&expectation_stack));
1326 continue;
1327 }
1328
1329 expectation_stack.push(schema::Expect::Array(Arc::clone(&elem)));
1330 expectation_stack.push(elem.to_expectation());
1332 }
1333 schema::Expect::Object(fields) => {
1334 if parent_path.is_root() {
1337 let next = match kind {
1338 ObjectKind::Array => schema::Expect::UnmatchedObject,
1339 ObjectKind::Object => schema::Expect::Object(fields),
1340 };
1341
1342 expectation_stack.push(next);
1343 trace!("{}", DisplayExpectStack(&expectation_stack));
1344 continue;
1345 }
1346 let Some(key) = parent_path.as_object_key() else {
1347 expectation_stack.push(schema::Expect::UnmatchedObject);
1348 trace!("{}", DisplayExpectStack(&expectation_stack));
1349 continue;
1350 };
1351
1352 let next = if let Some(elem) = fields.get(key.as_raw()) {
1353 open_object(kind, elem.as_ref())
1354 } else {
1355 unexpected_fields.push(parent_path);
1356 schema::Expect::OutOfSchema
1357 };
1358
1359 expectation_stack.push(schema::Expect::Object(fields));
1360 expectation_stack.push(next);
1361 }
1362 schema::Expect::OutOfSchema => {
1363 expectation_stack.push(expectation);
1371 expectation_stack.push(schema::Expect::OutOfSchema);
1372 }
1373 schema::Expect::UnmatchedArray | schema::Expect::UnmatchedObject => {
1374 expectation_stack.push(expectation);
1375 expectation_stack.push(schema::Expect::OutOfSchema);
1376 }
1377 _ => {
1378 expectation_stack.push(expectation);
1379 }
1380 }
1381
1382 trace!("{}", DisplayExpectStack(&expectation_stack));
1383 }
1384 parser::Event::Element { kind, parent_path } => {
1385 let Some(expectation) = expectation_stack.pop() else {
1388 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1389 .into_partial_error_without_token()
1390 .with_root_path());
1391 };
1392
1393 if let ValueKind::Array | ValueKind::Object = kind {
1396 if tracing::enabled!(Level::DEBUG) {
1397 match kind {
1398 ValueKind::Array => {
1399 trace!(
1400 "{parent_path} ] {}",
1401 DisplayExpectStack(&expectation_stack)
1402 );
1403 }
1404 ValueKind::Object => trace!(
1405 "{parent_path} }} {}",
1406 DisplayExpectStack(&expectation_stack)
1407 ),
1408 _ => (),
1409 }
1410 }
1411 continue;
1412 }
1413
1414 match expectation {
1415 #[expect(
1416 clippy::unreachable,
1417 reason = "The parser only emits an `Event::Complete` for a scalar object at the root"
1418 )]
1419 schema::Expect::Object(fields) => match &*parent_path {
1420 PathNode::Root => unreachable!(),
1421 PathNode::Array { .. } => {
1422 expectation_stack.push(schema::Expect::UnmatchedObject);
1423 }
1424 PathNode::Object { parent, key } => {
1425 trace!("{parent:#}.{key}");
1426
1427 if !fields.contains_key(key.as_raw()) {
1428 unexpected_fields.push(parent_path);
1429 }
1430
1431 expectation_stack.push(schema::Expect::Object(fields));
1432 }
1433 },
1434 schema::Expect::OutOfSchema => {
1435 unexpected_fields.push(parent_path);
1436 expectation_stack.push(expectation);
1437 }
1438 _ => {
1439 expectation_stack.push(expectation);
1440 }
1441 }
1442 }
1443 parser::Event::Complete(element) => {
1444 if element.value().is_scalar() {
1445 unexpected_fields.push(element.path_node());
1446 }
1447
1448 return Ok(ParseReport {
1451 element,
1452 unexpected_fields: UnexpectedFields::from_vec(unexpected_fields),
1453 });
1454 }
1455 }
1456 }
1457
1458 Err(ErrorKind::UnexpectedEOF
1459 .into_partial_error_without_token()
1460 .with_root_path())
1461}
1462
1463fn open_object(kind: ObjectKind, elem: Option<&Arc<schema::Element>>) -> schema::Expect {
1464 let Some(schema) = elem else {
1465 return schema::Expect::OutOfSchema;
1466 };
1467
1468 match (kind, &**schema) {
1469 (ObjectKind::Object | ObjectKind::Array, schema::Element::Scalar) => {
1470 schema::Expect::UnmatchedScalar
1471 }
1472 (ObjectKind::Object, schema::Element::Array(_)) => schema::Expect::UnmatchedArray,
1473 (ObjectKind::Object, schema::Element::Object(fields)) => {
1474 schema::Expect::Object(Arc::clone(fields))
1475 }
1476 (ObjectKind::Array, schema::Element::Array(element)) => {
1477 schema::Expect::Array(Arc::clone(element))
1478 }
1479 (ObjectKind::Array, schema::Element::Object(_)) => schema::Expect::UnmatchedObject,
1480 }
1481}
1482
1483#[derive(Debug)]
1486pub(crate) struct ParseReport<'buf> {
1487 pub element: Element<'buf>,
1489
1490 pub unexpected_fields: UnexpectedFields<'buf>,
1492}
1493
1494#[cfg(test)]
1495pub mod test {
1496 #![allow(clippy::missing_panics_doc, reason = "tests are allowed to panic")]
1497 #![allow(clippy::panic, reason = "tests are allowed panic")]
1498
1499 use crate::{json::match_path_node, test::Expectation};
1500
1501 use super::{
1502 parser::Span, walk::DepthFirst, ElemId, Element, Field, FieldsAsExt as _, PathNode,
1503 PathNodeRef, PathRef, RawStr, UnexpectedFields, Value,
1504 };
1505
1506 impl<'buf> Element<'buf> {
1507 pub fn span(&self) -> Span {
1509 self.span
1510 }
1511
1512 pub(crate) fn into_value(self) -> Value<'buf> {
1514 self.value
1515 }
1516
1517 pub(crate) fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1519 let Self {
1520 id,
1521 path_node: path,
1522 span,
1523 value,
1524 } = self;
1525 (id, path, span, value)
1526 }
1527
1528 pub(crate) fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
1529 self.as_object_fields()
1530 .and_then(|fields| fields.find_field(key))
1531 }
1532 }
1533
1534 impl<'buf> Value<'buf> {
1535 pub(crate) fn is_array(&self) -> bool {
1537 matches!(self, Value::Array(_))
1538 }
1539
1540 pub(crate) fn is_object(&self) -> bool {
1542 matches!(self, Value::Object(_))
1543 }
1544
1545 pub(crate) fn as_string(&self) -> Option<&RawStr<'buf>> {
1546 match self {
1547 Value::String(s) => Some(s),
1548 _ => None,
1549 }
1550 }
1551 }
1552
1553 impl<'buf> Field<'buf> {
1554 pub fn id(&self) -> ElemId {
1555 self.0.id()
1556 }
1557
1558 pub fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1559 self.0.into_parts()
1560 }
1561 }
1562
1563 impl<'buf> UnexpectedFields<'buf> {
1564 pub(super) fn into_inner(self) -> Vec<PathNodeRef<'buf>> {
1566 self.0
1567 }
1568
1569 fn filter_matches(&mut self, glob: &PathGlob) {
1571 self.0.retain(|path| !glob.matches(path));
1572 }
1573 }
1574
1575 #[derive(Debug)]
1578 pub(crate) struct PathGlob(String);
1579
1580 impl PathGlob {
1581 pub(crate) fn matches(&self, path: &PathNode<'_>) -> bool {
1583 const WILDCARD: &str = "*";
1584
1585 match_path_node(path, &self.0, |s| {
1586 s == WILDCARD
1588 })
1589 }
1590 }
1591
1592 impl From<usize> for ElemId {
1594 fn from(value: usize) -> Self {
1595 Self(value)
1596 }
1597 }
1598
1599 impl<'a> From<&'a str> for PathGlob {
1600 fn from(s: &'a str) -> Self {
1601 Self(s.into())
1602 }
1603 }
1604
1605 impl From<String> for PathGlob {
1606 fn from(s: String) -> Self {
1607 Self(s)
1608 }
1609 }
1610
1611 impl<'de> serde::Deserialize<'de> for PathGlob {
1612 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1613 where
1614 D: ::serde::Deserializer<'de>,
1615 {
1616 let s = <String as ::serde::Deserialize>::deserialize(deserializer)?;
1617 Ok(Self(s))
1618 }
1619 }
1620
1621 pub struct ElementMap<'a, 'buf>(Vec<&'a Element<'buf>>);
1623
1624 impl<'a, 'buf> ElementMap<'a, 'buf> {
1625 pub fn for_elem(root: &'a Element<'buf>) -> Self {
1627 let walker = DepthFirst::new(root);
1629 Self(walker.collect())
1630 }
1631
1632 pub fn get(&self, id: ElemId) -> &Element<'buf> {
1634 self.0.get(id.0).map(|e| &**e).unwrap()
1635 }
1636
1637 pub fn path(&self, id: ElemId) -> PathRef<'buf> {
1639 self.0.get(id.0).map(|elem| elem.path()).unwrap()
1640 }
1641 }
1642
1643 #[track_caller]
1645 pub(crate) fn expect_no_unexpected_fields(
1646 expect_file_name: &str,
1647 unexpected_fields: &UnexpectedFields<'_>,
1648 ) {
1649 if !unexpected_fields.is_empty() {
1650 const MAX_FIELD_DISPLAY: usize = 20;
1651
1652 if unexpected_fields.len() > MAX_FIELD_DISPLAY {
1653 let truncated_fields = unexpected_fields
1654 .iter()
1655 .take(MAX_FIELD_DISPLAY)
1656 .map(|path| path.to_string())
1657 .collect::<Vec<_>>();
1658
1659 panic!(
1660 "The expect file `{expect_file_name}` didn't expect `{}` unexpected fields;\n\
1661 displaying the first ({}):\n{}\n... and {} more",
1662 unexpected_fields.len(),
1663 truncated_fields.len(),
1664 truncated_fields.join(",\n"),
1665 unexpected_fields.len() - truncated_fields.len(),
1666 )
1667 } else {
1668 panic!(
1669 "The expect file `{expect_file_name}` didn't expect `{}` unexpected fields:\n{}",
1670 unexpected_fields.len(),
1671 unexpected_fields.to_strings().join(",\n")
1672 )
1673 };
1674 }
1675 }
1676
1677 #[track_caller]
1681 pub(crate) fn expect_unexpected_fields(
1682 expect_file_name: &str,
1683 unexpected_fields: &mut UnexpectedFields<'_>,
1684 expected: Expectation<Vec<PathGlob>>,
1685 ) {
1686 if let Expectation::Present(expectation) = expected {
1687 let unexpected_fields_expect = expectation.expect_value();
1688
1689 for expect_glob in unexpected_fields_expect {
1692 unexpected_fields.filter_matches(&expect_glob);
1693 }
1694
1695 expect_no_unexpected_fields(expect_file_name, unexpected_fields);
1696 } else {
1697 expect_no_unexpected_fields(expect_file_name, unexpected_fields);
1698 }
1699 }
1700
1701 #[cfg(test)]
1702 mod test_path_matches_glob {
1703 use std::sync::Arc;
1704
1705 use crate::test;
1706
1707 use super::{PathGlob, PathNode};
1708
1709 #[test]
1710 fn should_match_path() {
1711 test::setup();
1712
1713 let root = Arc::new(PathNode::Root);
1714 let path_a = Arc::new(PathNode::Array {
1715 parent: Arc::clone(&root),
1716 index: 1,
1717 });
1718 let path_b = Arc::new(PathNode::Object {
1719 parent: Arc::clone(&path_a),
1720 key: r#""name""#.into(),
1721 });
1722 let path_c = PathNode::Object {
1723 parent: Arc::clone(&path_b),
1724 key: r#""gene""#.into(),
1725 };
1726
1727 assert!(PathGlob::from("$").matches(&root));
1728 assert!(PathGlob::from("*").matches(&root));
1729
1730 assert!(!PathGlob::from("*").matches(&path_a));
1731 assert!(PathGlob::from("*.*").matches(&path_a));
1732 assert!(PathGlob::from("$.*").matches(&path_a));
1733 assert!(PathGlob::from("$.1").matches(&path_a));
1734
1735 assert!(!PathGlob::from("*").matches(&path_b));
1736 assert!(!PathGlob::from("*.*").matches(&path_b));
1737 assert!(PathGlob::from("*.*.*").matches(&path_b));
1738 assert!(PathGlob::from("$.*.*").matches(&path_b));
1739 assert!(PathGlob::from("$.1.*").matches(&path_b));
1740 assert!(PathGlob::from("$.*.name").matches(&path_b));
1741 assert!(PathGlob::from("$.1.name").matches(&path_b));
1742
1743 assert!(PathGlob::from("$.1.name.gene").matches(&path_c));
1744 }
1745 }
1746}
1747
1748#[cfg(test)]
1749mod test_path {
1750 use super::{Path, PathPiece};
1751
1752 #[test]
1753 fn path_should_cmp_with_str() {
1754 assert_ne!(Path::root(), "");
1755 assert_eq!(Path::root(), "$");
1756 assert_eq!(Path(vec![PathPiece::Object("field_a".into())]), "$.field_a");
1757 assert_eq!(Path(vec![PathPiece::Array(1)]), "$.1");
1758 assert_eq!(
1759 Path(vec![
1760 PathPiece::Object("field_a".into()),
1761 PathPiece::Array(1)
1762 ]),
1763 "$.field_a.1"
1764 );
1765 }
1766
1767 #[test]
1768 fn path_should_display() {
1769 assert_eq!(Path::root().to_string(), "$");
1770 assert_eq!(
1771 Path(vec![PathPiece::Object("field_a".into())]).to_string(),
1772 "$.field_a"
1773 );
1774 assert_eq!(Path(vec![PathPiece::Array(1)]).to_string(), "$.1");
1775 assert_eq!(
1776 Path(vec![
1777 PathPiece::Object("field_a".into()),
1778 PathPiece::Array(1)
1779 ])
1780 .to_string(),
1781 "$.field_a.1"
1782 );
1783 }
1784}
1785
1786#[cfg(test)]
1787mod test_parse_with_schema {
1788 use crate::{json_schema, test};
1789
1790 use super::{parse_with_schema, ParseReport};
1791
1792 #[test]
1793 fn should_report_unexpected_fields_for_root_element() {
1794 const JSON: &str = "null";
1795
1796 test::setup();
1797
1798 let schema = json_schema!({
1799 "id",
1800 "currency",
1801 });
1802
1803 let report = parse_with_schema(JSON, &schema).unwrap();
1804 let ParseReport {
1805 element: _,
1806 unexpected_fields,
1807 } = report;
1808
1809 {
1810 let [field_a] = unexpected_fields.into_inner().try_into().unwrap();
1811 assert_eq!(*field_a, "$");
1812 }
1813 }
1814
1815 #[test]
1816 fn should_report_unexpected_fields_in_flat_object() {
1817 const JSON: &str = r#"{
1818 "id": "tariff_id",
1819 "currency": "EUR",
1820 "name": "Barry",
1821 "address": "Barrystown"
1822}"#;
1823
1824 test::setup();
1825
1826 let schema = json_schema!({
1827 "id",
1828 "currency",
1829 });
1830
1831 let report = parse_with_schema(JSON, &schema).unwrap();
1832 let ParseReport {
1833 element: _,
1834 unexpected_fields,
1835 } = report;
1836
1837 {
1838 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1839 assert_eq!(*field_a, "$.name");
1840 assert_eq!(*field_b, "$.address");
1841 }
1842 }
1843
1844 #[test]
1845 fn should_report_unexpected_fields_in_nested_object() {
1846 const JSON: &str = r#"{
1847 "id": "tariff_id",
1848 "currency": "EUR",
1849 "owner": {
1850 "id": "456856",
1851 "subscription_id": "tedi4568",
1852 "name": "Barry",
1853 "address": "Barrystown"
1854 }
1855}"#;
1856
1857 test::setup();
1858
1859 let schema = json_schema!({
1860 "id",
1861 "currency",
1862 "owner": {
1863 "id",
1864 "subscription_id"
1865 }
1866 });
1867
1868 let report = parse_with_schema(JSON, &schema).unwrap();
1869 let ParseReport {
1870 element: _,
1871 unexpected_fields,
1872 } = report;
1873
1874 {
1875 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1876 assert_eq!(*field_a, "$.owner.name");
1877 assert_eq!(*field_b, "$.owner.address");
1878 }
1879 }
1880
1881 #[test]
1882 fn should_parse_nested_object_out_of_schema() {
1883 const JSON: &str = r#"{
1884 "id": "tariff_id",
1885 "owner": {
1886 "id": "456856",
1887 "subscription_id": "tedi4568",
1888 "name": "Barry",
1889 "address": {
1890 "city": "Barrystown",
1891 "street": "Barrysstreet"
1892 }
1893 },
1894 "currency": "EUR",
1895 "country": "NL"
1896}"#;
1897
1898 test::setup();
1899
1900 let schema = json_schema!({
1901 "id",
1902 "currency",
1903 "owner"
1904 });
1905
1906 let report = parse_with_schema(JSON, &schema).unwrap();
1907 let ParseReport {
1908 element: _,
1909 unexpected_fields,
1910 } = report;
1911
1912 {
1913 let [field_a, field_b, field_c, field_d, field_e, field_f] =
1914 unexpected_fields.into_inner().try_into().unwrap();
1915 assert_eq!(*field_a, "$.owner.id");
1916 assert_eq!(*field_b, "$.owner.subscription_id");
1917 assert_eq!(*field_c, "$.owner.name");
1918 assert_eq!(*field_d, "$.owner.address.city");
1919 assert_eq!(*field_e, "$.owner.address.street");
1920 assert_eq!(*field_f, "$.country");
1921 }
1922 }
1923
1924 #[test]
1925 fn should_report_unexpected_fields_in_array_with_nested_object() {
1926 const JSON: &str = r#"{
1927 "id": "tariff_id",
1928 "currency": "EUR",
1929 "elements": [{
1930 "id": "456856",
1931 "subscription_id": "tedi4568",
1932 "name": "Barry",
1933 "address": "Barrystown"
1934 }]
1935}"#;
1936
1937 test::setup();
1938
1939 let schema = json_schema!({
1940 "id",
1941 "currency",
1942 "elements": [{
1943 "id",
1944 "subscription_id"
1945 }]
1946 });
1947
1948 let report = parse_with_schema(JSON, &schema).unwrap();
1949 let ParseReport {
1950 element: _,
1951 unexpected_fields,
1952 } = report;
1953
1954 {
1955 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1956 assert_eq!(*field_a, "$.elements.0.name");
1957 assert_eq!(*field_b, "$.elements.0.address");
1958 }
1959 }
1960
1961 #[test]
1962 fn should_report_unexpected_fields_in_array_of_nested_objects() {
1963 const JSON: &str = r#"{
1964 "id": "tariff_id",
1965 "currency": "EUR",
1966 "elements": [
1967 {
1968 "id": "456856",
1969 "subscription_id": "tedi4568",
1970 "name": "Barry",
1971 "address": "Barrystown"
1972 },
1973 {
1974 "id": "8746we",
1975 "subscription_id": "dfr345",
1976 "name": "Gerry",
1977 "address": "Gerrystown"
1978 }
1979 ]
1980}"#;
1981
1982 test::setup();
1983
1984 let schema = json_schema!({
1985 "id",
1986 "currency",
1987 "elements": [{
1988 "id",
1989 "subscription_id"
1990 }]
1991 });
1992
1993 let report = parse_with_schema(JSON, &schema).unwrap();
1994 let ParseReport {
1995 element: _,
1996 unexpected_fields,
1997 } = report;
1998
1999 {
2000 let [field_a, field_b, field_c, field_d] =
2001 unexpected_fields.into_inner().try_into().unwrap();
2002 assert_eq!(*field_a, "$.elements.0.name");
2003 assert_eq!(*field_b, "$.elements.0.address");
2004 assert_eq!(*field_c, "$.elements.1.name");
2005 assert_eq!(*field_d, "$.elements.1.address");
2006 }
2007 }
2008
2009 #[test]
2010 fn should_report_unexpected_fields_in_array_of_objects() {
2011 const JSON: &str = r#"[
2012 {
2013 "id": "456856",
2014 "subscription_id": "tedi4568",
2015 "name": "Barry",
2016 "address": "Barrystown"
2017 },
2018 {
2019 "id": "8746we",
2020 "subscription_id": "dfr345",
2021 "name": "Gerry",
2022 "address": "Gerrystown"
2023 }
2024]"#;
2025
2026 test::setup();
2027
2028 let schema = json_schema!([
2029 {
2030 "id",
2031 "subscription_id"
2032 }
2033 ]);
2034
2035 let report = parse_with_schema(JSON, &schema).unwrap();
2036 let ParseReport {
2037 element: _,
2038 unexpected_fields,
2039 } = report;
2040
2041 {
2042 let [field_a, field_b, field_c, field_d] =
2043 unexpected_fields.into_inner().try_into().unwrap();
2044 assert_eq!(*field_a, "$.0.name");
2045 assert_eq!(*field_b, "$.0.address");
2046 assert_eq!(*field_c, "$.1.name");
2047 assert_eq!(*field_d, "$.1.address");
2048 }
2049 }
2050}
2051
2052#[cfg(test)]
2053mod test_source_json {
2054 use super::{parse, walk};
2055
2056 #[test]
2057 fn should_resolve_to_source_json() {
2058 const JSON: &str = r#"{
2059 "name": "David Byrne",
2060 "hobbies": ["song writing", "thinking about society"]
2061}"#;
2062
2063 let element = parse(JSON).unwrap();
2064
2065 let mut walk = walk::DepthFirst::new(&element);
2066
2067 let root = walk.next().unwrap();
2068 assert_eq!(root.source_json(JSON), JSON);
2069
2070 let field_name = walk.next().unwrap();
2071 assert_eq!(field_name.source_json(JSON), r#""name": "David Byrne""#);
2072 assert_eq!(field_name.source_json_value(JSON), r#""David Byrne""#);
2073
2074 let field_hobbies = walk.next().unwrap();
2075 assert_eq!(
2076 field_hobbies.source_json(JSON),
2077 r#""hobbies": ["song writing", "thinking about society"]"#
2078 );
2079 assert_eq!(
2080 field_hobbies.source_json_value(JSON),
2081 r#"["song writing", "thinking about society"]"#
2082 );
2083
2084 let hobbies_one = walk.next().unwrap();
2085 assert_eq!(hobbies_one.source_json(JSON), r#""song writing""#);
2086 assert_eq!(hobbies_one.source_json_value(JSON), r#""song writing""#);
2087
2088 let hobbies_two = walk.next().unwrap();
2089 assert_eq!(hobbies_two.source_json(JSON), r#""thinking about society""#);
2090 assert_eq!(
2091 hobbies_two.source_json_value(JSON),
2092 r#""thinking about society""#
2093 );
2094 }
2095}
2096
2097#[cfg(test)]
2098mod test_path_node {
2099 use std::sync::Arc;
2100
2101 use super::{
2102 parser::{RawStr, Token, TokenType},
2103 PathNode, Span,
2104 };
2105
2106 #[test]
2107 fn should_display_path() {
2108 let root = Arc::new(PathNode::Root);
2109 let path_a = Arc::new(PathNode::Array {
2110 parent: Arc::clone(&root),
2111 index: 1,
2112 });
2113 let path_b = Arc::new(PathNode::Object {
2114 parent: Arc::clone(&path_a),
2115 key: r#""name""#.into(),
2116 });
2117 let path_c = Arc::new(PathNode::Object {
2118 parent: Arc::clone(&path_b),
2119 key: r#""gene""#.into(),
2120 });
2121
2122 assert_eq!(*root, "$");
2123 assert_eq!(*path_a, "$.1");
2124 assert_eq!(*path_b, "$.1.name");
2125 assert_eq!(*path_c, "$.1.name.gene");
2126 }
2127
2128 impl<'buf> From<&'buf str> for RawStr<'buf> {
2129 #[track_caller]
2130 fn from(s: &'buf str) -> Self {
2131 RawStr::from_quoted_str(
2132 s,
2133 Token {
2134 kind: TokenType::String,
2135 span: Span::default(),
2136 },
2137 )
2138 .unwrap()
2139 }
2140 }
2141}