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, collections::BTreeMap, fmt, iter, ops::Deref, vec};
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:ty) => {
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.
79#[doc(hidden)]
80#[macro_export]
81macro_rules! into_caveat_all {
82    ($($kind:ty),+) => {
83        $(impl $crate::IntoCaveat for $kind {
84            fn into_caveat<K: $crate::warning::Kind>(
85                self,
86                warnings: $crate::warning::Set<K>,
87            ) -> $crate::Caveat<Self, K> {
88                $crate::Caveat::new(self, warnings)
89            }
90        })+
91    };
92}
93
94/// A `Verdict` is a standard [`Result`] with [`Warning`]s potentially issued for both the `Ok` and `Err` variants.
95pub type Verdict<T, K> = Result<Caveat<T, K>, Set<K>>;
96
97/// A value that may have associated [`Warning`]s.
98///
99/// Even though the value has been created there may be certain caveats you should be aware of before using it.
100#[derive(Debug)]
101pub struct Caveat<T, K: Kind> {
102    /// The value created by the function.
103    value: T,
104
105    /// A list of [`Warning`]s or caveats issued when creating the value.
106    warnings: Set<K>,
107}
108
109impl<T, K> Caveat<T, K>
110where
111    T: IntoCaveat,
112    K: Kind,
113{
114    /// The only way to create `Caveat<T>` is if `T` impls `IntoCaveat`.
115    pub(crate) fn new(value: T, warnings: Set<K>) -> Self {
116        Self { value, warnings }
117    }
118}
119
120/// A Caveat is simply a value with associated warnings, so providing an `impl Deref` makes sense for the interface.
121///
122/// > The same advice applies to both deref traits. In general, deref traits
123/// > **should** be implemented if:
124/// >
125/// > 1. a value of the type transparently behaves like a value of the target
126/// >    type;
127/// > 1. the implementation of the deref function is cheap; and
128/// > 1. users of the type will not be surprised by any deref coercion behavior.
129///
130/// See: <https://doc.rust-lang.org/std/ops/trait.Deref.html#when-to-implement-deref-or-derefmut>
131impl<T, K> Deref for Caveat<T, K>
132where
133    K: Kind,
134{
135    type Target = T;
136
137    fn deref(&self) -> &T {
138        &self.value
139    }
140}
141
142impl<T, K> Caveat<T, K>
143where
144    K: Kind,
145{
146    /// Return the value and any [`Warning`]s stored in the `Caveat`.
147    pub fn into_parts(self) -> (T, Set<K>) {
148        let Self { value, warnings } = self;
149        (value, warnings)
150    }
151
152    /// Return the value and drop any warnings contained within.
153    pub fn ignore_warnings(self) -> T {
154        self.value
155    }
156
157    /// Map the value to another target type while retaining the warnings about the source type.
158    pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Caveat<U, K> {
159        let Self { value, warnings } = self;
160        Caveat {
161            value: op(value),
162            warnings,
163        }
164    }
165}
166
167/// Convert a `Caveat`-like type into a `T` by gathering up it's [`Warning`]s.
168///
169/// Gathering warnings into a parent `warning::Set` move's the responsibility of alerting the
170/// caller to the existence of those warnings to the owner of the set.
171pub(crate) trait GatherWarnings<T, K>
172where
173    K: Kind,
174{
175    /// The output type of after all the warnings have been gathered.
176    type Output;
177
178    /// Convert a `Caveat`-like type into a `T` by gathering up it's [`Warning`]s.
179    #[must_use = "If you want to ignore the value use `let _ =`"]
180    fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
181    where
182        K: Into<KA>,
183        KA: Kind;
184}
185
186/// Convert a `Caveat<T>` into `T` by gathering up it's `Warning`s.
187impl<T, K> GatherWarnings<T, K> for Caveat<T, K>
188where
189    K: Kind,
190{
191    type Output = T;
192
193    /// Convert a `Caveat<T>` into `T` by gathering up 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        let Self {
200            value,
201            warnings: inner_warnings,
202        } = self;
203
204        warnings.extend(inner_warnings);
205
206        value
207    }
208}
209
210/// Convert a `Option<Caveat<T>>` into `Option<T>` by gathering up it's `Warning`s.
211impl<T, K> GatherWarnings<T, K> for Option<Caveat<T, K>>
212where
213    K: Kind,
214{
215    type Output = Option<T>;
216
217    /// Convert a `Caveat` related to type `T` into a `T` by gathering it's [`Warning`]s.
218    fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
219    where
220        K: Into<KA>,
221        KA: Kind,
222    {
223        match self {
224            Some(cv) => Some(cv.gather_warnings_into(warnings)),
225            None => None,
226        }
227    }
228}
229
230/// Convert a `Result<Caveat<T>>` into `Result<T>` by gathering up it's `Warning`s.
231impl<T, K, E> GatherWarnings<T, K> for Result<Caveat<T, K>, E>
232where
233    K: Kind,
234    E: std::error::Error,
235{
236    type Output = Result<T, E>;
237
238    /// Convert a `Caveat` related to type `T` into a `T` by gathering it's [`Warning`]s.
239    fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
240    where
241        K: Into<KA>,
242        KA: Kind,
243    {
244        match self {
245            Ok(cv) => Ok(cv.gather_warnings_into(warnings)),
246            Err(err) => Err(err),
247        }
248    }
249}
250
251/// Convert a `Result<Caveat<T>>` into `Result<T>` by gathering up it's `Warning`s.
252impl<T, K> GatherWarnings<T, K> for Verdict<T, K>
253where
254    K: Kind,
255{
256    type Output = Option<T>;
257
258    /// Convert a `Verdict` into an `Option` by collecting `Warnings` from the `Ok` and `Err` variants
259    /// and mapping `Ok` to `Some` and `Err` to `None`.
260    fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
261    where
262        K: Into<KA>,
263        KA: Kind,
264    {
265        match self {
266            Ok(cv) => Some(cv.gather_warnings_into(warnings)),
267            Err(inner_warnings) => {
268                warnings.extend(inner_warnings);
269                None
270            }
271        }
272    }
273}
274
275/// Convert a `Vec<Caveat<T>>` into `Vec<T>` by gathering up each elements `Warning`s.
276impl<T, K> GatherWarnings<T, K> for Vec<Caveat<T, K>>
277where
278    K: Kind,
279{
280    type Output = Vec<T>;
281
282    /// Convert a `Caveat` related to type `T` into a `T` by gathering it's [`Warning`]s.
283    fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
284    where
285        K: Into<KA>,
286        KA: Kind,
287    {
288        self.into_iter()
289            .map(|cv| cv.gather_warnings_into(warnings))
290            .collect()
291    }
292}
293
294/// Converts a value `T` into a `Caveat`.
295///
296/// Each module can use this to whitelist their types for conversion to `Caveat<T>`.
297pub trait IntoCaveat: Sized {
298    /// Any type can be converted to `Caveat<T>` only a list of [`Warning`]s is required.
299    fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K>;
300}
301
302macro_rules! peel {
303    ($name:ident, $($other:ident,)*) => (into_caveat_tuple! { $($other,)* })
304}
305
306macro_rules! into_caveat_tuple {
307    () => ();
308    ( $($name:ident,)+ ) => (
309        impl<$($name),+> $crate::IntoCaveat for ($($name,)+) {
310            fn into_caveat<K: $crate::warning::Kind>(
311                self,
312                warnings: $crate::warning::Set<K>,
313            ) -> $crate::Caveat<Self, K> {
314                $crate::Caveat::new(self, warnings)
315            }
316        }
317        peel! { $($name,)+ }
318    )
319}
320
321into_caveat_tuple! { E, D, C, B, A, Z, Y, X, W, V, U, T, }
322
323into_caveat_all!(
324    (),
325    bool,
326    char,
327    u8,
328    u16,
329    u32,
330    u64,
331    u128,
332    i8,
333    i16,
334    i32,
335    i64,
336    i128
337);
338
339/// Allow `Cow<'a, str>` to be converted into a `Caveat`.
340impl IntoCaveat for Cow<'_, str> {
341    fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K> {
342        Caveat::new(self, warnings)
343    }
344}
345
346/// Allow `Option<T: IntoCaveat>` to be converted into a `Caveat`.
347impl<T> IntoCaveat for Option<T>
348where
349    T: IntoCaveat,
350{
351    fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K> {
352        Caveat::new(self, warnings)
353    }
354}
355
356/// `Verdict` specific extension methods for the `Result` type.
357pub trait VerdictExt<T, K: Kind> {
358    /// Maps a `Verdict<T, E>` to `Verdict<U, E>` by applying a function to a
359    /// contained [`Ok`] value, leaving an [`Err`] value untouched.
360    fn map_caveat<F, U>(self, op: F) -> Verdict<U, K>
361    where
362        F: FnOnce(T) -> U;
363}
364
365impl<T, K: Kind> VerdictExt<T, K> for Verdict<T, K>
366where
367    T: IntoCaveat,
368{
369    fn map_caveat<F, U>(self, op: F) -> Verdict<U, K>
370    where
371        F: FnOnce(T) -> U,
372    {
373        match self {
374            Ok(c) => Ok(c.map(op)),
375            Err(w) => Err(w),
376        }
377    }
378}
379
380/// Implement a conversion from `warning::Set<A>` to `warning::Set<B>` so that the `Err` variant
381/// of a `Verdict<_, A>` can be converted using the `?` operator to `Verdict<_, B>`.
382///
383/// `warning::Set::into_set` is used to perform the conversion between set A and B.
384#[macro_export]
385#[doc(hidden)]
386macro_rules! from_warning_set_to {
387    ($kind_a:path => $kind_b:path) => {
388        impl From<$crate::warning::Set<$kind_a>> for $crate::warning::Set<$kind_b> {
389            fn from(set_a: warning::Set<$kind_a>) -> Self {
390                set_a.into_set()
391            }
392        }
393    };
394}
395
396/// Convert an `Option` into a `Verdict` ready to exit the fn.
397pub trait OptionExt<T, K>
398where
399    K: Kind,
400{
401    /// Convert an `Option` into a `Verdict` ready to exit the fn.
402    fn exit_with_warning<F>(self, warnings: Set<K>, f: F) -> Verdict<T, K>
403    where
404        F: FnOnce() -> Warning<K>;
405}
406
407impl<T, K> OptionExt<T, K> for Option<T>
408where
409    T: IntoCaveat,
410    K: Kind,
411{
412    fn exit_with_warning<F>(self, mut warnings: Set<K>, f: F) -> Verdict<T, K>
413    where
414        F: FnOnce() -> Warning<K>,
415    {
416        if let Some(v) = self {
417            Ok(v.into_caveat(warnings))
418        } else {
419            warnings.push(f());
420            Err(warnings)
421        }
422    }
423}
424
425/// Groups together a module's `Kind` and the associated [`json::Element`].
426///
427/// The [`json::Element`] is referenced by `ElemId`.
428#[derive(Debug)]
429pub struct Warning<K: Kind> {
430    /// The `Kind` of warning.
431    kind: K,
432
433    /// The Id of the element that caused the [`Warning`].
434    elem_id: json::ElemId,
435}
436
437/// A Display object for writing a set of warnings.
438///
439/// The warnings set is formatted as a tree with element paths on the first level
440/// and a list of warning ids on the second.
441///
442/// ```shell
443/// $path.to.json.[0].field:
444///   - list_of_warning_ids
445///   - next_warning_id
446///
447/// $next.path.to.[1].json.field
448///   - list_of_warning_ids
449/// ```
450pub struct SetWriter<'caller, 'buf, K: Kind> {
451    /// The [`json::Element`] that has [`Warning`]s.
452    root_elem: &'caller json::Element<'buf>,
453
454    /// The list of warnings for the [`json::Element`].
455    warnings: &'caller Set<K>,
456
457    /// The indent to prefix to each warning id.
458    indent: &'caller str,
459}
460
461impl<'caller, 'buf, K: Kind> SetWriter<'caller, 'buf, K> {
462    /// Create a new `SetWriter` with a default warning id indent of `"  - "`.
463    pub fn new(root_elem: &'caller json::Element<'buf>, warnings: &'caller Set<K>) -> Self {
464        Self {
465            root_elem,
466            warnings,
467            indent: "  - ",
468        }
469    }
470
471    /// Create a new `SetWriter` with a custom warning id indent.
472    pub fn with_indent(
473        root_elem: &'caller json::Element<'buf>,
474        warnings: &'caller Set<K>,
475        indent: &'caller str,
476    ) -> Self {
477        Self {
478            root_elem,
479            warnings,
480            indent,
481        }
482    }
483}
484
485impl<K: Kind> fmt::Debug for SetWriter<'_, '_, K> {
486    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487        fmt::Display::fmt(self, f)
488    }
489}
490
491impl<K: Kind> fmt::Display for SetWriter<'_, '_, K> {
492    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493        let mut iter = self.warnings.group_by_elem(self.root_elem);
494
495        {
496            // Write the first group without an empty line prefix.
497            let Some(Group { element, warnings }) = iter.next() else {
498                return Ok(());
499            };
500
501            writeln!(f, "{}", element.path())?;
502
503            for warning in warnings {
504                write!(f, "{}{}", self.indent, warning)?;
505            }
506        }
507
508        // Write the rest of the Groups with am empty line padding.
509        for Group { element, warnings } in iter {
510            writeln!(f, "\n{}", element.path())?;
511
512            for warning in warnings {
513                write!(f, "{}{}", self.indent, warning)?;
514            }
515        }
516
517        Ok(())
518    }
519}
520
521impl<K: Kind> Warning<K> {
522    /// Create a Warning from a `Kind`.
523    ///
524    /// The `Kind` is typically defined in a domain module.
525    pub(crate) const fn with_elem(kind: K, elem: &json::Element<'_>) -> Warning<K> {
526        Warning {
527            kind,
528            elem_id: elem.id(),
529        }
530    }
531
532    /// Return the warning `Kind`'s id.
533    pub fn id(&self) -> Cow<'static, str> {
534        self.kind.id()
535    }
536
537    /// Return the `Kind` as a reference.
538    pub fn kind(&self) -> &K {
539        &self.kind
540    }
541
542    /// Consume the Warning and return the `Kind` of warning.
543    pub fn into_kind(self) -> K {
544        self.kind
545    }
546}
547
548impl<K: Kind> fmt::Display for Warning<K> {
549    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550        write!(f, "{}", self.kind)
551    }
552}
553
554/// Each mod defines warnings for the type that it's trying to parse or lint from a [`json::Element`].
555///
556/// The `WarningKind` in the mod should impl this trait to take part in the [`Warning`] system.
557pub trait Kind: Sized + fmt::Debug + fmt::Display {
558    /// Return the human readable identifier for the [`Warning`].
559    ///
560    /// This is used in the `auto_test` assertion system.
561    /// Changing these strings may require updating `output_price__cdr.json` files.
562    fn id(&self) -> Cow<'static, str>;
563}
564
565/// A set of [`Warning`]s transported through the system using a `Verdict` or `Caveat`.
566#[derive(Debug)]
567pub struct Set<K: Kind>(Vec<Warning<K>>);
568
569impl<K: Kind> Set<K> {
570    /// Create a new list of [`Warning`]s
571    pub(crate) fn new() -> Self {
572        Self(vec![])
573    }
574
575    pub(crate) fn from_vec(warnings: Vec<Warning<K>>) -> Self {
576        Self(warnings)
577    }
578
579    /// Push a new [`Warning`] onto the list.
580    fn push(&mut self, warning: Warning<K>) {
581        self.0.push(warning);
582    }
583
584    /// Create and push a Warning from a kind defined in a domain module and as associated `json::Element`.
585    pub(crate) fn with_elem(&mut self, kind: K, elem: &json::Element<'_>) {
586        self.push(Warning::with_elem(kind, elem));
587    }
588
589    /// Converts `Set<K>` into `Set<KB>` using the `impl From<K> for KB`.
590    ///
591    /// This is used by the `from_warning_set_to` macro.
592    pub(crate) fn into_set<KB>(self) -> Set<KB>
593    where
594        KB: Kind + From<K>,
595    {
596        let warnings = self
597            .0
598            .into_iter()
599            .map(|warn| {
600                let Warning { kind, elem_id } = warn;
601                Warning {
602                    kind: kind.into(),
603                    elem_id,
604                }
605            })
606            .collect();
607
608        Set(warnings)
609    }
610
611    /// Converts `Set<K>` into `Set<KU>` using the `impl From<K> for KU`.
612    ///
613    /// This is used by the `from_warning_set_to` macro.
614    pub(crate) fn map_warning<KU>(self) -> Set<KU>
615    where
616        KU: Kind + From<K>,
617    {
618        let warnings = self
619            .0
620            .into_iter()
621            .map(|warn| {
622                let Warning { kind, elem_id } = warn;
623                Warning {
624                    kind: kind.into(),
625                    elem_id,
626                }
627            })
628            .collect();
629
630        Set(warnings)
631    }
632
633    /// Extend this set with the warnings of another.
634    ///
635    /// The other set's warnings will be converted if necessary.
636    pub(crate) fn extend<KA>(&mut self, warnings: Set<KA>)
637    where
638        KA: Into<K> + Kind,
639    {
640        self.0.extend(warnings.0.into_iter().map(|warn| {
641            let Warning { kind, elem_id } = warn;
642            Warning {
643                kind: kind.into(),
644                elem_id,
645            }
646        }));
647    }
648
649    /// Return true if the [`Warning`] set is empty.
650    pub fn is_empty(&self) -> bool {
651        self.0.is_empty()
652    }
653
654    /// Return the amount of [`Warning`]s in this set.
655    pub fn len(&self) -> usize {
656        self.0.len()
657    }
658
659    /// Return an iterator of [`Warning`]s grouped by [`json::Element`].
660    ///
661    /// The iterator emits [`Group`]s that borrows the warnings.
662    pub fn group_by_elem<'caller: 'buf, 'buf>(
663        &'caller self,
664        root: &'caller json::Element<'buf>,
665    ) -> GroupByElem<'caller, 'buf, K> {
666        let mut warnings = self.0.iter().collect::<Vec<_>>();
667        warnings.sort_unstable_by_key(|warning| warning.elem_id);
668
669        GroupByElem {
670            walker: json::walk::DepthFirst::new(root),
671            warnings: warnings.into_iter().peekable(),
672        }
673    }
674
675    /// Return an iterator of [`Warning`]s grouped by [`json::Element`].
676    ///
677    /// The iterator emits [`IntoGroup`]s that owns the warnings.
678    pub fn into_group_by_elem<'caller, 'buf>(
679        self,
680        root: &'caller json::Element<'buf>,
681    ) -> IntoGroupByElem<'caller, 'buf, K> {
682        let Self(mut warnings) = self;
683        warnings.sort_unstable_by_key(|warning| warning.elem_id);
684
685        IntoGroupByElem {
686            walker: json::walk::DepthFirst::new(root),
687            warnings: warnings.into_iter().peekable(),
688        }
689    }
690}
691
692/// An iterator of owned warning [`Kind`]s grouped by [`json::Element`].
693pub struct IntoGroupByElem<'caller, 'buf, K>
694where
695    K: Kind,
696{
697    /// The [`json::Element`] tree walker.
698    walker: json::walk::DepthFirst<'caller, 'buf>,
699
700    /// The iterator over every [`Warning`].
701    warnings: iter::Peekable<vec::IntoIter<Warning<K>>>,
702}
703
704/// A group of warning `Kind`s associated with an `Element`.
705///
706/// This group is emitted from the `IntoGroupByElem` iterator.
707/// The warning `Kind`s are owned and so can be moved to another location.
708#[derive(Debug)]
709pub struct IntoGroup<'caller, 'buf, K> {
710    /// The [`json::Element`] that has [`Warning`]s.
711    pub element: &'caller json::Element<'buf>,
712
713    /// The list of warnings for the [`json::Element`].
714    pub warnings: Vec<K>,
715}
716
717impl<'caller, 'buf, K: Kind> Iterator for IntoGroupByElem<'caller, 'buf, K> {
718    type Item = IntoGroup<'caller, 'buf, K>;
719
720    fn next(&mut self) -> Option<Self::Item> {
721        // The warnings are sorted and grouped by consecutive and compacted ids.
722        let warning = self.warnings.next()?;
723
724        // Search for the element associated with warning.
725        let element = loop {
726            let Some(element) = self.walker.next() else {
727                error!("An Element with id: `{}` was not found", warning.elem_id);
728                return None;
729            };
730
731            if element.id() < warning.elem_id {
732                // This element does not have any warnings continue the search for the
733                // element associated with the warning.
734                continue;
735            }
736
737            if element.id() > warning.elem_id {
738                debug_assert!(
739                    element.id() <= warning.elem_id,
740                    "The elements or the warnings are not sorted."
741                );
742                return None;
743            }
744
745            // We found the element for the first warning in the group.
746            break element;
747        };
748
749        // Insert the first warning into the `grouped` list.
750        let mut warnings_grouped = vec![warning.into_kind()];
751
752        // Collect all warnings in the group.
753        loop {
754            let warning = self.warnings.next_if(|w| w.elem_id == element.id());
755            let Some(warning) = warning else {
756                break;
757            };
758
759            warnings_grouped.push(warning.into_kind());
760        }
761
762        Some(IntoGroup {
763            element,
764            warnings: warnings_grouped,
765        })
766    }
767}
768
769/// An iterator of borrowed warning [`Kind`]s grouped by [`json::Element`].
770pub struct GroupByElem<'caller, 'buf, K>
771where
772    K: Kind,
773{
774    /// The [`json::Element`] tree walker.
775    walker: json::walk::DepthFirst<'caller, 'buf>,
776
777    /// The iterator over every [`Warning`].
778    warnings: iter::Peekable<vec::IntoIter<&'caller Warning<K>>>,
779}
780
781impl<K> GroupByElem<'_, '_, K>
782where
783    K: Kind,
784{
785    /// Return a map of [`json::Element`] paths to a list of [`Warning`] ids as Strings.
786    ///
787    /// This is designed to be used to print out maps of warnings associated with elements.
788    /// You can use the debug alternate format `{:#?}` to print the map 'pretty' over multiple lines
789    /// with indentation.
790    ///
791    /// Note: This representation is also valid JSON and can be copied directly to
792    /// a test expectation file.
793    pub fn into_id_map(self) -> BTreeMap<String, Vec<String>> {
794        self.map(Group::into_str_ids).collect()
795    }
796
797    /// Return a map of [`json::Element`] paths to a list of [`Warning`] messages as Strings.
798    ///
799    /// This is designed to be used to print out maps of warnings associated with elements.
800    /// You can use the debug alternate format `{:#?}` to print the map 'pretty' over multiple lines
801    /// with indentation.
802    pub fn into_msg_map(self) -> BTreeMap<String, Vec<String>> {
803        self.map(Group::into_str_msgs).collect()
804    }
805}
806
807/// A group of warning `Kind`s associated with an `Element`.
808///
809/// This group is emitted from the `GroupByElem` iterator.
810/// The warning `Kind`s are borrowed so the source Set does not need to be consumed/moved.
811#[derive(Debug)]
812pub struct Group<'caller, 'buf, K> {
813    /// The [`json::Element`] that has [`Warning`]s.
814    pub element: &'caller json::Element<'buf>,
815
816    /// The list of warnings for the [`json::Element`].
817    pub warnings: Vec<&'caller K>,
818}
819
820impl<K> Group<'_, '_, K>
821where
822    K: Kind,
823{
824    /// Convert the Group into String versions of its parts.
825    ///
826    /// The first tuple field is the [`Element`](json::Element) path as a String.
827    /// The second tuple field is a list of [`Warning`](Kind) ids.
828    pub fn into_str_ids(self) -> (String, Vec<String>) {
829        let Self { element, warnings } = self;
830        (
831            element.path().to_string(),
832            warnings.iter().map(|kind| kind.id().to_string()).collect(),
833        )
834    }
835
836    /// Convert the Group into String versions of its parts.
837    ///
838    /// The first tuple field is the [`Element`](json::Element) path as a String.
839    /// The second tuple field is a list of [`Warning`](Kind) messages.
840    pub fn into_str_msgs(self) -> (String, Vec<String>) {
841        let Self { element, warnings } = self;
842        (
843            element.path().to_string(),
844            warnings.iter().map(ToString::to_string).collect(),
845        )
846    }
847}
848
849impl<'caller, 'buf, K: Kind> Iterator for GroupByElem<'caller, 'buf, K> {
850    type Item = Group<'caller, 'buf, K>;
851
852    fn next(&mut self) -> Option<Self::Item> {
853        // The warnings are sorted and grouped by consecutive and compacted ids.
854        let warning = self.warnings.next()?;
855
856        // Search for the element associated with warning.
857        let element = loop {
858            let Some(element) = self.walker.next() else {
859                error!("An Element with id: `{}` was not found", warning.elem_id);
860                return None;
861            };
862
863            if element.id() < warning.elem_id {
864                // This element does not have any warnings continue the search for the
865                // element associated with the warning.
866                continue;
867            }
868
869            if element.id() > warning.elem_id {
870                debug_assert!(
871                    element.id() <= warning.elem_id,
872                    "The elements or the warnings are not sorted."
873                );
874                return None;
875            }
876
877            // We found the element for the first warning in the group.
878            break element;
879        };
880
881        // Insert the first warning into the `grouped` list.
882        let mut warnings_grouped = vec![warning.kind()];
883
884        // Collect all warnings in the group.
885        loop {
886            let warning = self.warnings.next_if(|w| w.elem_id == element.id());
887            let Some(warning) = warning else {
888                break;
889            };
890
891            warnings_grouped.push(warning.kind());
892        }
893
894        Some(Group {
895            element,
896            warnings: warnings_grouped,
897        })
898    }
899}
900
901#[cfg(test)]
902pub(crate) mod test {
903    use std::{
904        borrow::Cow,
905        collections::{BTreeMap, BTreeSet},
906        ops, slice,
907    };
908
909    use assert_matches::assert_matches;
910
911    use crate::{
912        json,
913        test::{ExpectValue, Expectation},
914    };
915
916    use super::{Caveat, Group, Kind, Set, Warning};
917
918    impl<K: Kind> Set<K> {
919        /// Return the inner storage.
920        pub fn into_vec(self) -> Vec<Warning<K>> {
921            self.0
922        }
923
924        pub fn into_parts_vec(self) -> Vec<(K, json::ElemId)> {
925            self.0
926                .into_iter()
927                .map(|Warning { kind, elem_id }| (kind, elem_id))
928                .collect()
929        }
930
931        /// Return the inner kinds.
932        ///
933        /// This should only be used in tests and the `mod price`.
934        pub fn into_kind_vec(self) -> Vec<K> {
935            self.0.into_iter().map(Warning::into_kind).collect()
936        }
937
938        /// Return the inner storage as a slice.
939        pub fn as_slice(&self) -> &[Warning<K>] {
940            self.0.as_slice()
941        }
942
943        /// Return an immutable iterator over the slice.
944        pub fn iter(&self) -> slice::Iter<'_, Warning<K>> {
945            self.0.iter()
946        }
947    }
948
949    impl<K: Kind> ops::Deref for Set<K> {
950        type Target = [Warning<K>];
951
952        fn deref(&self) -> &[Warning<K>] {
953            self.as_slice()
954        }
955    }
956
957    impl<K: Kind> IntoIterator for Set<K> {
958        type Item = Warning<K>;
959
960        type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
961
962        fn into_iter(self) -> Self::IntoIter {
963            self.0.into_iter()
964        }
965    }
966
967    impl<'a, K: Kind> IntoIterator for &'a Set<K> {
968        type Item = &'a Warning<K>;
969
970        type IntoIter = slice::Iter<'a, Warning<K>>;
971
972        fn into_iter(self) -> Self::IntoIter {
973            self.0.iter()
974        }
975    }
976
977    impl<T, K> Caveat<T, K>
978    where
979        K: Kind,
980    {
981        /// Return the value and assert there are no [`Warning`]s.
982        pub fn unwrap(self) -> T {
983            let Self { value, warnings } = self;
984            assert_matches!(warnings.into_vec().as_slice(), []);
985            value
986        }
987    }
988
989    /// Assert that the warnings given are expected.
990    ///
991    /// Panic with print out of the warnings and the expectations if any warnings were unexpected.
992    pub(crate) fn assert_warnings<K>(
993        expect_file_name: &str,
994        root: &json::Element<'_>,
995        warnings: &Set<K>,
996        expected: Expectation<BTreeMap<String, Vec<String>>>,
997    ) where
998        K: Kind,
999    {
1000        let Expectation::Present(ExpectValue::Some(expected)) = expected else {
1001            assert!(
1002                warnings.is_empty(),
1003                "There is no `warnings` field in the `{expect_file_name}` file but the tariff has warnings;\n{:?}",
1004                warnings.group_by_elem(root).into_id_map()
1005            );
1006            return;
1007        };
1008
1009        {
1010            // Assert that the `expect` file doesn't have extraneous entries.
1011            let warnings_grouped = warnings
1012                .group_by_elem(root)
1013                .map(|Group { element, warnings }| (element.path().to_string(), warnings))
1014                .collect::<BTreeMap<_, _>>();
1015
1016            let mut elems_in_expect_without_warning = vec![];
1017
1018            for elem_path in expected.keys() {
1019                if !warnings_grouped.contains_key(elem_path) {
1020                    elems_in_expect_without_warning.push(elem_path);
1021                }
1022            }
1023
1024            assert!(elems_in_expect_without_warning.is_empty(),
1025                "The expect file `{expect_file_name}` has entries for elements that have no warnings:\n\
1026                {elems_in_expect_without_warning:#?}"
1027            );
1028        }
1029
1030        // The elements that have warnings but have no entry for the elements path in the `expect` file.
1031        let mut elems_missing_from_expect = vec![];
1032        // The element that have warnings and an entry in the `expect` file, but the list of expected warnings
1033        // is not equal to the list of actual warnings.
1034        let mut unequal_warnings = vec![];
1035
1036        for group in warnings.group_by_elem(root) {
1037            let path_str = group.element.path().to_string();
1038            let Some(warnings_expected) = expected.get(&*path_str) else {
1039                elems_missing_from_expect.push(group);
1040                continue;
1041            };
1042
1043            // Make two sets of actual and expected warnings.
1044            let warnings_expected = warnings_expected
1045                .iter()
1046                .map(|s| Cow::Borrowed(&**s))
1047                .collect::<BTreeSet<_>>();
1048            let warnings = group
1049                .warnings
1050                .iter()
1051                .map(|w| w.id())
1052                .collect::<BTreeSet<_>>();
1053
1054            if warnings_expected != warnings {
1055                unequal_warnings.push(group);
1056            }
1057        }
1058
1059        if !elems_missing_from_expect.is_empty() || !unequal_warnings.is_empty() {
1060            let missing = elems_missing_from_expect
1061                .into_iter()
1062                .map(Group::into_str_ids)
1063                .collect::<BTreeMap<_, _>>();
1064
1065            let unequal = unequal_warnings
1066                .into_iter()
1067                .map(Group::into_str_ids)
1068                .collect::<BTreeMap<_, _>>();
1069
1070            match (!missing.is_empty(), !unequal.is_empty()) {
1071                (true, true) => panic!(
1072                    "Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}\n\
1073                    Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1074                    The warnings reported are: \n{unequal:#?}"                  
1075                ),
1076                (true, false) => panic!("Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}"),
1077                (false, true) => panic!(
1078                    "Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1079                    The warnings reported are: \n{unequal:#?}"
1080                ),
1081                (false, false) => (),
1082            }
1083        }
1084    }
1085}
1086
1087#[cfg(test)]
1088mod test_group_by_elem {
1089    use std::fmt;
1090
1091    use assert_matches::assert_matches;
1092
1093    use crate::{json, test};
1094
1095    use super::{Group, IntoGroup, Kind, Set, Warning};
1096
1097    const JSON: &str = r#"{
1098    "field_one#": "one",
1099    "field_two": "two",
1100    "field_three": "three"
1101}"#;
1102
1103    #[derive(Debug)]
1104    enum WarningKind {
1105        Root,
1106        One,
1107        OneAgain,
1108        Three,
1109    }
1110
1111    impl Kind for WarningKind {
1112        fn id(&self) -> std::borrow::Cow<'static, str> {
1113            match self {
1114                WarningKind::Root => "root".into(),
1115                WarningKind::One => "one".into(),
1116                WarningKind::OneAgain => "one_again".into(),
1117                WarningKind::Three => "three".into(),
1118            }
1119        }
1120    }
1121
1122    impl fmt::Display for WarningKind {
1123        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1124            match self {
1125                WarningKind::Root => write!(f, "NopeRoot"),
1126                WarningKind::One => write!(f, "NopeOne"),
1127                WarningKind::OneAgain => write!(f, "NopeOneAgain"),
1128                WarningKind::Three => write!(f, "NopeThree"),
1129            }
1130        }
1131    }
1132
1133    #[test]
1134    fn should_group_by_elem() {
1135        test::setup();
1136
1137        let elem = parse(JSON);
1138        let mut warnings = Set::<WarningKind>::new();
1139
1140        // Push warnings into the set out of order.
1141        // They should be sorted by `ElemId`.
1142        warnings.push(Warning {
1143            kind: WarningKind::Root,
1144            elem_id: json::ElemId::from(0),
1145        });
1146        warnings.push(Warning {
1147            kind: WarningKind::One,
1148            elem_id: json::ElemId::from(1),
1149        });
1150        warnings.push(Warning {
1151            kind: WarningKind::Three,
1152            elem_id: json::ElemId::from(3),
1153        });
1154        warnings.push(Warning {
1155            kind: WarningKind::OneAgain,
1156            elem_id: json::ElemId::from(1),
1157        });
1158
1159        let mut iter = warnings.group_by_elem(&elem);
1160
1161        {
1162            let Group { element, warnings } = iter.next().unwrap();
1163
1164            assert!(
1165                element.value().is_object(),
1166                "The root object should be emitted first"
1167            );
1168            assert_eq!(
1169                element.id(),
1170                json::ElemId::from(0),
1171                "The root object should be emitted first"
1172            );
1173            assert_matches!(warnings.as_slice(), [WarningKind::Root]);
1174        }
1175
1176        {
1177            let Group { element, warnings } = iter.next().unwrap();
1178
1179            assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "one");
1180            assert_eq!(element.id(), json::ElemId::from(1));
1181            assert_matches!(
1182                warnings.as_slice(),
1183                [WarningKind::One, WarningKind::OneAgain],
1184                "[`json::Element`] 1 should have two warnings"
1185            );
1186        }
1187
1188        {
1189            // [`json::Element`] 2 has no [`Warning`]s so we expect [`json::Element`] 3 next
1190            let Group { element, warnings } = iter.next().unwrap();
1191
1192            assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "three");
1193            assert_eq!(element.id(), json::ElemId::from(3));
1194            assert_matches!(warnings.as_slice(), [WarningKind::Three]);
1195        }
1196    }
1197
1198    #[test]
1199    fn should_into_group_by_elem() {
1200        test::setup();
1201
1202        let elem = parse(JSON);
1203        let mut warnings = Set::<WarningKind>::new();
1204
1205        // Push warnings into the set out of order.
1206        // They should be sorted by `ElemId`.
1207        warnings.push(Warning {
1208            kind: WarningKind::Root,
1209            elem_id: json::ElemId::from(0),
1210        });
1211        warnings.push(Warning {
1212            kind: WarningKind::Three,
1213            elem_id: json::ElemId::from(3),
1214        });
1215        warnings.push(Warning {
1216            kind: WarningKind::One,
1217            elem_id: json::ElemId::from(1),
1218        });
1219        warnings.push(Warning {
1220            kind: WarningKind::OneAgain,
1221            elem_id: json::ElemId::from(1),
1222        });
1223
1224        let mut iter = warnings.into_group_by_elem(&elem);
1225
1226        {
1227            let IntoGroup { element, warnings } = iter.next().unwrap();
1228
1229            assert!(
1230                element.value().is_object(),
1231                "The root object should be emitted first"
1232            );
1233            assert_eq!(
1234                element.id(),
1235                json::ElemId::from(0),
1236                "The root object should be emitted first"
1237            );
1238            assert_matches!(warnings.as_slice(), [WarningKind::Root]);
1239        }
1240
1241        {
1242            let IntoGroup { element, warnings } = iter.next().unwrap();
1243
1244            assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "one");
1245            assert_eq!(element.id(), json::ElemId::from(1));
1246            assert_matches!(
1247                warnings.as_slice(),
1248                [WarningKind::One, WarningKind::OneAgain],
1249                "[`json::Element`] 1 should have two warnings"
1250            );
1251        }
1252
1253        {
1254            // [`json::Element`] 2 has no [`Warning`]s so we expect [`json::Element`] 3 next
1255            let IntoGroup { element, warnings } = iter.next().unwrap();
1256
1257            assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "three");
1258            assert_eq!(element.id(), json::ElemId::from(3));
1259            assert_matches!(warnings.as_slice(), [WarningKind::Three]);
1260        }
1261    }
1262
1263    fn parse(json: &str) -> json::Element<'_> {
1264        json::parse(json).unwrap()
1265    }
1266}