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