1#[cfg(test)]
3pub(crate) mod test;
4
5#[cfg(test)]
6mod test_path;
7
8#[cfg(test)]
9mod test_parse_with_schema;
10
11#[cfg(test)]
12mod test_source_json;
13
14#[cfg(test)]
15mod test_path_node;
16
17#[cfg(test)]
18mod test_path_node_matches_str;
19
20#[cfg(test)]
21mod test_path_matches_glob;
22
23pub mod decode;
24pub mod parser;
25pub(crate) mod schema;
26pub(crate) mod walk;
27pub mod write;
28
29use std::{
30 collections::BTreeMap,
31 fmt::{self, Write as _},
32 sync::Arc,
33};
34
35use tracing::{trace, Level};
36
37use crate::{
38 warning::{self, IntoCaveat},
39 Verdict,
40};
41use parser::ErrorKind;
42use parser::{Parser, Span};
43
44#[doc(inline)]
45pub use parser::{line_col, Error, ErrorReport, LineCol};
46pub(crate) use parser::{parse, RawStr};
47
48const PATH_SEPARATOR: char = '.';
49const PATH_ROOT: &str = "$";
50
51#[doc(hidden)]
54#[macro_export]
55macro_rules! required_field_or_bail {
56 ($elem:expr, $fields:expr, $field_name:literal, $warnings:expr) => {
57 match $fields.get($field_name) {
58 Some(field_elem) => field_elem,
59 None => {
60 return $warnings.bail(
61 Warning::FieldRequired {
62 field_name: $field_name.into(),
63 },
64 $elem,
65 );
66 }
67 }
68 };
69}
70
71#[doc(hidden)]
74#[macro_export]
75macro_rules! required_field {
76 ($elem:expr, $fields:expr, $field_name:literal, $warnings:expr) => {{
77 let field = $fields.get($field_name);
78
79 if field.is_none() {
80 $warnings.insert(
81 Warning::FieldRequired {
82 field_name: $field_name.into(),
83 },
84 $elem,
85 );
86 }
87
88 field
89 }};
90}
91
92#[doc(hidden)]
95#[macro_export]
96macro_rules! expect_object_or_bail {
97 ($elem:expr, $warnings:expr) => {
98 match $elem.as_object_fields() {
99 Some(fields) => fields,
100 None => {
101 return $warnings.bail(
102 Warning::FieldInvalidType {
103 expected_type: json::ValueKind::Object,
104 },
105 $elem,
106 );
107 }
108 }
109 };
110}
111
112#[doc(hidden)]
115#[macro_export]
116macro_rules! expect_array_or_bail {
117 ($elem:expr, $warnings:expr) => {
118 match $elem.as_array() {
119 Some(fields) => fields,
120 None => {
121 return $warnings.bail(
122 Warning::FieldInvalidType {
123 expected_type: json::ValueKind::Array,
124 },
125 $elem,
126 );
127 }
128 }
129 };
130}
131
132#[doc(hidden)]
137#[macro_export]
138macro_rules! expect_string_or_bail {
139 ($elem:expr, $warnings:expr) => {{
140 use $crate::warning::GatherWarnings as _;
141
142 match $elem.as_raw_str() {
143 Some(s) => s.decode_escapes($elem).gather_warnings_into(&mut $warnings),
144 None => {
145 return $warnings.bail(
146 Warning::FieldInvalidType {
147 expected_type: json::ValueKind::String,
148 },
149 $elem,
150 );
151 }
152 }
153 }};
154}
155
156#[doc(hidden)]
160#[macro_export]
161macro_rules! parse_required_or_bail {
162 ($elem:expr, $fields:expr, $elem_name:literal, $target:ty, $warnings:expr) => {{
163 #[allow(
164 unused,
165 reason = "the macro uses the import but maybe the outside scope does too."
166 )]
167 use $crate::json::FromJson;
168 use $crate::warning::GatherWarnings as _;
169
170 let elem = $crate::required_field_or_bail!($elem, $fields, $elem_name, $warnings);
171 <$target as FromJson>::from_json(elem)?.gather_warnings_into(&mut $warnings)
172 }};
173}
174
175#[doc(hidden)]
179#[macro_export]
180macro_rules! parse_required {
181 ($elem:expr, $fields:expr, $elem_name:literal, $target:ty, $warnings:expr) => {{
182 #[allow(
183 unused,
184 reason = "the macro uses the import but maybe the outside scope does too."
185 )]
186 use $crate::json::FromJson;
187 use $crate::warning::GatherWarnings as _;
188
189 let elem = $crate::required_field!($elem, $fields, $elem_name, $warnings);
190
191 if let Some(elem) = elem {
192 let value =
193 <$target as FromJson>::from_json(elem)?.gather_warnings_into(&mut $warnings);
194 Some(value)
195 } else {
196 None
197 }
198 }};
199}
200
201#[doc(hidden)]
204#[macro_export]
205macro_rules! parse_nullable_or_bail {
206 ($fields:expr, $elem_name:literal, $target:ty, $warnings:expr) => {{
207 #[allow(
208 unused,
209 reason = "the macro uses the import but maybe the outside scope does too."
210 )]
211 use $crate::json::FromJson as _;
212 use $crate::warning::GatherWarnings as _;
213
214 match $fields.get($elem_name) {
215 Some(elem) => Option::<$target>::from_json(elem)?.gather_warnings_into(&mut $warnings),
216 None => None,
217 }
218 }};
219}
220
221pub(crate) trait FromJson<'elem, 'buf>: Sized {
223 type Warning: crate::Warning;
224
225 fn from_json(elem: &'elem Element<'buf>) -> Verdict<Self, Self::Warning>;
227}
228
229impl<'a, 'b, T> FromJson<'a, 'b> for Option<T>
233where
234 T: FromJson<'a, 'b> + IntoCaveat,
235{
236 type Warning = T::Warning;
237
238 fn from_json(elem: &'a Element<'b>) -> Verdict<Self, Self::Warning> {
239 let value = elem.as_value();
240
241 if value.is_null() {
242 Ok(None.into_caveat(warning::Set::new()))
243 } else {
244 let v = T::from_json(elem)?;
245 Ok(v.map(Some))
246 }
247 }
248}
249
250#[derive(Clone, Debug, Eq, PartialEq)]
254pub struct Element<'buf> {
255 id: ElemId,
257
258 path_node: PathNodeRef<'buf>,
260
261 span: Span,
265
266 value: Value<'buf>,
268}
269
270#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
274pub(crate) struct ElemId(usize);
275
276impl fmt::Display for ElemId {
277 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278 fmt::Display::fmt(&self.0, f)
279 }
280}
281
282impl<'buf> Element<'buf> {
283 fn new(id: ElemId, path: PathNodeRef<'buf>, span: Span, value: Value<'buf>) -> Element<'buf> {
285 Element {
286 id,
287 path_node: path,
288 span,
289 value,
290 }
291 }
292
293 pub(crate) const fn id(&self) -> ElemId {
295 self.id
296 }
297
298 pub fn path(&self) -> PathRef<'buf> {
300 PathRef(self.path_node())
301 }
302
303 pub(crate) fn path_node(&self) -> PathNodeRef<'buf> {
305 Arc::clone(&self.path_node)
306 }
307
308 pub fn span(&self) -> Span {
310 self.span
311 }
312
313 pub fn source_json(&self, source_json: &'buf str) -> SourceStr<'buf> {
327 if let PathNode::Object { key, .. } = *self.path_node {
328 let span = Span {
330 start: key.span().start,
331 end: self.span.end,
333 };
334 let field_str = &source_json
335 .get(span.start..span.end)
336 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR");
337 let field = RawStr::from_str(field_str, span);
338 let (key, value) = field_str
339 .split_once(':')
340 .expect("An objects field always contains a delimiting `:`");
341
342 SourceStr::Field { field, key, value }
343 } else {
344 let span = self.span;
345 let s = source_json
346 .get(span.start..span.end)
347 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR");
348 SourceStr::Value(RawStr::from_str(s, span))
349 }
350 }
351
352 pub fn source_json_value(&self, source_json: &'buf str) -> &'buf str {
365 source_json
366 .get(self.span.start..self.span.end)
367 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR")
368 }
369
370 pub(crate) fn value(&self) -> &Value<'buf> {
372 &self.value
373 }
374
375 pub(crate) fn as_value(&self) -> &Value<'buf> {
377 &self.value
378 }
379
380 pub(crate) fn as_raw_str(&self) -> Option<&RawStr<'buf>> {
382 self.value.as_raw_str()
383 }
384
385 pub(crate) fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
387 self.value.as_object_fields()
388 }
389
390 pub(crate) fn as_array(&self) -> Option<&[Element<'buf>]> {
391 self.value.as_array()
392 }
393
394 pub fn as_number_str(&self) -> Option<&str> {
395 self.value.as_number()
396 }
397}
398
399#[derive(Debug)]
400pub enum SourceStr<'buf> {
401 Value(RawStr<'buf>),
403
404 Field {
406 field: RawStr<'buf>,
408
409 key: &'buf str,
411
412 value: &'buf str,
414 },
415}
416
417impl fmt::Display for SourceStr<'_> {
418 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419 match self {
420 SourceStr::Value(s) => f.write_str(s.as_raw()),
421 SourceStr::Field { field, .. } => f.write_str(field.as_raw()),
422 }
423 }
424}
425
426impl PartialEq<&str> for SourceStr<'_> {
428 fn eq(&self, other: &&str) -> bool {
429 match self {
430 SourceStr::Value(s) => s.as_raw() == *other,
431 SourceStr::Field { field, .. } => field.as_raw() == *other,
432 }
433 }
434}
435
436impl PartialEq<String> for SourceStr<'_> {
438 fn eq(&self, other: &String) -> bool {
439 self.eq(&&**other)
440 }
441}
442
443impl PartialOrd for Element<'_> {
444 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
445 Some(self.cmp(other))
446 }
447}
448
449impl Ord for Element<'_> {
450 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
451 self.path_node.cmp(&other.path_node)
452 }
453}
454
455impl fmt::Display for Element<'_> {
456 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457 write!(f, "{} = {}", self.path_node, self.value)
458 }
459}
460
461#[derive(Clone, Debug, Eq, PartialEq)]
463pub(crate) struct Field<'buf>(Element<'buf>);
464
465impl<'buf> Field<'buf> {
466 #[expect(
467 clippy::unreachable,
468 reason = "A Field is created by the parser when the type is an Object."
469 )]
470 pub(crate) fn key(&self) -> RawStr<'buf> {
471 let PathNode::Object { key, .. } = *self.0.path_node else {
472 unreachable!();
473 };
474
475 key
476 }
477
478 #[expect(dead_code, reason = "pending use in `tariff::lint`")]
480 pub(crate) fn into_element(self) -> Element<'buf> {
481 self.0
482 }
483
484 pub(crate) fn element(&self) -> &Element<'buf> {
486 &self.0
487 }
488}
489
490#[derive(Clone, Debug, Eq, PartialEq)]
492pub(crate) enum Value<'buf> {
493 Null,
495
496 True,
498
499 False,
501
502 String(RawStr<'buf>),
504
505 Number(&'buf str),
510
511 Array(Vec<Element<'buf>>),
516
517 Object(Vec<Field<'buf>>),
523}
524
525impl<'buf> Value<'buf> {
526 pub(crate) fn kind(&self) -> ValueKind {
527 match self {
528 Value::Null => ValueKind::Null,
529 Value::True | Value::False => ValueKind::Bool,
530 Value::String(_) => ValueKind::String,
531 Value::Number(_) => ValueKind::Number,
532 Value::Array(_) => ValueKind::Array,
533 Value::Object(_) => ValueKind::Object,
534 }
535 }
536
537 pub(crate) fn is_null(&self) -> bool {
538 matches!(self, Value::Null)
539 }
540
541 pub(crate) fn is_scalar(&self) -> bool {
543 matches!(
544 self,
545 Value::Null | Value::True | Value::False | Value::String(_) | Value::Number(_)
546 )
547 }
548
549 pub(crate) fn as_array(&self) -> Option<&[Element<'buf>]> {
550 match self {
551 Value::Array(elems) => Some(elems),
552 _ => None,
553 }
554 }
555
556 pub(crate) fn as_number(&self) -> Option<&str> {
557 match self {
558 Value::Number(s) => Some(s),
559 _ => None,
560 }
561 }
562
563 pub(crate) fn as_raw_str(&self) -> Option<&RawStr<'buf>> {
565 match self {
566 Value::String(s) => Some(s),
567 _ => None,
568 }
569 }
570
571 pub(crate) fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
573 match self {
574 Value::Object(fields) => Some(fields),
575 _ => None,
576 }
577 }
578}
579
580impl fmt::Display for Value<'_> {
581 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
582 match self {
583 Self::Null => write!(f, "null"),
584 Self::True => write!(f, "true"),
585 Self::False => write!(f, "false"),
586 Self::String(s) => write!(f, "{s}"),
587 Self::Number(s) => write!(f, "{s}"),
588 Self::Array(..) => f.write_str("[...]"),
589 Self::Object(..) => f.write_str("{...}"),
590 }
591 }
592}
593
594#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
596pub enum ValueKind {
597 Null,
598 Bool,
599 Number,
600 String,
601 Array,
602 Object,
603}
604
605impl fmt::Display for ValueKind {
606 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607 match self {
608 ValueKind::Null => write!(f, "null"),
609 ValueKind::Bool => write!(f, "bool"),
610 ValueKind::Number => write!(f, "number"),
611 ValueKind::String => write!(f, "string"),
612 ValueKind::Array => write!(f, "array"),
613 ValueKind::Object => write!(f, "object"),
614 }
615 }
616}
617
618#[derive(Copy, Clone, Debug, Eq, PartialEq)]
622pub(crate) enum ObjectKind {
623 Object,
624 Array,
625}
626
627pub type RawMap<'buf> = BTreeMap<RawStr<'buf>, Element<'buf>>;
628pub type RawRefMap<'a, 'buf> = BTreeMap<RawStr<'buf>, &'a Element<'buf>>;
629
630#[expect(dead_code, reason = "pending use in `tariff::lint`")]
631pub(crate) trait FieldsIntoExt<'buf> {
632 fn into_map(self) -> RawMap<'buf>;
633}
634
635pub(crate) trait FieldsAsExt<'buf> {
636 fn as_raw_map(&self) -> RawRefMap<'_, 'buf>;
637 fn find_field(&self, key: &str) -> Option<&Field<'buf>>;
638}
639
640impl<'buf> FieldsIntoExt<'buf> for Vec<Field<'buf>> {
641 fn into_map(self) -> RawMap<'buf> {
642 self.into_iter()
643 .map(|field| (field.key(), field.into_element()))
644 .collect()
645 }
646}
647
648impl<'buf> FieldsAsExt<'buf> for Vec<Field<'buf>> {
649 fn as_raw_map(&self) -> RawRefMap<'_, 'buf> {
650 self.iter()
651 .map(|field| (field.key(), field.element()))
652 .collect()
653 }
654
655 fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
656 self.iter().find(|field| field.key().as_raw() == key)
657 }
658}
659
660impl<'buf> FieldsAsExt<'buf> for [Field<'buf>] {
661 fn as_raw_map(&self) -> RawRefMap<'_, 'buf> {
662 self.iter()
663 .map(|field| (field.key(), field.element()))
664 .collect()
665 }
666
667 fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
668 self.iter().find(|field| field.key().as_raw() == key)
669 }
670}
671
672#[derive(Clone, Debug)]
675pub struct UnexpectedFields<'buf>(Vec<PathNodeRef<'buf>>);
676
677impl fmt::Display for UnexpectedFields<'_> {
678 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
679 if f.alternate() {
680 f.write_str("[\n")?;
682 for entry in &self.0 {
683 writeln!(f, "\t\"{entry}\",")?;
684 }
685 f.write_str("]\n")?;
686 } else {
687 f.write_char('[')?;
689 for entry in &self.0 {
690 write!(f, "{entry},")?;
691 }
692 f.write_char(']')?;
693 }
694
695 Ok(())
696 }
697}
698
699impl<'buf> UnexpectedFields<'buf> {
700 pub(crate) fn empty() -> Self {
702 Self(vec![])
703 }
704
705 pub(crate) fn from_vec(v: Vec<PathNodeRef<'buf>>) -> Self {
707 Self(v)
708 }
709
710 pub fn to_strings(&self) -> Vec<String> {
712 self.0.iter().map(ToString::to_string).collect()
713 }
714
715 pub fn into_strings(self) -> Vec<String> {
717 self.0.into_iter().map(|path| path.to_string()).collect()
718 }
719
720 pub fn is_empty(&self) -> bool {
722 self.0.is_empty()
723 }
724
725 pub fn len(&self) -> usize {
727 self.0.len()
728 }
729
730 pub fn iter<'a>(&'a self) -> UnexpectedFieldsIter<'a, 'buf> {
732 UnexpectedFieldsIter(self.0.iter())
733 }
734}
735
736impl<'buf> IntoIterator for UnexpectedFields<'buf> {
737 type Item = PathRef<'buf>;
738
739 type IntoIter = UnexpectedFieldsIntoIter<'buf>;
740
741 fn into_iter(self) -> Self::IntoIter {
742 UnexpectedFieldsIntoIter(self.0.into_iter())
743 }
744}
745
746pub struct UnexpectedFieldsIntoIter<'buf>(std::vec::IntoIter<PathNodeRef<'buf>>);
747
748impl<'buf> Iterator for UnexpectedFieldsIntoIter<'buf> {
749 type Item = PathRef<'buf>;
750
751 fn next(&mut self) -> Option<Self::Item> {
752 let path_node = self.0.next()?;
753
754 Some(PathRef(path_node))
755 }
756}
757
758impl<'a, 'buf> IntoIterator for &'a UnexpectedFields<'buf> {
759 type Item = PathRef<'buf>;
760
761 type IntoIter = UnexpectedFieldsIter<'a, 'buf>;
762
763 fn into_iter(self) -> Self::IntoIter {
764 self.iter()
765 }
766}
767
768pub struct UnexpectedFieldsIter<'a, 'buf>(std::slice::Iter<'a, PathNodeRef<'buf>>);
770
771impl<'buf> Iterator for UnexpectedFieldsIter<'_, 'buf> {
772 type Item = PathRef<'buf>;
773
774 fn next(&mut self) -> Option<Self::Item> {
775 let path_node = self.0.next()?;
776
777 Some(PathRef(Arc::clone(path_node)))
778 }
779}
780
781pub struct PathRef<'buf>(PathNodeRef<'buf>);
789
790impl fmt::Debug for PathRef<'_> {
791 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
792 write!(f, "{self}")
793 }
794}
795
796impl<'buf> PathRef<'buf> {
797 pub fn components(&self) -> PathComponents<'buf> {
799 PathComponents(PathIter::new(Arc::clone(&self.0)))
800 }
801}
802
803pub struct PathComponents<'buf>(PathIter<'buf>);
805
806impl<'buf> Iterator for PathComponents<'buf> {
807 type Item = PathComponent<'buf>;
808
809 fn next(&mut self) -> Option<Self::Item> {
810 let path_node = self.0.next()?;
811 Some(PathComponent(path_node))
812 }
813}
814
815impl PartialEq<&str> for PathRef<'_> {
817 fn eq(&self, other: &&str) -> bool {
818 match_path_node(&self.0, other, |_| false)
819 }
820}
821
822impl PartialEq<String> for PathRef<'_> {
824 fn eq(&self, other: &String) -> bool {
825 match_path_node(&self.0, other, |_| false)
826 }
827}
828
829impl fmt::Display for PathRef<'_> {
830 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
831 fmt::Display::fmt(&self.0, f)
832 }
833}
834
835pub(crate) type PathNodeRef<'buf> = Arc<PathNode<'buf>>;
837
838#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
848pub(crate) enum PathNode<'buf> {
849 #[default]
851 Root,
852 Array {
854 parent: PathNodeRef<'buf>,
855 index: usize,
856 },
857 Object {
859 parent: PathNodeRef<'buf>,
860 key: RawStr<'buf>,
861 },
862}
863
864pub enum PathNodeKind {
867 Root,
869 Array,
871 Object,
873}
874
875#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
884pub struct Path(Vec<PathPiece>);
885
886impl Path {
887 const fn root() -> Self {
889 Self(vec![])
890 }
891
892 fn from_node(path: PathNodeRef<'_>) -> Self {
894 let paths: Vec<_> = PathIter::new(path).collect();
895
896 let pieces = paths
897 .into_iter()
898 .rev()
899 .filter_map(|path_node| match *path_node {
900 PathNode::Root => None,
901 PathNode::Array { index, .. } => Some(PathPiece::Array(index)),
902 PathNode::Object { key, .. } => Some(PathPiece::Object(key.to_string())),
903 })
904 .collect();
905
906 Self(pieces)
907 }
908}
909
910impl PartialEq<&str> for Path {
912 fn eq(&self, other: &&str) -> bool {
913 match_path(self, other)
914 }
915}
916
917impl PartialEq<String> for Path {
919 fn eq(&self, other: &String) -> bool {
920 match_path(self, other)
921 }
922}
923
924impl fmt::Display for Path {
925 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
926 let iter = self.0.iter();
927
928 write!(f, "$")?;
929
930 for path in iter {
931 write!(f, ".{path}")?;
932 }
933
934 Ok(())
935 }
936}
937
938#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
942enum PathPiece {
943 Array(usize),
945 Object(String),
947}
948
949impl fmt::Display for PathPiece {
950 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
951 match self {
952 PathPiece::Array(index) => write!(f, "{index}"),
953 PathPiece::Object(key) => write!(f, "{key}"),
954 }
955 }
956}
957
958pub struct PathComponent<'buf>(PathNodeRef<'buf>);
960
961impl PathComponent<'_> {
962 pub fn kind(&self) -> PathNodeKind {
964 match *self.0 {
965 PathNode::Root => PathNodeKind::Root,
966 PathNode::Array { .. } => PathNodeKind::Array,
967 PathNode::Object { .. } => PathNodeKind::Object,
968 }
969 }
970}
971
972impl fmt::Display for PathComponent<'_> {
973 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
974 match *self.0 {
975 PathNode::Root => f.write_str(PATH_ROOT),
976 PathNode::Array { index, .. } => write!(f, "{index}"),
977 PathNode::Object { key, .. } => write!(f, "{key}"),
978 }
979 }
980}
981
982impl fmt::Display for PathNode<'_> {
983 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
984 let paths: Vec<_> = PathIter::new(Arc::new(self.clone())).collect();
985 let mut iter = paths.into_iter().rev();
986
987 if f.alternate() {
988 for path in iter {
990 match *path {
991 PathNode::Root => f.write_str("")?,
992 PathNode::Array { .. } | PathNode::Object { .. } => f.write_str("...|")?,
993 }
994 }
995 } else {
996 if let Some(path) = iter.next() {
997 write!(f, "{}", PathComponent(path))?;
998 }
999
1000 for path in iter {
1001 write!(f, ".{}", PathComponent(path))?;
1002 }
1003 }
1004 Ok(())
1005 }
1006}
1007
1008impl<'buf> PathNode<'buf> {
1009 pub(crate) fn is_root(&self) -> bool {
1011 matches!(self, PathNode::Root)
1012 }
1013
1014 pub(crate) fn is_array(&self) -> bool {
1016 matches!(self, PathNode::Array { .. })
1017 }
1018
1019 pub(crate) fn as_object_key(&self) -> Option<&RawStr<'buf>> {
1021 match self {
1022 PathNode::Object { key, .. } => Some(key),
1023 PathNode::Root | PathNode::Array { .. } => None,
1024 }
1025 }
1026}
1027
1028fn match_path_node<F>(path: &PathNode<'_>, s: &str, mut skip: F) -> bool
1034where
1035 F: FnMut(&str) -> bool,
1036{
1037 let mut parts = s.rsplit(PATH_SEPARATOR);
1038 let mut paths_iter = PathIter::new(Arc::new(path.clone()));
1039
1040 loop {
1041 let node_segment = paths_iter.next();
1042 let str_segment = parts.next();
1043
1044 let (node_segment, str_segment) = match (node_segment, str_segment) {
1045 (None, None) => return true,
1047 (None, Some(_)) | (Some(_), None) => return false,
1049 (Some(a), Some(b)) => (a, b),
1051 };
1052
1053 if skip(str_segment) {
1055 continue;
1056 }
1057
1058 let yip = match *node_segment {
1059 PathNode::Root => str_segment == PATH_ROOT,
1060 PathNode::Array { index, .. } => {
1061 let Ok(b) = str_segment.parse::<usize>() else {
1062 return false;
1063 };
1064
1065 index == b
1066 }
1067 PathNode::Object { key, .. } => key.as_raw() == str_segment,
1068 };
1069
1070 if !yip {
1072 return false;
1073 }
1074 }
1075}
1076
1077fn match_path(path: &Path, s: &str) -> bool {
1079 let mut parts = s.split(PATH_SEPARATOR);
1080 let mut paths_iter = path.0.iter();
1081
1082 let Some(str_segment) = parts.next() else {
1083 return false;
1084 };
1085
1086 if str_segment != PATH_ROOT {
1089 return false;
1090 }
1091
1092 loop {
1093 let node_segment = paths_iter.next();
1094 let str_segment = parts.next();
1095
1096 let (node_segment, str_segment) = match (node_segment, str_segment) {
1097 (None, None) => return true,
1099 (None, Some(_)) | (Some(_), None) => return false,
1101 (Some(a), Some(b)) => (a, b),
1103 };
1104
1105 let yip = match node_segment {
1106 PathPiece::Array(index) => {
1107 let Ok(b) = str_segment.parse::<usize>() else {
1108 return false;
1109 };
1110
1111 *index == b
1112 }
1113 PathPiece::Object(key) => key == str_segment,
1114 };
1115
1116 if !yip {
1118 return false;
1119 }
1120 }
1121}
1122
1123impl PartialEq<&str> for PathNode<'_> {
1124 fn eq(&self, other: &&str) -> bool {
1125 match_path_node(self, other, |_| false)
1126 }
1127}
1128
1129impl PartialEq<String> for PathNode<'_> {
1130 fn eq(&self, other: &String) -> bool {
1131 match_path_node(self, other, |_| false)
1132 }
1133}
1134
1135struct PathIter<'buf> {
1137 complete: bool,
1139 path: PathNodeRef<'buf>,
1141}
1142
1143impl<'buf> PathIter<'buf> {
1144 fn new(path: PathNodeRef<'buf>) -> Self {
1146 Self {
1147 complete: false,
1148 path,
1149 }
1150 }
1151}
1152
1153impl<'buf> Iterator for PathIter<'buf> {
1154 type Item = PathNodeRef<'buf>;
1155
1156 fn next(&mut self) -> Option<Self::Item> {
1157 if self.complete {
1158 return None;
1159 }
1160
1161 match &*self.path {
1162 PathNode::Root => {
1163 self.complete = true;
1165 Some(Arc::clone(&self.path))
1166 }
1167 PathNode::Array { parent, .. } | PathNode::Object { parent, .. } => {
1168 let next = Arc::clone(&self.path);
1169 self.path = Arc::clone(parent);
1170 Some(next)
1171 }
1172 }
1173 }
1174}
1175
1176struct DisplayExpectStack<'a>(&'a [schema::Expect]);
1178
1179impl fmt::Display for DisplayExpectStack<'_> {
1180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1181 let mut iter = self.0.iter().rev();
1182 let last = iter.next();
1183
1184 f.write_str("~")?;
1186
1187 for _ in iter {
1188 f.write_str("...~")?;
1189 }
1190
1191 if let Some(exp) = last {
1192 match exp {
1193 schema::Expect::Scalar => f.write_str("~")?,
1194 schema::Expect::Array(element) => match &**element {
1195 schema::Element::Scalar => f.write_str("~")?,
1196 schema::Element::Array(element) => write!(f, "[{element:?}]")?,
1197 schema::Element::Object(fields) => {
1198 write!(f, "[{{{:}}}])", DisplayExpectFields(&**fields))?;
1199 }
1200 },
1201 schema::Expect::Object(fields) => {
1202 write!(f, "{{{:}}}", DisplayExpectFields(&**fields))?;
1203 }
1204 schema::Expect::UnmatchedScalar => write!(f, "unmatched(scalar)")?,
1205 schema::Expect::UnmatchedArray => write!(f, "unmatched(array)")?,
1206 schema::Expect::UnmatchedObject => write!(f, "unmatched(object)")?,
1207 schema::Expect::OutOfSchema => write!(f, "no_schema")?,
1208 }
1209 }
1210
1211 Ok(())
1212 }
1213}
1214
1215struct DisplayExpectFields<'a, V>(&'a BTreeMap<&'a str, V>);
1217
1218impl<V> fmt::Display for DisplayExpectFields<'_, V> {
1219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1220 const MAX_FIELDS: usize = 8;
1221
1222 let mut count = 0;
1223 let mut iter = self.0.keys().peekable();
1224
1225 loop {
1226 if count >= MAX_FIELDS {
1227 f.write_str("...")?;
1228 break;
1229 }
1230
1231 let Some(field) = iter.next() else {
1232 break;
1233 };
1234
1235 let Some(n) = count.checked_add(1) else {
1236 break;
1237 };
1238 count = n;
1239
1240 write!(f, "{field}")?;
1241
1242 let Some(_) = iter.peek() else {
1243 break;
1244 };
1245
1246 f.write_str(", ")?;
1247 }
1248
1249 Ok(())
1250 }
1251}
1252
1253#[derive(Debug)]
1254struct UnbalancedExpectStack;
1255
1256impl fmt::Display for UnbalancedExpectStack {
1257 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1258 f.write_str("unbalanced expectation stack")
1259 }
1260}
1261
1262impl std::error::Error for UnbalancedExpectStack {}
1263
1264pub(crate) fn parse_with_schema<'buf>(
1267 json: &'buf str,
1268 schema: &schema::Element,
1269) -> Result<ParseReport<'buf>, Error> {
1270 let parser = Parser::new(json);
1271 let mut unexpected_fields = vec![];
1272 let mut expectation_stack = vec![schema.to_expectation()];
1274
1275 for event in parser {
1276 match event? {
1277 parser::Event::Open { kind, parent_path } => {
1278 let Some(expectation) = expectation_stack.pop() else {
1281 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1282 .into_partial_error_without_token()
1283 .with_root_path());
1284 };
1285
1286 if tracing::enabled!(Level::DEBUG) {
1287 match kind {
1288 ObjectKind::Array => {
1289 trace!("{parent_path} [ {}", DisplayExpectStack(&expectation_stack));
1290 }
1291 ObjectKind::Object => trace!(
1292 "{parent_path} {{ {}",
1293 DisplayExpectStack(&expectation_stack)
1294 ),
1295 }
1296 }
1297
1298 match expectation {
1299 schema::Expect::Array(elem) => {
1300 if parent_path.is_root() {
1303 let next = match kind {
1304 ObjectKind::Array => schema::Expect::Array(elem),
1305 ObjectKind::Object => schema::Expect::UnmatchedArray,
1306 };
1307
1308 expectation_stack.push(next);
1309 trace!("{}", DisplayExpectStack(&expectation_stack));
1310 continue;
1311 }
1312
1313 if !parent_path.is_array() {
1314 expectation_stack.push(schema::Expect::UnmatchedArray);
1315 trace!("{}", DisplayExpectStack(&expectation_stack));
1316 continue;
1317 }
1318
1319 expectation_stack.push(schema::Expect::Array(Arc::clone(&elem)));
1320 expectation_stack.push(elem.to_expectation());
1322 }
1323 schema::Expect::Object(fields) => {
1324 if parent_path.is_root() {
1327 let next = match kind {
1328 ObjectKind::Array => schema::Expect::UnmatchedObject,
1329 ObjectKind::Object => schema::Expect::Object(fields),
1330 };
1331
1332 expectation_stack.push(next);
1333 trace!("{}", DisplayExpectStack(&expectation_stack));
1334 continue;
1335 }
1336 let Some(key) = parent_path.as_object_key() else {
1337 expectation_stack.push(schema::Expect::UnmatchedObject);
1338 trace!("{}", DisplayExpectStack(&expectation_stack));
1339 continue;
1340 };
1341
1342 let next = if let Some(elem) = fields.get(key.as_raw()) {
1343 open_object(kind, elem.as_ref())
1344 } else {
1345 unexpected_fields.push(parent_path);
1346 schema::Expect::OutOfSchema
1347 };
1348
1349 expectation_stack.push(schema::Expect::Object(fields));
1350 expectation_stack.push(next);
1351 }
1352 schema::Expect::OutOfSchema => {
1353 expectation_stack.push(expectation);
1361 expectation_stack.push(schema::Expect::OutOfSchema);
1362 }
1363 schema::Expect::UnmatchedArray | schema::Expect::UnmatchedObject => {
1364 expectation_stack.push(expectation);
1365 expectation_stack.push(schema::Expect::OutOfSchema);
1366 }
1367 _ => {
1368 expectation_stack.push(expectation);
1369 }
1370 }
1371
1372 trace!("{}", DisplayExpectStack(&expectation_stack));
1373 }
1374 parser::Event::Element { kind, parent_path } => {
1375 let Some(expectation) = expectation_stack.pop() else {
1378 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1379 .into_partial_error_without_token()
1380 .with_root_path());
1381 };
1382
1383 if let ValueKind::Array | ValueKind::Object = kind {
1386 if tracing::enabled!(Level::DEBUG) {
1387 match kind {
1388 ValueKind::Array => {
1389 trace!(
1390 "{parent_path} ] {}",
1391 DisplayExpectStack(&expectation_stack)
1392 );
1393 }
1394 ValueKind::Object => trace!(
1395 "{parent_path} }} {}",
1396 DisplayExpectStack(&expectation_stack)
1397 ),
1398 _ => (),
1399 }
1400 }
1401 continue;
1402 }
1403
1404 match expectation {
1405 #[expect(
1406 clippy::unreachable,
1407 reason = "The parser only emits an `Event::Complete` for a scalar object at the root"
1408 )]
1409 schema::Expect::Object(fields) => match &*parent_path {
1410 PathNode::Root => unreachable!(),
1411 PathNode::Array { .. } => {
1412 expectation_stack.push(schema::Expect::UnmatchedObject);
1413 }
1414 PathNode::Object { parent, key } => {
1415 trace!("{parent:#}.{key}");
1416
1417 if !fields.contains_key(key.as_raw()) {
1418 unexpected_fields.push(parent_path);
1419 }
1420
1421 expectation_stack.push(schema::Expect::Object(fields));
1422 }
1423 },
1424 schema::Expect::OutOfSchema => {
1425 unexpected_fields.push(parent_path);
1426 expectation_stack.push(expectation);
1427 }
1428 _ => {
1429 expectation_stack.push(expectation);
1430 }
1431 }
1432 }
1433 parser::Event::Complete(element) => {
1434 if element.value().is_scalar() {
1435 unexpected_fields.push(element.path_node());
1436 }
1437
1438 return Ok(ParseReport {
1441 element,
1442 unexpected_fields: UnexpectedFields::from_vec(unexpected_fields),
1443 });
1444 }
1445 }
1446 }
1447
1448 Err(ErrorKind::UnexpectedEOF
1449 .into_partial_error_without_token()
1450 .with_root_path())
1451}
1452
1453fn open_object(kind: ObjectKind, elem: Option<&Arc<schema::Element>>) -> schema::Expect {
1454 let Some(schema) = elem else {
1455 return schema::Expect::OutOfSchema;
1456 };
1457
1458 match (kind, &**schema) {
1459 (ObjectKind::Object | ObjectKind::Array, schema::Element::Scalar) => {
1460 schema::Expect::UnmatchedScalar
1461 }
1462 (ObjectKind::Object, schema::Element::Array(_)) => schema::Expect::UnmatchedArray,
1463 (ObjectKind::Object, schema::Element::Object(fields)) => {
1464 schema::Expect::Object(Arc::clone(fields))
1465 }
1466 (ObjectKind::Array, schema::Element::Array(element)) => {
1467 schema::Expect::Array(Arc::clone(element))
1468 }
1469 (ObjectKind::Array, schema::Element::Object(_)) => schema::Expect::UnmatchedObject,
1470 }
1471}
1472
1473#[derive(Debug)]
1476pub(crate) struct ParseReport<'buf> {
1477 pub element: Element<'buf>,
1479
1480 pub unexpected_fields: UnexpectedFields<'buf>,
1482}