1pub mod decode;
3pub mod parser;
4pub(crate) mod schema;
5pub(crate) mod walk;
6pub mod write;
7
8#[cfg(test)]
9mod test_parser;
10
11use std::{
12 collections::BTreeMap,
13 fmt::{self, Write as _},
14 rc::Rc,
15 sync::Arc,
16};
17
18use tracing::{trace, Level};
19
20use crate::{warning, Verdict};
21use parser::ErrorKind;
22use parser::{Parser, Span};
23
24#[doc(inline)]
25pub use parser::{line_col, Error, ErrorReport, LineCol};
26pub(crate) use parser::{parse, RawStr};
27
28const PATH_SEPARATOR: char = '.';
29const PATH_ROOT: &str = "$";
30
31pub(crate) trait FromJson<'elem, 'buf>: Sized {
33 type WarningKind: warning::Kind;
34
35 fn from_json(elem: &'elem Element<'buf>) -> Verdict<Self, Self::WarningKind>;
37}
38
39#[derive(Debug, Eq, PartialEq)]
43pub struct Element<'buf> {
44 id: ElemId,
46
47 path_node: PathNodeRef<'buf>,
49
50 span: Span,
54
55 value: Value<'buf>,
57}
58
59#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
63pub struct ElemId(usize);
64
65impl fmt::Display for ElemId {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 fmt::Display::fmt(&self.0, f)
68 }
69}
70
71impl<'buf> Element<'buf> {
72 fn new(id: ElemId, path: PathNodeRef<'buf>, span: Span, value: Value<'buf>) -> Element<'buf> {
74 Element {
75 id,
76 path_node: path,
77 span,
78 value,
79 }
80 }
81
82 pub(crate) const fn id(&self) -> ElemId {
84 self.id
85 }
86
87 pub fn path(&self) -> PathRef<'buf> {
89 PathRef(self.path_node())
90 }
91
92 pub(crate) fn path_node(&self) -> PathNodeRef<'buf> {
94 Rc::clone(&self.path_node)
95 }
96
97 pub fn source_json(&self, source_json: &'buf str) -> SourceStr<'buf> {
111 if let PathNode::Object { key, .. } = *self.path_node {
112 let span = Span {
114 start: key.span().start,
115 end: self.span.end,
117 };
118 let field_str = &source_json
119 .get(span.start..span.end)
120 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR");
121 let field = RawStr::from_str(field_str, span);
122 let (key, value) = field_str
123 .split_once(':')
124 .expect("An objects field always contains a delimiting `:`");
125
126 SourceStr::Field { field, key, value }
127 } else {
128 let span = self.span;
129 let s = source_json
130 .get(span.start..span.end)
131 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR");
132 SourceStr::Value(RawStr::from_str(s, span))
133 }
134 }
135
136 pub fn source_json_value(&self, source_json: &'buf str) -> &'buf str {
149 source_json
150 .get(self.span.start..self.span.end)
151 .expect("The disconnection between the source JSON and the `Element` will be fixed in a future PR")
152 }
153
154 pub(crate) fn value(&self) -> &Value<'buf> {
156 &self.value
157 }
158
159 pub(crate) fn as_value(&self) -> &Value<'buf> {
161 &self.value
162 }
163
164 pub(crate) fn as_raw_str(&self) -> Option<&RawStr<'buf>> {
166 self.value.as_raw_str()
167 }
168
169 pub(crate) fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
171 self.value.as_object_fields()
172 }
173
174 pub(crate) fn as_array(&self) -> Option<&[Element<'buf>]> {
175 self.value.as_array()
176 }
177
178 pub fn as_number_str(&self) -> Option<&str> {
179 self.value.as_number()
180 }
181}
182
183#[derive(Debug)]
184pub enum SourceStr<'buf> {
185 Value(RawStr<'buf>),
187
188 Field {
190 field: RawStr<'buf>,
192
193 key: &'buf str,
195
196 value: &'buf str,
198 },
199}
200
201impl fmt::Display for SourceStr<'_> {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 match self {
204 SourceStr::Value(s) => f.write_str(s.as_raw()),
205 SourceStr::Field { field, .. } => f.write_str(field.as_raw()),
206 }
207 }
208}
209
210impl PartialEq<&str> for SourceStr<'_> {
212 fn eq(&self, other: &&str) -> bool {
213 match self {
214 SourceStr::Value(s) => s.as_raw() == *other,
215 SourceStr::Field { field, .. } => field.as_raw() == *other,
216 }
217 }
218}
219
220impl PartialEq<String> for SourceStr<'_> {
222 fn eq(&self, other: &String) -> bool {
223 self.eq(&&**other)
224 }
225}
226
227impl PartialOrd for Element<'_> {
228 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
229 Some(self.cmp(other))
230 }
231}
232
233impl Ord for Element<'_> {
234 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
235 self.path_node.cmp(&other.path_node)
236 }
237}
238
239impl fmt::Display for Element<'_> {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 write!(f, "{} = {}", self.path_node, self.value)
242 }
243}
244
245#[derive(Debug, Eq, PartialEq)]
247pub(crate) struct Field<'buf>(Element<'buf>);
248
249impl<'buf> Field<'buf> {
250 #[expect(
251 clippy::unreachable,
252 reason = "A Field is created by the parser when the type is an Object."
253 )]
254 pub(crate) fn key(&self) -> RawStr<'buf> {
255 let PathNode::Object { key, .. } = *self.0.path_node else {
256 unreachable!();
257 };
258
259 key
260 }
261
262 #[expect(dead_code, reason = "pending use in `tariff::lint`")]
264 pub(crate) fn into_element(self) -> Element<'buf> {
265 self.0
266 }
267
268 pub(crate) fn element(&self) -> &Element<'buf> {
270 &self.0
271 }
272}
273
274#[derive(Debug, Eq, PartialEq)]
276pub(crate) enum Value<'buf> {
277 Null,
279
280 True,
282
283 False,
285
286 String(RawStr<'buf>),
288
289 Number(&'buf str),
294
295 Array(Vec<Element<'buf>>),
300
301 Object(Vec<Field<'buf>>),
307}
308
309impl<'buf> Value<'buf> {
310 pub(crate) fn kind(&self) -> ValueKind {
311 match self {
312 Value::Null => ValueKind::Null,
313 Value::True | Value::False => ValueKind::Bool,
314 Value::String(_) => ValueKind::String,
315 Value::Number(_) => ValueKind::Number,
316 Value::Array(_) => ValueKind::Array,
317 Value::Object(_) => ValueKind::Object,
318 }
319 }
320
321 pub(crate) fn is_scalar(&self) -> bool {
323 matches!(
324 self,
325 Value::Null | Value::True | Value::False | Value::String(_) | Value::Number(_)
326 )
327 }
328
329 pub(crate) fn as_array(&self) -> Option<&[Element<'buf>]> {
330 match self {
331 Value::Array(elems) => Some(elems),
332 _ => None,
333 }
334 }
335
336 pub(crate) fn as_number(&self) -> Option<&str> {
337 match self {
338 Value::Number(s) => Some(s),
339 _ => None,
340 }
341 }
342
343 pub(crate) fn as_raw_str(&self) -> Option<&RawStr<'buf>> {
345 match self {
346 Value::String(s) => Some(s),
347 _ => None,
348 }
349 }
350
351 pub(crate) fn as_object_fields(&self) -> Option<&[Field<'buf>]> {
353 match self {
354 Value::Object(fields) => Some(fields),
355 _ => None,
356 }
357 }
358}
359
360impl fmt::Display for Value<'_> {
361 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362 match self {
363 Self::Null => write!(f, "null"),
364 Self::True => write!(f, "true"),
365 Self::False => write!(f, "false"),
366 Self::String(s) => write!(f, "{s}"),
367 Self::Number(s) => write!(f, "{s}"),
368 Self::Array(..) => f.write_str("[...]"),
369 Self::Object(..) => f.write_str("{...}"),
370 }
371 }
372}
373
374#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
376pub enum ValueKind {
377 Null,
378 Bool,
379 Number,
380 String,
381 Array,
382 Object,
383}
384
385impl fmt::Display for ValueKind {
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387 match self {
388 ValueKind::Null => write!(f, "null"),
389 ValueKind::Bool => write!(f, "bool"),
390 ValueKind::Number => write!(f, "number"),
391 ValueKind::String => write!(f, "string"),
392 ValueKind::Array => write!(f, "array"),
393 ValueKind::Object => write!(f, "object"),
394 }
395 }
396}
397
398#[derive(Copy, Clone, Debug, Eq, PartialEq)]
402pub(crate) enum ObjectKind {
403 Object,
404 Array,
405}
406
407type RawMap<'buf> = BTreeMap<RawStr<'buf>, Element<'buf>>;
408type RawRefMap<'a, 'buf> = BTreeMap<RawStr<'buf>, &'a Element<'buf>>;
409
410#[expect(dead_code, reason = "pending use in `tariff::lint`")]
411pub(crate) trait FieldsIntoExt<'buf> {
412 fn into_map(self) -> RawMap<'buf>;
413}
414
415pub(crate) trait FieldsAsExt<'buf> {
416 fn as_raw_map(&self) -> RawRefMap<'_, 'buf>;
417 fn find_field(&self, key: &str) -> Option<&Field<'buf>>;
418}
419
420impl<'buf> FieldsIntoExt<'buf> for Vec<Field<'buf>> {
421 fn into_map(self) -> RawMap<'buf> {
422 self.into_iter()
423 .map(|field| (field.key(), field.into_element()))
424 .collect()
425 }
426}
427
428impl<'buf> FieldsAsExt<'buf> for Vec<Field<'buf>> {
429 fn as_raw_map(&self) -> RawRefMap<'_, 'buf> {
430 self.iter()
431 .map(|field| (field.key(), field.element()))
432 .collect()
433 }
434
435 fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
436 self.iter().find(|field| field.key().as_raw() == key)
437 }
438}
439
440impl<'buf> FieldsAsExt<'buf> for [Field<'buf>] {
441 fn as_raw_map(&self) -> RawRefMap<'_, 'buf> {
442 self.iter()
443 .map(|field| (field.key(), field.element()))
444 .collect()
445 }
446
447 fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
448 self.iter().find(|field| field.key().as_raw() == key)
449 }
450}
451
452pub(crate) type RawValue = serde_json::value::RawValue;
465
466#[derive(Clone, Debug)]
469pub struct UnexpectedFields<'buf>(Vec<PathNodeRef<'buf>>);
470
471impl fmt::Display for UnexpectedFields<'_> {
472 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473 if f.alternate() {
474 f.write_str("[\n")?;
476 for entry in &self.0 {
477 writeln!(f, "\t\"{entry}\",")?;
478 }
479 f.write_str("]\n")?;
480 } else {
481 f.write_char('[')?;
483 for entry in &self.0 {
484 write!(f, "{entry},")?;
485 }
486 f.write_char(']')?;
487 }
488
489 Ok(())
490 }
491}
492
493impl<'buf> UnexpectedFields<'buf> {
494 pub(crate) fn empty() -> Self {
496 Self(vec![])
497 }
498
499 pub(crate) fn from_vec(v: Vec<PathNodeRef<'buf>>) -> Self {
501 Self(v)
502 }
503
504 pub fn to_strings(&self) -> Vec<String> {
506 self.0.iter().map(ToString::to_string).collect()
507 }
508
509 pub fn into_strings(self) -> Vec<String> {
511 self.0.into_iter().map(|path| path.to_string()).collect()
512 }
513
514 pub fn is_empty(&self) -> bool {
516 self.0.is_empty()
517 }
518
519 pub fn len(&self) -> usize {
521 self.0.len()
522 }
523
524 pub fn iter<'a>(&'a self) -> UnexpectedFieldsIter<'a, 'buf> {
526 UnexpectedFieldsIter(self.0.iter())
527 }
528}
529
530impl<'buf> IntoIterator for UnexpectedFields<'buf> {
531 type Item = PathRef<'buf>;
532
533 type IntoIter = UnexpectedFieldsIntoIter<'buf>;
534
535 fn into_iter(self) -> Self::IntoIter {
536 UnexpectedFieldsIntoIter(self.0.into_iter())
537 }
538}
539
540pub struct UnexpectedFieldsIntoIter<'buf>(std::vec::IntoIter<PathNodeRef<'buf>>);
541
542impl<'buf> Iterator for UnexpectedFieldsIntoIter<'buf> {
543 type Item = PathRef<'buf>;
544
545 fn next(&mut self) -> Option<Self::Item> {
546 let path_node = self.0.next()?;
547
548 Some(PathRef(path_node))
549 }
550}
551
552impl<'a, 'buf> IntoIterator for &'a UnexpectedFields<'buf> {
553 type Item = PathRef<'buf>;
554
555 type IntoIter = UnexpectedFieldsIter<'a, 'buf>;
556
557 fn into_iter(self) -> Self::IntoIter {
558 self.iter()
559 }
560}
561
562pub struct UnexpectedFieldsIter<'a, 'buf>(std::slice::Iter<'a, PathNodeRef<'buf>>);
564
565impl<'buf> Iterator for UnexpectedFieldsIter<'_, 'buf> {
566 type Item = PathRef<'buf>;
567
568 fn next(&mut self) -> Option<Self::Item> {
569 let path_node = self.0.next()?;
570
571 Some(PathRef(Rc::clone(path_node)))
572 }
573}
574
575pub struct PathRef<'buf>(PathNodeRef<'buf>);
583
584impl fmt::Debug for PathRef<'_> {
585 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
586 write!(f, "{self}")
587 }
588}
589
590impl<'buf> PathRef<'buf> {
591 pub fn components(&self) -> PathComponents<'buf> {
593 PathComponents(PathIter::new(Rc::clone(&self.0)))
594 }
595}
596
597pub struct PathComponents<'buf>(PathIter<'buf>);
599
600impl<'buf> Iterator for PathComponents<'buf> {
601 type Item = PathComponent<'buf>;
602
603 fn next(&mut self) -> Option<Self::Item> {
604 let path_node = self.0.next()?;
605 Some(PathComponent(path_node))
606 }
607}
608
609impl PartialEq<&str> for PathRef<'_> {
611 fn eq(&self, other: &&str) -> bool {
612 match_path_node(&self.0, other, |_| false)
613 }
614}
615
616impl PartialEq<String> for PathRef<'_> {
618 fn eq(&self, other: &String) -> bool {
619 match_path_node(&self.0, other, |_| false)
620 }
621}
622
623impl fmt::Display for PathRef<'_> {
624 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
625 fmt::Display::fmt(&self.0, f)
626 }
627}
628
629#[cfg(test)]
630mod test_path_node_matches_str {
631 use std::rc::Rc;
632
633 use crate::test;
634
635 use super::PathNode;
636
637 #[test]
638 fn should_match_path() {
639 test::setup();
640
641 let root = Rc::new(PathNode::Root);
642 let path_a = Rc::new(PathNode::Array {
643 parent: Rc::clone(&root),
644 index: 1,
645 });
646 let path_b = Rc::new(PathNode::Object {
647 parent: Rc::clone(&path_a),
648 key: r#""name""#.into(),
649 });
650 let path_c = PathNode::Object {
651 parent: Rc::clone(&path_b),
652 key: r#""gene""#.into(),
653 };
654
655 assert_eq!(*root, "$");
656 assert_eq!(*path_a, "$.1");
657 assert_eq!(*path_b, "$.1.name");
658 assert_eq!(path_c, "$.1.name.gene");
659 }
660}
661
662pub(crate) type PathNodeRef<'buf> = Rc<PathNode<'buf>>;
664
665#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
675pub(crate) enum PathNode<'buf> {
676 #[default]
678 Root,
679 Array {
681 parent: PathNodeRef<'buf>,
682 index: usize,
683 },
684 Object {
686 parent: PathNodeRef<'buf>,
687 key: RawStr<'buf>,
688 },
689}
690
691pub enum PathNodeKind {
694 Root,
696 Array,
698 Object,
700}
701
702#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
711pub struct Path(Vec<PathPiece>);
712
713impl Path {
714 const fn root() -> Self {
716 Self(vec![])
717 }
718
719 fn from_node(path: PathNodeRef<'_>) -> Self {
721 let paths: Vec<_> = PathIter::new(path).collect();
722
723 let pieces = paths
724 .into_iter()
725 .rev()
726 .filter_map(|path_node| match *path_node {
727 PathNode::Root => None,
728 PathNode::Array { index, .. } => Some(PathPiece::Array(index)),
729 PathNode::Object { key, .. } => Some(PathPiece::Object(key.to_string())),
730 })
731 .collect();
732
733 Self(pieces)
734 }
735}
736
737impl PartialEq<&str> for Path {
739 fn eq(&self, other: &&str) -> bool {
740 match_path(self, other)
741 }
742}
743
744impl PartialEq<String> for Path {
746 fn eq(&self, other: &String) -> bool {
747 match_path(self, other)
748 }
749}
750
751impl fmt::Display for Path {
752 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
753 let iter = self.0.iter();
754
755 write!(f, "$")?;
756
757 for path in iter {
758 write!(f, ".{path}")?;
759 }
760
761 Ok(())
762 }
763}
764
765#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
769enum PathPiece {
770 Array(usize),
772 Object(String),
774}
775
776impl fmt::Display for PathPiece {
777 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
778 match self {
779 PathPiece::Array(index) => write!(f, "{index}"),
780 PathPiece::Object(key) => write!(f, "{key}"),
781 }
782 }
783}
784
785pub struct PathComponent<'buf>(PathNodeRef<'buf>);
787
788impl PathComponent<'_> {
789 pub fn kind(&self) -> PathNodeKind {
791 match *self.0 {
792 PathNode::Root => PathNodeKind::Root,
793 PathNode::Array { .. } => PathNodeKind::Array,
794 PathNode::Object { .. } => PathNodeKind::Object,
795 }
796 }
797}
798
799impl fmt::Display for PathComponent<'_> {
800 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
801 match *self.0 {
802 PathNode::Root => f.write_str(PATH_ROOT),
803 PathNode::Array { index, .. } => write!(f, "{index}"),
804 PathNode::Object { key, .. } => write!(f, "{key}"),
805 }
806 }
807}
808
809impl fmt::Display for PathNode<'_> {
810 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
811 let paths: Vec<_> = PathIter::new(Rc::new(self.clone())).collect();
812 let mut iter = paths.into_iter().rev();
813
814 if f.alternate() {
815 for path in iter {
817 match *path {
818 PathNode::Root => f.write_str("")?,
819 PathNode::Array { .. } | PathNode::Object { .. } => f.write_str("...|")?,
820 }
821 }
822 } else {
823 if let Some(path) = iter.next() {
824 write!(f, "{}", PathComponent(path))?;
825 }
826
827 for path in iter {
828 write!(f, ".{}", PathComponent(path))?;
829 }
830 }
831 Ok(())
832 }
833}
834
835impl<'buf> PathNode<'buf> {
836 pub(crate) fn is_root(&self) -> bool {
838 matches!(self, PathNode::Root)
839 }
840
841 pub(crate) fn is_array(&self) -> bool {
843 matches!(self, PathNode::Array { .. })
844 }
845
846 pub(crate) fn as_object_key(&self) -> Option<&RawStr<'buf>> {
848 match self {
849 PathNode::Object { key, .. } => Some(key),
850 PathNode::Root | PathNode::Array { .. } => None,
851 }
852 }
853}
854
855fn match_path_node<F>(path: &PathNode<'_>, s: &str, mut skip: F) -> bool
861where
862 F: FnMut(&str) -> bool,
863{
864 let mut parts = s.rsplit(PATH_SEPARATOR);
865 let mut paths_iter = PathIter::new(Rc::new(path.clone()));
866
867 loop {
868 let node_segment = paths_iter.next();
869 let str_segment = parts.next();
870
871 let (node_segment, str_segment) = match (node_segment, str_segment) {
872 (None, None) => return true,
874 (None, Some(_)) | (Some(_), None) => return false,
876 (Some(a), Some(b)) => (a, b),
878 };
879
880 if skip(str_segment) {
882 continue;
883 }
884
885 let yip = match *node_segment {
886 PathNode::Root => str_segment == PATH_ROOT,
887 PathNode::Array { index, .. } => {
888 let Ok(b) = str_segment.parse::<usize>() else {
889 return false;
890 };
891
892 index == b
893 }
894 PathNode::Object { key, .. } => key.as_raw() == str_segment,
895 };
896
897 if !yip {
899 return false;
900 }
901 }
902}
903
904fn match_path(path: &Path, s: &str) -> bool {
906 let mut parts = s.split(PATH_SEPARATOR);
907 let mut paths_iter = path.0.iter();
908
909 let Some(str_segment) = parts.next() else {
910 return false;
911 };
912
913 if str_segment != PATH_ROOT {
916 return false;
917 }
918
919 loop {
920 let node_segment = paths_iter.next();
921 let str_segment = parts.next();
922
923 let (node_segment, str_segment) = match (node_segment, str_segment) {
924 (None, None) => return true,
926 (None, Some(_)) | (Some(_), None) => return false,
928 (Some(a), Some(b)) => (a, b),
930 };
931
932 let yip = match node_segment {
933 PathPiece::Array(index) => {
934 let Ok(b) = str_segment.parse::<usize>() else {
935 return false;
936 };
937
938 *index == b
939 }
940 PathPiece::Object(key) => key == str_segment,
941 };
942
943 if !yip {
945 return false;
946 }
947 }
948}
949
950impl PartialEq<&str> for PathNode<'_> {
951 fn eq(&self, other: &&str) -> bool {
952 match_path_node(self, other, |_| false)
953 }
954}
955
956impl PartialEq<String> for PathNode<'_> {
957 fn eq(&self, other: &String) -> bool {
958 match_path_node(self, other, |_| false)
959 }
960}
961
962struct PathIter<'buf> {
964 complete: bool,
966 path: PathNodeRef<'buf>,
968}
969
970impl<'buf> PathIter<'buf> {
971 fn new(path: PathNodeRef<'buf>) -> Self {
973 Self {
974 complete: false,
975 path,
976 }
977 }
978}
979
980impl<'buf> Iterator for PathIter<'buf> {
981 type Item = PathNodeRef<'buf>;
982
983 fn next(&mut self) -> Option<Self::Item> {
984 if self.complete {
985 return None;
986 }
987
988 match &*self.path {
989 PathNode::Root => {
990 self.complete = true;
992 Some(Rc::clone(&self.path))
993 }
994 PathNode::Array { parent, .. } | PathNode::Object { parent, .. } => {
995 let next = Rc::clone(&self.path);
996 self.path = Rc::clone(parent);
997 Some(next)
998 }
999 }
1000 }
1001}
1002
1003struct DisplayExpectStack<'a>(&'a [schema::Expect]);
1005
1006impl fmt::Display for DisplayExpectStack<'_> {
1007 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1008 let mut iter = self.0.iter().rev();
1009 let last = iter.next();
1010
1011 f.write_str("~")?;
1013
1014 for _ in iter {
1015 f.write_str("...~")?;
1016 }
1017
1018 if let Some(exp) = last {
1019 match exp {
1020 schema::Expect::Scalar => f.write_str("~")?,
1021 schema::Expect::Array(element) => match &**element {
1022 schema::Element::Scalar => f.write_str("~")?,
1023 schema::Element::Array(element) => write!(f, "[{element:?}]")?,
1024 schema::Element::Object(fields) => {
1025 write!(f, "[{{{:}}}])", DisplayExpectFields(&**fields))?;
1026 }
1027 },
1028 schema::Expect::Object(fields) => {
1029 write!(f, "{{{:}}}", DisplayExpectFields(&**fields))?;
1030 }
1031 schema::Expect::UnmatchedScalar => write!(f, "unmatched(scalar)")?,
1032 schema::Expect::UnmatchedArray => write!(f, "unmatched(array)")?,
1033 schema::Expect::UnmatchedObject => write!(f, "unmatched(object)")?,
1034 schema::Expect::OutOfSchema => write!(f, "no_schema")?,
1035 }
1036 }
1037
1038 Ok(())
1039 }
1040}
1041
1042struct DisplayExpectFields<'a, V>(&'a BTreeMap<&'a str, V>);
1044
1045impl<V> fmt::Display for DisplayExpectFields<'_, V> {
1046 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1047 const MAX_FIELDS: usize = 8;
1048
1049 let mut count = 0;
1050 let mut iter = self.0.keys().peekable();
1051
1052 loop {
1053 if count >= MAX_FIELDS {
1054 f.write_str("...")?;
1055 break;
1056 }
1057
1058 let Some(field) = iter.next() else {
1059 break;
1060 };
1061
1062 count += 1;
1063 write!(f, "{field}")?;
1064
1065 let Some(_) = iter.peek() else {
1066 break;
1067 };
1068
1069 f.write_str(", ")?;
1070 }
1071
1072 Ok(())
1073 }
1074}
1075
1076#[derive(Debug)]
1077struct UnbalancedExpectStack;
1078
1079impl fmt::Display for UnbalancedExpectStack {
1080 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1081 f.write_str("unbalanced expectation stack")
1082 }
1083}
1084
1085impl std::error::Error for UnbalancedExpectStack {}
1086
1087pub(crate) fn parse_with_schema<'buf>(
1090 json: &'buf str,
1091 schema: &schema::Element,
1092) -> Result<ParseReport<'buf>, Error> {
1093 let parser = Parser::new(json);
1094 let mut unexpected_fields = vec![];
1095 let mut expectation_stack = vec![schema.to_expectation()];
1097
1098 for event in parser {
1099 match event? {
1100 parser::Event::Open { kind, parent_path } => {
1101 let Some(expectation) = expectation_stack.pop() else {
1104 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1105 .into_partial_error_without_token()
1106 .with_root_path());
1107 };
1108
1109 if tracing::enabled!(Level::DEBUG) {
1110 match kind {
1111 ObjectKind::Array => {
1112 trace!("{parent_path} [ {}", DisplayExpectStack(&expectation_stack));
1113 }
1114 ObjectKind::Object => trace!(
1115 "{parent_path} {{ {}",
1116 DisplayExpectStack(&expectation_stack)
1117 ),
1118 }
1119 }
1120
1121 match expectation {
1122 schema::Expect::Array(elem) => {
1123 if parent_path.is_root() {
1126 let next = match kind {
1127 ObjectKind::Array => schema::Expect::Array(elem),
1128 ObjectKind::Object => schema::Expect::UnmatchedArray,
1129 };
1130
1131 expectation_stack.push(next);
1132 trace!("{}", DisplayExpectStack(&expectation_stack));
1133 continue;
1134 }
1135
1136 if !parent_path.is_array() {
1137 expectation_stack.push(schema::Expect::UnmatchedArray);
1138 trace!("{}", DisplayExpectStack(&expectation_stack));
1139 continue;
1140 }
1141
1142 expectation_stack.push(schema::Expect::Array(Arc::clone(&elem)));
1143 expectation_stack.push(elem.to_expectation());
1145 }
1146 schema::Expect::Object(fields) => {
1147 if parent_path.is_root() {
1150 let next = match kind {
1151 ObjectKind::Array => schema::Expect::UnmatchedObject,
1152 ObjectKind::Object => schema::Expect::Object(fields),
1153 };
1154
1155 expectation_stack.push(next);
1156 trace!("{}", DisplayExpectStack(&expectation_stack));
1157 continue;
1158 }
1159 let Some(key) = parent_path.as_object_key() else {
1160 expectation_stack.push(schema::Expect::UnmatchedObject);
1161 trace!("{}", DisplayExpectStack(&expectation_stack));
1162 continue;
1163 };
1164
1165 let next = if let Some(elem) = fields.get(key.as_raw()) {
1166 open_object(kind, elem.as_ref())
1167 } else {
1168 unexpected_fields.push(parent_path);
1169 schema::Expect::OutOfSchema
1170 };
1171
1172 expectation_stack.push(schema::Expect::Object(fields));
1173 expectation_stack.push(next);
1174 }
1175 schema::Expect::OutOfSchema => {
1176 expectation_stack.push(expectation);
1184 expectation_stack.push(schema::Expect::OutOfSchema);
1185 }
1186 schema::Expect::UnmatchedArray | schema::Expect::UnmatchedObject => {
1187 expectation_stack.push(expectation);
1188 expectation_stack.push(schema::Expect::OutOfSchema);
1189 }
1190 _ => {
1191 expectation_stack.push(expectation);
1192 }
1193 }
1194
1195 trace!("{}", DisplayExpectStack(&expectation_stack));
1196 }
1197 parser::Event::Element { kind, parent_path } => {
1198 let Some(expectation) = expectation_stack.pop() else {
1201 return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1202 .into_partial_error_without_token()
1203 .with_root_path());
1204 };
1205
1206 if let ValueKind::Array | ValueKind::Object = kind {
1209 if tracing::enabled!(Level::DEBUG) {
1210 match kind {
1211 ValueKind::Array => {
1212 trace!(
1213 "{parent_path} ] {}",
1214 DisplayExpectStack(&expectation_stack)
1215 );
1216 }
1217 ValueKind::Object => trace!(
1218 "{parent_path} }} {}",
1219 DisplayExpectStack(&expectation_stack)
1220 ),
1221 _ => (),
1222 }
1223 }
1224 continue;
1225 }
1226
1227 match expectation {
1228 #[expect(
1229 clippy::unreachable,
1230 reason = "The parser only emits an `Event::Complete` for a scalar object at the root"
1231 )]
1232 schema::Expect::Object(fields) => match &*parent_path {
1233 PathNode::Root => unreachable!(),
1234 PathNode::Array { .. } => {
1235 expectation_stack.push(schema::Expect::UnmatchedObject);
1236 }
1237 PathNode::Object { parent, key } => {
1238 trace!("{parent:#}.{key}");
1239
1240 if !fields.contains_key(key.as_raw()) {
1241 unexpected_fields.push(parent_path);
1242 }
1243
1244 expectation_stack.push(schema::Expect::Object(fields));
1245 }
1246 },
1247 schema::Expect::OutOfSchema => {
1248 unexpected_fields.push(parent_path);
1249 expectation_stack.push(expectation);
1250 }
1251 _ => {
1252 expectation_stack.push(expectation);
1253 }
1254 }
1255 }
1256 parser::Event::Complete(element) => {
1257 if element.value().is_scalar() {
1258 unexpected_fields.push(element.path_node());
1259 }
1260
1261 return Ok(ParseReport {
1264 element,
1265 unexpected_fields: UnexpectedFields::from_vec(unexpected_fields),
1266 });
1267 }
1268 }
1269 }
1270
1271 Err(ErrorKind::UnexpectedEOF
1272 .into_partial_error_without_token()
1273 .with_root_path())
1274}
1275
1276fn open_object(kind: ObjectKind, elem: Option<&Arc<schema::Element>>) -> schema::Expect {
1277 let Some(schema) = elem else {
1278 return schema::Expect::OutOfSchema;
1279 };
1280
1281 match (kind, &**schema) {
1282 (ObjectKind::Object | ObjectKind::Array, schema::Element::Scalar) => {
1283 schema::Expect::UnmatchedScalar
1284 }
1285 (ObjectKind::Object, schema::Element::Array(_)) => schema::Expect::UnmatchedArray,
1286 (ObjectKind::Object, schema::Element::Object(fields)) => {
1287 schema::Expect::Object(Arc::clone(fields))
1288 }
1289 (ObjectKind::Array, schema::Element::Array(element)) => {
1290 schema::Expect::Array(Arc::clone(element))
1291 }
1292 (ObjectKind::Array, schema::Element::Object(_)) => schema::Expect::UnmatchedObject,
1293 }
1294}
1295
1296#[derive(Debug)]
1299pub(crate) struct ParseReport<'buf> {
1300 pub element: Element<'buf>,
1302
1303 pub unexpected_fields: UnexpectedFields<'buf>,
1305}
1306
1307#[cfg(test)]
1308pub mod test {
1309 #![allow(clippy::missing_panics_doc, reason = "tests are allowed to panic")]
1310 #![allow(clippy::panic, reason = "tests are allowed panic")]
1311
1312 use std::borrow::Cow;
1313
1314 use crate::json::match_path_node;
1315
1316 use super::{
1317 parser::Span, walk::DepthFirst, ElemId, Element, Field, FieldsAsExt as _, PathNode,
1318 PathNodeRef, PathRef, RawStr, UnexpectedFields, Value,
1319 };
1320
1321 impl<'buf> Element<'buf> {
1322 pub fn span(&self) -> Span {
1324 self.span
1325 }
1326
1327 pub(crate) fn into_value(self) -> Value<'buf> {
1329 self.value
1330 }
1331
1332 pub(crate) fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1334 let Self {
1335 id,
1336 path_node: path,
1337 span,
1338 value,
1339 } = self;
1340 (id, path, span, value)
1341 }
1342
1343 pub(crate) fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
1344 self.as_object_fields()
1345 .and_then(|fields| fields.find_field(key))
1346 }
1347 }
1348
1349 impl<'buf> Value<'buf> {
1350 pub(crate) fn is_array(&self) -> bool {
1352 matches!(self, Value::Array(_))
1353 }
1354
1355 pub(crate) fn is_object(&self) -> bool {
1357 matches!(self, Value::Object(_))
1358 }
1359
1360 pub(crate) fn as_string(&self) -> Option<&RawStr<'buf>> {
1361 match self {
1362 Value::String(s) => Some(s),
1363 _ => None,
1364 }
1365 }
1366 }
1367
1368 impl<'buf> Field<'buf> {
1369 pub fn id(&self) -> ElemId {
1370 self.0.id()
1371 }
1372
1373 pub fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1374 self.0.into_parts()
1375 }
1376 }
1377
1378 impl<'buf> UnexpectedFields<'buf> {
1379 pub(crate) fn into_inner(self) -> Vec<PathNodeRef<'buf>> {
1381 self.0
1382 }
1383
1384 pub(crate) fn filter_matches(&mut self, glob: &PathGlob<'_>) {
1386 self.0.retain(|path| !glob.matches(path));
1387 }
1388 }
1389
1390 #[derive(Debug)]
1393 pub(crate) struct PathGlob<'a>(Cow<'a, str>);
1394
1395 impl PathGlob<'_> {
1396 pub(crate) fn matches(&self, path: &PathNode<'_>) -> bool {
1398 const WILDCARD: &str = "*";
1399
1400 match_path_node(path, &self.0, |s| {
1401 s == WILDCARD
1403 })
1404 }
1405 }
1406
1407 impl From<usize> for ElemId {
1409 fn from(value: usize) -> Self {
1410 Self(value)
1411 }
1412 }
1413
1414 impl<'a> From<&'a str> for PathGlob<'a> {
1415 fn from(s: &'a str) -> Self {
1416 Self(s.into())
1417 }
1418 }
1419
1420 impl From<String> for PathGlob<'_> {
1421 fn from(s: String) -> Self {
1422 Self(s.into())
1423 }
1424 }
1425
1426 impl<'de, 'buf> serde::Deserialize<'de> for PathGlob<'buf> {
1427 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1428 where
1429 D: ::serde::Deserializer<'de>,
1430 {
1431 let s = <Cow<'buf, str> as ::serde::Deserialize>::deserialize(deserializer)?;
1432 Ok(Self(s))
1433 }
1434 }
1435
1436 pub struct ElementMap<'a, 'bin>(Vec<&'a Element<'bin>>);
1438
1439 impl<'a, 'bin> ElementMap<'a, 'bin> {
1440 pub fn for_elem(root: &'a Element<'bin>) -> Self {
1442 let walker = DepthFirst::new(root);
1444 Self(walker.collect())
1445 }
1446
1447 pub fn get(&self, id: ElemId) -> &Element<'bin> {
1449 self.0.get(id.0).map(|e| &**e).unwrap()
1450 }
1451
1452 pub fn path(&self, id: ElemId) -> PathRef<'bin> {
1454 self.0.get(id.0).map(|elem| elem.path()).unwrap()
1455 }
1456 }
1457
1458 #[cfg(test)]
1459 mod test_path_matches_glob {
1460 use std::rc::Rc;
1461
1462 use crate::test;
1463
1464 use super::{PathGlob, PathNode};
1465
1466 #[test]
1467 fn should_match_path() {
1468 test::setup();
1469
1470 let root = Rc::new(PathNode::Root);
1471 let path_a = Rc::new(PathNode::Array {
1472 parent: Rc::clone(&root),
1473 index: 1,
1474 });
1475 let path_b = Rc::new(PathNode::Object {
1476 parent: Rc::clone(&path_a),
1477 key: r#""name""#.into(),
1478 });
1479 let path_c = PathNode::Object {
1480 parent: Rc::clone(&path_b),
1481 key: r#""gene""#.into(),
1482 };
1483
1484 assert!(PathGlob::from("$").matches(&root));
1485 assert!(PathGlob::from("*").matches(&root));
1486
1487 assert!(!PathGlob::from("*").matches(&path_a));
1488 assert!(PathGlob::from("*.*").matches(&path_a));
1489 assert!(PathGlob::from("$.*").matches(&path_a));
1490 assert!(PathGlob::from("$.1").matches(&path_a));
1491
1492 assert!(!PathGlob::from("*").matches(&path_b));
1493 assert!(!PathGlob::from("*.*").matches(&path_b));
1494 assert!(PathGlob::from("*.*.*").matches(&path_b));
1495 assert!(PathGlob::from("$.*.*").matches(&path_b));
1496 assert!(PathGlob::from("$.1.*").matches(&path_b));
1497 assert!(PathGlob::from("$.*.name").matches(&path_b));
1498 assert!(PathGlob::from("$.1.name").matches(&path_b));
1499
1500 assert!(PathGlob::from("$.1.name.gene").matches(&path_c));
1501 }
1502 }
1503}
1504
1505#[cfg(test)]
1506mod test_path {
1507 use super::{Path, PathPiece};
1508
1509 #[test]
1510 fn path_should_cmp_with_str() {
1511 assert_ne!(Path::root(), "");
1512 assert_eq!(Path::root(), "$");
1513 assert_eq!(Path(vec![PathPiece::Object("field_a".into())]), "$.field_a");
1514 assert_eq!(Path(vec![PathPiece::Array(1)]), "$.1");
1515 assert_eq!(
1516 Path(vec![
1517 PathPiece::Object("field_a".into()),
1518 PathPiece::Array(1)
1519 ]),
1520 "$.field_a.1"
1521 );
1522 }
1523
1524 #[test]
1525 fn path_should_display() {
1526 assert_eq!(Path::root().to_string(), "$");
1527 assert_eq!(
1528 Path(vec![PathPiece::Object("field_a".into())]).to_string(),
1529 "$.field_a"
1530 );
1531 assert_eq!(Path(vec![PathPiece::Array(1)]).to_string(), "$.1");
1532 assert_eq!(
1533 Path(vec![
1534 PathPiece::Object("field_a".into()),
1535 PathPiece::Array(1)
1536 ])
1537 .to_string(),
1538 "$.field_a.1"
1539 );
1540 }
1541}
1542
1543#[cfg(test)]
1544mod test_parse_with_schema {
1545 use crate::{json_schema, test};
1546
1547 use super::{parse_with_schema, ParseReport};
1548
1549 #[test]
1550 fn should_report_unexpected_fields_for_root_element() {
1551 const JSON: &str = "null";
1552
1553 test::setup();
1554
1555 let schema = json_schema!({
1556 "id",
1557 "currency",
1558 });
1559
1560 let report = parse_with_schema(JSON, &schema).unwrap();
1561 let ParseReport {
1562 element: _,
1563 unexpected_fields,
1564 } = report;
1565
1566 {
1567 let [field_a] = unexpected_fields.into_inner().try_into().unwrap();
1568 assert_eq!(*field_a, "$");
1569 }
1570 }
1571
1572 #[test]
1573 fn should_report_unexpected_fields_in_flat_object() {
1574 const JSON: &str = r#"{
1575 "id": "tariff_id",
1576 "currency": "EUR",
1577 "name": "Barry",
1578 "address": "Barrystown"
1579}"#;
1580
1581 test::setup();
1582
1583 let schema = json_schema!({
1584 "id",
1585 "currency",
1586 });
1587
1588 let report = parse_with_schema(JSON, &schema).unwrap();
1589 let ParseReport {
1590 element: _,
1591 unexpected_fields,
1592 } = report;
1593
1594 {
1595 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1596 assert_eq!(*field_a, "$.name");
1597 assert_eq!(*field_b, "$.address");
1598 }
1599 }
1600
1601 #[test]
1602 fn should_report_unexpected_fields_in_nested_object() {
1603 const JSON: &str = r#"{
1604 "id": "tariff_id",
1605 "currency": "EUR",
1606 "owner": {
1607 "id": "456856",
1608 "subscription_id": "tedi4568",
1609 "name": "Barry",
1610 "address": "Barrystown"
1611 }
1612}"#;
1613
1614 test::setup();
1615
1616 let schema = json_schema!({
1617 "id",
1618 "currency",
1619 "owner": {
1620 "id",
1621 "subscription_id"
1622 }
1623 });
1624
1625 let report = parse_with_schema(JSON, &schema).unwrap();
1626 let ParseReport {
1627 element: _,
1628 unexpected_fields,
1629 } = report;
1630
1631 {
1632 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1633 assert_eq!(*field_a, "$.owner.name");
1634 assert_eq!(*field_b, "$.owner.address");
1635 }
1636 }
1637
1638 #[test]
1639 fn should_parse_nested_object_out_of_schema() {
1640 const JSON: &str = r#"{
1641 "id": "tariff_id",
1642 "owner": {
1643 "id": "456856",
1644 "subscription_id": "tedi4568",
1645 "name": "Barry",
1646 "address": {
1647 "city": "Barrystown",
1648 "street": "Barrysstreet"
1649 }
1650 },
1651 "currency": "EUR",
1652 "country": "NL"
1653}"#;
1654
1655 test::setup();
1656
1657 let schema = json_schema!({
1658 "id",
1659 "currency",
1660 "owner"
1661 });
1662
1663 let report = parse_with_schema(JSON, &schema).unwrap();
1664 let ParseReport {
1665 element: _,
1666 unexpected_fields,
1667 } = report;
1668
1669 {
1670 let [field_a, field_b, field_c, field_d, field_e, field_f] =
1671 unexpected_fields.into_inner().try_into().unwrap();
1672 assert_eq!(*field_a, "$.owner.id");
1673 assert_eq!(*field_b, "$.owner.subscription_id");
1674 assert_eq!(*field_c, "$.owner.name");
1675 assert_eq!(*field_d, "$.owner.address.city");
1676 assert_eq!(*field_e, "$.owner.address.street");
1677 assert_eq!(*field_f, "$.country");
1678 }
1679 }
1680
1681 #[test]
1682 fn should_report_unexpected_fields_in_array_with_nested_object() {
1683 const JSON: &str = r#"{
1684 "id": "tariff_id",
1685 "currency": "EUR",
1686 "elements": [{
1687 "id": "456856",
1688 "subscription_id": "tedi4568",
1689 "name": "Barry",
1690 "address": "Barrystown"
1691 }]
1692}"#;
1693
1694 test::setup();
1695
1696 let schema = json_schema!({
1697 "id",
1698 "currency",
1699 "elements": [{
1700 "id",
1701 "subscription_id"
1702 }]
1703 });
1704
1705 let report = parse_with_schema(JSON, &schema).unwrap();
1706 let ParseReport {
1707 element: _,
1708 unexpected_fields,
1709 } = report;
1710
1711 {
1712 let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1713 assert_eq!(*field_a, "$.elements.0.name");
1714 assert_eq!(*field_b, "$.elements.0.address");
1715 }
1716 }
1717
1718 #[test]
1719 fn should_report_unexpected_fields_in_array_of_nested_objects() {
1720 const JSON: &str = r#"{
1721 "id": "tariff_id",
1722 "currency": "EUR",
1723 "elements": [
1724 {
1725 "id": "456856",
1726 "subscription_id": "tedi4568",
1727 "name": "Barry",
1728 "address": "Barrystown"
1729 },
1730 {
1731 "id": "8746we",
1732 "subscription_id": "dfr345",
1733 "name": "Gerry",
1734 "address": "Gerrystown"
1735 }
1736 ]
1737}"#;
1738
1739 test::setup();
1740
1741 let schema = json_schema!({
1742 "id",
1743 "currency",
1744 "elements": [{
1745 "id",
1746 "subscription_id"
1747 }]
1748 });
1749
1750 let report = parse_with_schema(JSON, &schema).unwrap();
1751 let ParseReport {
1752 element: _,
1753 unexpected_fields,
1754 } = report;
1755
1756 {
1757 let [field_a, field_b, field_c, field_d] =
1758 unexpected_fields.into_inner().try_into().unwrap();
1759 assert_eq!(*field_a, "$.elements.0.name");
1760 assert_eq!(*field_b, "$.elements.0.address");
1761 assert_eq!(*field_c, "$.elements.1.name");
1762 assert_eq!(*field_d, "$.elements.1.address");
1763 }
1764 }
1765
1766 #[test]
1767 fn should_report_unexpected_fields_in_array_of_objects() {
1768 const JSON: &str = r#"[
1769 {
1770 "id": "456856",
1771 "subscription_id": "tedi4568",
1772 "name": "Barry",
1773 "address": "Barrystown"
1774 },
1775 {
1776 "id": "8746we",
1777 "subscription_id": "dfr345",
1778 "name": "Gerry",
1779 "address": "Gerrystown"
1780 }
1781]"#;
1782
1783 test::setup();
1784
1785 let schema = json_schema!([
1786 {
1787 "id",
1788 "subscription_id"
1789 }
1790 ]);
1791
1792 let report = parse_with_schema(JSON, &schema).unwrap();
1793 let ParseReport {
1794 element: _,
1795 unexpected_fields,
1796 } = report;
1797
1798 {
1799 let [field_a, field_b, field_c, field_d] =
1800 unexpected_fields.into_inner().try_into().unwrap();
1801 assert_eq!(*field_a, "$.0.name");
1802 assert_eq!(*field_b, "$.0.address");
1803 assert_eq!(*field_c, "$.1.name");
1804 assert_eq!(*field_d, "$.1.address");
1805 }
1806 }
1807}
1808
1809#[cfg(test)]
1810mod test_source_json {
1811 use super::{parse, walk};
1812
1813 #[test]
1814 fn should_resolve_to_source_json() {
1815 const JSON: &str = r#"{
1816 "name": "David Byrne",
1817 "hobbies": ["song writing", "thinking about society"]
1818}"#;
1819
1820 let element = parse(JSON).unwrap();
1821
1822 let mut walk = walk::DepthFirst::new(&element);
1823
1824 let root = walk.next().unwrap();
1825 assert_eq!(root.source_json(JSON), JSON);
1826
1827 let field_name = walk.next().unwrap();
1828 assert_eq!(field_name.source_json(JSON), r#""name": "David Byrne""#);
1829 assert_eq!(field_name.source_json_value(JSON), r#""David Byrne""#);
1830
1831 let field_hobbies = walk.next().unwrap();
1832 assert_eq!(
1833 field_hobbies.source_json(JSON),
1834 r#""hobbies": ["song writing", "thinking about society"]"#
1835 );
1836 assert_eq!(
1837 field_hobbies.source_json_value(JSON),
1838 r#"["song writing", "thinking about society"]"#
1839 );
1840
1841 let hobbies_one = walk.next().unwrap();
1842 assert_eq!(hobbies_one.source_json(JSON), r#""song writing""#);
1843 assert_eq!(hobbies_one.source_json_value(JSON), r#""song writing""#);
1844
1845 let hobbies_two = walk.next().unwrap();
1846 assert_eq!(hobbies_two.source_json(JSON), r#""thinking about society""#);
1847 assert_eq!(
1848 hobbies_two.source_json_value(JSON),
1849 r#""thinking about society""#
1850 );
1851 }
1852}
1853
1854#[cfg(test)]
1855mod test_path_node {
1856 use std::rc::Rc;
1857
1858 use super::{
1859 parser::{RawStr, Token, TokenType},
1860 PathNode, Span,
1861 };
1862
1863 #[test]
1864 fn should_display_path() {
1865 let root = Rc::new(PathNode::Root);
1866 let path_a = Rc::new(PathNode::Array {
1867 parent: Rc::clone(&root),
1868 index: 1,
1869 });
1870 let path_b = Rc::new(PathNode::Object {
1871 parent: Rc::clone(&path_a),
1872 key: r#""name""#.into(),
1873 });
1874 let path_c = Rc::new(PathNode::Object {
1875 parent: Rc::clone(&path_b),
1876 key: r#""gene""#.into(),
1877 });
1878
1879 assert_eq!(*root, "$");
1880 assert_eq!(*path_a, "$.1");
1881 assert_eq!(*path_b, "$.1.name");
1882 assert_eq!(*path_c, "$.1.name.gene");
1883 }
1884
1885 impl<'buf> From<&'buf str> for RawStr<'buf> {
1886 #[track_caller]
1887 fn from(s: &'buf str) -> Self {
1888 RawStr::from_quoted_str(
1889 s,
1890 Token {
1891 kind: TokenType::String,
1892 span: Span::default(),
1893 },
1894 )
1895 .unwrap()
1896 }
1897 }
1898}
1899
1900#[cfg(test)]
1901mod test_raw_json {
1902 use std::{borrow::Cow, rc::Rc};
1903
1904 use serde::{de::IntoDeserializer, Deserialize};
1905
1906 use super::{ElemId, Element, PathNode, RawValue, Span, Value, ValueKind};
1907 use crate::json::decode::unescape_str;
1908
1909 const TITLE: &str = "همّا مين واحنا مين (Who Are They and Who Are We?)";
1910
1911 pub(crate) trait RawValueExt {
1917 fn kind(&self) -> ValueKind;
1918
1919 fn is_string(&self) -> bool;
1920
1921 fn as_str(&self) -> Option<Cow<'_, str>>;
1925 }
1926
1927 impl RawValueExt for RawValue {
1928 fn kind(&self) -> ValueKind {
1929 let s = self.get();
1930 let first = s
1931 .as_bytes()
1932 .first()
1933 .expect("A RawValue can`t be an empty string, it has to contain a JSON value");
1934
1935 match *first {
1944 b'n' => ValueKind::Null,
1945 b't' | b'f' => ValueKind::Bool,
1946 b'"' => ValueKind::String,
1947 b'[' => ValueKind::Array,
1948 b'{' => ValueKind::Object,
1949 _ => ValueKind::Number,
1952 }
1953 }
1954
1955 fn is_string(&self) -> bool {
1956 matches!(self.kind(), ValueKind::String)
1957 }
1958
1959 fn as_str(&self) -> Option<Cow<'_, str>> {
1960 if !self.is_string() {
1961 return None;
1962 }
1963
1964 let s = self.get().trim_matches('"');
1965 let elem = Element {
1968 id: ElemId(0),
1969 path_node: Rc::new(PathNode::Root),
1970 span: Span::default(),
1971 value: Value::Null,
1972 };
1973 let (s, _warnings) = unescape_str(s, &elem).into_parts();
1974 Some(s)
1975 }
1976 }
1977
1978 #[test]
1979 fn should_fail_to_parse_whitespace_only_string_as_json() {
1980 const JSON: &str = " ";
1981 let err = serde_json::from_str::<&RawValue>(JSON).unwrap_err();
1982
1983 assert_eq!(
1984 err.classify(),
1985 serde_json::error::Category::Eof,
1986 "A JSON string can't be empty"
1987 );
1988
1989 let err = RawValue::from_string(JSON.to_string()).unwrap_err();
1990
1991 assert_eq!(
1992 err.classify(),
1993 serde_json::error::Category::Eof,
1994 "A JSON string can't be empty"
1995 );
1996 }
1997
1998 #[test]
1999 fn should_validate_json_without_allocating_for_each_token() {
2000 #[derive(Deserialize)]
2001 struct Song {
2002 title: String,
2003 }
2004
2005 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2006
2007 let json: Box<RawValue> = serde_json::from_str(&json).unwrap();
2017
2018 let song = Song::deserialize(json.into_deserializer()).unwrap();
2023
2024 assert_eq!(song.title, TITLE);
2025 }
2026
2027 #[test]
2028 fn should_compare_raw_title_correctly() {
2029 #[derive(Deserialize)]
2030 struct Song<'a> {
2031 #[serde(borrow)]
2032 title: &'a RawValue,
2033 }
2034
2035 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2036 let song: Song<'_> = serde_json::from_str(&json).unwrap();
2037
2038 assert_ne!(
2039 song.title.get(),
2040 TITLE,
2041 "The raw `title` field contains the delimiting '\"' and so is technically not directly equal to the `TITLE` const"
2042 );
2043
2044 let title = song.title.as_str().unwrap();
2045
2046 assert_eq!(
2047 title, TITLE,
2048 "When the quotes are removed the `title` field is the same as the `TITLE` const"
2049 );
2050 }
2051
2052 #[test]
2053 fn should_fail_to_parse_invalid_json() {
2054 const JSON: &str = r#"{ "title": }"#;
2055
2056 let err = serde_json::from_str::<Box<RawValue>>(JSON).unwrap_err();
2057
2058 assert_eq!(
2059 err.classify(),
2060 serde_json::error::Category::Syntax,
2061 "The bytes contained within `RawValue` are valid JSON"
2062 );
2063 }
2064
2065 #[test]
2066 fn should_parse_raw_json_to_rust_struct() {
2067 #[derive(Deserialize)]
2069 struct Song {
2070 title: String,
2071 }
2072
2073 struct SongMessage {
2075 song: Song,
2076 original: Box<RawValue>,
2077 }
2078
2079 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2081
2082 let raw_json: Box<RawValue> = serde_json::from_str(&json)
2084 .expect("Typically we want to parse some JSON from an endpoint first");
2085 let song = Song::deserialize(raw_json.into_deserializer()).expect("And then introspect it");
2087
2088 let message = SongMessage {
2089 song,
2090 original: raw_json,
2091 };
2092
2093 assert_eq!(
2094 message.song.title, TITLE,
2095 "The title is a normal `String` and so can be directly compared"
2096 );
2097 assert_eq!(
2098 message.original.get(),
2099 &json,
2100 "The original has not been modified in any way"
2101 );
2102 }
2103
2104 #[test]
2105 fn should_parse_borrowed_raw_json_to_rust_struct() {
2106 #[derive(Deserialize)]
2108 struct Song<'a> {
2109 title: &'a str,
2110 }
2111
2112 struct SongMessage<'a> {
2116 song: Song<'a>,
2117 original: &'a RawValue,
2118 }
2119
2120 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2122
2123 let raw_json: &RawValue = serde_json::from_str(&json)
2125 .expect("Typically we want to parse some JSON from an endpoint first");
2126 let song =
2128 Song::<'_>::deserialize(raw_json.into_deserializer()).expect("And then introspect it");
2129 let message = SongMessage {
2131 song,
2132 original: raw_json,
2133 };
2134
2135 assert_eq!(
2136 message.song.title, TITLE,
2137 "The title is a normal `&str` and so can be directly compared"
2138 );
2139 assert_eq!(
2140 message.original.get(),
2141 &json,
2142 "The original has not been modified in any way"
2143 );
2144 }
2145
2146 #[test]
2147 fn should_deser_number_as_i64() {
2148 const JSON: &str = "123";
2149 let json: &RawValue = serde_json::from_str(JSON).unwrap();
2150
2151 let n = i64::deserialize(json.into_deserializer()).unwrap();
2152
2153 assert_eq!(n, 123);
2154 }
2155
2156 #[test]
2157 fn should_convert_json_string_to_str() {
2158 #[derive(Deserialize)]
2160 struct Song<'a> {
2161 title: &'a str,
2162 }
2163
2164 struct SongMessage<'a> {
2168 song: Song<'a>,
2169 original: &'a RawValue,
2170 }
2171
2172 let json = format!(r#"{{ "title": "{TITLE}" }}"#);
2174
2175 let raw_json: &RawValue = serde_json::from_str(&json)
2177 .expect("Typically we want to parse some JSON from an endpoint first");
2178 let song =
2180 Song::<'_>::deserialize(raw_json.into_deserializer()).expect("And then introspect it");
2181 let message = SongMessage {
2183 song,
2184 original: raw_json,
2185 };
2186
2187 assert_eq!(
2188 message.song.title, TITLE,
2189 "The title is a normal `&str` and so can be directly compared"
2190 );
2191 assert_eq!(
2192 message.original.get(),
2193 &json,
2194 "The original has not been modified in any way"
2195 );
2196 }
2197}