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: 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: String,
714}
715
716impl<K: Kind> std::error::Error for Cause<K> {}
717
718impl<K: Kind> fmt::Display for Cause<K> {
719    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
720        write!(
721            f,
722            "A warning for element at `{}` was upgraded to an `error`: {}",
723            self.elem, self.kind
724        )
725    }
726}
727
728/// Associate a [`json::Element`] with a set of warning [`Kind`]s.
729pub trait WithElement<T, K: Kind> {
730    fn with_element(self, elem: &json::Element<'_>) -> Verdict<T, K>;
731}
732
733impl<T, K: Kind> WithElement<T, K> for VerdictDeferred<T, K> {
734    /// Associate a [`json::Element`] with a set of warning [`Kind`]s.
735    fn with_element(self, elem: &json::Element<'_>) -> Verdict<T, K> {
736        match self {
737            Ok(v) => {
738                let CaveatDeferred { value, warnings } = v;
739                Ok(Caveat {
740                    value,
741                    warnings: Set::from_kind_vec(elem, warnings.0),
742                })
743            }
744            Err(warning_kinds) => Err(Set::from_kind_vec(elem, warning_kinds.0)),
745        }
746    }
747}
748
749/// Convert an `Option` into a `Verdict` ready to exit the fn.
750pub trait OptionExt<T, K>
751where
752    K: Kind,
753{
754    /// Convert an `Option` into a `Verdict` ready to exit the fn.
755    fn exit_with_warning<F>(self, warnings: Set<K>, f: F) -> Verdict<T, K>
756    where
757        F: FnOnce() -> Warning<K>;
758}
759
760impl<T, K> OptionExt<T, K> for Option<T>
761where
762    T: IntoCaveat,
763    K: Kind,
764{
765    fn exit_with_warning<F>(self, mut warnings: Set<K>, f: F) -> Verdict<T, K>
766    where
767        F: FnOnce() -> Warning<K>,
768    {
769        if let Some(v) = self {
770            Ok(v.into_caveat(warnings))
771        } else {
772            warnings.push(f());
773            Err(warnings)
774        }
775    }
776}
777
778/// Groups together a module's `Kind` and the associated [`json::Element`].
779///
780/// The [`json::Element`] is referenced by id. The id needs to be resolved
781/// to a [`json::Element`] before being printed.
782///
783/// You can use the [`Set::group_by_elem`] to resolve the ids en masse.
784#[derive(Debug)]
785pub struct Warning<K: Kind> {
786    /// The `Kind` of warning.
787    kind: K,
788
789    /// The Id of the element that caused the [`Warning`].
790    elem_id: json::ElemId,
791}
792
793/// A Display object for writing a set of warnings.
794///
795/// The warnings set is formatted as a tree with element paths on the first level
796/// and a list of warning ids on the second.
797///
798/// ```shell
799/// $path.to.json.[0].field:
800///   - list_of_warning_ids
801///   - next_warning_id
802///
803/// $next.path.to.[1].json.field
804///   - list_of_warning_ids
805/// ```
806pub struct SetWriter<'caller, 'buf, K: Kind> {
807    /// The [`json::Element`] that has [`Warning`]s.
808    root_elem: &'caller json::Element<'buf>,
809
810    /// The list of warnings for the [`json::Element`].
811    warnings: &'caller Set<K>,
812
813    /// The indent to prefix to each warning id.
814    indent: &'caller str,
815}
816
817impl<'caller, 'buf, K: Kind> SetWriter<'caller, 'buf, K> {
818    /// Create a new `SetWriter` with a default warning id indent of `"  - "`.
819    pub fn new(root_elem: &'caller json::Element<'buf>, warnings: &'caller Set<K>) -> Self {
820        Self {
821            root_elem,
822            warnings,
823            indent: "  - ",
824        }
825    }
826
827    /// Create a new `SetWriter` with a custom warning id indent.
828    pub fn with_indent(
829        root_elem: &'caller json::Element<'buf>,
830        warnings: &'caller Set<K>,
831        indent: &'caller str,
832    ) -> Self {
833        Self {
834            root_elem,
835            warnings,
836            indent,
837        }
838    }
839}
840
841impl<K: Kind> fmt::Debug for SetWriter<'_, '_, K> {
842    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
843        fmt::Display::fmt(self, f)
844    }
845}
846
847impl<K: Kind> fmt::Display for SetWriter<'_, '_, K> {
848    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
849        let mut iter = self.warnings.group_by_elem(self.root_elem);
850
851        {
852            // Write the first group without an empty line prefix.
853            let Some(Group { element, warnings }) = iter.next() else {
854                return Ok(());
855            };
856
857            writeln!(f, "{}", element.path())?;
858
859            for warning in warnings {
860                write!(f, "{}{}", self.indent, warning)?;
861            }
862        }
863
864        // Write the rest of the Groups with am empty line padding.
865        for Group { element, warnings } in iter {
866            writeln!(f, "\n{}", element.path())?;
867
868            for warning in warnings {
869                write!(f, "{}{}", self.indent, warning)?;
870            }
871        }
872
873        Ok(())
874    }
875}
876
877impl<K: Kind> Warning<K> {
878    /// Create a Warning from a `Kind`.
879    ///
880    /// The `Kind` is typically defined in a domain module.
881    pub(crate) const fn with_elem(kind: K, elem: &json::Element<'_>) -> Warning<K> {
882        Warning {
883            kind,
884            elem_id: elem.id(),
885        }
886    }
887
888    /// Return the warning `Kind`'s id.
889    pub fn id(&self) -> Cow<'static, str> {
890        self.kind.id()
891    }
892
893    /// Return the `Kind` as a reference.
894    pub fn kind(&self) -> &K {
895        &self.kind
896    }
897
898    /// Consume the Warning and return the `Kind` of warning.
899    pub fn into_kind(self) -> K {
900        self.kind
901    }
902}
903
904impl<K: Kind> fmt::Display for Warning<K> {
905    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
906        write!(f, "{}", self.kind)
907    }
908}
909
910/// Each mod defines warnings for the type that it's trying to parse or lint from a [`json::Element`].
911///
912/// The `WarningKind` in the mod should impl this trait to take part in the [`Warning`] system.
913pub trait Kind: Sized + fmt::Debug + fmt::Display + Send + Sync {
914    /// Return the human readable identifier for the [`Warning`].
915    ///
916    /// This is used in the `auto_test` assertion system.
917    /// Changing these strings may require updating `output_price__cdr.json` files.
918    fn id(&self) -> Cow<'static, str>;
919}
920
921/// A set of warning [`Kind`]s transported through the system using a `VerdictDeferred` or `CaveatDeferred`.
922///
923///
924/// This set is considered deferred as the warning [`Kind`]s need to be associated with a [`json::Element`]
925/// to become [`Warning`]s.
926///
927/// NOTE: The deferred types are used to avoid passing [`json::Element`] references
928/// to functions just to create [`Warning`]s.
929#[derive(Debug)]
930pub struct SetDeferred<K: Kind>(Vec<K>);
931
932impl<K: Kind> SetDeferred<K> {
933    /// Create a new list of warning [`Kind`]s
934    pub(crate) fn new() -> Self {
935        Self(vec![])
936    }
937
938    /// Create a new list of warning [`Kind`]s
939    pub(crate) fn with_warn(warning: K) -> Self {
940        Self(vec![warning])
941    }
942
943    /// Create and add a warning [`Kind`] to the set while consuming the set into a [`VerdictDeferred`].
944    ///
945    /// This is designed for use as the last warning [`Kind`] of a function. The function should exit with the `Err` returned.
946    pub(crate) fn bail<T>(mut self, warning: K) -> VerdictDeferred<T, K> {
947        self.0.push(warning);
948        Err(self)
949    }
950
951    /// Converts `SetDeferred<K>` into `SetDeferred<KB>` using the `impl From<K> for KB`.
952    ///
953    /// This is used by the [`from_warning_all`] macro.
954    pub(crate) fn into_set_deferred<KB>(self) -> SetDeferred<KB>
955    where
956        KB: Kind + From<K>,
957    {
958        let warnings = self.0.into_iter().map(Into::into).collect();
959
960        SetDeferred(warnings)
961    }
962
963    /// Extend this set with the warnings of another.
964    ///
965    /// The other set's warnings will be converted if necessary.
966    pub(crate) fn extend<KA>(&mut self, warnings: SetDeferred<KA>)
967    where
968        KA: Into<K> + Kind,
969    {
970        self.0.extend(warnings.0.into_iter().map(Into::into));
971    }
972}
973
974/// A set of [`Warning`]s transported through the system using a [`Verdict`] or [`Caveat`].
975#[derive(Debug)]
976pub struct Set<K: Kind>(Vec<Warning<K>>);
977
978impl<K: Kind> Set<K> {
979    /// Create a new list of [`Warning`]s
980    pub(crate) fn new() -> Self {
981        Self(vec![])
982    }
983
984    /// Create a `Set` from a `Vec` of [`Warning`]s.
985    pub(crate) fn from_vec(warnings: Vec<Warning<K>>) -> Self {
986        Self(warnings)
987    }
988
989    /// Create a `Set` from a list of warning [`Kind`]s all for a single [`json::Element`].
990    pub(crate) fn from_kind_vec(elem: &json::Element<'_>, warning_kinds: Vec<K>) -> Self {
991        let warnings = warning_kinds
992            .into_iter()
993            .map(|kind| Warning::with_elem(kind, elem))
994            .collect();
995        Self(warnings)
996    }
997
998    /// Push a new [`Warning`] onto the list.
999    fn push(&mut self, warning: Warning<K>) {
1000        self.0.push(warning);
1001    }
1002
1003    /// Create and push a [`Warning`] from a [`Kind`] defined in a domain module and it's associated [`json::Element`].
1004    pub(crate) fn with_elem(&mut self, kind: K, elem: &json::Element<'_>) {
1005        self.push(Warning::with_elem(kind, elem));
1006    }
1007
1008    /// Create and add a [`Warning`] from a [`Kind`] to the set while consuming the set into a [`Verdict`].
1009    ///
1010    /// This is designed for use as the last [`Warning`] of a function. The function should exit with the `Err` returned.
1011    pub(crate) fn bail<T>(mut self, kind: K, elem: &json::Element<'_>) -> Verdict<T, K> {
1012        self.0.push(Warning::with_elem(kind, elem));
1013        Err(self)
1014    }
1015
1016    /// Converts `Set<K>` into `Set<KB>` using the `impl From<K> for KB`.
1017    ///
1018    /// This is used by the [`from_warning_all`] macro.
1019    pub(crate) fn into_set<KB>(self) -> Set<KB>
1020    where
1021        KB: Kind + From<K>,
1022    {
1023        let warnings = self
1024            .0
1025            .into_iter()
1026            .map(|warn| {
1027                let Warning { kind, elem_id } = warn;
1028                Warning {
1029                    kind: kind.into(),
1030                    elem_id,
1031                }
1032            })
1033            .collect();
1034
1035        Set(warnings)
1036    }
1037
1038    /// Converts `Set<K>` into `Set<KU>` using the `impl From<K> for KU`.
1039    ///
1040    /// This is used by the [`from_warning_all`] macro.
1041    pub(crate) fn map_warning<KU>(self) -> Set<KU>
1042    where
1043        KU: Kind + From<K>,
1044    {
1045        let warnings = self
1046            .0
1047            .into_iter()
1048            .map(|warn| {
1049                let Warning { kind, elem_id } = warn;
1050                Warning {
1051                    kind: kind.into(),
1052                    elem_id,
1053                }
1054            })
1055            .collect();
1056
1057        Set(warnings)
1058    }
1059
1060    /// Extend this set with the warnings of another.
1061    ///
1062    /// The other set's warnings will be converted if necessary.
1063    pub(crate) fn extend<KA>(&mut self, warnings: Set<KA>)
1064    where
1065        KA: Into<K> + Kind,
1066    {
1067        self.0.extend(warnings.0.into_iter().map(|warn| {
1068            let Warning { kind, elem_id } = warn;
1069            Warning {
1070                kind: kind.into(),
1071                elem_id,
1072            }
1073        }));
1074    }
1075
1076    /// Return true if the [`Warning`] set is empty.
1077    pub fn is_empty(&self) -> bool {
1078        self.0.is_empty()
1079    }
1080
1081    /// Return the amount of [`Warning`]s in this set.
1082    pub fn len(&self) -> usize {
1083        self.0.len()
1084    }
1085
1086    /// Return an iterator of [`Warning`]s grouped by [`json::Element`].
1087    ///
1088    /// The iterator emits [`Group`]s that borrows the warnings.
1089    pub fn group_by_elem<'caller: 'buf, 'buf>(
1090        &'caller self,
1091        root: &'caller json::Element<'buf>,
1092    ) -> GroupByElem<'caller, 'buf, K> {
1093        let mut warnings = self.0.iter().collect::<Vec<_>>();
1094        warnings.sort_unstable_by_key(|warning| warning.elem_id);
1095
1096        GroupByElem {
1097            walker: json::walk::DepthFirst::new(root),
1098            warnings: warnings.into_iter().peekable(),
1099        }
1100    }
1101
1102    /// Return an iterator of [`Warning`]s grouped by [`json::Element`].
1103    ///
1104    /// The iterator emits [`IntoGroup`]s that owns the warnings.
1105    pub fn into_group_by_elem<'caller, 'buf>(
1106        self,
1107        root: &'caller json::Element<'buf>,
1108    ) -> IntoGroupByElem<'caller, 'buf, K> {
1109        let Self(mut warnings) = self;
1110        warnings.sort_unstable_by_key(|warning| warning.elem_id);
1111
1112        IntoGroupByElem {
1113            walker: json::walk::DepthFirst::new(root),
1114            warnings: warnings.into_iter().peekable(),
1115        }
1116    }
1117}
1118
1119/// An iterator of owned warning [`Kind`]s grouped by [`json::Element`].
1120pub struct IntoGroupByElem<'caller, 'buf, K>
1121where
1122    K: Kind,
1123{
1124    /// The [`json::Element`] tree walker.
1125    walker: json::walk::DepthFirst<'caller, 'buf>,
1126
1127    /// The iterator over every [`Warning`].
1128    warnings: iter::Peekable<vec::IntoIter<Warning<K>>>,
1129}
1130
1131impl<K> IntoGroupByElem<'_, '_, K>
1132where
1133    K: Kind,
1134{
1135    /// Return a map of [`json::Element`] paths to a list of [`Kind`]s.
1136    pub fn into_kind_map(self) -> BTreeMap<String, Vec<K>> {
1137        self.map(IntoGroup::into_kinds).collect()
1138    }
1139
1140    /// Return a map of [`json::Element`] paths to a list of [`Warning`] ids as Strings.
1141    ///
1142    /// This is designed to be used to print out maps of warnings associated with elements.
1143    /// You can use the debug alternate format `{:#?}` to print the map 'pretty' over multiple lines
1144    /// with indentation.
1145    ///
1146    /// Note: This representation is also valid JSON and can be copied directly to
1147    /// a test expectation file.
1148    pub fn into_id_map(self) -> BTreeMap<String, Vec<String>> {
1149        self.map(IntoGroup::into_str_ids).collect()
1150    }
1151
1152    /// Return a map of [`json::Element`] paths to a list of [`Warning`] messages as Strings.
1153    ///
1154    /// This is designed to be used to print out maps of warnings associated with elements.
1155    /// You can use the debug alternate format `{:#?}` to print the map 'pretty' over multiple lines
1156    /// with indentation.
1157    pub fn into_msg_map(self) -> BTreeMap<String, Vec<String>> {
1158        self.map(IntoGroup::into_str_msgs).collect()
1159    }
1160}
1161
1162/// A group of warning `Kind`s associated with an `Element`.
1163///
1164/// This group is emitted from the `IntoGroupByElem` iterator.
1165/// The warning `Kind`s are owned and so can be moved to another location.
1166#[derive(Debug)]
1167pub struct IntoGroup<'caller, 'buf, K> {
1168    /// The [`json::Element`] that has [`Warning`]s.
1169    pub element: &'caller json::Element<'buf>,
1170
1171    /// The list of warnings for the [`json::Element`].
1172    pub warnings: Vec<K>,
1173}
1174
1175impl<K> IntoGroup<'_, '_, K>
1176where
1177    K: Kind,
1178{
1179    /// Convert the Group into String versions of its Element path.
1180    ///
1181    /// The first tuple field is the [`Element`](json::Element) path as a String.
1182    /// The second tuple field is a list of [`Warning`](Kind)s.
1183    pub fn into_kinds(self) -> (String, Vec<K>) {
1184        let Self { element, warnings } = self;
1185        (element.path().to_string(), warnings)
1186    }
1187
1188    /// Convert the Group into String versions of its parts.
1189    ///
1190    /// The first tuple field is the [`Element`](json::Element) path as a String.
1191    /// The second tuple field is a list of [`Warning`](Kind) ids.
1192    pub fn into_str_ids(self) -> (String, Vec<String>) {
1193        let Self { element, warnings } = self;
1194        (
1195            element.path().to_string(),
1196            warnings.iter().map(|kind| kind.id().to_string()).collect(),
1197        )
1198    }
1199
1200    /// Convert the Group into String versions of its parts.
1201    ///
1202    /// The first tuple field is the [`Element`](json::Element) path as a String.
1203    /// The second tuple field is a list of [`Warning`](Kind) messages.
1204    pub fn into_str_msgs(self) -> (String, Vec<String>) {
1205        let Self { element, warnings } = self;
1206        (
1207            element.path().to_string(),
1208            warnings.iter().map(ToString::to_string).collect(),
1209        )
1210    }
1211}
1212
1213impl<'caller, 'buf, K: Kind> Iterator for IntoGroupByElem<'caller, 'buf, K> {
1214    type Item = IntoGroup<'caller, 'buf, K>;
1215
1216    fn next(&mut self) -> Option<Self::Item> {
1217        // The warnings are sorted and grouped by consecutive and compacted ids.
1218        let warning = self.warnings.next()?;
1219
1220        // Search for the element associated with warning.
1221        let element = loop {
1222            let Some(element) = self.walker.next() else {
1223                error!("An Element with id: `{}` was not found", warning.elem_id);
1224                return None;
1225            };
1226
1227            if element.id() < warning.elem_id {
1228                // This element does not have any warnings continue the search for the
1229                // element associated with the warning.
1230                continue;
1231            }
1232
1233            if element.id() > warning.elem_id {
1234                debug_assert!(
1235                    element.id() <= warning.elem_id,
1236                    "The elements or the warnings are not sorted."
1237                );
1238                return None;
1239            }
1240
1241            // We found the element for the first warning in the group.
1242            break element;
1243        };
1244
1245        // Insert the first warning into the `grouped` list.
1246        let mut warnings_grouped = vec![warning.into_kind()];
1247
1248        // Collect all warnings in the group.
1249        loop {
1250            let warning = self.warnings.next_if(|w| w.elem_id == element.id());
1251            let Some(warning) = warning else {
1252                break;
1253            };
1254
1255            warnings_grouped.push(warning.into_kind());
1256        }
1257
1258        Some(IntoGroup {
1259            element,
1260            warnings: warnings_grouped,
1261        })
1262    }
1263}
1264
1265/// An iterator of borrowed warning [`Kind`]s grouped by [`json::Element`].
1266pub struct GroupByElem<'caller, 'buf, K>
1267where
1268    K: Kind,
1269{
1270    /// The [`json::Element`] tree walker.
1271    walker: json::walk::DepthFirst<'caller, 'buf>,
1272
1273    /// The iterator over every [`Warning`].
1274    warnings: iter::Peekable<vec::IntoIter<&'caller Warning<K>>>,
1275}
1276
1277impl<K> GroupByElem<'_, '_, K>
1278where
1279    K: Kind,
1280{
1281    /// Return a map of [`json::Element`] paths to a list of [`Warning`] ids as Strings.
1282    ///
1283    /// This is designed to be used to print out maps of warnings associated with elements.
1284    /// You can use the debug alternate format `{:#?}` to print the map 'pretty' over multiple lines
1285    /// with indentation.
1286    ///
1287    /// Note: This representation is also valid JSON and can be copied directly to
1288    /// a test expectation file.
1289    pub fn into_id_map(self) -> BTreeMap<String, Vec<String>> {
1290        self.map(Group::into_str_ids).collect()
1291    }
1292
1293    /// Return a map of [`json::Element`] paths to a list of [`Warning`] messages as Strings.
1294    ///
1295    /// This is designed to be used to print out maps of warnings associated with elements.
1296    /// You can use the debug alternate format `{:#?}` to print the map 'pretty' over multiple lines
1297    /// with indentation.
1298    pub fn into_msg_map(self) -> BTreeMap<String, Vec<String>> {
1299        self.map(Group::into_str_msgs).collect()
1300    }
1301}
1302
1303/// A group of warning `Kind`s associated with an `Element`.
1304///
1305/// This group is emitted from the `GroupByElem` iterator.
1306/// The warning `Kind`s are borrowed so the source Set does not need to be consumed/moved.
1307#[derive(Debug)]
1308pub struct Group<'caller, 'buf, K> {
1309    /// The [`json::Element`] that has [`Warning`]s.
1310    pub element: &'caller json::Element<'buf>,
1311
1312    /// The list of warnings for the [`json::Element`].
1313    pub warnings: Vec<&'caller K>,
1314}
1315
1316impl<K> Group<'_, '_, K>
1317where
1318    K: Kind,
1319{
1320    /// Convert the Group into String versions of its parts.
1321    ///
1322    /// The first tuple field is the [`Element`](json::Element) path as a String.
1323    /// The second tuple field is a list of [`Warning`](Kind) ids.
1324    pub fn into_str_ids(self) -> (String, Vec<String>) {
1325        let Self { element, warnings } = self;
1326        (
1327            element.path().to_string(),
1328            warnings.iter().map(|kind| kind.id().to_string()).collect(),
1329        )
1330    }
1331
1332    /// Convert the Group into String versions of its parts.
1333    ///
1334    /// The first tuple field is the [`Element`](json::Element) path as a String.
1335    /// The second tuple field is a list of [`Warning`](Kind) messages.
1336    pub fn into_str_msgs(self) -> (String, Vec<String>) {
1337        let Self { element, warnings } = self;
1338        (
1339            element.path().to_string(),
1340            warnings.iter().map(ToString::to_string).collect(),
1341        )
1342    }
1343}
1344
1345impl<'caller, 'buf, K: Kind> Iterator for GroupByElem<'caller, 'buf, K> {
1346    type Item = Group<'caller, 'buf, K>;
1347
1348    fn next(&mut self) -> Option<Self::Item> {
1349        // The warnings are sorted and grouped by consecutive and compacted ids.
1350        let warning = self.warnings.next()?;
1351
1352        // Search for the element associated with warning.
1353        let element = loop {
1354            let Some(element) = self.walker.next() else {
1355                error!("An Element with id: `{}` was not found", warning.elem_id);
1356                return None;
1357            };
1358
1359            if element.id() < warning.elem_id {
1360                // This element does not have any warnings continue the search for the
1361                // element associated with the warning.
1362                continue;
1363            }
1364
1365            if element.id() > warning.elem_id {
1366                debug_assert!(
1367                    element.id() <= warning.elem_id,
1368                    "The elements or the warnings are not sorted."
1369                );
1370                return None;
1371            }
1372
1373            // We found the element for the first warning in the group.
1374            break element;
1375        };
1376
1377        // Insert the first warning into the `grouped` list.
1378        let mut warnings_grouped = vec![warning.kind()];
1379
1380        // Collect all warnings in the group.
1381        loop {
1382            let warning = self.warnings.next_if(|w| w.elem_id == element.id());
1383            let Some(warning) = warning else {
1384                break;
1385            };
1386
1387            warnings_grouped.push(warning.kind());
1388        }
1389
1390        Some(Group {
1391            element,
1392            warnings: warnings_grouped,
1393        })
1394    }
1395}
1396
1397#[cfg(test)]
1398pub(crate) mod test {
1399    use std::{
1400        borrow::Cow,
1401        collections::{BTreeMap, BTreeSet},
1402        ops, slice,
1403    };
1404
1405    use assert_matches::assert_matches;
1406
1407    use crate::{
1408        json,
1409        test::{ExpectValue, Expectation},
1410        warning::CaveatDeferred,
1411    };
1412
1413    use super::{Caveat, Group, Kind, Set, Warning};
1414
1415    impl<K: Kind> Set<K> {
1416        /// Return the inner storage.
1417        pub fn into_vec(self) -> Vec<Warning<K>> {
1418            self.0
1419        }
1420
1421        pub fn into_parts_vec(self) -> Vec<(K, json::ElemId)> {
1422            self.0
1423                .into_iter()
1424                .map(|Warning { kind, elem_id }| (kind, elem_id))
1425                .collect()
1426        }
1427
1428        /// Return the inner kinds.
1429        ///
1430        /// This should only be used in tests and the `mod price`.
1431        pub fn into_kind_vec(self) -> Vec<K> {
1432            self.0.into_iter().map(Warning::into_kind).collect()
1433        }
1434
1435        /// Return the inner storage as a slice.
1436        pub fn as_slice(&self) -> &[Warning<K>] {
1437            self.0.as_slice()
1438        }
1439
1440        /// Return an immutable iterator over the slice.
1441        pub fn iter(&self) -> slice::Iter<'_, Warning<K>> {
1442            self.0.iter()
1443        }
1444    }
1445
1446    impl<K: Kind> ops::Deref for Set<K> {
1447        type Target = [Warning<K>];
1448
1449        fn deref(&self) -> &[Warning<K>] {
1450            self.as_slice()
1451        }
1452    }
1453
1454    impl<K: Kind> IntoIterator for Set<K> {
1455        type Item = Warning<K>;
1456
1457        type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
1458
1459        fn into_iter(self) -> Self::IntoIter {
1460            self.0.into_iter()
1461        }
1462    }
1463
1464    impl<'a, K: Kind> IntoIterator for &'a Set<K> {
1465        type Item = &'a Warning<K>;
1466
1467        type IntoIter = slice::Iter<'a, Warning<K>>;
1468
1469        fn into_iter(self) -> Self::IntoIter {
1470            self.0.iter()
1471        }
1472    }
1473
1474    impl<T, K> Caveat<T, K>
1475    where
1476        K: Kind,
1477    {
1478        /// Return the value and assert there are no [`Warning`]s.
1479        pub fn unwrap(self) -> T {
1480            let Self { value, warnings } = self;
1481            assert_matches!(warnings.into_vec().as_slice(), []);
1482            value
1483        }
1484    }
1485
1486    impl<T, K> CaveatDeferred<T, K>
1487    where
1488        K: Kind,
1489    {
1490        /// Return the value and assert there are no [`Warning`]s.
1491        pub fn unwrap(self) -> T {
1492            let Self { value, warnings } = self;
1493            assert_matches!(warnings.0.as_slice(), []);
1494            value
1495        }
1496    }
1497
1498    /// Assert that the warnings given are expected.
1499    ///
1500    /// Panic with print out of the warnings and the expectations if any warnings were unexpected.
1501    pub(crate) fn assert_warnings<K>(
1502        expect_file_name: &str,
1503        root: &json::Element<'_>,
1504        warnings: &Set<K>,
1505        expected: Expectation<BTreeMap<String, Vec<String>>>,
1506    ) where
1507        K: Kind,
1508    {
1509        let Expectation::Present(ExpectValue::Some(expected)) = expected else {
1510            assert!(
1511                warnings.is_empty(),
1512                "There is no `warnings` field in the `{expect_file_name}` file but the tariff has warnings;\n{:?}",
1513                warnings.group_by_elem(root).into_id_map()
1514            );
1515            return;
1516        };
1517
1518        {
1519            // Assert that the `expect` file doesn't have extraneous entries.
1520            let warnings_grouped = warnings
1521                .group_by_elem(root)
1522                .map(|Group { element, warnings }| (element.path().to_string(), warnings))
1523                .collect::<BTreeMap<_, _>>();
1524
1525            let mut elems_in_expect_without_warning = vec![];
1526
1527            for elem_path in expected.keys() {
1528                if !warnings_grouped.contains_key(elem_path) {
1529                    elems_in_expect_without_warning.push(elem_path);
1530                }
1531            }
1532
1533            assert!(elems_in_expect_without_warning.is_empty(),
1534                "The expect file `{expect_file_name}` has entries for elements that have no warnings:\n\
1535                {elems_in_expect_without_warning:#?}"
1536            );
1537        }
1538
1539        // The elements that have warnings but have no entry for the elements path in the `expect` file.
1540        let mut elems_missing_from_expect = vec![];
1541        // The element that have warnings and an entry in the `expect` file, but the list of expected warnings
1542        // is not equal to the list of actual warnings.
1543        let mut unequal_warnings = vec![];
1544
1545        for group in warnings.group_by_elem(root) {
1546            let path_str = group.element.path().to_string();
1547            let Some(warnings_expected) = expected.get(&*path_str) else {
1548                elems_missing_from_expect.push(group);
1549                continue;
1550            };
1551
1552            // Make two sets of actual and expected warnings.
1553            let warnings_expected = warnings_expected
1554                .iter()
1555                .map(|s| Cow::Borrowed(&**s))
1556                .collect::<BTreeSet<_>>();
1557            let warnings = group
1558                .warnings
1559                .iter()
1560                .map(|w| w.id())
1561                .collect::<BTreeSet<_>>();
1562
1563            if warnings_expected != warnings {
1564                unequal_warnings.push(group);
1565            }
1566        }
1567
1568        if !elems_missing_from_expect.is_empty() || !unequal_warnings.is_empty() {
1569            let missing = elems_missing_from_expect
1570                .into_iter()
1571                .map(Group::into_str_ids)
1572                .collect::<BTreeMap<_, _>>();
1573
1574            let unequal = unequal_warnings
1575                .into_iter()
1576                .map(Group::into_str_ids)
1577                .collect::<BTreeMap<_, _>>();
1578
1579            match (!missing.is_empty(), !unequal.is_empty()) {
1580                (true, true) => panic!(
1581                    "Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}\n\
1582                    Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1583                    The warnings reported are: \n{unequal:#?}"                  
1584                ),
1585                (true, false) => panic!("Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}"),
1586                (false, true) => panic!(
1587                    "Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1588                    The warnings reported are: \n{unequal:#?}"
1589                ),
1590                (false, false) => (),
1591            }
1592        }
1593    }
1594}
1595
1596#[cfg(test)]
1597mod test_group_by_elem {
1598    use std::fmt;
1599
1600    use assert_matches::assert_matches;
1601
1602    use crate::{json, test};
1603
1604    use super::{Group, IntoGroup, Kind, Set, Warning};
1605
1606    const JSON: &str = r#"{
1607    "field_one#": "one",
1608    "field_two": "two",
1609    "field_three": "three"
1610}"#;
1611
1612    #[derive(Debug)]
1613    enum WarningKind {
1614        Root,
1615        One,
1616        OneAgain,
1617        Three,
1618    }
1619
1620    impl Kind for WarningKind {
1621        fn id(&self) -> std::borrow::Cow<'static, str> {
1622            match self {
1623                WarningKind::Root => "root".into(),
1624                WarningKind::One => "one".into(),
1625                WarningKind::OneAgain => "one_again".into(),
1626                WarningKind::Three => "three".into(),
1627            }
1628        }
1629    }
1630
1631    impl fmt::Display for WarningKind {
1632        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1633            match self {
1634                WarningKind::Root => write!(f, "NopeRoot"),
1635                WarningKind::One => write!(f, "NopeOne"),
1636                WarningKind::OneAgain => write!(f, "NopeOneAgain"),
1637                WarningKind::Three => write!(f, "NopeThree"),
1638            }
1639        }
1640    }
1641
1642    #[test]
1643    fn should_group_by_elem() {
1644        test::setup();
1645
1646        let elem = parse(JSON);
1647        let mut warnings = Set::<WarningKind>::new();
1648
1649        // Push warnings into the set out of order.
1650        // They should be sorted by `ElemId`.
1651        warnings.push(Warning {
1652            kind: WarningKind::Root,
1653            elem_id: json::ElemId::from(0),
1654        });
1655        warnings.push(Warning {
1656            kind: WarningKind::One,
1657            elem_id: json::ElemId::from(1),
1658        });
1659        warnings.push(Warning {
1660            kind: WarningKind::Three,
1661            elem_id: json::ElemId::from(3),
1662        });
1663        warnings.push(Warning {
1664            kind: WarningKind::OneAgain,
1665            elem_id: json::ElemId::from(1),
1666        });
1667
1668        let mut iter = warnings.group_by_elem(&elem);
1669
1670        {
1671            let Group { element, warnings } = iter.next().unwrap();
1672
1673            assert!(
1674                element.value().is_object(),
1675                "The root object should be emitted first"
1676            );
1677            assert_eq!(
1678                element.id(),
1679                json::ElemId::from(0),
1680                "The root object should be emitted first"
1681            );
1682            assert_matches!(warnings.as_slice(), [WarningKind::Root]);
1683        }
1684
1685        {
1686            let Group { element, warnings } = iter.next().unwrap();
1687
1688            assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "one");
1689            assert_eq!(element.id(), json::ElemId::from(1));
1690            assert_matches!(
1691                warnings.as_slice(),
1692                [WarningKind::One, WarningKind::OneAgain],
1693                "[`json::Element`] 1 should have two warnings"
1694            );
1695        }
1696
1697        {
1698            // [`json::Element`] 2 has no [`Warning`]s so we expect [`json::Element`] 3 next
1699            let Group { element, warnings } = iter.next().unwrap();
1700
1701            assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "three");
1702            assert_eq!(element.id(), json::ElemId::from(3));
1703            assert_matches!(warnings.as_slice(), [WarningKind::Three]);
1704        }
1705    }
1706
1707    #[test]
1708    fn should_into_group_by_elem() {
1709        test::setup();
1710
1711        let elem = parse(JSON);
1712        let mut warnings = Set::<WarningKind>::new();
1713
1714        // Push warnings into the set out of order.
1715        // They should be sorted by `ElemId`.
1716        warnings.push(Warning {
1717            kind: WarningKind::Root,
1718            elem_id: json::ElemId::from(0),
1719        });
1720        warnings.push(Warning {
1721            kind: WarningKind::Three,
1722            elem_id: json::ElemId::from(3),
1723        });
1724        warnings.push(Warning {
1725            kind: WarningKind::One,
1726            elem_id: json::ElemId::from(1),
1727        });
1728        warnings.push(Warning {
1729            kind: WarningKind::OneAgain,
1730            elem_id: json::ElemId::from(1),
1731        });
1732
1733        let mut iter = warnings.into_group_by_elem(&elem);
1734
1735        {
1736            let IntoGroup { element, warnings } = iter.next().unwrap();
1737
1738            assert!(
1739                element.value().is_object(),
1740                "The root object should be emitted first"
1741            );
1742            assert_eq!(
1743                element.id(),
1744                json::ElemId::from(0),
1745                "The root object should be emitted first"
1746            );
1747            assert_matches!(warnings.as_slice(), [WarningKind::Root]);
1748        }
1749
1750        {
1751            let IntoGroup { element, warnings } = iter.next().unwrap();
1752
1753            assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "one");
1754            assert_eq!(element.id(), json::ElemId::from(1));
1755            assert_matches!(
1756                warnings.as_slice(),
1757                [WarningKind::One, WarningKind::OneAgain],
1758                "[`json::Element`] 1 should have two warnings"
1759            );
1760        }
1761
1762        {
1763            // [`json::Element`] 2 has no [`Warning`]s so we expect [`json::Element`] 3 next
1764            let IntoGroup { element, warnings } = iter.next().unwrap();
1765
1766            assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "three");
1767            assert_eq!(element.id(), json::ElemId::from(3));
1768            assert_matches!(warnings.as_slice(), [WarningKind::Three]);
1769        }
1770    }
1771
1772    fn parse(json: &str) -> json::Element<'_> {
1773        json::parse(json).unwrap()
1774    }
1775}