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