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