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