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