Skip to main content

ocpi_tariffs/
json.rs

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