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    SmartString, Verdict, WriteSmartString,
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    pub fn to_smart_string(&self) -> SmartString {
785        self.0.to_smart_string()
786    }
787}
788
789/// An [`Iterator`] over the components of a path.
790pub struct PathComponents<'buf>(PathIter<'buf>);
791
792impl<'buf> Iterator for PathComponents<'buf> {
793    type Item = PathComponent<'buf>;
794
795    fn next(&mut self) -> Option<Self::Item> {
796        let path_node = self.0.next()?;
797        Some(PathComponent(path_node))
798    }
799}
800
801/// The `PathRef` can be compared with other strings just like a `String`.
802impl PartialEq<&str> for PathRef<'_> {
803    fn eq(&self, other: &&str) -> bool {
804        match_path_node(&self.0, other, |_| false)
805    }
806}
807
808/// The `PathRef` can be compared with other strings just like a `String`.
809impl PartialEq<String> for PathRef<'_> {
810    fn eq(&self, other: &String) -> bool {
811        match_path_node(&self.0, other, |_| false)
812    }
813}
814
815impl fmt::Display for PathRef<'_> {
816    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
817        fmt::Display::fmt(&self.0, f)
818    }
819}
820
821#[cfg(test)]
822mod test_path_node_matches_str {
823    use std::sync::Arc;
824
825    use crate::test;
826
827    use super::PathNode;
828
829    #[test]
830    fn should_match_path() {
831        test::setup();
832
833        let root = Arc::new(PathNode::Root);
834        let path_a = Arc::new(PathNode::Array {
835            parent: Arc::clone(&root),
836            index: 1,
837        });
838        let path_b = Arc::new(PathNode::Object {
839            parent: Arc::clone(&path_a),
840            key: r#""name""#.into(),
841        });
842        let path_c = PathNode::Object {
843            parent: Arc::clone(&path_b),
844            key: r#""gene""#.into(),
845        };
846
847        assert_eq!(*root, "$");
848        assert_eq!(*path_a, "$.1");
849        assert_eq!(*path_b, "$.1.name");
850        assert_eq!(path_c, "$.1.name.gene");
851    }
852}
853
854/// The path to a JSON `Element`.
855pub(crate) type PathNodeRef<'buf> = Arc<PathNode<'buf>>;
856
857/// A single node of a complete path.
858///
859/// Path's are structured as a linked list from the leaf of the path back to the root through the parents.
860///
861/// The Display impl of the `Path` outputs strings like:
862///
863/// - `$` The root is represented by a dollar.
864/// - `$.object_key` Dots separate the path elements.
865/// - `$.object_key.2` Arrays are represented as integers.
866#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
867pub(crate) enum PathNode<'buf> {
868    /// The root of the JSON `Element` tree.
869    #[default]
870    Root,
871    /// An `Array` element referenced by index.
872    Array {
873        parent: PathNodeRef<'buf>,
874        index: usize,
875    },
876    /// An `Object` field referenced be key value.
877    Object {
878        parent: PathNodeRef<'buf>,
879        key: RawStr<'buf>,
880    },
881}
882
883/// A lightweight enum used to indicate the kind of component being visited when using the
884/// [`PathComponents`] [`Iterator`].
885pub enum PathNodeKind {
886    /// The root of the JSON `Element` tree.
887    Root,
888    /// An `Array` element referenced by index.
889    Array,
890    /// An `Object` field referenced be key value.
891    Object,
892}
893
894/// A path to a JSON `Element` where the path components are heap allocated and so do not require
895/// a lifetime back to the source JSON `&str`.
896///
897/// The Display impl outputs strings like:
898///
899/// - `$` The root is represented by a dollar.
900/// - `$.object_key` Dots separate the path elements.
901/// - `$.object_key.2` Arrays are represented as integers.
902#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
903pub struct Path(Vec<PathPiece>);
904
905impl Path {
906    /// Create a root `Path`.
907    const fn root() -> Self {
908        Self(vec![])
909    }
910
911    /// Create an `Path` by iterating over a [`PathNode`].
912    fn from_node(path: PathNodeRef<'_>) -> Self {
913        let paths: Vec<_> = PathIter::new(path).collect();
914
915        let pieces = paths
916            .into_iter()
917            .rev()
918            .filter_map(|path_node| match *path_node {
919                PathNode::Root => None,
920                PathNode::Array { index, .. } => Some(PathPiece::Array(index)),
921                PathNode::Object { key, .. } => Some(PathPiece::Object(key.to_string())),
922            })
923            .collect();
924
925        Self(pieces)
926    }
927}
928
929/// The `Path` can be compared with other strings just like a `String`.
930impl PartialEq<&str> for Path {
931    fn eq(&self, other: &&str) -> bool {
932        match_path(self, other)
933    }
934}
935
936/// The `Path` can be compared with other strings just like a `String`.
937impl PartialEq<String> for Path {
938    fn eq(&self, other: &String) -> bool {
939        match_path(self, other)
940    }
941}
942
943impl fmt::Display for Path {
944    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
945        let iter = self.0.iter();
946
947        write!(f, "$")?;
948
949        for path in iter {
950            write!(f, ".{path}")?;
951        }
952
953        Ok(())
954    }
955}
956
957/// A piece/component of a [`Path`].
958///
959/// The `PathComponent` name is already taken and this type is private.
960#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
961enum PathPiece {
962    /// An `Array` element referenced by index.
963    Array(usize),
964    /// An `Object` field referenced be key value.
965    Object(String),
966}
967
968impl fmt::Display for PathPiece {
969    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
970        match self {
971            PathPiece::Array(index) => write!(f, "{index}"),
972            PathPiece::Object(key) => write!(f, "{key}"),
973        }
974    }
975}
976
977/// A single component of a [`PathRef`].
978pub struct PathComponent<'buf>(PathNodeRef<'buf>);
979
980impl PathComponent<'_> {
981    /// Return the kind of component this is.
982    pub fn kind(&self) -> PathNodeKind {
983        match *self.0 {
984            PathNode::Root => PathNodeKind::Root,
985            PathNode::Array { .. } => PathNodeKind::Array,
986            PathNode::Object { .. } => PathNodeKind::Object,
987        }
988    }
989}
990
991impl fmt::Display for PathComponent<'_> {
992    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
993        match *self.0 {
994            PathNode::Root => f.write_str(PATH_ROOT),
995            PathNode::Array { index, .. } => write!(f, "{index}"),
996            PathNode::Object { key, .. } => write!(f, "{key}"),
997        }
998    }
999}
1000
1001impl WriteSmartString for PathComponent<'_> {
1002    fn write_smart(&self, s: &mut SmartString) -> fmt::Result {
1003        match *self.0 {
1004            PathNode::Root => {
1005                s.push_str(PATH_ROOT);
1006                Ok(())
1007            }
1008            PathNode::Array { index, .. } => write!(s, "{index}"),
1009            PathNode::Object { key, .. } => write!(s, "{key}"),
1010        }
1011    }
1012}
1013
1014impl fmt::Display for PathNode<'_> {
1015    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1016        let paths: Vec<_> = PathIter::new(Arc::new(self.clone())).collect();
1017        let mut iter = paths.into_iter().rev();
1018
1019        if f.alternate() {
1020            // Print out each path element as a debugging tab-stop.
1021            for path in iter {
1022                match *path {
1023                    PathNode::Root => f.write_str("")?,
1024                    PathNode::Array { .. } | PathNode::Object { .. } => f.write_str("...|")?,
1025                }
1026            }
1027        } else {
1028            if let Some(path) = iter.next() {
1029                write!(f, "{}", PathComponent(path))?;
1030            }
1031
1032            for path in iter {
1033                write!(f, ".{}", PathComponent(path))?;
1034            }
1035        }
1036        Ok(())
1037    }
1038}
1039
1040impl<'buf> PathNode<'buf> {
1041    /// Returns true if the `Path` refers to the root.
1042    pub(crate) fn is_root(&self) -> bool {
1043        matches!(self, PathNode::Root)
1044    }
1045
1046    /// Returns true if the `Path` refers to an `Array`.
1047    pub(crate) fn is_array(&self) -> bool {
1048        matches!(self, PathNode::Array { .. })
1049    }
1050
1051    /// Return a key as `Some(&str)` if the `Path` refers to an `Object`.
1052    pub(crate) fn as_object_key(&self) -> Option<&RawStr<'buf>> {
1053        match self {
1054            PathNode::Object { key, .. } => Some(key),
1055            PathNode::Root | PathNode::Array { .. } => None,
1056        }
1057    }
1058
1059    pub(crate) fn to_smart_string(&self) -> SmartString {
1060        let mut s = SmartString::new();
1061        self.write_smart(&mut s)
1062            .expect("Unable to write string: out of memory");
1063        s
1064    }
1065}
1066
1067impl WriteSmartString for PathNode<'_> {
1068    fn write_smart(&self, s: &mut SmartString) -> fmt::Result {
1069        let paths: Vec<_> = PathIter::new(Arc::new(self.clone())).collect();
1070        let mut iter = paths.into_iter().rev();
1071
1072        if let Some(path) = iter.next() {
1073            write!(s, "{}", PathComponent(path))?;
1074        }
1075
1076        for path in iter {
1077            write!(s, ".{}", PathComponent(path))?;
1078        }
1079
1080        Ok(())
1081    }
1082}
1083
1084/// Return true if the given `PathNode` matches the given `&str`.
1085///
1086/// If the `skip` function returns true, that path component is skipped.
1087/// The `skip` function allows this single function to implement comparisons between `PathNode` and `&str`;
1088/// and comparisons between `PathNode` and `PathGlob`.
1089fn match_path_node<F>(path: &PathNode<'_>, s: &str, mut skip: F) -> bool
1090where
1091    F: FnMut(&str) -> bool,
1092{
1093    let mut parts = s.rsplit(PATH_SEPARATOR);
1094    let mut paths_iter = PathIter::new(Arc::new(path.clone()));
1095
1096    loop {
1097        let node_segment = paths_iter.next();
1098        let str_segment = parts.next();
1099
1100        let (node_segment, str_segment) = match (node_segment, str_segment) {
1101            // If we have exhausted both iterators then the `&str` is equal to the path.
1102            (None, None) => return true,
1103            // If either of the iters are a different size, then they don't match.
1104            (None, Some(_)) | (Some(_), None) => return false,
1105            // If both iters have another item, continue on to try match them.
1106            (Some(a), Some(b)) => (a, b),
1107        };
1108
1109        // If the skip function says to skip a path segment, then continue to the next segment.
1110        if skip(str_segment) {
1111            continue;
1112        }
1113
1114        let yip = match *node_segment {
1115            PathNode::Root => str_segment == PATH_ROOT,
1116            PathNode::Array { index, .. } => {
1117                let Ok(b) = str_segment.parse::<usize>() else {
1118                    return false;
1119                };
1120
1121                index == b
1122            }
1123            PathNode::Object { key, .. } => key.as_raw() == str_segment,
1124        };
1125
1126        // Return false on the first mismatch.
1127        if !yip {
1128            return false;
1129        }
1130    }
1131}
1132
1133/// Return true if the given `Path` matches the given `&str`.
1134fn match_path(path: &Path, s: &str) -> bool {
1135    let mut parts = s.split(PATH_SEPARATOR);
1136    let mut paths_iter = path.0.iter();
1137
1138    let Some(str_segment) = parts.next() else {
1139        return false;
1140    };
1141
1142    // The root path segment is not explicitly stored in a `Path` so we just match the first
1143    // `str` segment to the expected `$` nomenclature.
1144    if str_segment != PATH_ROOT {
1145        return false;
1146    }
1147
1148    loop {
1149        let node_segment = paths_iter.next();
1150        let str_segment = parts.next();
1151
1152        let (node_segment, str_segment) = match (node_segment, str_segment) {
1153            // If we have exhausted both iterators then the `&str` is equal to the path.
1154            (None, None) => return true,
1155            // If either of the iters are a different size, then they don't match.
1156            (None, Some(_)) | (Some(_), None) => return false,
1157            // If both iters have another item, continue on to try match them.
1158            (Some(a), Some(b)) => (a, b),
1159        };
1160
1161        let yip = match node_segment {
1162            PathPiece::Array(index) => {
1163                let Ok(b) = str_segment.parse::<usize>() else {
1164                    return false;
1165                };
1166
1167                *index == b
1168            }
1169            PathPiece::Object(key) => key == str_segment,
1170        };
1171
1172        // Return false on the first mismatch.
1173        if !yip {
1174            return false;
1175        }
1176    }
1177}
1178
1179impl PartialEq<&str> for PathNode<'_> {
1180    fn eq(&self, other: &&str) -> bool {
1181        match_path_node(self, other, |_| false)
1182    }
1183}
1184
1185impl PartialEq<String> for PathNode<'_> {
1186    fn eq(&self, other: &String) -> bool {
1187        match_path_node(self, other, |_| false)
1188    }
1189}
1190
1191/// Traverse a `Path` from the leaf to the root.
1192struct PathIter<'buf> {
1193    /// The root has been reached.
1194    complete: bool,
1195    /// The current path node to introspect when `Iterator::next` is called.
1196    path: PathNodeRef<'buf>,
1197}
1198
1199impl<'buf> PathIter<'buf> {
1200    /// Create a new `PathIter` from a leaf node.
1201    fn new(path: PathNodeRef<'buf>) -> Self {
1202        Self {
1203            complete: false,
1204            path,
1205        }
1206    }
1207}
1208
1209impl<'buf> Iterator for PathIter<'buf> {
1210    type Item = PathNodeRef<'buf>;
1211
1212    fn next(&mut self) -> Option<Self::Item> {
1213        if self.complete {
1214            return None;
1215        }
1216
1217        match &*self.path {
1218            PathNode::Root => {
1219                // The iteration is complete once we've arrived at the root node.
1220                self.complete = true;
1221                Some(Arc::clone(&self.path))
1222            }
1223            PathNode::Array { parent, .. } | PathNode::Object { parent, .. } => {
1224                let next = Arc::clone(&self.path);
1225                self.path = Arc::clone(parent);
1226                Some(next)
1227            }
1228        }
1229    }
1230}
1231
1232/// Display the expectation stack for debugging
1233struct DisplayExpectStack<'a>(&'a [schema::Expect]);
1234
1235impl fmt::Display for DisplayExpectStack<'_> {
1236    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1237        let mut iter = self.0.iter().rev();
1238        let last = iter.next();
1239
1240        // Use the `~` to represent a schema stack.
1241        f.write_str("~")?;
1242
1243        for _ in iter {
1244            f.write_str("...~")?;
1245        }
1246
1247        if let Some(exp) = last {
1248            match exp {
1249                schema::Expect::Scalar => f.write_str("~")?,
1250                schema::Expect::Array(element) => match &**element {
1251                    schema::Element::Scalar => f.write_str("~")?,
1252                    schema::Element::Array(element) => write!(f, "[{element:?}]")?,
1253                    schema::Element::Object(fields) => {
1254                        write!(f, "[{{{:}}}])", DisplayExpectFields(&**fields))?;
1255                    }
1256                },
1257                schema::Expect::Object(fields) => {
1258                    write!(f, "{{{:}}}", DisplayExpectFields(&**fields))?;
1259                }
1260                schema::Expect::UnmatchedScalar => write!(f, "unmatched(scalar)")?,
1261                schema::Expect::UnmatchedArray => write!(f, "unmatched(array)")?,
1262                schema::Expect::UnmatchedObject => write!(f, "unmatched(object)")?,
1263                schema::Expect::OutOfSchema => write!(f, "no_schema")?,
1264            }
1265        }
1266
1267        Ok(())
1268    }
1269}
1270
1271/// Display the fields of a schema expect stack level.
1272struct DisplayExpectFields<'a, V>(&'a BTreeMap<&'a str, V>);
1273
1274impl<V> fmt::Display for DisplayExpectFields<'_, V> {
1275    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1276        const MAX_FIELDS: usize = 8;
1277
1278        let mut count = 0;
1279        let mut iter = self.0.keys().peekable();
1280
1281        loop {
1282            if count >= MAX_FIELDS {
1283                f.write_str("...")?;
1284                break;
1285            }
1286
1287            let Some(field) = iter.next() else {
1288                break;
1289            };
1290
1291            count += 1;
1292            write!(f, "{field}")?;
1293
1294            let Some(_) = iter.peek() else {
1295                break;
1296            };
1297
1298            f.write_str(", ")?;
1299        }
1300
1301        Ok(())
1302    }
1303}
1304
1305#[derive(Debug)]
1306struct UnbalancedExpectStack;
1307
1308impl fmt::Display for UnbalancedExpectStack {
1309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1310        f.write_str("unbalanced expectation stack")
1311    }
1312}
1313
1314impl std::error::Error for UnbalancedExpectStack {}
1315
1316/// Parse the JSON into a [`ParseReport`] checking the parsed [`Element`]s names against the given
1317/// [`schema`] and reporting fields that are unexpected in the [`ParseReport::unexpected_fields`].
1318pub(crate) fn parse_with_schema<'buf>(
1319    json: &'buf str,
1320    schema: &schema::Element,
1321) -> Result<ParseReport<'buf>, Error> {
1322    let parser = Parser::new(json);
1323    let mut unexpected_fields = vec![];
1324    // The current node of the schema is the last element of this `Vec`.
1325    let mut expectation_stack = vec![schema.to_expectation()];
1326
1327    for event in parser {
1328        match event? {
1329            parser::Event::Open { kind, parent_path } => {
1330                // Take the schema expectation off the stack.
1331                // This is simpler than taking a `&mut` to the last element.
1332                let Some(expectation) = expectation_stack.pop() else {
1333                    return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1334                        .into_partial_error_without_token()
1335                        .with_root_path());
1336                };
1337
1338                if tracing::enabled!(Level::DEBUG) {
1339                    match kind {
1340                        ObjectKind::Array => {
1341                            trace!("{parent_path} [ {}", DisplayExpectStack(&expectation_stack));
1342                        }
1343                        ObjectKind::Object => trace!(
1344                            "{parent_path} {{ {}",
1345                            DisplayExpectStack(&expectation_stack)
1346                        ),
1347                    }
1348                }
1349
1350                match expectation {
1351                    schema::Expect::Array(elem) => {
1352                        // If the opening element is at the root we only care if the element
1353                        // is an array or not.
1354                        if parent_path.is_root() {
1355                            let next = match kind {
1356                                ObjectKind::Array => schema::Expect::Array(elem),
1357                                ObjectKind::Object => schema::Expect::UnmatchedArray,
1358                            };
1359
1360                            expectation_stack.push(next);
1361                            trace!("{}", DisplayExpectStack(&expectation_stack));
1362                            continue;
1363                        }
1364
1365                        if !parent_path.is_array() {
1366                            expectation_stack.push(schema::Expect::UnmatchedArray);
1367                            trace!("{}", DisplayExpectStack(&expectation_stack));
1368                            continue;
1369                        }
1370
1371                        expectation_stack.push(schema::Expect::Array(Arc::clone(&elem)));
1372                        // Each array element should match this expectation
1373                        expectation_stack.push(elem.to_expectation());
1374                    }
1375                    schema::Expect::Object(fields) => {
1376                        // If the opening element is at the root there is no path to inspect.
1377                        // We only care if the element is an object or not.
1378                        if parent_path.is_root() {
1379                            let next = match kind {
1380                                ObjectKind::Array => schema::Expect::UnmatchedObject,
1381                                ObjectKind::Object => schema::Expect::Object(fields),
1382                            };
1383
1384                            expectation_stack.push(next);
1385                            trace!("{}", DisplayExpectStack(&expectation_stack));
1386                            continue;
1387                        }
1388                        let Some(key) = parent_path.as_object_key() else {
1389                            expectation_stack.push(schema::Expect::UnmatchedObject);
1390                            trace!("{}", DisplayExpectStack(&expectation_stack));
1391                            continue;
1392                        };
1393
1394                        let next = if let Some(elem) = fields.get(key.as_raw()) {
1395                            open_object(kind, elem.as_ref())
1396                        } else {
1397                            unexpected_fields.push(parent_path);
1398                            schema::Expect::OutOfSchema
1399                        };
1400
1401                        expectation_stack.push(schema::Expect::Object(fields));
1402                        expectation_stack.push(next);
1403                    }
1404                    schema::Expect::OutOfSchema => {
1405                        // If we're already outside of the schema we put that back on the stack
1406                        // and add a new one for the object that just opened.
1407                        //
1408                        // We need to track the object level even though the schema expectations
1409                        // have been exhausted, as we'll pop these placeholder `OutOfSchema`s
1410                        // off the stack when the object has closed so we land on the correct
1411                        // schema again.
1412                        expectation_stack.push(expectation);
1413                        expectation_stack.push(schema::Expect::OutOfSchema);
1414                    }
1415                    schema::Expect::UnmatchedArray | schema::Expect::UnmatchedObject => {
1416                        expectation_stack.push(expectation);
1417                        expectation_stack.push(schema::Expect::OutOfSchema);
1418                    }
1419                    _ => {
1420                        expectation_stack.push(expectation);
1421                    }
1422                }
1423
1424                trace!("{}", DisplayExpectStack(&expectation_stack));
1425            }
1426            parser::Event::Element { kind, parent_path } => {
1427                // Take the schema expectation off the stack.
1428                // This is simpler than taking a `&mut` to the last element.
1429                let Some(expectation) = expectation_stack.pop() else {
1430                    return Err(ErrorKind::Internal(Box::new(UnbalancedExpectStack))
1431                        .into_partial_error_without_token()
1432                        .with_root_path());
1433                };
1434
1435                // An `Element` of kind `Array` or `Object` means the `Element` is closed
1436                // and has completed construction. The expectation can remain off the stack.
1437                if let ValueKind::Array | ValueKind::Object = kind {
1438                    if tracing::enabled!(Level::DEBUG) {
1439                        match kind {
1440                            ValueKind::Array => {
1441                                trace!(
1442                                    "{parent_path} ] {}",
1443                                    DisplayExpectStack(&expectation_stack)
1444                                );
1445                            }
1446                            ValueKind::Object => trace!(
1447                                "{parent_path} }} {}",
1448                                DisplayExpectStack(&expectation_stack)
1449                            ),
1450                            _ => (),
1451                        }
1452                    }
1453                    continue;
1454                }
1455
1456                match expectation {
1457                    #[expect(
1458                        clippy::unreachable,
1459                        reason = "The parser only emits an `Event::Complete` for a scalar object at the root"
1460                    )]
1461                    schema::Expect::Object(fields) => match &*parent_path {
1462                        PathNode::Root => unreachable!(),
1463                        PathNode::Array { .. } => {
1464                            expectation_stack.push(schema::Expect::UnmatchedObject);
1465                        }
1466                        PathNode::Object { parent, key } => {
1467                            trace!("{parent:#}.{key}");
1468
1469                            if !fields.contains_key(key.as_raw()) {
1470                                unexpected_fields.push(parent_path);
1471                            }
1472
1473                            expectation_stack.push(schema::Expect::Object(fields));
1474                        }
1475                    },
1476                    schema::Expect::OutOfSchema => {
1477                        unexpected_fields.push(parent_path);
1478                        expectation_stack.push(expectation);
1479                    }
1480                    _ => {
1481                        expectation_stack.push(expectation);
1482                    }
1483                }
1484            }
1485            parser::Event::Complete(element) => {
1486                if element.value().is_scalar() {
1487                    unexpected_fields.push(element.path_node());
1488                }
1489
1490                // Parsing the JSON is complete.
1491                // Return the `Element` and the unexpected fields collected during parsing
1492                return Ok(ParseReport {
1493                    element,
1494                    unexpected_fields: UnexpectedFields::from_vec(unexpected_fields),
1495                });
1496            }
1497        }
1498    }
1499
1500    Err(ErrorKind::UnexpectedEOF
1501        .into_partial_error_without_token()
1502        .with_root_path())
1503}
1504
1505fn open_object(kind: ObjectKind, elem: Option<&Arc<schema::Element>>) -> schema::Expect {
1506    let Some(schema) = elem else {
1507        return schema::Expect::OutOfSchema;
1508    };
1509
1510    match (kind, &**schema) {
1511        (ObjectKind::Object | ObjectKind::Array, schema::Element::Scalar) => {
1512            schema::Expect::UnmatchedScalar
1513        }
1514        (ObjectKind::Object, schema::Element::Array(_)) => schema::Expect::UnmatchedArray,
1515        (ObjectKind::Object, schema::Element::Object(fields)) => {
1516            schema::Expect::Object(Arc::clone(fields))
1517        }
1518        (ObjectKind::Array, schema::Element::Array(element)) => {
1519            schema::Expect::Array(Arc::clone(element))
1520        }
1521        (ObjectKind::Array, schema::Element::Object(_)) => schema::Expect::UnmatchedObject,
1522    }
1523}
1524
1525/// The output of the `parse_with_schema` function where the parsed JSON `Element` is returned
1526/// along with a list of fields that the schema did not define.
1527#[derive(Debug)]
1528pub(crate) struct ParseReport<'buf> {
1529    /// The root JSON [`Element`].
1530    pub element: Element<'buf>,
1531
1532    /// A list of fields that were not expected: The schema did not define them.
1533    pub unexpected_fields: UnexpectedFields<'buf>,
1534}
1535
1536#[cfg(test)]
1537pub mod test {
1538    #![allow(clippy::missing_panics_doc, reason = "tests are allowed to panic")]
1539    #![allow(clippy::panic, reason = "tests are allowed panic")]
1540
1541    use crate::{json::match_path_node, test::Expectation};
1542
1543    use super::{
1544        parser::Span, walk::DepthFirst, ElemId, Element, Field, FieldsAsExt as _, PathNode,
1545        PathNodeRef, PathRef, RawStr, UnexpectedFields, Value,
1546    };
1547
1548    impl<'buf> Element<'buf> {
1549        /// Consume the `Element` and return only the `Value`.
1550        pub(crate) fn into_value(self) -> Value<'buf> {
1551            self.value
1552        }
1553
1554        /// Consume the `Element` and return the `Path`, `Span` and `Value` as a tuple.
1555        pub(crate) fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1556            let Self {
1557                id,
1558                path_node: path,
1559                span,
1560                value,
1561            } = self;
1562            (id, path, span, value)
1563        }
1564
1565        pub(crate) fn find_field(&self, key: &str) -> Option<&Field<'buf>> {
1566            self.as_object_fields()
1567                .and_then(|fields| fields.find_field(key))
1568        }
1569    }
1570
1571    impl<'buf> Value<'buf> {
1572        /// Return true if the `Value` is an `Array`.
1573        pub(crate) fn is_array(&self) -> bool {
1574            matches!(self, Value::Array(_))
1575        }
1576
1577        /// Return true if the `Value` is an `Object`.
1578        pub(crate) fn is_object(&self) -> bool {
1579            matches!(self, Value::Object(_))
1580        }
1581
1582        pub(crate) fn as_string(&self) -> Option<&RawStr<'buf>> {
1583            match self {
1584                Value::String(s) => Some(s),
1585                _ => None,
1586            }
1587        }
1588    }
1589
1590    impl<'buf> Field<'buf> {
1591        pub fn id(&self) -> ElemId {
1592            self.0.id()
1593        }
1594
1595        pub fn into_parts(self) -> (ElemId, PathNodeRef<'buf>, Span, Value<'buf>) {
1596            self.0.into_parts()
1597        }
1598    }
1599
1600    impl<'buf> UnexpectedFields<'buf> {
1601        /// The tests need to assert against the contents.
1602        pub(super) fn into_inner(self) -> Vec<PathNodeRef<'buf>> {
1603            self.0
1604        }
1605
1606        /// Filter off the fields that match the glob.
1607        fn filter_matches(&mut self, glob: &PathGlob) {
1608            self.0.retain(|path| !glob.matches(path));
1609        }
1610    }
1611
1612    /// A string based `Path` that can contain glob `*` patterns in place of a literal path element.
1613    /// The glob means that the path section can be any valid section.
1614    #[derive(Debug)]
1615    pub(crate) struct PathGlob(String);
1616
1617    impl PathGlob {
1618        /// Return true if this `PathGlob` matches the given `PathNode`.
1619        pub(crate) fn matches(&self, path: &PathNode<'_>) -> bool {
1620            const WILDCARD: &str = "*";
1621
1622            match_path_node(path, &self.0, |s| {
1623                // If the `PathGlob` segment is a glob, then continue to the next segment.
1624                s == WILDCARD
1625            })
1626        }
1627    }
1628
1629    /// The tests need to assert against literal `ElemId`s.
1630    impl From<usize> for ElemId {
1631        fn from(value: usize) -> Self {
1632            Self(value)
1633        }
1634    }
1635
1636    impl<'a> From<&'a str> for PathGlob {
1637        fn from(s: &'a str) -> Self {
1638            Self(s.into())
1639        }
1640    }
1641
1642    impl From<String> for PathGlob {
1643        fn from(s: String) -> Self {
1644            Self(s)
1645        }
1646    }
1647
1648    impl<'de> serde::Deserialize<'de> for PathGlob {
1649        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1650        where
1651            D: ::serde::Deserializer<'de>,
1652        {
1653            let s = <String as ::serde::Deserialize>::deserialize(deserializer)?;
1654            Ok(Self(s))
1655        }
1656    }
1657
1658    /// A map of `Element`s referenced by their unique Id.
1659    pub struct ElementMap<'a, 'buf>(Vec<&'a Element<'buf>>);
1660
1661    impl<'a, 'buf> ElementMap<'a, 'buf> {
1662        /// Create a new `ElementMap` by traversing the `Element` tree from the given node.
1663        pub fn for_elem(root: &'a Element<'buf>) -> Self {
1664            // The walker will emit `Element`s ordered by their Id.
1665            let walker = DepthFirst::new(root);
1666            Self(walker.collect())
1667        }
1668
1669        /// Return the `Element` with the given id.
1670        pub fn get(&self, id: ElemId) -> &Element<'buf> {
1671            self.0.get(id.0).map(|e| &**e).unwrap()
1672        }
1673
1674        /// Return the `Path` of the `Element` with the given id.
1675        pub fn path(&self, id: ElemId) -> PathRef<'buf> {
1676            self.0.get(id.0).map(|elem| elem.path()).unwrap()
1677        }
1678    }
1679
1680    /// The `unexpected_fields` should be empty. If not then panic with a truncated list of the fields.
1681    #[track_caller]
1682    pub(crate) fn expect_no_unexpected_fields(
1683        expect_file_name: &str,
1684        unexpected_fields: &UnexpectedFields<'_>,
1685    ) {
1686        if !unexpected_fields.is_empty() {
1687            const MAX_FIELD_DISPLAY: usize = 20;
1688
1689            if unexpected_fields.len() > MAX_FIELD_DISPLAY {
1690                let truncated_fields = unexpected_fields
1691                    .iter()
1692                    .take(MAX_FIELD_DISPLAY)
1693                    .map(|path| path.to_string())
1694                    .collect::<Vec<_>>();
1695
1696                panic!(
1697                    "The expect file `{expect_file_name}` didn't expect `{}` unexpected fields;\n\
1698                    displaying the first ({}):\n{}\n... and {} more",
1699                    unexpected_fields.len(),
1700                    truncated_fields.len(),
1701                    truncated_fields.join(",\n"),
1702                    unexpected_fields.len() - truncated_fields.len(),
1703                )
1704            } else {
1705                panic!(
1706                    "The expect file `{expect_file_name}` didn't expect `{}` unexpected fields:\n{}",
1707                    unexpected_fields.len(),
1708                    unexpected_fields.to_strings().join(",\n")
1709                )
1710            };
1711        }
1712    }
1713
1714    /// Compare the `unexpected_fields` to the expected fields globs.
1715    ///
1716    /// Panic if there are any fields not expected.
1717    #[track_caller]
1718    pub(crate) fn expect_unexpected_fields(
1719        expect_file_name: &str,
1720        unexpected_fields: &mut UnexpectedFields<'_>,
1721        expected: Expectation<Vec<PathGlob>>,
1722    ) {
1723        if let Expectation::Present(expectation) = expected {
1724            let unexpected_fields_expect = expectation.expect_value();
1725
1726            // Remove any fields that match the expected glob.
1727            // The remaining fields are truly unexpected.
1728            for expect_glob in unexpected_fields_expect {
1729                unexpected_fields.filter_matches(&expect_glob);
1730            }
1731
1732            expect_no_unexpected_fields(expect_file_name, unexpected_fields);
1733        } else {
1734            expect_no_unexpected_fields(expect_file_name, unexpected_fields);
1735        }
1736    }
1737
1738    #[cfg(test)]
1739    mod test_path_matches_glob {
1740        use std::sync::Arc;
1741
1742        use crate::test;
1743
1744        use super::{PathGlob, PathNode};
1745
1746        #[test]
1747        fn should_match_path() {
1748            test::setup();
1749
1750            let root = Arc::new(PathNode::Root);
1751            let path_a = Arc::new(PathNode::Array {
1752                parent: Arc::clone(&root),
1753                index: 1,
1754            });
1755            let path_b = Arc::new(PathNode::Object {
1756                parent: Arc::clone(&path_a),
1757                key: r#""name""#.into(),
1758            });
1759            let path_c = PathNode::Object {
1760                parent: Arc::clone(&path_b),
1761                key: r#""gene""#.into(),
1762            };
1763
1764            assert!(PathGlob::from("$").matches(&root));
1765            assert!(PathGlob::from("*").matches(&root));
1766
1767            assert!(!PathGlob::from("*").matches(&path_a));
1768            assert!(PathGlob::from("*.*").matches(&path_a));
1769            assert!(PathGlob::from("$.*").matches(&path_a));
1770            assert!(PathGlob::from("$.1").matches(&path_a));
1771
1772            assert!(!PathGlob::from("*").matches(&path_b));
1773            assert!(!PathGlob::from("*.*").matches(&path_b));
1774            assert!(PathGlob::from("*.*.*").matches(&path_b));
1775            assert!(PathGlob::from("$.*.*").matches(&path_b));
1776            assert!(PathGlob::from("$.1.*").matches(&path_b));
1777            assert!(PathGlob::from("$.*.name").matches(&path_b));
1778            assert!(PathGlob::from("$.1.name").matches(&path_b));
1779
1780            assert!(PathGlob::from("$.1.name.gene").matches(&path_c));
1781        }
1782    }
1783}
1784
1785#[cfg(test)]
1786mod test_path {
1787    use super::{Path, PathPiece};
1788
1789    #[test]
1790    fn path_should_cmp_with_str() {
1791        assert_ne!(Path::root(), "");
1792        assert_eq!(Path::root(), "$");
1793        assert_eq!(Path(vec![PathPiece::Object("field_a".into())]), "$.field_a");
1794        assert_eq!(Path(vec![PathPiece::Array(1)]), "$.1");
1795        assert_eq!(
1796            Path(vec![
1797                PathPiece::Object("field_a".into()),
1798                PathPiece::Array(1)
1799            ]),
1800            "$.field_a.1"
1801        );
1802    }
1803
1804    #[test]
1805    fn path_should_display() {
1806        assert_eq!(Path::root().to_string(), "$");
1807        assert_eq!(
1808            Path(vec![PathPiece::Object("field_a".into())]).to_string(),
1809            "$.field_a"
1810        );
1811        assert_eq!(Path(vec![PathPiece::Array(1)]).to_string(), "$.1");
1812        assert_eq!(
1813            Path(vec![
1814                PathPiece::Object("field_a".into()),
1815                PathPiece::Array(1)
1816            ])
1817            .to_string(),
1818            "$.field_a.1"
1819        );
1820    }
1821}
1822
1823#[cfg(test)]
1824mod test_parse_with_schema {
1825    use crate::{json_schema, test};
1826
1827    use super::{parse_with_schema, ParseReport};
1828
1829    #[test]
1830    fn should_report_unexpected_fields_for_root_element() {
1831        const JSON: &str = "null";
1832
1833        test::setup();
1834
1835        let schema = json_schema!({
1836            "id",
1837            "currency",
1838        });
1839
1840        let report = parse_with_schema(JSON, &schema).unwrap();
1841        let ParseReport {
1842            element: _,
1843            unexpected_fields,
1844        } = report;
1845
1846        {
1847            let [field_a] = unexpected_fields.into_inner().try_into().unwrap();
1848            assert_eq!(*field_a, "$");
1849        }
1850    }
1851
1852    #[test]
1853    fn should_report_unexpected_fields_in_flat_object() {
1854        const JSON: &str = r#"{
1855    "id": "tariff_id",
1856    "currency": "EUR",
1857    "name": "Barry",
1858    "address": "Barrystown"
1859}"#;
1860
1861        test::setup();
1862
1863        let schema = json_schema!({
1864            "id",
1865            "currency",
1866        });
1867
1868        let report = parse_with_schema(JSON, &schema).unwrap();
1869        let ParseReport {
1870            element: _,
1871            unexpected_fields,
1872        } = report;
1873
1874        {
1875            let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1876            assert_eq!(*field_a, "$.name");
1877            assert_eq!(*field_b, "$.address");
1878        }
1879    }
1880
1881    #[test]
1882    fn should_report_unexpected_fields_in_nested_object() {
1883        const JSON: &str = r#"{
1884    "id": "tariff_id",
1885    "currency": "EUR",
1886    "owner": {
1887        "id": "456856",
1888        "subscription_id": "tedi4568",
1889        "name": "Barry",
1890        "address": "Barrystown"
1891    }
1892}"#;
1893
1894        test::setup();
1895
1896        let schema = json_schema!({
1897            "id",
1898            "currency",
1899            "owner": {
1900                "id",
1901                "subscription_id"
1902            }
1903        });
1904
1905        let report = parse_with_schema(JSON, &schema).unwrap();
1906        let ParseReport {
1907            element: _,
1908            unexpected_fields,
1909        } = report;
1910
1911        {
1912            let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1913            assert_eq!(*field_a, "$.owner.name");
1914            assert_eq!(*field_b, "$.owner.address");
1915        }
1916    }
1917
1918    #[test]
1919    fn should_parse_nested_object_out_of_schema() {
1920        const JSON: &str = r#"{
1921    "id": "tariff_id",
1922    "owner": {
1923        "id": "456856",
1924        "subscription_id": "tedi4568",
1925        "name": "Barry",
1926        "address": {
1927            "city": "Barrystown",
1928            "street": "Barrysstreet"
1929        }
1930    },
1931    "currency": "EUR",
1932    "country": "NL"
1933}"#;
1934
1935        test::setup();
1936
1937        let schema = json_schema!({
1938            "id",
1939            "currency",
1940            "owner"
1941        });
1942
1943        let report = parse_with_schema(JSON, &schema).unwrap();
1944        let ParseReport {
1945            element: _,
1946            unexpected_fields,
1947        } = report;
1948
1949        {
1950            let [field_a, field_b, field_c, field_d, field_e, field_f] =
1951                unexpected_fields.into_inner().try_into().unwrap();
1952            assert_eq!(*field_a, "$.owner.id");
1953            assert_eq!(*field_b, "$.owner.subscription_id");
1954            assert_eq!(*field_c, "$.owner.name");
1955            assert_eq!(*field_d, "$.owner.address.city");
1956            assert_eq!(*field_e, "$.owner.address.street");
1957            assert_eq!(*field_f, "$.country");
1958        }
1959    }
1960
1961    #[test]
1962    fn should_report_unexpected_fields_in_array_with_nested_object() {
1963        const JSON: &str = r#"{
1964    "id": "tariff_id",
1965    "currency": "EUR",
1966    "elements": [{
1967        "id": "456856",
1968        "subscription_id": "tedi4568",
1969        "name": "Barry",
1970        "address": "Barrystown"
1971    }]
1972}"#;
1973
1974        test::setup();
1975
1976        let schema = json_schema!({
1977            "id",
1978            "currency",
1979            "elements": [{
1980                "id",
1981                "subscription_id"
1982            }]
1983        });
1984
1985        let report = parse_with_schema(JSON, &schema).unwrap();
1986        let ParseReport {
1987            element: _,
1988            unexpected_fields,
1989        } = report;
1990
1991        {
1992            let [field_a, field_b] = unexpected_fields.into_inner().try_into().unwrap();
1993            assert_eq!(*field_a, "$.elements.0.name");
1994            assert_eq!(*field_b, "$.elements.0.address");
1995        }
1996    }
1997
1998    #[test]
1999    fn should_report_unexpected_fields_in_array_of_nested_objects() {
2000        const JSON: &str = r#"{
2001    "id": "tariff_id",
2002    "currency": "EUR",
2003    "elements": [
2004        {
2005            "id": "456856",
2006            "subscription_id": "tedi4568",
2007            "name": "Barry",
2008            "address": "Barrystown"
2009        },
2010        {
2011            "id": "8746we",
2012            "subscription_id": "dfr345",
2013            "name": "Gerry",
2014            "address": "Gerrystown"
2015        }
2016    ]
2017}"#;
2018
2019        test::setup();
2020
2021        let schema = json_schema!({
2022            "id",
2023            "currency",
2024            "elements": [{
2025                "id",
2026                "subscription_id"
2027            }]
2028        });
2029
2030        let report = parse_with_schema(JSON, &schema).unwrap();
2031        let ParseReport {
2032            element: _,
2033            unexpected_fields,
2034        } = report;
2035
2036        {
2037            let [field_a, field_b, field_c, field_d] =
2038                unexpected_fields.into_inner().try_into().unwrap();
2039            assert_eq!(*field_a, "$.elements.0.name");
2040            assert_eq!(*field_b, "$.elements.0.address");
2041            assert_eq!(*field_c, "$.elements.1.name");
2042            assert_eq!(*field_d, "$.elements.1.address");
2043        }
2044    }
2045
2046    #[test]
2047    fn should_report_unexpected_fields_in_array_of_objects() {
2048        const JSON: &str = r#"[
2049    {
2050        "id": "456856",
2051        "subscription_id": "tedi4568",
2052        "name": "Barry",
2053        "address": "Barrystown"
2054    },
2055    {
2056        "id": "8746we",
2057        "subscription_id": "dfr345",
2058        "name": "Gerry",
2059        "address": "Gerrystown"
2060    }
2061]"#;
2062
2063        test::setup();
2064
2065        let schema = json_schema!([
2066            {
2067                "id",
2068                "subscription_id"
2069            }
2070        ]);
2071
2072        let report = parse_with_schema(JSON, &schema).unwrap();
2073        let ParseReport {
2074            element: _,
2075            unexpected_fields,
2076        } = report;
2077
2078        {
2079            let [field_a, field_b, field_c, field_d] =
2080                unexpected_fields.into_inner().try_into().unwrap();
2081            assert_eq!(*field_a, "$.0.name");
2082            assert_eq!(*field_b, "$.0.address");
2083            assert_eq!(*field_c, "$.1.name");
2084            assert_eq!(*field_d, "$.1.address");
2085        }
2086    }
2087}
2088
2089#[cfg(test)]
2090mod test_source_json {
2091    use super::{parse, walk};
2092
2093    #[test]
2094    fn should_resolve_to_source_json() {
2095        const JSON: &str = r#"{
2096    "name": "David Byrne",
2097    "hobbies": ["song writing", "thinking about society"]
2098}"#;
2099
2100        let element = parse(JSON).unwrap();
2101
2102        let mut walk = walk::DepthFirst::new(&element);
2103
2104        let root = walk.next().unwrap();
2105        assert_eq!(root.source_json(JSON), JSON);
2106
2107        let field_name = walk.next().unwrap();
2108        assert_eq!(field_name.source_json(JSON), r#""name": "David Byrne""#);
2109        assert_eq!(field_name.source_json_value(JSON), r#""David Byrne""#);
2110
2111        let field_hobbies = walk.next().unwrap();
2112        assert_eq!(
2113            field_hobbies.source_json(JSON),
2114            r#""hobbies": ["song writing", "thinking about society"]"#
2115        );
2116        assert_eq!(
2117            field_hobbies.source_json_value(JSON),
2118            r#"["song writing", "thinking about society"]"#
2119        );
2120
2121        let hobbies_one = walk.next().unwrap();
2122        assert_eq!(hobbies_one.source_json(JSON), r#""song writing""#);
2123        assert_eq!(hobbies_one.source_json_value(JSON), r#""song writing""#);
2124
2125        let hobbies_two = walk.next().unwrap();
2126        assert_eq!(hobbies_two.source_json(JSON), r#""thinking about society""#);
2127        assert_eq!(
2128            hobbies_two.source_json_value(JSON),
2129            r#""thinking about society""#
2130        );
2131    }
2132}
2133
2134#[cfg(test)]
2135mod test_path_node {
2136    use std::sync::Arc;
2137
2138    use super::{
2139        parser::{RawStr, Token, TokenType},
2140        PathNode, PathNodeRef, Span,
2141    };
2142
2143    #[test]
2144    const fn should_be_send_and_sync() {
2145        const fn f<T: Send + Sync>() {}
2146
2147        f::<PathNode<'_>>();
2148        f::<PathNodeRef<'_>>();
2149    }
2150
2151    #[test]
2152    fn should_display_path() {
2153        let root = Arc::new(PathNode::Root);
2154        let path_a = Arc::new(PathNode::Array {
2155            parent: Arc::clone(&root),
2156            index: 1,
2157        });
2158        let path_b = Arc::new(PathNode::Object {
2159            parent: Arc::clone(&path_a),
2160            key: r#""name""#.into(),
2161        });
2162        let path_c = Arc::new(PathNode::Object {
2163            parent: Arc::clone(&path_b),
2164            key: r#""gene""#.into(),
2165        });
2166
2167        assert_eq!(*root, "$");
2168        assert_eq!(*path_a, "$.1");
2169        assert_eq!(*path_b, "$.1.name");
2170        assert_eq!(*path_c, "$.1.name.gene");
2171    }
2172
2173    impl<'buf> From<&'buf str> for RawStr<'buf> {
2174        #[track_caller]
2175        fn from(s: &'buf str) -> Self {
2176            RawStr::from_quoted_str(
2177                s,
2178                Token {
2179                    kind: TokenType::String,
2180                    span: Span::default(),
2181                },
2182            )
2183            .unwrap()
2184        }
2185    }
2186}