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