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