ocpi_tariffs/
warning.rs

1//! These types are the basis for writing functions that can emit a set of [`Warning`]s based on the value they are trying to create.
2//!
3//! The aim is for functions to be as resilient as possible while creating the value and emit commentary on their progress in the form of a growing set of [`Warning`]s.
4//!
5//! The caller of the function can use the set of [`Warning`]s to decide whether the operation was a success or failure and whether the value can be used or needs to be modified.
6//!  
7//! A concrete example is the conversion of a JSON [`json::Element`] into a `country::Code`. The [`json::Element`] may be the incorrect type and so the function issues a [`Warning`] and exits as it cannot continue with the given data. The signature of this fn is something like:
8//!
9//! ```rust ignore
10//! // file: country.rs
11//!  
12//! pub enum Warning {
13//!     InvalidType,
14//!     ...
15//! }
16//!
17//! pub enum Expect {
18//!     Alpha2,
19//!     Alpha3
20//! }
21//!
22//! pub enum Code {
23//!     fn from_json_element(json: json::Element, expect: Expect) -> Verdict<Code, Warning> {
24//!         ...
25//!     }
26//! }
27//! ```
28//!
29//! A [`Verdict`] is a [`Result`] where both the `Ok` and `Err` variants return a potential set of [`Warning`]s.
30//! The `Ok` variant is `Caveat<T>`, where a [`Caveat`] contains a value but potentially contains cautionary details to be taken into account when using the value.
31//! Hence the name.
32//!
33//! The `Err` variant is `Warnings<K>`, a collection of [`Warning`]s. A [`Warning`] can be converted into an `Error` by the caller. A `Caveat<T>` is more completely described as `Caveat<T, K>` where the `Caveat` contains a value `T` and a set of `Warnings<K>`.
34//!
35//! All of this is to say that a resilient function can always return [`Warning`]s and the caller can gather them
36//! together into a new set or fail.
37//!
38//! Returning to the example of the [`country::Code`](crate::country::Code), if the [`json::Element`] is the expected string type, then processing continues.
39//! The string may contain control chars or escape chars and both these cases will emit a [`Warning`].
40//! The string may be made up of three chars when two were expected.
41//! This is the interesting case, as some [`country::Code`](crate::country::Code) fields are `alpha-3` where others are `alpha-2`.
42//! Processing can still continue, as an `alpha-3` code can be converted to an `alpha-2` simply, while emitting a [`Warning`].
43//!
44//! The caller can decide whether this is acceptable or not.
45
46use std::{borrow::Cow, fmt, slice};
47
48use tracing::error;
49
50use crate::json;
51
52/// Implement `IntoCaveat` for the given type so that it can take part in the `Warning` system.
53#[doc(hidden)]
54#[macro_export]
55macro_rules! into_caveat {
56    ($kind:ident<$life:lifetime>) => {
57        impl<$life> $crate::IntoCaveat for $kind<$life> {
58            fn into_caveat<K: $crate::warning::Kind>(
59                self,
60                warnings: $crate::warning::Set<K>,
61            ) -> $crate::Caveat<Self, K> {
62                $crate::Caveat::new(self, warnings)
63            }
64        }
65    };
66    ($kind:path) => {
67        impl $crate::IntoCaveat for $kind {
68            fn into_caveat<K: $crate::warning::Kind>(
69                self,
70                warnings: $crate::warning::Set<K>,
71            ) -> $crate::Caveat<Self, K> {
72                $crate::Caveat::new(self, warnings)
73            }
74        }
75    };
76}
77
78/// Implement `IntoCaveat` for the given type so that it can take part in the `Warning` system.
79macro_rules! into_caveat_all {
80    ($($kind:ty),+) => {
81        $(impl IntoCaveat for $kind {
82            fn into_caveat<K: Kind>(
83                self,
84                warnings: Set<K>,
85            ) -> $crate::Caveat<Self, K> {
86                $crate::Caveat::new(self, warnings)
87            }
88        })+
89    };
90}
91
92/// A `Verdict` is a standard [`Result`] with [`Warning`]s potentially issued for both the `Ok` and `Err` variants.
93pub type Verdict<T, K> = Result<Caveat<T, K>, Set<K>>;
94
95/// A value that may have associated [`Warning`]s.
96///
97/// Even though the value has been created there may be certain caveats you should be aware of before using it.
98#[derive(Debug)]
99pub struct Caveat<T, K: Kind> {
100    /// The value created by the function.
101    value: T,
102
103    /// A list of [`Warning`]s or caveats issued when creating the value.
104    warnings: Set<K>,
105}
106
107impl<T, K> Caveat<T, K>
108where
109    T: IntoCaveat,
110    K: Kind,
111{
112    /// The only way to create `Caveat<T>` is if `T` impls `IntoCaveat`.
113    pub(crate) fn new(value: T, warnings: Set<K>) -> Self {
114        Self { value, warnings }
115    }
116}
117
118impl<T, K> Caveat<T, K>
119where
120    K: Kind,
121{
122    /// Return the value and any [`Warning`]s stored in the `Caveat`.
123    pub fn into_parts(self) -> (T, Set<K>) {
124        let Self { value, warnings } = self;
125        (value, warnings)
126    }
127
128    /// Return the value and drop any warnings contained within.
129    pub fn ignore_warnings(self) -> T {
130        self.value
131    }
132
133    pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Caveat<U, K> {
134        let Self { value, warnings } = self;
135        Caveat {
136            value: op(value),
137            warnings,
138        }
139    }
140}
141
142/// Convert a `Caveat`-like type into a `T` by gathering up it's [`Warning`]s.
143pub(crate) trait GatherWarnings<T, K>
144where
145    K: Kind,
146{
147    type Output;
148
149    /// Convert a `Caveat`-like type into a `T` by gathering up it's [`Warning`]s.
150    fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
151    where
152        K: Into<KA>,
153        KA: Kind;
154}
155
156/// Convert a `Caveat<T>` into `T` by gathering up it's `Warning`s.
157impl<T, K> GatherWarnings<T, K> for Caveat<T, K>
158where
159    K: Kind,
160{
161    type Output = T;
162
163    /// Convert a `Caveat<T>` into `T` by gathering up it's `Warning`s.
164    fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
165    where
166        K: Into<KA>,
167        KA: Kind,
168    {
169        let Self {
170            value,
171            warnings: inner_warnings,
172        } = self;
173
174        warnings.0.extend(inner_warnings.0.into_iter().map(|warn| {
175            let Warning { kind, elem_id } = warn;
176            Warning {
177                kind: kind.into(),
178                elem_id,
179            }
180        }));
181
182        value
183    }
184}
185
186/// Convert a `Option<Caveat<T>>` into `Option<T>` by gathering up it's `Warning`s.
187impl<T, K> GatherWarnings<T, K> for Option<Caveat<T, K>>
188where
189    K: Kind,
190{
191    type Output = Option<T>;
192
193    /// Convert a `Caveat` related to type `T` into a `T` by gathering it's [`Warning`]s.
194    fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
195    where
196        K: Into<KA>,
197        KA: Kind,
198    {
199        match self {
200            Some(cv) => Some(cv.gather_warnings_into(warnings)),
201            None => None,
202        }
203    }
204}
205
206/// Convert a `Result<Caveat<T>>` into `Result<T>` by gathering up it's `Warning`s.
207impl<T, K, E> GatherWarnings<T, K> for Result<Caveat<T, K>, E>
208where
209    K: Kind,
210    E: std::error::Error,
211{
212    type Output = Result<T, E>;
213
214    /// Convert a `Caveat` related to type `T` into a `T` by gathering it's [`Warning`]s.
215    fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
216    where
217        K: Into<KA>,
218        KA: Kind,
219    {
220        match self {
221            Ok(cv) => Ok(cv.gather_warnings_into(warnings)),
222            Err(err) => Err(err),
223        }
224    }
225}
226
227/// Convert a `Vec<Caveat<T>>` into `Vec<T>` by gathering up each elements `Warning`s.
228impl<T, K> GatherWarnings<T, K> for Vec<Caveat<T, K>>
229where
230    K: Kind,
231{
232    type Output = Vec<T>;
233
234    /// Convert a `Caveat` related to type `T` into a `T` by gathering it's [`Warning`]s.
235    fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
236    where
237        K: Into<KA>,
238        KA: Kind,
239    {
240        self.into_iter()
241            .map(|cv| cv.gather_warnings_into(warnings))
242            .collect()
243    }
244}
245
246/// Converts a value `T` into a `Caveat`.
247///
248/// Each module can use this to whitelist their types for conversion to `Caveat<T>`.
249pub trait IntoCaveat: Sized {
250    /// Any type can be converted to `Caveat<T>` only a list of [`Warning`]s is required.
251    fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K>;
252}
253
254into_caveat_all!(
255    (),
256    bool,
257    char,
258    u8,
259    u16,
260    u32,
261    u64,
262    u128,
263    i8,
264    i16,
265    i32,
266    i64,
267    i128
268);
269
270/// Allow `Cow<'a, str>` to be converted into a `Caveat`.
271impl IntoCaveat for Cow<'_, str> {
272    fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K> {
273        Caveat::new(self, warnings)
274    }
275}
276
277/// Allow `Option<T: IntoCaveat>` to be converted into a `Caveat`.
278impl<T> IntoCaveat for Option<T>
279where
280    T: IntoCaveat,
281{
282    fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K> {
283        Caveat::new(self, warnings)
284    }
285}
286
287/// `Verdict` specific extinsion methods for the `Result` type.
288pub trait VerdictExt<T, K: Kind> {
289    /// Converts from `Verdict<T, K>` to `Caveat<Option<T>, K>`.
290    ///
291    /// The `Ok` and `Err` variants are encoded as `Some(T)` and `None` respectively and the [`Warning`]s are retained.
292    fn ok_caveat(self) -> Caveat<Option<T>, K>;
293}
294
295impl<T, K: Kind> VerdictExt<T, K> for Verdict<T, K>
296where
297    T: IntoCaveat,
298{
299    fn ok_caveat(self) -> Caveat<Option<T>, K> {
300        match self {
301            Ok(v) => {
302                let (v, warnings) = v.into_parts();
303                Some(v).into_caveat(warnings)
304            }
305            Err(warnings) => None.into_caveat(warnings),
306        }
307    }
308}
309
310/// Implement a conversion from `warning::Set<A>` to `warning::Set<B>` so that the `Err` variant
311/// of a `Verdict<_, A>` can be converted using the `?` operator to `Verdict<_, B>`.
312///
313/// `warning::Set::into_set` is used to perform the conversion between set A and B.
314#[macro_export]
315#[doc(hidden)]
316macro_rules! from_warning_set_to {
317    ($kind_a:path => $kind_b:path) => {
318        impl From<$crate::warning::Set<$kind_a>> for $crate::warning::Set<$kind_b> {
319            fn from(set_a: warning::Set<$kind_a>) -> Self {
320                set_a.into_set()
321            }
322        }
323    };
324}
325
326/// Convert an `Option` into a `Verdict` ready to exit the fn.
327pub trait OptionExt<T, K>
328where
329    K: Kind,
330{
331    /// Convert an `Option` into a `Verdict` ready to exit the fn.
332    fn exit_with_warning<F>(self, warnings: Set<K>, f: F) -> Verdict<T, K>
333    where
334        F: FnOnce() -> Warning<K>;
335}
336
337impl<T, K> OptionExt<T, K> for Option<T>
338where
339    T: IntoCaveat,
340    K: Kind,
341{
342    fn exit_with_warning<F>(self, mut warnings: Set<K>, f: F) -> Verdict<T, K>
343    where
344        F: FnOnce() -> Warning<K>,
345    {
346        if let Some(v) = self {
347            Ok(v.into_caveat(warnings))
348        } else {
349            warnings.push(f());
350            Err(warnings)
351        }
352    }
353}
354
355/// Groups together a module's `Kind` and the associated [`json::Element`].
356///
357/// The [`json::Element`] is referenced by `ElemId`.
358#[derive(Debug)]
359pub struct Warning<K: Kind> {
360    /// The kind of warning.
361    kind: K,
362
363    /// The Id of the element that caused the [`Warning`].
364    elem_id: Option<json::ElemId>,
365}
366
367impl<K: Kind> Warning<K> {
368    /// Create a Warning from a kind defined in a domain module and as associated `json::Element`.
369    pub(crate) const fn with_elem(kind: K, elem: &json::Element<'_>) -> Warning<K> {
370        Warning {
371            kind,
372            elem_id: Some(elem.id()),
373        }
374    }
375
376    /// Create a Warning from a kind defined in a domain module.
377    pub(crate) const fn only_kind(kind: K) -> Warning<K> {
378        Warning {
379            kind,
380            elem_id: None,
381        }
382    }
383
384    pub fn id(&self) -> Cow<'static, str> {
385        self.kind.id()
386    }
387
388    pub fn kind(&self) -> &K {
389        &self.kind
390    }
391
392    /// Consume the Warning and return the Kind of Warning.
393    pub fn into_kind(self) -> K {
394        self.kind
395    }
396}
397
398impl<K: Kind> fmt::Display for Warning<K> {
399    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400        write!(f, "{}", self.kind)
401    }
402}
403
404/// Each mod defines warnings for the type that it's trying to parse or lint from a [`json::Element`].
405///
406/// The `mod::Kind` should impl this trait to take part in the [`Warning`] system.
407pub trait Kind: Sized + fmt::Debug + fmt::Display {
408    /// Return the human readable identifier for the [`Warning`].
409    ///
410    /// This is used in the `auto_test` assertion system.
411    /// Changing these strings may require updating `output_price__cdr.json` files.
412    fn id(&self) -> Cow<'static, str>;
413}
414
415/// A set of [`Warning`]s transported through the system using a `Verdict` or `Caveat`.
416#[derive(Debug)]
417pub struct Set<K: Kind>(Vec<Warning<K>>);
418
419impl<K: Kind> Set<K> {
420    /// Create a new list of [`Warning`]s
421    pub(crate) fn new() -> Self {
422        Self(vec![])
423    }
424
425    /// Push a new [`Warning`] onto the list.
426    fn push(&mut self, warning: Warning<K>) {
427        self.0.push(warning);
428    }
429
430    /// Create and push a Warning from a kind defined in a domain module and as associated `json::Element`.
431    pub(crate) fn with_elem(&mut self, kind: K, elem: &json::Element<'_>) {
432        self.push(Warning::with_elem(kind, elem));
433    }
434
435    /// Create and push a Warning from a kind defined in a domain module.
436    pub(crate) fn only_kind(&mut self, kind: K) {
437        self.push(Warning::only_kind(kind));
438    }
439
440    /// Converts `Set<K>` into `Set<KB>` using the `impl From<K> for KB`.
441    ///
442    /// This is used by the `from_warning_set_to` macro.
443    pub(crate) fn into_set<KB>(self) -> Set<KB>
444    where
445        KB: Kind + From<K>,
446    {
447        let warnings = self
448            .0
449            .into_iter()
450            .map(|warn| {
451                let Warning { kind, elem_id } = warn;
452                Warning {
453                    kind: kind.into(),
454                    elem_id,
455                }
456            })
457            .collect();
458
459        Set(warnings)
460    }
461
462    pub(crate) fn into_parts_vec(self) -> Vec<(K, Option<json::ElemId>)> {
463        self.0
464            .into_iter()
465            .map(|Warning { kind, elem_id }| (kind, elem_id))
466            .collect()
467    }
468
469    /// Return the inner kinds.
470    ///
471    /// This should only be used in tests and the `mod price`.
472    #[expect(
473        dead_code,
474        reason = "This should only be used in tests and the `mod price`"
475    )]
476    pub(crate) fn to_kind_vec(&self) -> Vec<&K> {
477        self.0.iter().map(Warning::kind).collect()
478    }
479
480    /// Return true if the [`Warning`] list is empty.
481    pub fn is_empty(&self) -> bool {
482        self.0.is_empty()
483    }
484
485    /// Return true if the [`Warning`] list is empty.
486    pub fn len(&self) -> usize {
487        self.0.len()
488    }
489
490    /// Convert the set of [`Warning`]s into a `Report` ready for presentation.
491    pub fn into_report(self) -> Report<K> {
492        Report::new(self)
493    }
494}
495
496/// A collection of [`Warning`]s grouped by [`json::Element`].
497///
498/// Use the `Report::iter` to iterate over the [`json::Element`]s that have [`Warning`]s in order.
499#[derive(Debug)]
500pub struct Report<K>
501where
502    K: Kind,
503{
504    groups: Vec<Group<K>>,
505}
506
507/// A `Group` of `WarningKind`s that all share the same `ElemeId`.
508#[derive(Debug)]
509struct Group<K> {
510    /// The `ElemId` all [`Warning`]s share.
511    id: Option<json::ElemId>,
512
513    /// The group of `WarningKind`s.
514    warning_kinds: Vec<K>,
515}
516
517impl<K> Report<K>
518where
519    K: Kind,
520{
521    /// Create a new `Report` ready to present each [`json::Element`] that has [`Warning`]s.
522    fn new(warnings: Set<K>) -> Self {
523        let Set(mut warnings) = warnings;
524        // sort the warnings list so that we know the warngs are grouped by `ElemId`.
525        warnings.sort_unstable_by(|a, b| a.elem_id.cmp(&b.elem_id));
526        let mut warnings = warnings.into_iter();
527
528        let Some(first) = warnings.next() else {
529            return Self { groups: vec![] };
530        };
531
532        // Insert the first warning into the resulting elements list.
533        // This is used as the first id to start grouping by.
534        let mut groups = vec![Group {
535            id: first.elem_id,
536            warning_kinds: vec![first.kind],
537        }];
538
539        // Group the warnings by `ElemId`, the warnings list is already compacted so that the warnings
540        // Are sorted by `ElemId`. This loop simplify cuts the [`Warning`] list up into those groups.
541        for warn in warnings {
542            // Compare the current [`Warning`] with the next to detect the end of the current run/group.
543            if let Some(s) = groups.last_mut() {
544                if warn.elem_id == s.id {
545                    // The run continues, add the current `WarningKind` to the `Group`.
546                    s.warning_kinds.push(warn.kind);
547                } else {
548                    // This is the end of the run/group. Create a new `Group`.
549                    groups.push(Group {
550                        id: warn.elem_id,
551                        warning_kinds: vec![warn.kind],
552                    });
553                }
554            }
555        }
556
557        Self { groups }
558    }
559
560    /// Iterate over all [`json::Element`]s that have [`Warning`]s.
561    ///
562    /// The iteration happens in the order of the `Element::id`
563    pub fn iter<'a, 'bin>(&'a self, root: &'a json::Element<'bin>) -> ReportIter<'a, 'bin, K> {
564        ReportIter {
565            walker: json::walk::DepthFirst::new(root),
566            groups: self.groups.iter(),
567        }
568    }
569
570    /// Return true if there are no [`json::Element`]s with [`Warning`]s.
571    pub fn is_empty(&self) -> bool {
572        self.groups.is_empty()
573    }
574
575    /// Return the number of [`json::Element`]s with [`Warning`]s.
576    pub fn len(&self) -> usize {
577        self.groups.len()
578    }
579}
580
581/// An iterator the walks over all [`json::Element`]s and returns `Some` if the given
582/// [`json::Element`] has any [`Warning`]s.
583pub struct ReportIter<'a, 'bin, K: Kind> {
584    /// The [`json::Element`] tree walker.
585    walker: json::walk::DepthFirst<'a, 'bin>,
586
587    /// The iterator for all [`Warning`] `Group`s.
588    groups: slice::Iter<'a, Group<K>>,
589}
590
591impl<'a, 'bin, K: Kind> Iterator for ReportIter<'a, 'bin, K> {
592    type Item = ElementReport<'a, 'bin, K>;
593
594    fn next(&mut self) -> Option<Self::Item> {
595        let group = self.groups.next()?;
596
597        // Resolve the [`Warning`] `Group`'s `ElemId` to an [`json::Element`] in the given tree.
598        loop {
599            let Some(element) = self.walker.next() else {
600                if let Some(id) = group.id {
601                    error!("An Element with id: `{id}` was not found");
602                } else {
603                    error!("An Element without an id was not found");
604                }
605                return None;
606            };
607
608            if let Some(id) = group.id {
609                if element.id() == id {
610                    return Some(ElementReport {
611                        element,
612                        warnings: &group.warning_kinds,
613                    });
614                }
615            }
616        }
617    }
618}
619
620/// A report of all warnings for a given [`json::Element`].
621pub struct ElementReport<'a, 'bin, K: Kind> {
622    /// The [`json::Element`] that has [`Warning`]s.
623    pub element: &'a json::Element<'bin>,
624
625    /// The list of warnings for the [`json::Element`].
626    pub warnings: &'a [K],
627}
628
629impl<K: Kind> fmt::Debug for ElementReport<'_, '_, K> {
630    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
631        f.debug_struct("ElementReport")
632            .field("element", &self.element.path())
633            .field("warnings", &self.warnings)
634            .finish()
635    }
636}
637
638#[cfg(test)]
639mod test {
640    use std::{ops, slice};
641
642    use assert_matches::assert_matches;
643
644    use crate::json;
645
646    use super::{Caveat, Kind, Set, Warning};
647    impl<K: Kind> Warning<K> {
648        pub(crate) fn elem_id(&self) -> Option<json::ElemId> {
649            self.elem_id
650        }
651    }
652
653    impl<K: Kind> Set<K> {
654        /// Return the inner storage.
655        pub fn into_vec(self) -> Vec<Warning<K>> {
656            self.0
657        }
658
659        /// Return the inner kinds.
660        ///
661        /// This should only be used in tests and the `mod price`.
662        pub fn into_kind_vec(self) -> Vec<K> {
663            self.0.into_iter().map(Warning::into_kind).collect()
664        }
665
666        /// Return the inner storgae as a slice.
667        pub fn as_slice(&self) -> &[Warning<K>] {
668            self.0.as_slice()
669        }
670
671        /// Return an immutable iterator over the slice.
672        pub fn iter(&self) -> slice::Iter<'_, Warning<K>> {
673            self.0.iter()
674        }
675    }
676
677    impl<K: Kind> ops::Deref for Set<K> {
678        type Target = [Warning<K>];
679
680        fn deref(&self) -> &[Warning<K>] {
681            self.as_slice()
682        }
683    }
684
685    impl<K: Kind> IntoIterator for Set<K> {
686        type Item = Warning<K>;
687
688        type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
689
690        fn into_iter(self) -> Self::IntoIter {
691            self.0.into_iter()
692        }
693    }
694
695    impl<'a, K: Kind> IntoIterator for &'a Set<K> {
696        type Item = &'a Warning<K>;
697
698        type IntoIter = slice::Iter<'a, Warning<K>>;
699
700        fn into_iter(self) -> Self::IntoIter {
701            self.0.iter()
702        }
703    }
704
705    impl<T, K> Caveat<T, K>
706    where
707        K: Kind,
708    {
709        /// Return the value and assert there are no [`Warning`]s.
710        pub fn unwrap(self) -> T {
711            let Self { value, warnings } = self;
712            assert_matches!(warnings.into_vec().as_slice(), []);
713            value
714        }
715    }
716}
717
718#[cfg(test)]
719mod test_report {
720    use std::fmt;
721
722    use assert_matches::assert_matches;
723
724    use crate::{json, test, warning::ElementReport};
725
726    use super::{Kind, Report, Set, Warning};
727
728    #[derive(Debug)]
729    enum WarningKind {
730        Root,
731        One,
732        OneAgain,
733        Three,
734    }
735
736    impl Kind for WarningKind {
737        fn id(&self) -> std::borrow::Cow<'static, str> {
738            match self {
739                WarningKind::Root => "root".into(),
740                WarningKind::One => "one".into(),
741                WarningKind::OneAgain => "one_again".into(),
742                WarningKind::Three => "three".into(),
743            }
744        }
745    }
746
747    impl fmt::Display for WarningKind {
748        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
749            match self {
750                WarningKind::Root => write!(f, "NopeRoot"),
751                WarningKind::One => write!(f, "NopeOne"),
752                WarningKind::OneAgain => write!(f, "NopeOneAgain"),
753                WarningKind::Three => write!(f, "NopeThree"),
754            }
755        }
756    }
757
758    #[test]
759    fn should_group_warnings() {
760        const JSON: &str = r#"{
761    "field_one#": "one",
762    "field_two": "two",
763    "field_three": "three"
764}"#;
765
766        test::setup();
767
768        let elem = parse(JSON);
769        let mut warnings = Set::<WarningKind>::new();
770
771        // Push warnings into the set out of order.
772        // They should be sorted by `ElemId`.
773        warnings.push(Warning {
774            kind: WarningKind::Root,
775            elem_id: Some(Into::into(0)),
776        });
777        warnings.push(Warning {
778            kind: WarningKind::Three,
779            elem_id: Some(Into::into(3)),
780        });
781        warnings.push(Warning {
782            kind: WarningKind::One,
783            elem_id: Some(Into::into(1)),
784        });
785        warnings.push(Warning {
786            kind: WarningKind::OneAgain,
787            elem_id: Some(Into::into(1)),
788        });
789
790        let report = Report::new(warnings);
791        let mut iter = report.iter(&elem);
792        let ElementReport { element, warnings } = iter.next().unwrap();
793
794        assert!(
795            element.value().is_object(),
796            "The root object should be reported first"
797        );
798        assert_eq!(
799            element.id(),
800            json::ElemId::from(0),
801            "The root object should be reported first"
802        );
803        assert_matches!(warnings, [WarningKind::Root]);
804
805        let ElementReport { element, warnings } = iter.next().unwrap();
806
807        assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "one");
808        assert_eq!(element.id(), json::ElemId::from(1));
809        assert_matches!(
810            warnings,
811            [WarningKind::One, WarningKind::OneAgain],
812            "[`json::Element`] 1 should have two warnings"
813        );
814
815        // [`json::Element`] 2 has no [`Warning`]s so we expect [`json::Element`] 3 next
816        let ElementReport { element, warnings } = iter.next().unwrap();
817
818        assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "three");
819        assert_eq!(element.id(), json::ElemId::from(3));
820        assert_matches!(warnings, [WarningKind::Three]);
821    }
822
823    fn parse(json: &str) -> json::Element<'_> {
824        json::parse(json).unwrap()
825    }
826}