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 SmartString, Verdict, WriteSmartString,
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 pub fn to_smart_string(&self) -> SmartString {
785 self.0.to_smart_string()
786 }
787}
788
789pub struct PathComponents<'buf>(PathIter<'buf>);
791
792impl<'buf> Iterator for PathComponents<'buf> {
793 type Item = PathComponent<'buf>;
794
795 fn next(&mut self) -> Option<Self::Item> {
796 let path_node = self.0.next()?;
797 Some(PathComponent(path_node))
798 }
799}
800
801impl PartialEq<&str> for PathRef<'_> {
803 fn eq(&self, other: &&str) -> bool {
804 match_path_node(&self.0, other, |_| false)
805 }
806}
807
808impl PartialEq<String> for PathRef<'_> {
810 fn eq(&self, other: &String) -> bool {
811 match_path_node(&self.0, other, |_| false)
812 }
813}
814
815impl fmt::Display for PathRef<'_> {
816 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
817 fmt::Display::fmt(&self.0, f)
818 }
819}
820
821#[cfg(test)]
822mod test_path_node_matches_str {
823 use std::sync::Arc;
824
825 use crate::test;
826
827 use super::PathNode;
828
829 #[test]
830 fn should_match_path() {
831 test::setup();
832
833 let root = Arc::new(PathNode::Root);
834 let path_a = Arc::new(PathNode::Array {
835 parent: Arc::clone(&root),
836 index: 1,
837 });
838 let path_b = Arc::new(PathNode::Object {
839 parent: Arc::clone(&path_a),
840 key: r#""name""#.into(),
841 });
842 let path_c = PathNode::Object {
843 parent: Arc::clone(&path_b),
844 key: r#""gene""#.into(),
845 };
846
847 assert_eq!(*root, "$");
848 assert_eq!(*path_a, "$.1");
849 assert_eq!(*path_b, "$.1.name");
850 assert_eq!(path_c, "$.1.name.gene");
851 }
852}
853
854pub(crate) type PathNodeRef<'buf> = Arc<PathNode<'buf>>;
856
857#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
867pub(crate) enum PathNode<'buf> {
868 #[default]
870 Root,
871 Array {
873 parent: PathNodeRef<'buf>,
874 index: usize,
875 },
876 Object {
878 parent: PathNodeRef<'buf>,
879 key: RawStr<'buf>,
880 },
881}
882
883pub enum PathNodeKind {
886 Root,
888 Array,
890 Object,
892}
893
894#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
903pub struct Path(Vec<PathPiece>);
904
905impl Path {
906 const fn root() -> Self {
908 Self(vec![])
909 }
910
911 fn from_node(path: PathNodeRef<'_>) -> Self {
913 let paths: Vec<_> = PathIter::new(path).collect();
914
915 let pieces = paths
916 .into_iter()
917 .rev()
918 .filter_map(|path_node| match *path_node {
919 PathNode::Root => None,
920 PathNode::Array { index, .. } => Some(PathPiece::Array(index)),
921 PathNode::Object { key, .. } => Some(PathPiece::Object(key.to_string())),
922 })
923 .collect();
924
925 Self(pieces)
926 }
927}
928
929impl PartialEq<&str> for Path {
931 fn eq(&self, other: &&str) -> bool {
932 match_path(self, other)
933 }
934}
935
936impl PartialEq<String> for Path {
938 fn eq(&self, other: &String) -> bool {
939 match_path(self, other)
940 }
941}
942
943impl fmt::Display for Path {
944 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
945 let iter = self.0.iter();
946
947 write!(f, "$")?;
948
949 for path in iter {
950 write!(f, ".{path}")?;
951 }
952
953 Ok(())
954 }
955}
956
957#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
961enum PathPiece {
962 Array(usize),
964 Object(String),
966}
967
968impl fmt::Display for PathPiece {
969 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
970 match self {
971 PathPiece::Array(index) => write!(f, "{index}"),
972 PathPiece::Object(key) => write!(f, "{key}"),
973 }
974 }
975}
976
977pub struct PathComponent<'buf>(PathNodeRef<'buf>);
979
980impl PathComponent<'_> {
981 pub fn kind(&self) -> PathNodeKind {
983 match *self.0 {
984 PathNode::Root => PathNodeKind::Root,
985 PathNode::Array { .. } => PathNodeKind::Array,
986 PathNode::Object { .. } => PathNodeKind::Object,
987 }
988 }
989}
990
991impl fmt::Display for PathComponent<'_> {
992 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
993 match *self.0 {
994 PathNode::Root => f.write_str(PATH_ROOT),
995 PathNode::Array { index, .. } => write!(f, "{index}"),
996 PathNode::Object { key, .. } => write!(f, "{key}"),
997 }
998 }
999}
1000
1001impl WriteSmartString for PathComponent<'_> {
1002 fn write_smart(&self, s: &mut SmartString) -> fmt::Result {
1003 match *self.0 {
1004 PathNode::Root => {
1005 s.push_str(PATH_ROOT);
1006 Ok(())
1007 }
1008 PathNode::Array { index, .. } => write!(s, "{index}"),
1009 PathNode::Object { key, .. } => write!(s, "{key}"),
1010 }
1011 }
1012}
1013
1014impl fmt::Display for PathNode<'_> {
1015 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1016 let paths: Vec<_> = PathIter::new(Arc::new(self.clone())).collect();
1017 let mut iter = paths.into_iter().rev();
1018
1019 if f.alternate() {
1020 for path in iter {
1022 match *path {
1023 PathNode::Root => f.write_str("")?,
1024 PathNode::Array { .. } | PathNode::Object { .. } => f.write_str("...|")?,
1025 }
1026 }
1027 } else {
1028 if let Some(path) = iter.next() {
1029 write!(f, "{}", PathComponent(path))?;
1030 }
1031
1032 for path in iter {
1033 write!(f, ".{}", PathComponent(path))?;
1034 }
1035 }
1036 Ok(())
1037 }
1038}
1039
1040impl<'buf> PathNode<'buf> {
1041 pub(crate) fn is_root(&self) -> bool {
1043 matches!(self, PathNode::Root)
1044 }
1045
1046 pub(crate) fn is_array(&self) -> bool {
1048 matches!(self, PathNode::Array { .. })
1049 }
1050
1051 pub(crate) fn as_object_key(&self) -> Option<&RawStr<'buf>> {
1053 match self {
1054 PathNode::Object { key, .. } => Some(key),
1055 PathNode::Root | PathNode::Array { .. } => None,
1056 }
1057 }
1058
1059 pub(crate) fn to_smart_string(&self) -> SmartString {
1060 let mut s = SmartString::new();
1061 self.write_smart(&mut s)
1062 .expect("Unable to write string: out of memory");
1063 s
1064 }
1065}
1066
1067impl WriteSmartString for PathNode<'_> {
1068 fn write_smart(&self, s: &mut SmartString) -> fmt::Result {
1069 let paths: Vec<_> = PathIter::new(Arc::new(self.clone())).collect();
1070 let mut iter = paths.into_iter().rev();
1071
1072 if let Some(path) = iter.next() {
1073 write!(s, "{}", PathComponent(path))?;
1074 }
1075
1076 for path in iter {
1077 write!(s, ".{}", PathComponent(path))?;
1078 }
1079
1080 Ok(())
1081 }
1082}
1083
1084fn match_path_node<F>(path: &PathNode<'_>, s: &str, mut skip: F) -> bool
1090where
1091 F: FnMut(&str) -> bool,
1092{
1093 let mut parts = s.rsplit(PATH_SEPARATOR);
1094 let mut paths_iter = PathIter::new(Arc::new(path.clone()));
1095
1096 loop {
1097 let node_segment = paths_iter.next();
1098 let str_segment = parts.next();
1099
1100 let (node_segment, str_segment) = match (node_segment, str_segment) {
1101 (None, None) => return true,
1103 (None, Some(_)) | (Some(_), None) => return false,
1105 (Some(a), Some(b)) => (a, b),
1107 };
1108
1109 if skip(str_segment) {
1111 continue;
1112 }
1113
1114 let yip = match *node_segment {
1115 PathNode::Root => str_segment == PATH_ROOT,
1116 PathNode::Array { index, .. } => {
1117 let Ok(b) = str_segment.parse::<usize>() else {
1118 return false;
1119 };
1120
1121 index == b
1122 }
1123 PathNode::Object { key, .. } => key.as_raw() == str_segment,
1124 };
1125
1126 if !yip {
1128 return false;
1129 }
1130 }
1131}
1132
1133fn match_path(path: &Path, s: &str) -> bool {
1135 let mut parts = s.split(PATH_SEPARATOR);
1136 let mut paths_iter = path.0.iter();
1137
1138 let Some(str_segment) = parts.next() else {
1139 return false;
1140 };
1141
1142 if str_segment != PATH_ROOT {
1145 return false;
1146 }
1147
1148 loop {
1149 let node_segment = paths_iter.next();
1150 let str_segment = parts.next();
1151
1152 let (node_segment, str_segment) = match (node_segment, str_segment) {
1153 (None, None) => return true,
1155 (None, Some(_)) | (Some(_), None) => return false,
1157 (Some(a), Some(b)) => (a, b),
1159 };
1160
1161 let yip = match node_segment {
1162 PathPiece::Array(index) => {
1163 let Ok(b) = str_segment.parse::<usize>() else {
1164 return false;
1165 };
1166
1167 *index == b
1168 }
1169 PathPiece::Object(key) => key == str_segment,
1170 };
1171
1172 if !yip {
1174 return false;
1175 }
1176 }
1177}
1178
1179impl PartialEq<&str> for PathNode<'_> {
1180 fn eq(&self, other: &&str) -> bool {
1181 match_path_node(self, other, |_| false)
1182 }
1183}
1184
1185impl PartialEq<String> for PathNode<'_> {
1186 fn eq(&self, other: &String) -> bool {
1187 match_path_node(self, other, |_| false)
1188 }
1189}
1190
1191struct PathIter<'buf> {
1193 complete: bool,
1195 path: PathNodeRef<'buf>,
1197}
1198
1199impl<'buf> PathIter<'buf> {
1200 fn new(path: PathNodeRef<'buf>) -> Self {
1202 Self {
1203 complete: false,
1204 path,
1205 }
1206 }
1207}
1208
1209impl<'buf> Iterator for PathIter<'buf> {
1210 type Item = PathNodeRef<'buf>;
1211
1212 fn next(&mut self) -> Option<Self::Item> {
1213 if self.complete {
1214 return None;
1215 }
1216
1217 match &*self.path {
1218 PathNode::Root => {
1219 self.complete = true;
1221 Some(Arc::clone(&self.path))
1222 }
1223 PathNode::Array { parent, .. } | PathNode::Object { parent, .. } => {
1224 let next = Arc::clone(&self.path);
1225 self.path = Arc::clone(parent);
1226 Some(next)
1227 }
1228 }
1229 }
1230}
1231
1232struct DisplayExpectStack<'a>(&'a [schema::Expect]);
1234
1235impl fmt::Display for DisplayExpectStack<'_> {
1236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1237 let mut iter = self.0.iter().rev();
1238 let last = iter.next();
1239
1240 f.write_str("~")?;
1242
1243 for _ in iter {
1244 f.write_str("...~")?;
1245 }
1246
1247 if let Some(exp) = last {
1248 match exp {
1249 schema::Expect::Scalar => f.write_str("~")?,
1250 schema::Expect::Array(element) => match &**element {
1251 schema::Element::Scalar => f.write_str("~")?,
1252 schema::Element::Array(element) => write!(f, "[{element:?}]")?,
1253 schema::Element::Object(fields) => {
1254 write!(f, "[{{{:}}}])", DisplayExpectFields(&**fields))?;
1255 }
1256 },
1257 schema::Expect::Object(fields) => {
1258 write!(f, "{{{:}}}", DisplayExpectFields(&**fields))?;
1259 }
1260 schema::Expect::UnmatchedScalar => write!(f, "unmatched(scalar)")?,
1261 schema::Expect::UnmatchedArray => write!(f, "unmatched(array)")?,
1262 schema::Expect::UnmatchedObject => write!(f, "unmatched(object)")?,
1263 schema::Expect::OutOfSchema => write!(f, "no_schema")?,
1264 }
1265 }
1266
1267 Ok(())
1268 }
1269}
1270
1271struct DisplayExpectFields<'a, V>(&'a BTreeMap<&'a str, V>);
1273
1274impl<V> fmt::Display for DisplayExpectFields<'_, V> {
1275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1276 const MAX_FIELDS: usize = 8;
1277
1278 let mut count = 0;
1279 let mut iter = self.0.keys().peekable();
1280
1281 loop {
1282 if count >= MAX_FIELDS {
1283 f.write_str("...")?;
1284 break;
1285 }
1286
1287 let Some(field) = iter.next() else {
1288 break;
1289 };
1290
1291 count += 1;
1292 write!(f, "{field}")?;
1293
1294 let Some(_) = iter.peek() else {
1295 break;
1296 };
1297
1298 f.write_str(", ")?;
1299 }
1300
1301 Ok(())
1302 }
1303}
1304
1305#[derive(Debug)]
1306struct UnbalancedExpectStack;
1307
1308impl fmt::Display for UnbalancedExpectStack {
1309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1310 f.write_str("unbalanced expectation stack")
1311 }
1312}
1313
1314impl std::error::Error for UnbalancedExpectStack {}
1315
1316pub(crate) fn parse_with_schema<'buf>(
1319 json: &'buf str,
1320 schema: &schema::Element,
1321) -> Result<ParseReport<'buf>, Error> {
1322 let parser = Parser::new(json);
1323 let mut unexpected_fields = vec![];
1324 let mut expectation_stack = vec![schema.to_expectation()];
1326
1327 for event in parser {
1328 match event? {
1329 parser::Event::Open { kind, parent_path } => {
1330 let Some(expectation) = expectation_stack.pop() else {
1333 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1334 .into_partial_error_without_token()
1335 .with_root_path());
1336 };
1337
1338 if tracing::enabled!(Level::DEBUG) {
1339 match kind {
1340 ObjectKind::Array => {
1341 trace!("{parent_path} [ {}", DisplayExpectStack(&expectation_stack));
1342 }
1343 ObjectKind::Object => trace!(
1344 "{parent_path} {{ {}",
1345 DisplayExpectStack(&expectation_stack)
1346 ),
1347 }
1348 }
1349
1350 match expectation {
1351 schema::Expect::Array(elem) => {
1352 if parent_path.is_root() {
1355 let next = match kind {
1356 ObjectKind::Array => schema::Expect::Array(elem),
1357 ObjectKind::Object => schema::Expect::UnmatchedArray,
1358 };
1359
1360 expectation_stack.push(next);
1361 trace!("{}", DisplayExpectStack(&expectation_stack));
1362 continue;
1363 }
1364
1365 if !parent_path.is_array() {
1366 expectation_stack.push(schema::Expect::UnmatchedArray);
1367 trace!("{}", DisplayExpectStack(&expectation_stack));
1368 continue;
1369 }
1370
1371 expectation_stack.push(schema::Expect::Array(Arc::clone(&elem)));
1372 expectation_stack.push(elem.to_expectation());
1374 }
1375 schema::Expect::Object(fields) => {
1376 if parent_path.is_root() {
1379 let next = match kind {
1380 ObjectKind::Array => schema::Expect::UnmatchedObject,
1381 ObjectKind::Object => schema::Expect::Object(fields),
1382 };
1383
1384 expectation_stack.push(next);
1385 trace!("{}", DisplayExpectStack(&expectation_stack));
1386 continue;
1387 }
1388 let Some(key) = parent_path.as_object_key() else {
1389 expectation_stack.push(schema::Expect::UnmatchedObject);
1390 trace!("{}", DisplayExpectStack(&expectation_stack));
1391 continue;
1392 };
1393
1394 let next = if let Some(elem) = fields.get(key.as_raw()) {
1395 open_object(kind, elem.as_ref())
1396 } else {
1397 unexpected_fields.push(parent_path);
1398 schema::Expect::OutOfSchema
1399 };
1400
1401 expectation_stack.push(schema::Expect::Object(fields));
1402 expectation_stack.push(next);
1403 }
1404 schema::Expect::OutOfSchema => {
1405 expectation_stack.push(expectation);
1413 expectation_stack.push(schema::Expect::OutOfSchema);
1414 }
1415 schema::Expect::UnmatchedArray | schema::Expect::UnmatchedObject => {
1416 expectation_stack.push(expectation);
1417 expectation_stack.push(schema::Expect::OutOfSchema);
1418 }
1419 _ => {
1420 expectation_stack.push(expectation);
1421 }
1422 }
1423
1424 trace!("{}", DisplayExpectStack(&expectation_stack));
1425 }
1426 parser::Event::Element { kind, parent_path } => {
1427 let Some(expectation) = expectation_stack.pop() else {
1430 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1431 .into_partial_error_without_token()
1432 .with_root_path());
1433 };
1434
1435 if let ValueKind::Array | ValueKind::Object = kind {
1438 if tracing::enabled!(Level::DEBUG) {
1439 match kind {
1440 ValueKind::Array => {
1441 trace!(
1442 "{parent_path} ] {}",
1443 DisplayExpectStack(&expectation_stack)
1444 );
1445 }
1446 ValueKind::Object => trace!(
1447 "{parent_path} }} {}",
1448 DisplayExpectStack(&expectation_stack)
1449 ),
1450 _ => (),
1451 }
1452 }
1453 continue;
1454 }
1455
1456 match expectation {
1457 #[expect(
1458 clippy::unreachable,
1459 reason = "The parser only emits an `Event::Complete` for a scalar object at the root"
1460 )]
1461 schema::Expect::Object(fields) => match &*parent_path {
1462 PathNode::Root => unreachable!(),
1463 PathNode::Array { .. } => {
1464 expectation_stack.push(schema::Expect::UnmatchedObject);
1465 }
1466 PathNode::Object { parent, key } => {
1467 trace!("{parent:#}.{key}");
1468
1469 if !fields.contains_key(key.as_raw()) {
1470 unexpected_fields.push(parent_path);
1471 }
1472
1473 expectation_stack.push(schema::Expect::Object(fields));
1474 }
1475 },
1476 schema::Expect::OutOfSchema => {
1477 unexpected_fields.push(parent_path);
1478 expectation_stack.push(expectation);
1479 }
1480 _ => {
1481 expectation_stack.push(expectation);
1482 }
1483 }
1484 }
1485 parser::Event::Complete(element) => {
1486 if element.value().is_scalar() {
1487 unexpected_fields.push(element.path_node());
1488 }
1489
1490 return Ok(ParseReport {
1493 element,
1494 unexpected_fields: UnexpectedFields::from_vec(unexpected_fields),
1495 });
1496 }
1497 }
1498 }
1499
1500 Err(ErrorKind::UnexpectedEOF
1501 .into_partial_error_without_token()
1502 .with_root_path())
1503}
1504
1505fn open_object(kind: ObjectKind, elem: Option<&Arc<schema::Element>>) -> schema::Expect {
1506 let Some(schema) = elem else {
1507 return schema::Expect::OutOfSchema;
1508 };
1509
1510 match (kind, &**schema) {
1511 (ObjectKind::Object | ObjectKind::Array, schema::Element::Scalar) => {
1512 schema::Expect::UnmatchedScalar
1513 }
1514 (ObjectKind::Object, schema::Element::Array(_)) => schema::Expect::UnmatchedArray,
1515 (ObjectKind::Object, schema::Element::Object(fields)) => {
1516 schema::Expect::Object(Arc::clone(fields))
1517 }
1518 (ObjectKind::Array, schema::Element::Array(element)) => {
1519 schema::Expect::Array(Arc::clone(element))
1520 }
1521 (ObjectKind::Array, schema::Element::Object(_)) => schema::Expect::UnmatchedObject,
1522 }
1523}
1524
1525#[derive(Debug)]
1528pub(crate) struct ParseReport<'buf> {
1529 pub element: Element<'buf>,
1531
1532 pub unexpected_fields: UnexpectedFields<'buf>,
1534}
1535
1536#[cfg(test)]
1537pub mod test {
1538 #![allow(clippy::missing_panics_doc, reason = "tests are allowed to panic")]
1539 #![allow(clippy::panic, reason = "tests are allowed panic")]
1540
1541 use crate::{json::match_path_node, test::Expectation};
1542
1543 use super::{
1544 parser::Span, walk::DepthFirst, ElemId, Element, Field, FieldsAsExt as _, PathNode,
1545 PathNodeRef, PathRef, RawStr, UnexpectedFields, Value,
1546 };
1547
1548 impl<'buf> Element<'buf> {
1549 pub(crate) fn into_value(self) -> Value<'buf> {
1551 self.value
1552 }
1553
1554 pub(crate) fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1556 let Self {
1557 id,
1558 path_node: path,
1559 span,
1560 value,
1561 } = self;
1562 (id, path, span, value)
1563 }
1564
1565 pub(crate) fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
1566 self.as_object_fields()
1567 .and_then(|fields| fields.find_field(key))
1568 }
1569 }
1570
1571 impl<'buf> Value<'buf> {
1572 pub(crate) fn is_array(&self) -> bool {
1574 matches!(self, Value::Array(_))
1575 }
1576
1577 pub(crate) fn is_object(&self) -> bool {
1579 matches!(self, Value::Object(_))
1580 }
1581
1582 pub(crate) fn as_string(&self) -> Option<&RawStr<'buf>> {
1583 match self {
1584 Value::String(s) => Some(s),
1585 _ => None,
1586 }
1587 }
1588 }
1589
1590 impl<'buf> Field<'buf> {
1591 pub fn id(&self) -> ElemId {
1592 self.0.id()
1593 }
1594
1595 pub fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1596 self.0.into_parts()
1597 }
1598 }
1599
1600 impl<'buf> UnexpectedFields<'buf> {
1601 pub(super) fn into_inner(self) -> Vec<PathNodeRef<'buf>> {
1603 self.0
1604 }
1605
1606 fn filter_matches(&mut self, glob: &PathGlob) {
1608 self.0.retain(|path| !glob.matches(path));
1609 }
1610 }
1611
1612 #[derive(Debug)]
1615 pub(crate) struct PathGlob(String);
1616
1617 impl PathGlob {
1618 pub(crate) fn matches(&self, path: &PathNode<'_>) -> bool {
1620 const WILDCARD: &str = "*";
1621
1622 match_path_node(path, &self.0, |s| {
1623 s == WILDCARD
1625 })
1626 }
1627 }
1628
1629 impl From<usize> for ElemId {
1631 fn from(value: usize) -> Self {
1632 Self(value)
1633 }
1634 }
1635
1636 impl<'a> From<&'a str> for PathGlob {
1637 fn from(s: &'a str) -> Self {
1638 Self(s.into())
1639 }
1640 }
1641
1642 impl From<String> for PathGlob {
1643 fn from(s: String) -> Self {
1644 Self(s)
1645 }
1646 }
1647
1648 impl<'de> serde::Deserialize<'de> for PathGlob {
1649 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1650 where
1651 D: ::serde::Deserializer<'de>,
1652 {
1653 let s = <String as ::serde::Deserialize>::deserialize(deserializer)?;
1654 Ok(Self(s))
1655 }
1656 }
1657
1658 pub struct ElementMap<'a, 'buf>(Vec<&'a Element<'buf>>);
1660
1661 impl<'a, 'buf> ElementMap<'a, 'buf> {
1662 pub fn for_elem(root: &'a Element<'buf>) -> Self {
1664 let walker = DepthFirst::new(root);
1666 Self(walker.collect())
1667 }
1668
1669 pub fn get(&self, id: ElemId) -> &Element<'buf> {
1671 self.0.get(id.0).map(|e| &**e).unwrap()
1672 }
1673
1674 pub fn path(&self, id: ElemId) -> PathRef<'buf> {
1676 self.0.get(id.0).map(|elem| elem.path()).unwrap()
1677 }
1678 }
1679
1680 #[track_caller]
1682 pub(crate) fn expect_no_unexpected_fields(
1683 expect_file_name: &str,
1684 unexpected_fields: &UnexpectedFields<'_>,
1685 ) {
1686 if !unexpected_fields.is_empty() {
1687 const MAX_FIELD_DISPLAY: usize = 20;
1688
1689 if unexpected_fields.len() > MAX_FIELD_DISPLAY {
1690 let truncated_fields = unexpected_fields
1691 .iter()
1692 .take(MAX_FIELD_DISPLAY)
1693 .map(|path| path.to_string())
1694 .collect::<Vec<_>>();
1695
1696 panic!(
1697 "The expect file `{expect_file_name}` didn't expect `{}` unexpected fields;\n\
1698 displaying the first ({}):\n{}\n... and {} more",
1699 unexpected_fields.len(),
1700 truncated_fields.len(),
1701 truncated_fields.join(",\n"),
1702 unexpected_fields.len() - truncated_fields.len(),
1703 )
1704 } else {
1705 panic!(
1706 "The expect file `{expect_file_name}` didn't expect `{}` unexpected fields:\n{}",
1707 unexpected_fields.len(),
1708 unexpected_fields.to_strings().join(",\n")
1709 )
1710 };
1711 }
1712 }
1713
1714 #[track_caller]
1718 pub(crate) fn expect_unexpected_fields(
1719 expect_file_name: &str,
1720 unexpected_fields: &mut UnexpectedFields<'_>,
1721 expected: Expectation<Vec<PathGlob>>,
1722 ) {
1723 if let Expectation::Present(expectation) = expected {
1724 let unexpected_fields_expect = expectation.expect_value();
1725
1726 for expect_glob in unexpected_fields_expect {
1729 unexpected_fields.filter_matches(&expect_glob);
1730 }
1731
1732 expect_no_unexpected_fields(expect_file_name, unexpected_fields);
1733 } else {
1734 expect_no_unexpected_fields(expect_file_name, unexpected_fields);
1735 }
1736 }
1737
1738 #[cfg(test)]
1739 mod test_path_matches_glob {
1740 use std::sync::Arc;
1741
1742 use crate::test;
1743
1744 use super::{PathGlob, PathNode};
1745
1746 #[test]
1747 fn should_match_path() {
1748 test::setup();
1749
1750 let root = Arc::new(PathNode::Root);
1751 let path_a = Arc::new(PathNode::Array {
1752 parent: Arc::clone(&root),
1753 index: 1,
1754 });
1755 let path_b = Arc::new(PathNode::Object {
1756 parent: Arc::clone(&path_a),
1757 key: r#""name""#.into(),
1758 });
1759 let path_c = PathNode::Object {
1760 parent: Arc::clone(&path_b),
1761 key: r#""gene""#.into(),
1762 };
1763
1764 assert!(PathGlob::from("$").matches(&root));
1765 assert!(PathGlob::from("*").matches(&root));
1766
1767 assert!(!PathGlob::from("*").matches(&path_a));
1768 assert!(PathGlob::from("*.*").matches(&path_a));
1769 assert!(PathGlob::from("$.*").matches(&path_a));
1770 assert!(PathGlob::from("$.1").matches(&path_a));
1771
1772 assert!(!PathGlob::from("*").matches(&path_b));
1773 assert!(!PathGlob::from("*.*").matches(&path_b));
1774 assert!(PathGlob::from("*.*.*").matches(&path_b));
1775 assert!(PathGlob::from("$.*.*").matches(&path_b));
1776 assert!(PathGlob::from("$.1.*").matches(&path_b));
1777 assert!(PathGlob::from("$.*.name").matches(&path_b));
1778 assert!(PathGlob::from("$.1.name").matches(&path_b));
1779
1780 assert!(PathGlob::from("$.1.name.gene").matches(&path_c));
1781 }
1782 }
1783}
1784
1785#[cfg(test)]
1786mod test_path {
1787 use super::{Path, PathPiece};
1788
1789 #[test]
1790 fn path_should_cmp_with_str() {
1791 assert_ne!(Path::root(), "");
1792 assert_eq!(Path::root(), "$");
1793 assert_eq!(Path(vec![PathPiece::Object("field_a".into())]), "$.field_a");
1794 assert_eq!(Path(vec![PathPiece::Array(1)]), "$.1");
1795 assert_eq!(
1796 Path(vec![
1797 PathPiece::Object("field_a".into()),
1798 PathPiece::Array(1)
1799 ]),
1800 "$.field_a.1"
1801 );
1802 }
1803
1804 #[test]
1805 fn path_should_display() {
1806 assert_eq!(Path::root().to_string(), "$");
1807 assert_eq!(
1808 Path(vec![PathPiece::Object("field_a".into())]).to_string(),
1809 "$.field_a"
1810 );
1811 assert_eq!(Path(vec![PathPiece::Array(1)]).to_string(), "$.1");
1812 assert_eq!(
1813 Path(vec![
1814 PathPiece::Object("field_a".into()),
1815 PathPiece::Array(1)
1816 ])
1817 .to_string(),
1818 "$.field_a.1"
1819 );
1820 }
1821}
1822
1823#[cfg(test)]
1824mod test_parse_with_schema {
1825 use crate::{json_schema, test};
1826
1827 use super::{parse_with_schema, ParseReport};
1828
1829 #[test]
1830 fn should_report_unexpected_fields_for_root_element() {
1831 const JSON: &str = "null";
1832
1833 test::setup();
1834
1835 let schema = json_schema!({
1836 "id",
1837 "currency",
1838 });
1839
1840 let report = parse_with_schema(JSON, &schema).unwrap();
1841 let ParseReport {
1842 element: _,
1843 unexpected_fields,
1844 } = report;
1845
1846 {
1847 let [field_a] = unexpected_fields.into_inner().try_into().unwrap();
1848 assert_eq!(*field_a, "$");
1849 }
1850 }
1851
1852 #[test]
1853 fn should_report_unexpected_fields_in_flat_object() {
1854 const JSON: &str = r#"{
1855 "id": "tariff_id",
1856 "currency": "EUR",
1857 "name": "Barry",
1858 "address": "Barrystown"
1859}"#;
1860
1861 test::setup();
1862
1863 let schema = json_schema!({
1864 "id",
1865 "currency",
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, "$.name");
1877 assert_eq!(*field_b, "$.address");
1878 }
1879 }
1880
1881 #[test]
1882 fn should_report_unexpected_fields_in_nested_object() {
1883 const JSON: &str = r#"{
1884 "id": "tariff_id",
1885 "currency": "EUR",
1886 "owner": {
1887 "id": "456856",
1888 "subscription_id": "tedi4568",
1889 "name": "Barry",
1890 "address": "Barrystown"
1891 }
1892}"#;
1893
1894 test::setup();
1895
1896 let schema = json_schema!({
1897 "id",
1898 "currency",
1899 "owner": {
1900 "id",
1901 "subscription_id"
1902 }
1903 });
1904
1905 let report = parse_with_schema(JSON, &schema).unwrap();
1906 let ParseReport {
1907 element: _,
1908 unexpected_fields,
1909 } = report;
1910
1911 {
1912 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1913 assert_eq!(*field_a, "$.owner.name");
1914 assert_eq!(*field_b, "$.owner.address");
1915 }
1916 }
1917
1918 #[test]
1919 fn should_parse_nested_object_out_of_schema() {
1920 const JSON: &str = r#"{
1921 "id": "tariff_id",
1922 "owner": {
1923 "id": "456856",
1924 "subscription_id": "tedi4568",
1925 "name": "Barry",
1926 "address": {
1927 "city": "Barrystown",
1928 "street": "Barrysstreet"
1929 }
1930 },
1931 "currency": "EUR",
1932 "country": "NL"
1933}"#;
1934
1935 test::setup();
1936
1937 let schema = json_schema!({
1938 "id",
1939 "currency",
1940 "owner"
1941 });
1942
1943 let report = parse_with_schema(JSON, &schema).unwrap();
1944 let ParseReport {
1945 element: _,
1946 unexpected_fields,
1947 } = report;
1948
1949 {
1950 let [field_a, field_b, field_c, field_d, field_e, field_f] =
1951 unexpected_fields.into_inner().try_into().unwrap();
1952 assert_eq!(*field_a, "$.owner.id");
1953 assert_eq!(*field_b, "$.owner.subscription_id");
1954 assert_eq!(*field_c, "$.owner.name");
1955 assert_eq!(*field_d, "$.owner.address.city");
1956 assert_eq!(*field_e, "$.owner.address.street");
1957 assert_eq!(*field_f, "$.country");
1958 }
1959 }
1960
1961 #[test]
1962 fn should_report_unexpected_fields_in_array_with_nested_object() {
1963 const JSON: &str = r#"{
1964 "id": "tariff_id",
1965 "currency": "EUR",
1966 "elements": [{
1967 "id": "456856",
1968 "subscription_id": "tedi4568",
1969 "name": "Barry",
1970 "address": "Barrystown"
1971 }]
1972}"#;
1973
1974 test::setup();
1975
1976 let schema = json_schema!({
1977 "id",
1978 "currency",
1979 "elements": [{
1980 "id",
1981 "subscription_id"
1982 }]
1983 });
1984
1985 let report = parse_with_schema(JSON, &schema).unwrap();
1986 let ParseReport {
1987 element: _,
1988 unexpected_fields,
1989 } = report;
1990
1991 {
1992 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1993 assert_eq!(*field_a, "$.elements.0.name");
1994 assert_eq!(*field_b, "$.elements.0.address");
1995 }
1996 }
1997
1998 #[test]
1999 fn should_report_unexpected_fields_in_array_of_nested_objects() {
2000 const JSON: &str = r#"{
2001 "id": "tariff_id",
2002 "currency": "EUR",
2003 "elements": [
2004 {
2005 "id": "456856",
2006 "subscription_id": "tedi4568",
2007 "name": "Barry",
2008 "address": "Barrystown"
2009 },
2010 {
2011 "id": "8746we",
2012 "subscription_id": "dfr345",
2013 "name": "Gerry",
2014 "address": "Gerrystown"
2015 }
2016 ]
2017}"#;
2018
2019 test::setup();
2020
2021 let schema = json_schema!({
2022 "id",
2023 "currency",
2024 "elements": [{
2025 "id",
2026 "subscription_id"
2027 }]
2028 });
2029
2030 let report = parse_with_schema(JSON, &schema).unwrap();
2031 let ParseReport {
2032 element: _,
2033 unexpected_fields,
2034 } = report;
2035
2036 {
2037 let [field_a, field_b, field_c, field_d] =
2038 unexpected_fields.into_inner().try_into().unwrap();
2039 assert_eq!(*field_a, "$.elements.0.name");
2040 assert_eq!(*field_b, "$.elements.0.address");
2041 assert_eq!(*field_c, "$.elements.1.name");
2042 assert_eq!(*field_d, "$.elements.1.address");
2043 }
2044 }
2045
2046 #[test]
2047 fn should_report_unexpected_fields_in_array_of_objects() {
2048 const JSON: &str = r#"[
2049 {
2050 "id": "456856",
2051 "subscription_id": "tedi4568",
2052 "name": "Barry",
2053 "address": "Barrystown"
2054 },
2055 {
2056 "id": "8746we",
2057 "subscription_id": "dfr345",
2058 "name": "Gerry",
2059 "address": "Gerrystown"
2060 }
2061]"#;
2062
2063 test::setup();
2064
2065 let schema = json_schema!([
2066 {
2067 "id",
2068 "subscription_id"
2069 }
2070 ]);
2071
2072 let report = parse_with_schema(JSON, &schema).unwrap();
2073 let ParseReport {
2074 element: _,
2075 unexpected_fields,
2076 } = report;
2077
2078 {
2079 let [field_a, field_b, field_c, field_d] =
2080 unexpected_fields.into_inner().try_into().unwrap();
2081 assert_eq!(*field_a, "$.0.name");
2082 assert_eq!(*field_b, "$.0.address");
2083 assert_eq!(*field_c, "$.1.name");
2084 assert_eq!(*field_d, "$.1.address");
2085 }
2086 }
2087}
2088
2089#[cfg(test)]
2090mod test_source_json {
2091 use super::{parse, walk};
2092
2093 #[test]
2094 fn should_resolve_to_source_json() {
2095 const JSON: &str = r#"{
2096 "name": "David Byrne",
2097 "hobbies": ["song writing", "thinking about society"]
2098}"#;
2099
2100 let element = parse(JSON).unwrap();
2101
2102 let mut walk = walk::DepthFirst::new(&element);
2103
2104 let root = walk.next().unwrap();
2105 assert_eq!(root.source_json(JSON), JSON);
2106
2107 let field_name = walk.next().unwrap();
2108 assert_eq!(field_name.source_json(JSON), r#""name": "David Byrne""#);
2109 assert_eq!(field_name.source_json_value(JSON), r#""David Byrne""#);
2110
2111 let field_hobbies = walk.next().unwrap();
2112 assert_eq!(
2113 field_hobbies.source_json(JSON),
2114 r#""hobbies": ["song writing", "thinking about society"]"#
2115 );
2116 assert_eq!(
2117 field_hobbies.source_json_value(JSON),
2118 r#"["song writing", "thinking about society"]"#
2119 );
2120
2121 let hobbies_one = walk.next().unwrap();
2122 assert_eq!(hobbies_one.source_json(JSON), r#""song writing""#);
2123 assert_eq!(hobbies_one.source_json_value(JSON), r#""song writing""#);
2124
2125 let hobbies_two = walk.next().unwrap();
2126 assert_eq!(hobbies_two.source_json(JSON), r#""thinking about society""#);
2127 assert_eq!(
2128 hobbies_two.source_json_value(JSON),
2129 r#""thinking about society""#
2130 );
2131 }
2132}
2133
2134#[cfg(test)]
2135mod test_path_node {
2136 use std::sync::Arc;
2137
2138 use super::{
2139 parser::{RawStr, Token, TokenType},
2140 PathNode, PathNodeRef, Span,
2141 };
2142
2143 #[test]
2144 const fn should_be_send_and_sync() {
2145 const fn f<T: Send + Sync>() {}
2146
2147 f::<PathNode<'_>>();
2148 f::<PathNodeRef<'_>>();
2149 }
2150
2151 #[test]
2152 fn should_display_path() {
2153 let root = Arc::new(PathNode::Root);
2154 let path_a = Arc::new(PathNode::Array {
2155 parent: Arc::clone(&root),
2156 index: 1,
2157 });
2158 let path_b = Arc::new(PathNode::Object {
2159 parent: Arc::clone(&path_a),
2160 key: r#""name""#.into(),
2161 });
2162 let path_c = Arc::new(PathNode::Object {
2163 parent: Arc::clone(&path_b),
2164 key: r#""gene""#.into(),
2165 });
2166
2167 assert_eq!(*root, "$");
2168 assert_eq!(*path_a, "$.1");
2169 assert_eq!(*path_b, "$.1.name");
2170 assert_eq!(*path_c, "$.1.name.gene");
2171 }
2172
2173 impl<'buf> From<&'buf str> for RawStr<'buf> {
2174 #[track_caller]
2175 fn from(s: &'buf str) -> Self {
2176 RawStr::from_quoted_str(
2177 s,
2178 Token {
2179 kind: TokenType::String,
2180 span: Span::default(),
2181 },
2182 )
2183 .unwrap()
2184 }
2185 }
2186}