Skip to main content

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<W>`, 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, W>` where the `Caveat` contains a value `T` and a set of `Warnings<W>`.
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, ops::Deref, vec};
47
48use tracing::{debug, info};
49
50use crate::{json, SmartString};
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<W: $crate::Warning>(
59                self,
60                warnings: $crate::warning::Set<W>,
61            ) -> $crate::Caveat<Self, W> {
62                $crate::Caveat::new(self, warnings)
63            }
64        }
65    };
66    ($kind:ty) => {
67        impl $crate::IntoCaveat for $kind {
68            fn into_caveat<W: $crate::Warning>(
69                self,
70                warnings: $crate::warning::Set<W>,
71            ) -> $crate::Caveat<Self, W> {
72                $crate::Caveat::new(self, warnings)
73            }
74        }
75
76        impl $crate::warning::IntoCaveatDeferred for $kind {
77            fn into_caveat_deferred<W: $crate::Warning>(
78                self,
79                warnings: $crate::warning::SetDeferred<W>,
80            ) -> $crate::warning::CaveatDeferred<Self, W> {
81                $crate::warning::CaveatDeferred::new(self, warnings)
82            }
83        }
84    };
85}
86
87/// Implement `IntoCaveat` for all the given types so that it can take part in the `Warning` system.
88#[doc(hidden)]
89#[macro_export]
90macro_rules! into_caveat_all {
91    ($($kind:ty),+) => {
92        $($crate::into_caveat!($kind);)+
93    };
94}
95
96#[doc(hidden)]
97#[macro_export]
98macro_rules! from_warning_all {
99    ($($source_kind:path => $target_kind:ident::$target_variant:ident),+) => {
100        $(
101            /// Convert from `Warning` A to B
102            impl From<$source_kind> for $target_kind {
103                fn from(warning: $source_kind) -> Self {
104                    $target_kind::$target_variant(warning)
105                }
106            }
107
108            /// Implement a conversion from `warning::Set<A>` to `warning::Set<B>` so that the `Err` variant
109            /// of a `Verdict<_, A>` can be converted using the `?` operator to `Verdict<_, B>`.
110            ///
111            /// `warning::Set::into_other` is used to perform the conversion between set `A` and `B`.
112            impl From<$crate::warning::ErrorSet<$source_kind>> for $crate::warning::ErrorSet<$target_kind> {
113                fn from(set_a: $crate::warning::ErrorSet<$source_kind>) -> Self {
114                    set_a.into_other()
115                }
116            }
117
118            /// Implement a conversion from `warning::SetDeferred<A>` to `warning::SetDeferred<B>` so that the `Err` variant
119            /// of a `VerdictDeferred<_, A>` can be converted using the `?` operator to `VerdictDeferred<_, B>`.
120            ///
121            /// `warning::SetDeferred::into_other` is used to perform the conversion between set `A` and `B`.
122            impl From<$crate::warning::ErrorSetDeferred<$source_kind>> for $crate::warning::ErrorSetDeferred<$target_kind> {
123                fn from(set_a: $crate::warning::ErrorSetDeferred<$source_kind>) -> Self {
124                    set_a.into_other()
125                }
126            }
127        )+
128    };
129}
130
131/// A `Verdict` is a standard [`Result`] with [`Warning`]s potentially issued for both the `Ok` and `Err` variants.
132pub type Verdict<T, W> = Result<Caveat<T, W>, ErrorSet<W>>;
133
134/// A `VerdictDeferred` is a standard [`Result`] with [`Warning`]s potentially issued for both the `Ok` and `Err` variants.
135///
136/// This verdict is considered deferred as the warnings still need to be associated with a [`json::Element`].
137///
138/// NOTE: The deferred types are used to avoid passing [`json::Element`] references
139/// to functions just to create [`Warning`]s.
140pub(crate) type VerdictDeferred<T, W> = Result<CaveatDeferred<T, W>, ErrorSetDeferred<W>>;
141
142/// A value that may have associated [`Warning`]s.
143///
144/// This caveat is considered deferred as the warning still need to be associated with
145/// a [`json::Element`] to become [`Warning`]s.
146///
147/// Even though the value has been created there may be certain caveats you should be aware of before using it.
148///
149/// NOTE: The deferred types are used to avoid passing [`json::Element`] references
150/// to functions just to create [`Warning`]s.
151#[derive(Debug)]
152pub struct CaveatDeferred<T, W: Warning> {
153    /// The value created by the function.
154    value: T,
155
156    /// A list of [`Warning`]s or caveats issued when creating the value.
157    warnings: SetDeferred<W>,
158}
159
160impl<T, W> CaveatDeferred<T, W>
161where
162    T: IntoCaveatDeferred,
163    W: Warning,
164{
165    /// The only way to create `CaveatDeferred<T>` is if `T` impls `IntoCaveatDeferred`.
166    pub(crate) fn new(value: T, warnings: SetDeferred<W>) -> Self {
167        Self { value, warnings }
168    }
169}
170
171/// A deferred Caveat is simply a value with associated [`Warning`]s that still need to be associated
172/// with a [`json::Element`].
173///
174/// Providing an `impl Deref` makes sense for given that it's an annotated value.
175///
176/// > The same advice applies to both deref traits. In general, deref traits
177/// > **should** be implemented if:
178/// >
179/// > 1. a value of the type transparently behaves like a value of the target
180/// >    type;
181/// > 1. the implementation of the deref function is cheap; and
182/// > 1. users of the type will not be surprised by any deref coercion behavior.
183///
184/// See: <https://doc.rust-lang.org/std/ops/trait.Deref.html#when-to-implement-deref-or-derefmut>
185impl<T, W> Deref for CaveatDeferred<T, W>
186where
187    W: Warning,
188{
189    type Target = T;
190
191    fn deref(&self) -> &T {
192        &self.value
193    }
194}
195
196impl<T, W> CaveatDeferred<T, W>
197where
198    W: Warning,
199{
200    /// Return the value and any [`Warning`]s stored in the `CaveatDeferred`.
201    pub fn into_parts(self) -> (T, SetDeferred<W>) {
202        let Self { value, warnings } = self;
203        (value, warnings)
204    }
205
206    /// Return the value and drop any warnings contained within.
207    pub fn ignore_warnings(self) -> T {
208        self.value
209    }
210
211    /// Map the value to another target type while retaining the warnings about the source type.
212    pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> CaveatDeferred<U, W> {
213        let Self { value, warnings } = self;
214        CaveatDeferred {
215            value: op(value),
216            warnings,
217        }
218    }
219}
220
221/// A value that may have associated [`Warning`]s.
222///
223/// Even though the value has been created there may be certain caveats you should be aware of before using it.
224#[derive(Debug)]
225pub struct Caveat<T, W: Warning> {
226    /// The value created by the function.
227    value: T,
228
229    /// A list of [`Warning`]s or caveats issued when creating the value.
230    warnings: Set<W>,
231}
232
233impl<T, W> Caveat<T, W>
234where
235    T: IntoCaveat,
236    W: Warning,
237{
238    /// The only way to create `Caveat<T>` is if `T` impls `IntoCaveat`.
239    pub(crate) fn new(value: T, warnings: Set<W>) -> Self {
240        Self { value, warnings }
241    }
242}
243
244/// A Caveat is simply a value with associated warnings.
245/// Providing an `impl Deref` makes sense for given that it's an annotated value.
246///
247/// > The same advice applies to both deref traits. In general, deref traits
248/// > **should** be implemented if:
249/// >
250/// > 1. a value of the type transparently behaves like a value of the target
251/// >    type;
252/// > 1. the implementation of the deref function is cheap; and
253/// > 1. users of the type will not be surprised by any deref coercion behavior.
254///
255/// See: <https://doc.rust-lang.org/std/ops/trait.Deref.html#when-to-implement-deref-or-derefmut>
256impl<T, W> Deref for Caveat<T, W>
257where
258    W: Warning,
259{
260    type Target = T;
261
262    fn deref(&self) -> &T {
263        &self.value
264    }
265}
266
267impl<T, W> Caveat<T, W>
268where
269    W: Warning,
270{
271    /// Return the value and any [`Warning`]s stored in the `Caveat`.
272    pub fn into_parts(self) -> (T, Set<W>) {
273        let Self { value, warnings } = self;
274        (value, warnings)
275    }
276
277    /// Return the value and drop any warnings contained within.
278    pub fn ignore_warnings(self) -> T {
279        self.value
280    }
281
282    /// Map the value to another target type while retaining the warnings about the source type.
283    pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Caveat<U, W> {
284        let Self { value, warnings } = self;
285        Caveat {
286            value: op(value),
287            warnings,
288        }
289    }
290}
291
292/// Convert a `Caveat`-like type into a `T` by gathering up its [`Warning`]s.
293///
294/// Gathering warnings into a parent `warning::Set` move's the responsibility of alerting the
295/// caller to the existence of those warnings to the owner of the set.
296pub(crate) trait GatherWarnings<T, W>
297where
298    W: Warning,
299{
300    /// The output type of after all the warnings have been gathered.
301    type Output;
302
303    /// Convert a `Caveat`-like type into a `T` by gathering up its [`Warning`]s.
304    #[must_use = "If you want to ignore the value use `let _ =`"]
305    fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
306    where
307        W: Into<WA>,
308        WA: Warning;
309}
310
311/// Convert a `Caveat<T>` into `T` by gathering up its `Warning`s.
312impl<T, W> GatherWarnings<T, W> for Caveat<T, W>
313where
314    W: Warning,
315{
316    type Output = T;
317
318    /// Convert a `Caveat<T>` into `T` by gathering up its `Warning`s.
319    fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
320    where
321        W: Into<WA>,
322        WA: Warning,
323    {
324        let Self {
325            value,
326            warnings: inner_warnings,
327        } = self;
328
329        warnings.extend(inner_warnings);
330
331        value
332    }
333}
334
335/// Convert a `Option<Caveat<T>>` into `Option<T>` by gathering up its `Warning`s.
336impl<T, W> GatherWarnings<T, W> for Option<Caveat<T, W>>
337where
338    W: Warning,
339{
340    type Output = Option<T>;
341
342    /// Convert a `Caveat` related to type `T` into a `T` by gathering its [`Warning`]s.
343    fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
344    where
345        W: Into<WA>,
346        WA: Warning,
347    {
348        match self {
349            Some(cv) => Some(cv.gather_warnings_into(warnings)),
350            None => None,
351        }
352    }
353}
354
355/// Convert a `Result<Caveat<T>>` into `Result<T>` by gathering up its `Warning`s.
356impl<T, W, E> GatherWarnings<T, W> for Result<Caveat<T, W>, E>
357where
358    W: Warning,
359    E: std::error::Error,
360{
361    type Output = Result<T, E>;
362
363    /// Convert a `Caveat` related to type `T` into a `T` by gathering its [`Warning`]s.
364    fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
365    where
366        W: Into<WA>,
367        WA: Warning,
368    {
369        match self {
370            Ok(cv) => Ok(cv.gather_warnings_into(warnings)),
371            Err(err) => Err(err),
372        }
373    }
374}
375
376/// Convert a `Result<Caveat<T>>` into `Result<T>` by gathering up its `Warning`s.
377impl<T, W> GatherWarnings<T, W> for Verdict<T, W>
378where
379    W: Warning,
380{
381    type Output = Result<T, ErrorSet<W>>;
382
383    /// Convert a `Verdict` into an `Option` by collecting `Warnings` from the `Ok` and `Err` variants
384    /// and mapping `Ok` to `Some` and `Err` to `None`.
385    fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
386    where
387        W: Into<WA>,
388        WA: Warning,
389    {
390        match self {
391            Ok(cv) => Ok(cv.gather_warnings_into(warnings)),
392            Err(err_set) => Err(err_set),
393        }
394    }
395}
396
397/// Convert a `Result` that contains an `ErrorSet` into a `T` by gathering up its [`Warning`]s.
398///
399/// Gathering warnings into a parent `warning::Set` move's the responsibility of alerting the
400/// caller to the existence of those warnings to the owner of the set.
401pub(crate) trait DeescalateError<T, W>
402where
403    W: Warning,
404{
405    /// Convert a `Caveat`-like type into a `T` by gathering up its [`Warning`]s.
406    #[must_use = "If you want to ignore the value use `let _ =`"]
407    fn deescalate_error_into<WA>(self, warnings: &mut Set<WA>) -> Option<T>
408    where
409        W: Into<WA>,
410        WA: Warning;
411}
412
413/// Convert a `Result<Caveat<T>>` into `Option<T>` by deescalating its [`Error`] and gathering up its [`Warning`]s.
414impl<T, W> DeescalateError<T, W> for Verdict<T, W>
415where
416    W: Warning,
417{
418    /// Convert a `Verdict` into an `Option` by collecting `Warnings` from the `Ok` and `Err` variants
419    /// and mapping `Ok` to `Some` and `Err` to `None`.
420    fn deescalate_error_into<WA>(self, warnings: &mut Set<WA>) -> Option<T>
421    where
422        W: Into<WA>,
423        WA: Warning,
424    {
425        match self {
426            Ok(cv) => Some(cv.gather_warnings_into(warnings)),
427            Err(err_set) => {
428                warnings.deescalate_error(err_set.into_other());
429                None
430            }
431        }
432    }
433}
434
435/// Convert a `Result<T>` into `Option<T>` by deescalating its [`Error`] and gathering up its [`Warning`]s.
436impl<T, W> DeescalateError<T, W> for Result<T, ErrorSet<W>>
437where
438    W: Warning,
439{
440    /// Convert a `Verdict` into an `Option` by collecting `Warnings` from the `Ok` and `Err` variants
441    /// and mapping `Ok` to `Some` and `Err` to `None`.
442    fn deescalate_error_into<WA>(self, warnings: &mut Set<WA>) -> Option<T>
443    where
444        W: Into<WA>,
445        WA: Warning,
446    {
447        match self {
448            Ok(cv) => Some(cv),
449            Err(err_set) => {
450                warnings.deescalate_error(err_set.into_other());
451                None
452            }
453        }
454    }
455}
456
457/// Convert a `Vec<Caveat<T>>` into `Vec<T>` by gathering up each elements `Warning`s.
458impl<T, W> GatherWarnings<T, W> for Vec<Caveat<T, W>>
459where
460    W: Warning,
461{
462    type Output = Vec<T>;
463
464    /// Convert a `Caveat` related to type `T` into a `T` by gathering its [`Warning`]s.
465    fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
466    where
467        W: Into<WA>,
468        WA: Warning,
469    {
470        self.into_iter()
471            .map(|cv| cv.gather_warnings_into(warnings))
472            .collect()
473    }
474}
475
476/// Convert a `Caveat`-like type into a `T` by gathering up its [`Warning`]s.
477///
478/// Gathering [`Warning`]s into a parent `warning::SetDeferred` move's the responsibility of alerting the
479/// caller to the existence of those [`Warning`]s to the owner of the set.
480pub(crate) trait GatherDeferredWarnings<T, W>
481where
482    W: Warning,
483{
484    /// The output type of after all the warnings have been gathered.
485    type Output;
486
487    /// Convert a `Caveat`-like type into a `T` by gathering up its [`Warning`]s.
488    #[must_use = "If you want to ignore the value use `let _ =`"]
489    fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
490    where
491        W: Into<WA>,
492        WA: Warning;
493}
494
495/// Convert a `CaveatDeferred<T>` into `T` by gathering up its [`Warning`]s.
496impl<T, W> GatherDeferredWarnings<T, W> for CaveatDeferred<T, W>
497where
498    W: Warning,
499{
500    type Output = T;
501
502    /// Convert a `Caveat<T>` into `T` by gathering up its `Warning`s.
503    fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
504    where
505        W: Into<WA>,
506        WA: Warning,
507    {
508        let Self {
509            value,
510            warnings: inner_warnings,
511        } = self;
512
513        warnings.extend(inner_warnings);
514
515        value
516    }
517}
518
519/// Convert a `Option<CaveatDeferred<T>>` into `Option<T>` by gathering up its warning `Warning`s.
520impl<T, W> GatherDeferredWarnings<T, W> for Option<CaveatDeferred<T, W>>
521where
522    W: Warning,
523{
524    type Output = Option<T>;
525
526    /// Convert a `Caveat` related to type `T` into a `T` by gathering its [`Warning`]s.
527    fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
528    where
529        W: Into<WA>,
530        WA: Warning,
531    {
532        match self {
533            Some(cv) => Some(cv.gather_deferred_warnings_into(warnings)),
534            None => None,
535        }
536    }
537}
538
539/// Convert a `Result<CaveatDeferred<T>>` into `Result<T>` by gathering up its [`Warning`]s.
540impl<T, W, E> GatherDeferredWarnings<T, W> for Result<CaveatDeferred<T, W>, E>
541where
542    W: Warning,
543    E: std::error::Error,
544{
545    type Output = Result<T, E>;
546
547    /// Convert a `Caveat` related to type `T` into a `T` by gathering its [`Warning`]s.
548    fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
549    where
550        W: Into<WA>,
551        WA: Warning,
552    {
553        match self {
554            Ok(cv) => Ok(cv.gather_deferred_warnings_into(warnings)),
555            Err(err) => Err(err),
556        }
557    }
558}
559
560/// Convert a `Result<CaveatDeferred<T>>` into `Result<T>` by gathering up its [`Warning`]s.
561impl<T, W> GatherDeferredWarnings<T, W> for VerdictDeferred<T, W>
562where
563    W: Warning,
564{
565    type Output = Result<T, ErrorSetDeferred<W>>;
566
567    /// Convert a `VerdictDeferred` into an `Option` by collecting [`Warning`]s from the `Ok` and `Err` variants
568    /// and mapping `Ok` to `Some` and `Err` to `None`.
569    fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
570    where
571        W: Into<WA>,
572        WA: Warning,
573    {
574        match self {
575            Ok(cv) => Ok(cv.gather_deferred_warnings_into(warnings)),
576            Err(err_set) => Err(err_set),
577        }
578    }
579}
580
581/// Convert a `Vec<CaveatDeferred<T>>` into `Vec<T>` by gathering up each elements [`Warning`]s.
582impl<T, W> GatherDeferredWarnings<T, W> for Vec<CaveatDeferred<T, W>>
583where
584    W: Warning,
585{
586    type Output = Vec<T>;
587
588    /// Convert a `CaveatDeferred` related to type `T` into a `T` by gathering its [`Warning`]s.
589    fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
590    where
591        W: Into<WA>,
592        WA: Warning,
593    {
594        self.into_iter()
595            .map(|cv| cv.gather_deferred_warnings_into(warnings))
596            .collect()
597    }
598}
599
600/// Converts a value `T` into a `Caveat`.
601///
602/// Each module can use this to whitelist their types for conversion to `Caveat<T>`.
603pub trait IntoCaveat: Sized {
604    /// Any type can be converted to `Caveat<T>` by supplying a list of [`Warning`]s.
605    fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W>;
606}
607
608/// Converts a value `T` into a `CaveatDeferred`.
609///
610/// Each module can use this to whitelist their types for conversion to `CaveatDeferred<T>`.
611pub trait IntoCaveatDeferred: Sized {
612    /// Any type can be converted to `CaveatDeferred<T>` by supplying a list of [`Warning`]s.
613    fn into_caveat_deferred<W: Warning>(self, warnings: SetDeferred<W>) -> CaveatDeferred<Self, W>;
614}
615
616// Peel an argument off the list and recurse into `into_caveat_tuple!`
617macro_rules! peel {
618    ($name:ident, $($other:ident,)*) => (into_caveat_tuple! { $($other,)* })
619}
620
621// Implement `IntoCaveat` and `IntoCaveatDeferred` for tuples of size N to 0.
622// Where N is the length of the given type names list.
623macro_rules! into_caveat_tuple {
624    () => ();
625    ( $($name:ident,)+ ) => (
626        impl<$($name),+> $crate::IntoCaveat for ($($name,)+) {
627            fn into_caveat<W: $crate::Warning>(
628                self,
629                warnings: $crate::warning::Set<W>,
630            ) -> $crate::Caveat<Self, W> {
631                $crate::Caveat::new(self, warnings)
632            }
633        }
634
635        impl<$($name),+> $crate::warning::IntoCaveatDeferred for ($($name,)+) {
636            fn into_caveat_deferred<W: $crate::Warning>(
637                self,
638                warnings: SetDeferred<W>,
639            ) -> $crate::warning::CaveatDeferred<Self, W> {
640                $crate::warning::CaveatDeferred::new(self, warnings)
641            }
642        }
643        peel! { $($name,)+ }
644    )
645}
646
647// Implement `IntoCaveat` and `IntoCaveatDeferred` for tuples of size N to 0.
648// Where N is the length of the given type names list.
649into_caveat_tuple! { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
650
651// Implement `IntoCaveat` and `IntoCaveatDeferred` for all the given types.
652into_caveat_all!(
653    (),
654    bool,
655    char,
656    u8,
657    u16,
658    u32,
659    u64,
660    u128,
661    i8,
662    i16,
663    i32,
664    i64,
665    i128
666);
667
668/// Allow `Cow<'a, str>` to be converted into a `Caveat`.
669impl IntoCaveat for Cow<'_, str> {
670    fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W> {
671        Caveat::new(self, warnings)
672    }
673}
674
675/// Allow `Option<T: IntoCaveat>` to be converted into a `Caveat`.
676impl<T> IntoCaveat for Option<T>
677where
678    T: IntoCaveat,
679{
680    fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W> {
681        Caveat::new(self, warnings)
682    }
683}
684
685/// Allow `Vec<T: IntoCaveat>` to be converted into a `Caveat`.
686impl<T> IntoCaveat for Vec<T>
687where
688    T: IntoCaveat,
689{
690    fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W> {
691        Caveat::new(self, warnings)
692    }
693}
694
695/// Allow `Vec<T: IntoCaveat>` to be converted into a `CaveatDeferred`.
696impl<T> IntoCaveatDeferred for Vec<T>
697where
698    T: IntoCaveat,
699{
700    fn into_caveat_deferred<W: Warning>(self, warnings: SetDeferred<W>) -> CaveatDeferred<Self, W> {
701        CaveatDeferred::new(self, warnings)
702    }
703}
704
705/// `Verdict` specific extension methods for the `Result` type.
706pub trait VerdictExt<T, W: Warning> {
707    /// Maps a `Verdict<T, E>` to `Verdict<U, E>` by applying a function to a
708    /// contained [`Ok`] value, leaving an [`Err`] value untouched.
709    fn map_caveat<F, U>(self, op: F) -> Verdict<U, W>
710    where
711        F: FnOnce(T) -> U;
712
713    /// Discard all warnings in the `Err` variant and keep only the warning that caused the error.
714    fn only_error(self) -> Result<Caveat<T, W>, Error<W>>;
715}
716
717#[allow(dead_code, reason = "for debugging")]
718pub(crate) trait VerdictTrace<T, W: Warning> {
719    fn info_verdict(self, msg: &'static str) -> Self;
720    fn debug_verdict(self, msg: &'static str) -> Self;
721}
722
723#[allow(dead_code, reason = "for debugging")]
724pub(crate) trait ResultTrace<T, W: Warning> {
725    fn info_result(self, msg: &'static str) -> Self;
726    fn debug_result(self, msg: &'static str) -> Self;
727}
728
729impl<T, W: Warning> VerdictExt<T, W> for Verdict<T, W> {
730    fn map_caveat<F, U>(self, op: F) -> Verdict<U, W>
731    where
732        F: FnOnce(T) -> U,
733    {
734        match self {
735            Ok(c) => Ok(c.map(op)),
736            Err(w) => Err(w),
737        }
738    }
739
740    fn only_error(self) -> Result<Caveat<T, W>, Error<W>> {
741        match self {
742            Ok(c) => Ok(c),
743            Err(err_set) => {
744                let ErrorSet { error, warnings: _ } = err_set;
745                Err(*error)
746            }
747        }
748    }
749}
750
751impl<T, W: Warning> VerdictTrace<T, W> for Verdict<T, W>
752where
753    T: fmt::Debug,
754{
755    fn info_verdict(self, msg: &'static str) -> Self {
756        match self {
757            Ok(c) => {
758                info!("{msg}: {c:#?}");
759                Ok(c)
760            }
761            Err(err_set) => {
762                info!("{msg}: {err_set:#?}");
763                Err(err_set)
764            }
765        }
766    }
767
768    fn debug_verdict(self, msg: &'static str) -> Self {
769        match self {
770            Ok(c) => {
771                debug!("{msg}: {c:#?}");
772                Ok(c)
773            }
774            Err(err_set) => {
775                debug!("{msg}: {err_set:#?}");
776                Err(err_set)
777            }
778        }
779    }
780}
781
782impl<T, W: Warning> ResultTrace<T, W> for Result<T, ErrorSet<W>>
783where
784    T: fmt::Debug,
785{
786    fn info_result(self, msg: &'static str) -> Self {
787        match self {
788            Ok(c) => {
789                info!("{msg}: {c:#?}");
790                Ok(c)
791            }
792            Err(err_set) => {
793                info!("{msg}: {err_set:#?}");
794                Err(err_set)
795            }
796        }
797    }
798
799    fn debug_result(self, msg: &'static str) -> Self {
800        match self {
801            Ok(c) => {
802                debug!("{msg}: {c:#?}");
803                Ok(c)
804            }
805            Err(err_set) => {
806                debug!("{msg}: {err_set:#?}");
807                Err(err_set)
808            }
809        }
810    }
811}
812
813/// The warning that caused an operation to fail.
814///
815/// The [`Warning`] is referred to by the [`json::Element`]s path as a `String`.
816#[derive(Debug)]
817pub struct Error<W: Warning> {
818    /// The `Warning` of warning.
819    warning: W,
820
821    /// The path of the element that caused the [`Warning`].
822    element: Element,
823}
824
825impl<W: Warning> Error<W> {
826    /// Return reference to the `Warning`.
827    pub fn warning(&self) -> &W {
828        &self.warning
829    }
830
831    /// Consume the `Error` and return the `Warning`.
832    pub fn into_warning(self) -> W {
833        self.warning
834    }
835
836    /// Return a reference to the [`Element`] that caused the [`Warning`].
837    pub fn element(&self) -> &Element {
838        &self.element
839    }
840
841    /// Return the constituent parts.
842    pub fn parts(&self) -> (&W, &Element) {
843        (&self.warning, &self.element)
844    }
845
846    /// Consume the `Cause` and return the constituent parts.
847    pub fn into_parts(self) -> (W, Element) {
848        let Self { warning, element } = self;
849        (warning, element)
850    }
851
852    /// Converts `Error<W>` into `Error<WA>` using the `impl Into<WA> for W`.
853    ///
854    /// This is used by the [`from_warning_all`] macro.
855    fn into_other<WA>(self) -> Error<WA>
856    where
857        W: Into<WA>,
858        WA: Warning,
859    {
860        let Self { warning, element } = self;
861        Error {
862            warning: warning.into(),
863            element,
864        }
865    }
866}
867
868impl<W: Warning> std::error::Error for Error<W> {}
869
870impl<W: Warning> fmt::Display for Error<W> {
871    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
872        write!(
873            f,
874            "A warning for element at `{}` was upgraded to an `error`: {}",
875            self.element.path, self.warning
876        )
877    }
878}
879
880/// Associate a [`json::Element`] with a set of [`Warning`]s contained by a [`VerdictDeferred`].
881pub trait WithElement<T, W: Warning> {
882    fn with_element(self, element: &json::Element<'_>) -> Verdict<T, W>;
883}
884
885impl<T, W: Warning> WithElement<T, W> for VerdictDeferred<T, W> {
886    /// Associate a [`json::Element`] with a set of [`Warning`]s.
887    fn with_element(self, element: &json::Element<'_>) -> Verdict<T, W> {
888        match self {
889            Ok(v) => {
890                let CaveatDeferred { value, warnings } = v;
891                let SetDeferred(warnings) = warnings;
892                Ok(Caveat {
893                    value,
894                    warnings: Set(warnings),
895                })
896            }
897            Err(set) => {
898                let ErrorSetDeferred { error, warnings } = set;
899                Err(ErrorSet {
900                    error: Box::new(Error {
901                        warning: error,
902                        element: Element::from(element),
903                    }),
904                    warnings,
905                })
906            }
907        }
908    }
909}
910
911/// A representation of a JSON element that satisfies the needs of most consumers of a [`Warning`].
912///
913/// This representation avoids the complexity of needing to provide a `'buf` lifetime to the [`json::Element`].
914/// This would complicate all warnings types with that lifetime.
915///
916/// A consumer of warnings wants to group them by [`json::ElemId`] using `Warning::group_by_elem` and then
917/// display or report the warnings by path.
918///
919/// The linter report also wants to highlight the source JSON that a warning refers too.
920#[derive(Debug)]
921pub struct Element {
922    /// The Id of the element that caused the [`Warning`].
923    ///
924    /// This is used for sorting warnings and can be used to retrieve the [`json::Element`] object.
925    pub id: json::ElemId,
926
927    /// The `Span` that delimits the [`json::Element`].
928    pub span: json::parser::Span,
929
930    /// The elements path.
931    ///
932    /// Most consumers of warnings just want this data.
933    pub path: SmartString,
934}
935
936impl<'buf> From<&json::Element<'buf>> for Element {
937    fn from(elem: &json::Element<'buf>) -> Self {
938        Self {
939            id: elem.id(),
940            span: elem.span(),
941            path: elem.path().to_smart_string(),
942        }
943    }
944}
945
946/// A Display object for writing a set of warnings.
947///
948/// The warnings set is formatted as a tree with element paths on the first level
949/// and a list of warning ids on the second.
950///
951/// ```shell
952/// $path.to.json.[0].field:
953///   - list_of_warning_ids
954///   - next_warning_id
955///
956/// $next.path.to.[1].json.field
957///   - list_of_warning_ids
958/// ```
959pub struct SetWriter<'caller, W: Warning> {
960    /// The list of warnings for the [`json::Element`].
961    warnings: &'caller Set<W>,
962
963    /// The indent to prefix to each warning id.
964    indent: &'caller str,
965}
966
967impl<'caller, W: Warning> SetWriter<'caller, W> {
968    /// Create a new `SetWriter` with a default warning id indent of `"  - "`.
969    pub fn new(warnings: &'caller Set<W>) -> Self {
970        Self {
971            warnings,
972            indent: "  - ",
973        }
974    }
975
976    /// Create a new `SetWriter` with a custom warning id indent.
977    pub fn with_indent(warnings: &'caller Set<W>, indent: &'caller str) -> Self {
978        Self { warnings, indent }
979    }
980}
981
982impl<W: Warning> fmt::Debug for SetWriter<'_, W> {
983    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
984        fmt::Display::fmt(self, f)
985    }
986}
987
988impl<W: Warning> fmt::Display for SetWriter<'_, W> {
989    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
990        let mut iter = self.warnings.iter();
991
992        {
993            // Write the first group without an empty line prefix.
994            let Some(Group { element, warnings }) = iter.next() else {
995                return Ok(());
996            };
997
998            writeln!(f, "{}", element.path)?;
999
1000            for warning in warnings {
1001                write!(f, "{}{}", self.indent, warning)?;
1002            }
1003        }
1004
1005        // Write the rest of the Groups with am empty line padding.
1006        for Group { element, warnings } in iter {
1007            writeln!(f, "\n{}", element.path)?;
1008
1009            for warning in warnings {
1010                write!(f, "{}{}", self.indent, warning)?;
1011            }
1012        }
1013
1014        Ok(())
1015    }
1016}
1017
1018/// Each mod defines warnings for the type that it's trying to parse or lint from a [`json::Element`].
1019///
1020/// The `Warning` in the mod should impl this trait to take part in the [`Warning`] system.
1021pub trait Warning: Sized + fmt::Debug + fmt::Display + Send + Sync {
1022    /// Return the human readable identifier for the [`Warning`].
1023    ///
1024    /// This is used in the `auto_test` assertion system.
1025    /// Changing these strings may require updating `output_price__cdr.json` files.
1026    fn id(&self) -> SmartString;
1027}
1028
1029/// A set of [`Warning`]s transported through the system using a `VerdictDeferred` or `CaveatDeferred`.
1030///
1031///
1032/// This set is considered deferred as the [`Warning`]s need to be associated with a [`json::Element`]
1033/// to become [`Warning`]s.
1034///
1035/// NOTE: The deferred types are used to avoid passing [`json::Element`] references
1036/// to functions just to create [`Warning`]s.
1037#[derive(Debug)]
1038pub struct SetDeferred<W: Warning>(BTreeMap<json::ElemId, Group<W>>);
1039
1040impl<W: Warning> SetDeferred<W> {
1041    /// Create a new list of [`Warning`]s
1042    pub(crate) fn new() -> Self {
1043        Self(BTreeMap::new())
1044    }
1045
1046    /// Create and add a [`Warning`] to the set while consuming the set into a [`VerdictDeferred`].
1047    ///
1048    /// This is designed for use as the last [`Warning`] of a function. The function should exit with the `Err` returned.
1049    pub(crate) fn bail<T>(self, warning: W) -> VerdictDeferred<T, W> {
1050        let Self(warnings) = self;
1051        Err(ErrorSetDeferred {
1052            error: warning,
1053            warnings,
1054        })
1055    }
1056
1057    /// Return true if the [`Warning`] set is empty.
1058    pub fn is_empty(&self) -> bool {
1059        self.0.is_empty()
1060    }
1061
1062    /// Extend this set with the warnings of another.
1063    ///
1064    /// The other set's warnings will be converted if necessary.
1065    pub(crate) fn extend<WA>(&mut self, warnings: SetDeferred<WA>)
1066    where
1067        WA: Into<W> + Warning,
1068    {
1069        let SetDeferred(warnings) = warnings;
1070        self.0
1071            .extend(warnings.into_iter().map(|(k, v)| (k, v.into_other())));
1072    }
1073}
1074
1075/// A set of [`Warning`]s and a [`Warning`] that caused an operation to fail represented as an [`Error`].
1076///
1077/// This set is transported through the system using a [`VerdictDeferred`]s `Err` variant.
1078///
1079/// This set is considered deferred as the [`Warning`]s need to be associated with a [`json::Element`]
1080/// to become [`Warning`]s.
1081///
1082/// NOTE: The deferred types are used to avoid passing [`json::Element`] references
1083/// to functions just to create [`Warning`]s.
1084#[derive(Debug)]
1085pub struct ErrorSetDeferred<W: Warning> {
1086    error: W,
1087    warnings: BTreeMap<json::ElemId, Group<W>>,
1088}
1089
1090impl<W: Warning> ErrorSetDeferred<W> {
1091    /// Create a new list of [`Warning`]s
1092    pub(crate) fn with_warn(warning: W) -> Self {
1093        Self {
1094            warnings: BTreeMap::new(),
1095            error: warning,
1096        }
1097    }
1098
1099    /// Converts `ErrorSetDeferred<W>` into `ErrorSetDeferred<WA>` using the `impl Into<WA> for W`.
1100    ///
1101    /// This is used by the [`from_warning_all`] macro.
1102    pub(crate) fn into_other<WA>(self) -> ErrorSetDeferred<WA>
1103    where
1104        W: Into<WA>,
1105        WA: Warning,
1106    {
1107        let Self { error, warnings } = self;
1108        let warnings = warnings
1109            .into_iter()
1110            .map(|(k, v)| (k, v.into_other()))
1111            .collect();
1112        ErrorSetDeferred {
1113            error: error.into(),
1114            warnings,
1115        }
1116    }
1117}
1118
1119/// A set of [`Warning`]s transported through the system using a [`Caveat`].
1120#[derive(Debug)]
1121pub struct Set<W: Warning>(BTreeMap<json::ElemId, Group<W>>);
1122
1123impl<W: Warning> Set<W> {
1124    /// Create a new list of [`Warning`]s
1125    pub(crate) fn new() -> Self {
1126        Self(BTreeMap::new())
1127    }
1128
1129    /// Insert a [`Warning`] defined in a domain module and it's associated [`json::Element`].
1130    pub(crate) fn with_elem(&mut self, warning: W, element: &json::Element<'_>) {
1131        self.insert_warning(warning, element.id(), || Element::from(element));
1132    }
1133
1134    /// Insert [`Warning`] defined in a domain module and it's associated [`Element`].
1135    ///
1136    /// Note: The [`Element`] is created lazily.
1137    fn insert_warning<F>(&mut self, warning: W, elem_id: json::ElemId, f: F)
1138    where
1139        F: FnOnce() -> Element,
1140    {
1141        use std::collections::btree_map::Entry;
1142
1143        match self.0.entry(elem_id) {
1144            Entry::Vacant(entry) => {
1145                let element = f();
1146                entry.insert_entry(Group {
1147                    element,
1148                    warnings: vec![warning],
1149                });
1150            }
1151            Entry::Occupied(mut entry) => {
1152                entry.get_mut().warnings.push(warning);
1153            }
1154        }
1155    }
1156
1157    /// Consume the set and insert a [`Warning`] while returning a [`Verdict`].
1158    ///
1159    /// This is designed for use as the last [`Warning`] of a function. The function should exit with the `Err` returned.
1160    pub(crate) fn bail<T>(self, warning: W, element: &json::Element<'_>) -> Verdict<T, W> {
1161        let Self(warnings) = self;
1162
1163        Err(ErrorSet {
1164            error: Box::new(Error {
1165                warning,
1166                element: Element::from(element),
1167            }),
1168            warnings,
1169        })
1170    }
1171
1172    /// Converts `Set<W>` into `Set<WA>` using the `impl Into<WA> for W`.
1173    ///
1174    /// This is used by the [`from_warning_all`] macro.
1175    pub(crate) fn into_other<WA>(self) -> Set<WA>
1176    where
1177        W: Into<WA>,
1178        WA: Warning,
1179    {
1180        let Set(warnings) = self;
1181        let warnings = warnings
1182            .into_iter()
1183            .map(|(elem_id, group)| (elem_id, group.into_other()))
1184            .collect();
1185        Set(warnings)
1186    }
1187
1188    /// Return true if the [`Warning`] set is empty.
1189    pub fn is_empty(&self) -> bool {
1190        self.0.is_empty()
1191    }
1192
1193    /// Return the amount of [`Element`]s in this set.
1194    ///
1195    /// Each [`Element`] can have many [`Warning`]s associated with it.
1196    pub fn len_elements(&self) -> usize {
1197        self.0.len()
1198    }
1199
1200    /// Return the total amount of [`Warning`]s in this set for all [`Element`]s.
1201    pub fn len_warnings(&self) -> usize {
1202        self.0
1203            .values()
1204            .fold(0, |acc, group| acc + group.warnings.len())
1205    }
1206
1207    /// Return an iterator of [`Warning`]s grouped by [`json::Element`].
1208    pub fn iter(&self) -> Iter<'_, W> {
1209        Iter {
1210            warnings: self.0.iter(),
1211        }
1212    }
1213
1214    /// Return a map of [`json::Element`] paths to a list of [`Warning`].
1215    ///
1216    /// This is designed to be used to print out maps of warnings associated with elements.
1217    /// You can use the debug alternate format `{:#?}` to print the map 'pretty' over multiple lines
1218    /// with indentation.
1219    pub fn path_map(&self) -> BTreeMap<&str, &[W]> {
1220        self.0
1221            .values()
1222            .map(|group| (group.element.path.as_str(), group.warnings.as_slice()))
1223            .collect()
1224    }
1225
1226    /// Consume the `Set` and return a map of [`json::Element`] paths to a list of [`Warning`]s.
1227    ///
1228    /// This is designed to be used to print out maps of warnings associated with elements.
1229    pub fn into_path_map(self) -> BTreeMap<SmartString, Vec<W>> {
1230        self.0
1231            .into_values()
1232            .map(|group| (group.element.path, group.warnings))
1233            .collect()
1234    }
1235
1236    /// Return a map of [`json::Element`] paths to a list of [`Warning`] ids as Strings.
1237    ///
1238    /// This is designed to be used to print out maps of warnings associated with elements.
1239    /// You can use the debug alternate format `{:#?}` to print the map 'pretty' over multiple lines
1240    /// with indentation.
1241    ///
1242    /// Note: This representation is also valid JSON and can be copied directly to
1243    /// a test expectation file.
1244    pub fn path_id_map(&self) -> BTreeMap<&str, Vec<SmartString>> {
1245        self.0
1246            .values()
1247            .map(|group| {
1248                let warnings = group.warnings.iter().map(Warning::id).collect();
1249                (group.element.path.as_str(), warnings)
1250            })
1251            .collect()
1252    }
1253
1254    /// Return a map of [`json::Element`] paths to a list of [`Warning`] messages as Strings.
1255    ///
1256    /// This is designed to be used to print out maps of warnings associated with elements.
1257    /// You can use the debug alternate format `{:#?}` to print the map 'pretty' over multiple lines
1258    /// with indentation.
1259    pub fn path_msg_map(&self) -> BTreeMap<&str, Vec<String>> {
1260        self.0
1261            .values()
1262            .map(|group| {
1263                let warnings = group.warnings.iter().map(ToString::to_string).collect();
1264                (group.element.path.as_str(), warnings)
1265            })
1266            .collect()
1267    }
1268
1269    /// Deescalate an [`Error`] by subsuming it back into a `Set`.
1270    pub(crate) fn deescalate_error(&mut self, err_set: ErrorSet<W>) {
1271        let ErrorSet { error, warnings } = err_set;
1272        let Error { warning, element } = *error;
1273        self.0.extend(warnings);
1274        self.insert_warning(warning, element.id, || element);
1275    }
1276
1277    /// Extend this set with the warnings of another.
1278    ///
1279    /// The other set's warnings will be converted if necessary.
1280    fn extend<WA>(&mut self, warnings: Set<WA>)
1281    where
1282        WA: Into<W> + Warning,
1283    {
1284        use std::collections::btree_map::Entry;
1285
1286        let Set(warnings) = warnings;
1287        let warnings = warnings
1288            .into_iter()
1289            .map(|(elem_id, group)| (elem_id, group.into_other()));
1290
1291        for (elem_id, group) in warnings {
1292            match self.0.entry(elem_id) {
1293                Entry::Vacant(entry) => {
1294                    entry.insert_entry(group);
1295                }
1296                Entry::Occupied(mut entry) => {
1297                    let Group {
1298                        element: _,
1299                        warnings,
1300                    } = group;
1301                    entry.get_mut().warnings.extend(warnings);
1302                }
1303            }
1304        }
1305    }
1306}
1307
1308/// A set of [`Warning`]s and a [`Warning`] that caused an operation to fail represented as an [`Error`].
1309///
1310/// This set is transported through the system using a [`Verdict`]s `Err` variant.
1311#[derive(Debug)]
1312pub struct ErrorSet<W: Warning> {
1313    /// The warning that caused an operation to fail.
1314    ///
1315    /// The warning is converted to an [`Error`] so it's ready to take part in Rust's error system.
1316    error: Box<Error<W>>,
1317
1318    /// The warnings accumulated up until the failure moment.
1319    ///
1320    /// This list does not included the warning that caused the operation to fail.
1321    warnings: BTreeMap<json::ElemId, Group<W>>,
1322}
1323
1324impl<W> ErrorSet<W>
1325where
1326    W: Warning,
1327{
1328    /// Consume the [`ErrorSet`] and return the [`Error`] and warnings as a `Set`.
1329    pub fn into_parts(self) -> (Error<W>, Set<W>) {
1330        let Self { error, warnings } = self;
1331        (*error, Set(warnings))
1332    }
1333
1334    /// Converts `ErrorSet<W>` into `ErrorSet<WA>` using the `impl Into<WA> for K`.
1335    ///
1336    /// This is used by the [`from_warning_all`] macro.
1337    pub(crate) fn into_other<WA>(self) -> ErrorSet<WA>
1338    where
1339        W: Into<WA>,
1340        WA: Warning,
1341    {
1342        let Self { error, warnings } = self;
1343        let warnings = warnings
1344            .into_iter()
1345            .map(|(elem_id, group)| (elem_id, group.into_other()))
1346            .collect();
1347        ErrorSet {
1348            error: Box::new(Error::into_other(*error)),
1349            warnings,
1350        }
1351    }
1352}
1353
1354/// A group of warning `Warning`s associated with an `Element`.
1355///
1356/// This group is emitted from the `IntoGroupByElem` iterator.
1357/// The warning `Warning`s are owned and so can be moved to another location.
1358#[derive(Debug)]
1359pub struct Group<W> {
1360    /// The [`json::Element`] that has [`Warning`]s.
1361    pub element: Element,
1362
1363    /// The list of warnings for the [`json::Element`].
1364    pub warnings: Vec<W>,
1365}
1366
1367impl<W> Group<W>
1368where
1369    W: Warning,
1370{
1371    /// Converts `IntoGroup<W>` into `IntoGroup<WA>` using the `impl Into<WA> for K`.
1372    ///
1373    /// This is used by the [`from_warning_all`] macro.
1374    fn into_other<WA>(self) -> Group<WA>
1375    where
1376        W: Into<WA>,
1377        WA: Warning,
1378    {
1379        let Self { element, warnings } = self;
1380        let warnings = warnings.into_iter().map(Into::into).collect();
1381        Group { element, warnings }
1382    }
1383}
1384
1385/// An iterator of borrowed [`Warning`]s grouped by [`json::Element`].
1386pub struct Iter<'caller, W>
1387where
1388    W: Warning,
1389{
1390    /// The iterator over every [`Warning`].
1391    warnings: std::collections::btree_map::Iter<'caller, json::ElemId, Group<W>>,
1392}
1393
1394impl<W> Iter<'_, W> where W: Warning {}
1395
1396impl<'caller, W: Warning> Iterator for Iter<'caller, W> {
1397    type Item = &'caller Group<W>;
1398
1399    fn next(&mut self) -> Option<Self::Item> {
1400        let (_elem_id, group) = self.warnings.next()?;
1401        Some(group)
1402    }
1403}
1404
1405/// An iterator of borrowed [`Warning`]s grouped by [`json::Element`].
1406pub struct IntoIter<W>
1407where
1408    W: Warning,
1409{
1410    /// The iterator over every [`Warning`].
1411    warnings: std::collections::btree_map::IntoIter<json::ElemId, Group<W>>,
1412}
1413
1414impl<W: Warning> Iterator for IntoIter<W> {
1415    type Item = Group<W>;
1416
1417    fn next(&mut self) -> Option<Self::Item> {
1418        let (_elem_id, group) = self.warnings.next()?;
1419        Some(group)
1420    }
1421}
1422
1423impl<W: Warning> IntoIterator for Set<W> {
1424    type Item = Group<W>;
1425    type IntoIter = IntoIter<W>;
1426
1427    fn into_iter(self) -> Self::IntoIter {
1428        let Set(warnings) = self;
1429        IntoIter {
1430            warnings: warnings.into_iter(),
1431        }
1432    }
1433}
1434
1435impl<'a, W: Warning> IntoIterator for &'a Set<W> {
1436    type Item = &'a Group<W>;
1437    type IntoIter = Iter<'a, W>;
1438
1439    fn into_iter(self) -> Self::IntoIter {
1440        self.iter()
1441    }
1442}
1443
1444#[cfg(test)]
1445pub(crate) mod test {
1446    use std::{
1447        collections::{BTreeMap, BTreeSet},
1448        fmt,
1449    };
1450
1451    use crate::{
1452        test::{ExpectValue, Expectation},
1453        SmartString,
1454    };
1455
1456    use super::{Caveat, CaveatDeferred, Error, ErrorSet, Group, Set, Verdict, Warning};
1457
1458    /// `Verdict` specific extension methods for the `Result` type.
1459    pub trait VerdictTestExt<T, W: Warning> {
1460        /// Discard all warnings in the `ErrorSet` variant and keep only the warning that caused the error.
1461        fn unwrap_only_error(self) -> Error<W>;
1462    }
1463
1464    impl<T, W: Warning> VerdictTestExt<T, W> for Verdict<T, W>
1465    where
1466        T: fmt::Debug,
1467    {
1468        fn unwrap_only_error(self) -> Error<W> {
1469            match self {
1470                Ok(c) => panic!("called `Result::unwrap_only_error` on an `Ok` value: {c:?}"),
1471                Err(set) => {
1472                    let ErrorSet { error, warnings: _ } = set;
1473                    *error
1474                }
1475            }
1476        }
1477    }
1478
1479    impl<T, W> Caveat<T, W>
1480    where
1481        W: Warning,
1482    {
1483        /// Return the value and assert there are no [`Warning`]s.
1484        ///
1485        /// # Panics
1486        ///
1487        /// Asserts that the warning is empty.
1488        pub fn unwrap(self) -> T {
1489            let Self { value, warnings } = self;
1490            assert!(warnings.is_empty(), "{warnings:#?}");
1491            value
1492        }
1493    }
1494
1495    impl<T, W> CaveatDeferred<T, W>
1496    where
1497        W: Warning,
1498    {
1499        /// Return the value and assert there are no [`Warning`]s.
1500        ///
1501        /// # Panics
1502        ///
1503        /// Asserts that the warning is empty.
1504        pub fn unwrap(self) -> T {
1505            let Self { value, warnings } = self;
1506            assert!(warnings.is_empty(), "{warnings:#?}");
1507            value
1508        }
1509    }
1510
1511    impl<W> Group<W>
1512    where
1513        W: Warning,
1514    {
1515        /// Convert the Group into String versions of its parts.
1516        ///
1517        /// The first tuple field is the [`json::Element`] path as a String.
1518        /// The second tuple field is a list of [`Warning`] ids.
1519        fn path_and_ids(&self) -> (&str, Vec<SmartString>) {
1520            let Self { element, warnings } = self;
1521            (
1522                element.path.as_str(),
1523                warnings.iter().map(Warning::id).collect(),
1524            )
1525        }
1526    }
1527
1528    /// Assert that the warnings given are expected.
1529    ///
1530    /// Panic with print out of the warnings and the expectations if any warnings were unexpected.
1531    pub(crate) fn assert_warnings<W>(
1532        expect_file_name: &str,
1533        warnings: &Set<W>,
1534        expected: Expectation<BTreeMap<String, Vec<String>>>,
1535    ) where
1536        W: Warning,
1537    {
1538        let Expectation::Present(ExpectValue::Some(expected)) = expected else {
1539            assert!(
1540                warnings.is_empty(),
1541                "There is no `warnings` field in the `{expect_file_name}` file but the tariff has warnings;\n{:?}",
1542                warnings.path_id_map()
1543            );
1544            return;
1545        };
1546
1547        {
1548            // Assert that the `expect` file doesn't have extraneous entries.
1549            let warnings_grouped = warnings
1550                .iter()
1551                .map(|Group { element, warnings }| (element.path.clone(), warnings))
1552                .collect::<BTreeMap<_, _>>();
1553
1554            let mut elems_in_expect_without_warning = vec![];
1555
1556            for elem_path in expected.keys() {
1557                let elem_path = SmartString::from(elem_path);
1558                if !warnings_grouped.contains_key(&elem_path) {
1559                    elems_in_expect_without_warning.push(elem_path);
1560                }
1561            }
1562
1563            assert!(elems_in_expect_without_warning.is_empty(),
1564                "The expect file `{expect_file_name}` has entries for elements that have no warnings:\n\
1565                {elems_in_expect_without_warning:#?}"
1566            );
1567        }
1568
1569        // The elements that have warnings but have no entry for the elements path in the `expect` file.
1570        let mut elems_missing_from_expect = vec![];
1571        // The element that have warnings and an entry in the `expect` file, but the list of expected warnings
1572        // is not equal to the list of actual warnings.
1573        let mut unequal_warnings = vec![];
1574
1575        for group in warnings {
1576            let Some(warnings_expected) = expected.get(&*group.element.path) else {
1577                elems_missing_from_expect.push(group);
1578                continue;
1579            };
1580
1581            // Make two sets of actual and expected warnings.
1582            let warnings_expected = warnings_expected
1583                .iter()
1584                .map(SmartString::from)
1585                .collect::<BTreeSet<_>>();
1586            let warnings = group
1587                .warnings
1588                .iter()
1589                .map(Warning::id)
1590                .collect::<BTreeSet<_>>();
1591
1592            if warnings_expected != warnings {
1593                unequal_warnings.push(group);
1594            }
1595        }
1596
1597        if !elems_missing_from_expect.is_empty() || !unequal_warnings.is_empty() {
1598            let missing = elems_missing_from_expect
1599                .into_iter()
1600                .map(Group::path_and_ids)
1601                .collect::<BTreeMap<_, _>>();
1602
1603            let unequal = unequal_warnings
1604                .into_iter()
1605                .map(Group::path_and_ids)
1606                .collect::<BTreeMap<_, _>>();
1607
1608            match (!missing.is_empty(), !unequal.is_empty()) {
1609                (true, true) => panic!(
1610                    "Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}\n\
1611                    Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1612                    The warnings reported are: \n{unequal:#?}"                  
1613                ),
1614                (true, false) => panic!("Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}"),
1615                (false, true) => panic!(
1616                    "Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1617                    The warnings reported are: \n{unequal:#?}"
1618                ),
1619                (false, false) => (),
1620            }
1621        }
1622    }
1623}
1624
1625#[cfg(test)]
1626mod test_group_by_elem {
1627    use std::fmt;
1628
1629    use assert_matches::assert_matches;
1630
1631    use crate::{json, test, warning};
1632
1633    use super::{Group, Set};
1634
1635    #[derive(Debug)]
1636    enum Warning {
1637        Root,
1638        One,
1639        OneAgain,
1640        Three,
1641    }
1642
1643    impl super::Warning for Warning {
1644        fn id(&self) -> crate::SmartString {
1645            match self {
1646                Warning::Root => "root".into(),
1647                Warning::One => "one".into(),
1648                Warning::OneAgain => "one_again".into(),
1649                Warning::Three => "three".into(),
1650            }
1651        }
1652    }
1653
1654    impl fmt::Display for Warning {
1655        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1656            match self {
1657                Warning::Root => write!(f, "NopeRoot"),
1658                Warning::One => write!(f, "NopeOne"),
1659                Warning::OneAgain => write!(f, "NopeOneAgain"),
1660                Warning::Three => write!(f, "NopeThree"),
1661            }
1662        }
1663    }
1664
1665    #[test]
1666    fn should_group_by_elem() {
1667        test::setup();
1668
1669        let mut warnings = Set::<Warning>::new();
1670
1671        // Push warnings into the set out of order.
1672        // They should be sorted by `ElemId`.
1673        warnings.insert_warning(Warning::Root, json::ElemId::from(0), || warning::Element {
1674            id: json::ElemId::from(0),
1675            path: "$".into(),
1676            span: json::parser::Span::default(),
1677        });
1678        warnings.insert_warning(Warning::One, json::ElemId::from(1), || warning::Element {
1679            id: json::ElemId::from(1),
1680            path: "$.field_one".into(),
1681            span: json::parser::Span::default(),
1682        });
1683        warnings.insert_warning(Warning::Three, json::ElemId::from(3), || warning::Element {
1684            id: json::ElemId::from(3),
1685            path: "$.field_three".into(),
1686            span: json::parser::Span::default(),
1687        });
1688        warnings.insert_warning(Warning::OneAgain, json::ElemId::from(1), || {
1689            warning::Element {
1690                id: json::ElemId::from(1),
1691                path: "$.field_one".into(),
1692                span: json::parser::Span::default(),
1693            }
1694        });
1695
1696        let mut iter = warnings.iter();
1697
1698        {
1699            let Group { element, warnings } = iter.next().unwrap();
1700
1701            assert_eq!(element.path, "$", "The root object should be emitted first");
1702            assert_matches!(warnings.as_slice(), [Warning::Root]);
1703        }
1704
1705        {
1706            let Group { element, warnings } = iter.next().unwrap();
1707
1708            assert_eq!(element.path, "$.field_one");
1709            assert_matches!(
1710                warnings.as_slice(),
1711                [Warning::One, Warning::OneAgain],
1712                "[`json::Element`] 1 should have two warnings"
1713            );
1714        }
1715
1716        {
1717            // [`json::Element`] 2 has no [`Warning`]s so we expect [`json::Element`] 3 next
1718            let Group { element, warnings } = iter.next().unwrap();
1719
1720            assert_eq!(element.path, "$.field_three");
1721            assert_matches!(warnings.as_slice(), [Warning::Three]);
1722        }
1723    }
1724
1725    #[test]
1726    fn should_into_group_by_elem() {
1727        test::setup();
1728
1729        let mut warnings = Set::<Warning>::new();
1730
1731        // Push warnings into the set out of order.
1732        // They should be sorted by `ElemId`.
1733        warnings.insert_warning(Warning::Root, json::ElemId::from(0), || warning::Element {
1734            id: json::ElemId::from(0),
1735            path: "$".into(),
1736            span: json::parser::Span::default(),
1737        });
1738        warnings.insert_warning(Warning::One, json::ElemId::from(1), || warning::Element {
1739            id: json::ElemId::from(1),
1740            path: "$.field_one".into(),
1741            span: json::parser::Span::default(),
1742        });
1743        warnings.insert_warning(Warning::Three, json::ElemId::from(3), || warning::Element {
1744            id: json::ElemId::from(3),
1745            path: "$.field_three".into(),
1746            span: json::parser::Span::default(),
1747        });
1748        warnings.insert_warning(Warning::OneAgain, json::ElemId::from(1), || {
1749            warning::Element {
1750                id: json::ElemId::from(1),
1751                path: "$.field_one".into(),
1752                span: json::parser::Span::default(),
1753            }
1754        });
1755
1756        let mut iter = warnings.iter();
1757
1758        {
1759            let Group { element, warnings } = iter.next().unwrap();
1760
1761            assert_eq!(element.path, "$");
1762            assert_matches!(warnings.as_slice(), [Warning::Root]);
1763        }
1764
1765        {
1766            let Group { element, warnings } = iter.next().unwrap();
1767
1768            assert_eq!(element.path, "$.field_one");
1769            assert_matches!(
1770                warnings.as_slice(),
1771                [Warning::One, Warning::OneAgain],
1772                "[`json::Element`] 1 should have two warnings"
1773            );
1774        }
1775
1776        {
1777            // [`json::Element`] 2 has no [`Warning`]s so we expect [`json::Element`] 3 next
1778            let Group { element, warnings } = iter.next().unwrap();
1779
1780            assert_eq!(element.path, "$.field_three");
1781            assert_matches!(warnings.as_slice(), [Warning::Three]);
1782        }
1783    }
1784}