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