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