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