Skip to main content

ocpi_tariffs/
json.rs

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