1use std::{borrow::Cow, collections::BTreeMap, fmt, ops::Deref, vec};
47
48use tracing::{debug, info};
49
50use crate::{json, SmartString};
51
52#[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<W: $crate::Warning>(
59 self,
60 warnings: $crate::warning::Set<W>,
61 ) -> $crate::Caveat<Self, W> {
62 $crate::Caveat::new(self, warnings)
63 }
64 }
65 };
66 ($kind:ty) => {
67 impl $crate::IntoCaveat for $kind {
68 fn into_caveat<W: $crate::Warning>(
69 self,
70 warnings: $crate::warning::Set<W>,
71 ) -> $crate::Caveat<Self, W> {
72 $crate::Caveat::new(self, warnings)
73 }
74 }
75
76 impl $crate::warning::IntoCaveatDeferred for $kind {
77 fn into_caveat_deferred<W: $crate::Warning>(
78 self,
79 warnings: $crate::warning::SetDeferred<W>,
80 ) -> $crate::warning::CaveatDeferred<Self, W> {
81 $crate::warning::CaveatDeferred::new(self, warnings)
82 }
83 }
84 };
85}
86
87#[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 impl From<$source_kind> for $target_kind {
103 fn from(warning: $source_kind) -> Self {
104 $target_kind::$target_variant(warning)
105 }
106 }
107
108 impl From<$crate::warning::ErrorSet<$source_kind>> for $crate::warning::ErrorSet<$target_kind> {
113 fn from(set_a: $crate::warning::ErrorSet<$source_kind>) -> Self {
114 set_a.into_other()
115 }
116 }
117
118 impl From<$crate::warning::ErrorSetDeferred<$source_kind>> for $crate::warning::ErrorSetDeferred<$target_kind> {
123 fn from(set_a: $crate::warning::ErrorSetDeferred<$source_kind>) -> Self {
124 set_a.into_other()
125 }
126 }
127 )+
128 };
129}
130
131pub type Verdict<T, W> = Result<Caveat<T, W>, ErrorSet<W>>;
133
134pub(crate) type VerdictDeferred<T, W> = Result<CaveatDeferred<T, W>, ErrorSetDeferred<W>>;
141
142#[derive(Debug)]
152pub struct CaveatDeferred<T, W: Warning> {
153 value: T,
155
156 warnings: SetDeferred<W>,
158}
159
160impl<T, W> CaveatDeferred<T, W>
161where
162 T: IntoCaveatDeferred,
163 W: Warning,
164{
165 pub(crate) fn new(value: T, warnings: SetDeferred<W>) -> Self {
167 Self { value, warnings }
168 }
169}
170
171impl<T, W> Deref for CaveatDeferred<T, W>
186where
187 W: Warning,
188{
189 type Target = T;
190
191 fn deref(&self) -> &T {
192 &self.value
193 }
194}
195
196impl<T, W> CaveatDeferred<T, W>
197where
198 W: Warning,
199{
200 pub fn into_parts(self) -> (T, SetDeferred<W>) {
202 let Self { value, warnings } = self;
203 (value, warnings)
204 }
205
206 pub fn ignore_warnings(self) -> T {
208 self.value
209 }
210
211 pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> CaveatDeferred<U, W> {
213 let Self { value, warnings } = self;
214 CaveatDeferred {
215 value: op(value),
216 warnings,
217 }
218 }
219}
220
221#[derive(Debug)]
225pub struct Caveat<T, W: Warning> {
226 value: T,
228
229 warnings: Set<W>,
231}
232
233impl<T, W> Caveat<T, W>
234where
235 T: IntoCaveat,
236 W: Warning,
237{
238 pub(crate) fn new(value: T, warnings: Set<W>) -> Self {
240 Self { value, warnings }
241 }
242}
243
244impl<T, W> Deref for Caveat<T, W>
257where
258 W: Warning,
259{
260 type Target = T;
261
262 fn deref(&self) -> &T {
263 &self.value
264 }
265}
266
267impl<T, W> Caveat<T, W>
268where
269 W: Warning,
270{
271 pub fn into_parts(self) -> (T, Set<W>) {
273 let Self { value, warnings } = self;
274 (value, warnings)
275 }
276
277 pub fn ignore_warnings(self) -> T {
279 self.value
280 }
281
282 pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Caveat<U, W> {
284 let Self { value, warnings } = self;
285 Caveat {
286 value: op(value),
287 warnings,
288 }
289 }
290}
291
292pub(crate) trait GatherWarnings<T, W>
297where
298 W: Warning,
299{
300 type Output;
302
303 #[must_use = "If you want to ignore the value use `let _ =`"]
305 fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
306 where
307 W: Into<WA>,
308 WA: Warning;
309}
310
311impl<T, W> GatherWarnings<T, W> for Caveat<T, W>
313where
314 W: Warning,
315{
316 type Output = T;
317
318 fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
320 where
321 W: Into<WA>,
322 WA: Warning,
323 {
324 let Self {
325 value,
326 warnings: inner_warnings,
327 } = self;
328
329 warnings.extend(inner_warnings);
330
331 value
332 }
333}
334
335impl<T, W> GatherWarnings<T, W> for Option<Caveat<T, W>>
337where
338 W: Warning,
339{
340 type Output = Option<T>;
341
342 fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
344 where
345 W: Into<WA>,
346 WA: Warning,
347 {
348 match self {
349 Some(cv) => Some(cv.gather_warnings_into(warnings)),
350 None => None,
351 }
352 }
353}
354
355impl<T, W, E> GatherWarnings<T, W> for Result<Caveat<T, W>, E>
357where
358 W: Warning,
359 E: std::error::Error,
360{
361 type Output = Result<T, E>;
362
363 fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
365 where
366 W: Into<WA>,
367 WA: Warning,
368 {
369 match self {
370 Ok(cv) => Ok(cv.gather_warnings_into(warnings)),
371 Err(err) => Err(err),
372 }
373 }
374}
375
376impl<T, W> GatherWarnings<T, W> for Verdict<T, W>
378where
379 W: Warning,
380{
381 type Output = Result<T, ErrorSet<W>>;
382
383 fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
386 where
387 W: Into<WA>,
388 WA: Warning,
389 {
390 match self {
391 Ok(cv) => Ok(cv.gather_warnings_into(warnings)),
392 Err(err_set) => Err(err_set),
393 }
394 }
395}
396
397pub(crate) trait DeescalateError<T, W>
402where
403 W: Warning,
404{
405 #[must_use = "If you want to ignore the value use `let _ =`"]
407 fn deescalate_error_into<WA>(self, warnings: &mut Set<WA>) -> Option<T>
408 where
409 W: Into<WA>,
410 WA: Warning;
411}
412
413impl<T, W> DeescalateError<T, W> for Verdict<T, W>
415where
416 W: Warning,
417{
418 fn deescalate_error_into<WA>(self, warnings: &mut Set<WA>) -> Option<T>
421 where
422 W: Into<WA>,
423 WA: Warning,
424 {
425 match self {
426 Ok(cv) => Some(cv.gather_warnings_into(warnings)),
427 Err(err_set) => {
428 warnings.deescalate_error(err_set.into_other());
429 None
430 }
431 }
432 }
433}
434
435impl<T, W> DeescalateError<T, W> for Result<T, ErrorSet<W>>
437where
438 W: Warning,
439{
440 fn deescalate_error_into<WA>(self, warnings: &mut Set<WA>) -> Option<T>
443 where
444 W: Into<WA>,
445 WA: Warning,
446 {
447 match self {
448 Ok(cv) => Some(cv),
449 Err(err_set) => {
450 warnings.deescalate_error(err_set.into_other());
451 None
452 }
453 }
454 }
455}
456
457impl<T, W> GatherWarnings<T, W> for Vec<Caveat<T, W>>
459where
460 W: Warning,
461{
462 type Output = Vec<T>;
463
464 fn gather_warnings_into<WA>(self, warnings: &mut Set<WA>) -> Self::Output
466 where
467 W: Into<WA>,
468 WA: Warning,
469 {
470 self.into_iter()
471 .map(|cv| cv.gather_warnings_into(warnings))
472 .collect()
473 }
474}
475
476pub(crate) trait GatherDeferredWarnings<T, W>
481where
482 W: Warning,
483{
484 type Output;
486
487 #[must_use = "If you want to ignore the value use `let _ =`"]
489 fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
490 where
491 W: Into<WA>,
492 WA: Warning;
493}
494
495impl<T, W> GatherDeferredWarnings<T, W> for CaveatDeferred<T, W>
497where
498 W: Warning,
499{
500 type Output = T;
501
502 fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
504 where
505 W: Into<WA>,
506 WA: Warning,
507 {
508 let Self {
509 value,
510 warnings: inner_warnings,
511 } = self;
512
513 warnings.extend(inner_warnings);
514
515 value
516 }
517}
518
519impl<T, W> GatherDeferredWarnings<T, W> for Option<CaveatDeferred<T, W>>
521where
522 W: Warning,
523{
524 type Output = Option<T>;
525
526 fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
528 where
529 W: Into<WA>,
530 WA: Warning,
531 {
532 match self {
533 Some(cv) => Some(cv.gather_deferred_warnings_into(warnings)),
534 None => None,
535 }
536 }
537}
538
539impl<T, W, E> GatherDeferredWarnings<T, W> for Result<CaveatDeferred<T, W>, E>
541where
542 W: Warning,
543 E: std::error::Error,
544{
545 type Output = Result<T, E>;
546
547 fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
549 where
550 W: Into<WA>,
551 WA: Warning,
552 {
553 match self {
554 Ok(cv) => Ok(cv.gather_deferred_warnings_into(warnings)),
555 Err(err) => Err(err),
556 }
557 }
558}
559
560impl<T, W> GatherDeferredWarnings<T, W> for VerdictDeferred<T, W>
562where
563 W: Warning,
564{
565 type Output = Result<T, ErrorSetDeferred<W>>;
566
567 fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
570 where
571 W: Into<WA>,
572 WA: Warning,
573 {
574 match self {
575 Ok(cv) => Ok(cv.gather_deferred_warnings_into(warnings)),
576 Err(err_set) => Err(err_set),
577 }
578 }
579}
580
581impl<T, W> GatherDeferredWarnings<T, W> for Vec<CaveatDeferred<T, W>>
583where
584 W: Warning,
585{
586 type Output = Vec<T>;
587
588 fn gather_deferred_warnings_into<WA>(self, warnings: &mut SetDeferred<WA>) -> Self::Output
590 where
591 W: Into<WA>,
592 WA: Warning,
593 {
594 self.into_iter()
595 .map(|cv| cv.gather_deferred_warnings_into(warnings))
596 .collect()
597 }
598}
599
600pub trait IntoCaveat: Sized {
604 fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W>;
606}
607
608pub trait IntoCaveatDeferred: Sized {
612 fn into_caveat_deferred<W: Warning>(self, warnings: SetDeferred<W>) -> CaveatDeferred<Self, W>;
614}
615
616macro_rules! peel {
618 ($name:ident, $($other:ident,)*) => (into_caveat_tuple! { $($other,)* })
619}
620
621macro_rules! into_caveat_tuple {
624 () => ();
625 ( $($name:ident,)+ ) => (
626 impl<$($name),+> $crate::IntoCaveat for ($($name,)+) {
627 fn into_caveat<W: $crate::Warning>(
628 self,
629 warnings: $crate::warning::Set<W>,
630 ) -> $crate::Caveat<Self, W> {
631 $crate::Caveat::new(self, warnings)
632 }
633 }
634
635 impl<$($name),+> $crate::warning::IntoCaveatDeferred for ($($name,)+) {
636 fn into_caveat_deferred<W: $crate::Warning>(
637 self,
638 warnings: SetDeferred<W>,
639 ) -> $crate::warning::CaveatDeferred<Self, W> {
640 $crate::warning::CaveatDeferred::new(self, warnings)
641 }
642 }
643 peel! { $($name,)+ }
644 )
645}
646
647into_caveat_tuple! { T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, }
650
651into_caveat_all!(
653 (),
654 bool,
655 char,
656 u8,
657 u16,
658 u32,
659 u64,
660 u128,
661 i8,
662 i16,
663 i32,
664 i64,
665 i128
666);
667
668impl IntoCaveat for Cow<'_, str> {
670 fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W> {
671 Caveat::new(self, warnings)
672 }
673}
674
675impl<T> IntoCaveat for Option<T>
677where
678 T: IntoCaveat,
679{
680 fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W> {
681 Caveat::new(self, warnings)
682 }
683}
684
685impl<T> IntoCaveat for Vec<T>
687where
688 T: IntoCaveat,
689{
690 fn into_caveat<W: Warning>(self, warnings: Set<W>) -> Caveat<Self, W> {
691 Caveat::new(self, warnings)
692 }
693}
694
695impl<T> IntoCaveatDeferred for Vec<T>
697where
698 T: IntoCaveat,
699{
700 fn into_caveat_deferred<W: Warning>(self, warnings: SetDeferred<W>) -> CaveatDeferred<Self, W> {
701 CaveatDeferred::new(self, warnings)
702 }
703}
704
705pub trait VerdictExt<T, W: Warning> {
707 fn map_caveat<F, U>(self, op: F) -> Verdict<U, W>
710 where
711 F: FnOnce(T) -> U;
712
713 fn only_error(self) -> Result<Caveat<T, W>, Error<W>>;
715}
716
717#[allow(dead_code, reason = "for debugging")]
718pub(crate) trait VerdictTrace<T, W: Warning> {
719 fn info_verdict(self, msg: &'static str) -> Self;
720 fn debug_verdict(self, msg: &'static str) -> Self;
721}
722
723#[allow(dead_code, reason = "for debugging")]
724pub(crate) trait ResultTrace<T, W: Warning> {
725 fn info_result(self, msg: &'static str) -> Self;
726 fn debug_result(self, msg: &'static str) -> Self;
727}
728
729impl<T, W: Warning> VerdictExt<T, W> for Verdict<T, W> {
730 fn map_caveat<F, U>(self, op: F) -> Verdict<U, W>
731 where
732 F: FnOnce(T) -> U,
733 {
734 match self {
735 Ok(c) => Ok(c.map(op)),
736 Err(w) => Err(w),
737 }
738 }
739
740 fn only_error(self) -> Result<Caveat<T, W>, Error<W>> {
741 match self {
742 Ok(c) => Ok(c),
743 Err(err_set) => {
744 let ErrorSet { error, warnings: _ } = err_set;
745 Err(*error)
746 }
747 }
748 }
749}
750
751impl<T, W: Warning> VerdictTrace<T, W> for Verdict<T, W>
752where
753 T: fmt::Debug,
754{
755 fn info_verdict(self, msg: &'static str) -> Self {
756 match self {
757 Ok(c) => {
758 info!("{msg}: {c:#?}");
759 Ok(c)
760 }
761 Err(err_set) => {
762 info!("{msg}: {err_set:#?}");
763 Err(err_set)
764 }
765 }
766 }
767
768 fn debug_verdict(self, msg: &'static str) -> Self {
769 match self {
770 Ok(c) => {
771 debug!("{msg}: {c:#?}");
772 Ok(c)
773 }
774 Err(err_set) => {
775 debug!("{msg}: {err_set:#?}");
776 Err(err_set)
777 }
778 }
779 }
780}
781
782impl<T, W: Warning> ResultTrace<T, W> for Result<T, ErrorSet<W>>
783where
784 T: fmt::Debug,
785{
786 fn info_result(self, msg: &'static str) -> Self {
787 match self {
788 Ok(c) => {
789 info!("{msg}: {c:#?}");
790 Ok(c)
791 }
792 Err(err_set) => {
793 info!("{msg}: {err_set:#?}");
794 Err(err_set)
795 }
796 }
797 }
798
799 fn debug_result(self, msg: &'static str) -> Self {
800 match self {
801 Ok(c) => {
802 debug!("{msg}: {c:#?}");
803 Ok(c)
804 }
805 Err(err_set) => {
806 debug!("{msg}: {err_set:#?}");
807 Err(err_set)
808 }
809 }
810 }
811}
812
813#[derive(Debug)]
817pub struct Error<W: Warning> {
818 warning: W,
820
821 element: Element,
823}
824
825impl<W: Warning> Error<W> {
826 pub fn warning(&self) -> &W {
828 &self.warning
829 }
830
831 pub fn into_warning(self) -> W {
833 self.warning
834 }
835
836 pub fn element(&self) -> &Element {
838 &self.element
839 }
840
841 pub fn parts(&self) -> (&W, &Element) {
843 (&self.warning, &self.element)
844 }
845
846 pub fn into_parts(self) -> (W, Element) {
848 let Self { warning, element } = self;
849 (warning, element)
850 }
851
852 fn into_other<WA>(self) -> Error<WA>
856 where
857 W: Into<WA>,
858 WA: Warning,
859 {
860 let Self { warning, element } = self;
861 Error {
862 warning: warning.into(),
863 element,
864 }
865 }
866}
867
868impl<W: Warning> std::error::Error for Error<W> {}
869
870impl<W: Warning> fmt::Display for Error<W> {
871 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
872 write!(
873 f,
874 "A warning for element at `{}` was upgraded to an `error`: {}",
875 self.element.path, self.warning
876 )
877 }
878}
879
880pub trait WithElement<T, W: Warning> {
882 fn with_element(self, element: &json::Element<'_>) -> Verdict<T, W>;
883}
884
885impl<T, W: Warning> WithElement<T, W> for VerdictDeferred<T, W> {
886 fn with_element(self, element: &json::Element<'_>) -> Verdict<T, W> {
888 match self {
889 Ok(v) => {
890 let CaveatDeferred { value, warnings } = v;
891 let SetDeferred(warnings) = warnings;
892 Ok(Caveat {
893 value,
894 warnings: Set(warnings),
895 })
896 }
897 Err(set) => {
898 let ErrorSetDeferred { error, warnings } = set;
899 Err(ErrorSet {
900 error: Box::new(Error {
901 warning: error,
902 element: Element::from(element),
903 }),
904 warnings,
905 })
906 }
907 }
908 }
909}
910
911#[derive(Debug)]
921pub struct Element {
922 pub id: json::ElemId,
926
927 pub span: json::parser::Span,
929
930 pub path: SmartString,
934}
935
936impl<'buf> From<&json::Element<'buf>> for Element {
937 fn from(elem: &json::Element<'buf>) -> Self {
938 Self {
939 id: elem.id(),
940 span: elem.span(),
941 path: elem.path().to_smart_string(),
942 }
943 }
944}
945
946pub struct SetWriter<'caller, W: Warning> {
960 warnings: &'caller Set<W>,
962
963 indent: &'caller str,
965}
966
967impl<'caller, W: Warning> SetWriter<'caller, W> {
968 pub fn new(warnings: &'caller Set<W>) -> Self {
970 Self {
971 warnings,
972 indent: " - ",
973 }
974 }
975
976 pub fn with_indent(warnings: &'caller Set<W>, indent: &'caller str) -> Self {
978 Self { warnings, indent }
979 }
980}
981
982impl<W: Warning> fmt::Debug for SetWriter<'_, W> {
983 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
984 fmt::Display::fmt(self, f)
985 }
986}
987
988impl<W: Warning> fmt::Display for SetWriter<'_, W> {
989 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
990 let mut iter = self.warnings.iter();
991
992 {
993 let Some(Group { element, warnings }) = iter.next() else {
995 return Ok(());
996 };
997
998 writeln!(f, "{}", element.path)?;
999
1000 for warning in warnings {
1001 write!(f, "{}{}", self.indent, warning)?;
1002 }
1003 }
1004
1005 for Group { element, warnings } in iter {
1007 writeln!(f, "\n{}", element.path)?;
1008
1009 for warning in warnings {
1010 write!(f, "{}{}", self.indent, warning)?;
1011 }
1012 }
1013
1014 Ok(())
1015 }
1016}
1017
1018pub trait Warning: Sized + fmt::Debug + fmt::Display + Send + Sync {
1022 fn id(&self) -> SmartString;
1027}
1028
1029#[derive(Debug)]
1038pub struct SetDeferred<W: Warning>(BTreeMap<json::ElemId, Group<W>>);
1039
1040impl<W: Warning> SetDeferred<W> {
1041 pub(crate) fn new() -> Self {
1043 Self(BTreeMap::new())
1044 }
1045
1046 pub(crate) fn bail<T>(self, warning: W) -> VerdictDeferred<T, W> {
1050 let Self(warnings) = self;
1051 Err(ErrorSetDeferred {
1052 error: warning,
1053 warnings,
1054 })
1055 }
1056
1057 pub fn is_empty(&self) -> bool {
1059 self.0.is_empty()
1060 }
1061
1062 pub(crate) fn extend<WA>(&mut self, warnings: SetDeferred<WA>)
1066 where
1067 WA: Into<W> + Warning,
1068 {
1069 let SetDeferred(warnings) = warnings;
1070 self.0
1071 .extend(warnings.into_iter().map(|(k, v)| (k, v.into_other())));
1072 }
1073}
1074
1075#[derive(Debug)]
1085pub struct ErrorSetDeferred<W: Warning> {
1086 error: W,
1087 warnings: BTreeMap<json::ElemId, Group<W>>,
1088}
1089
1090impl<W: Warning> ErrorSetDeferred<W> {
1091 pub(crate) fn with_warn(warning: W) -> Self {
1093 Self {
1094 warnings: BTreeMap::new(),
1095 error: warning,
1096 }
1097 }
1098
1099 pub(crate) fn into_other<WA>(self) -> ErrorSetDeferred<WA>
1103 where
1104 W: Into<WA>,
1105 WA: Warning,
1106 {
1107 let Self { error, warnings } = self;
1108 let warnings = warnings
1109 .into_iter()
1110 .map(|(k, v)| (k, v.into_other()))
1111 .collect();
1112 ErrorSetDeferred {
1113 error: error.into(),
1114 warnings,
1115 }
1116 }
1117}
1118
1119#[derive(Debug)]
1121pub struct Set<W: Warning>(BTreeMap<json::ElemId, Group<W>>);
1122
1123impl<W: Warning> Set<W> {
1124 pub(crate) fn new() -> Self {
1126 Self(BTreeMap::new())
1127 }
1128
1129 pub(crate) fn with_elem(&mut self, warning: W, element: &json::Element<'_>) {
1131 self.insert_warning(warning, element.id(), || Element::from(element));
1132 }
1133
1134 fn insert_warning<F>(&mut self, warning: W, elem_id: json::ElemId, f: F)
1138 where
1139 F: FnOnce() -> Element,
1140 {
1141 use std::collections::btree_map::Entry;
1142
1143 match self.0.entry(elem_id) {
1144 Entry::Vacant(entry) => {
1145 let element = f();
1146 entry.insert_entry(Group {
1147 element,
1148 warnings: vec![warning],
1149 });
1150 }
1151 Entry::Occupied(mut entry) => {
1152 entry.get_mut().warnings.push(warning);
1153 }
1154 }
1155 }
1156
1157 pub(crate) fn bail<T>(self, warning: W, element: &json::Element<'_>) -> Verdict<T, W> {
1161 let Self(warnings) = self;
1162
1163 Err(ErrorSet {
1164 error: Box::new(Error {
1165 warning,
1166 element: Element::from(element),
1167 }),
1168 warnings,
1169 })
1170 }
1171
1172 pub(crate) fn into_other<WA>(self) -> Set<WA>
1176 where
1177 W: Into<WA>,
1178 WA: Warning,
1179 {
1180 let Set(warnings) = self;
1181 let warnings = warnings
1182 .into_iter()
1183 .map(|(elem_id, group)| (elem_id, group.into_other()))
1184 .collect();
1185 Set(warnings)
1186 }
1187
1188 pub fn is_empty(&self) -> bool {
1190 self.0.is_empty()
1191 }
1192
1193 pub fn len_elements(&self) -> usize {
1197 self.0.len()
1198 }
1199
1200 pub fn len_warnings(&self) -> usize {
1202 self.0
1203 .values()
1204 .fold(0, |acc, group| acc + group.warnings.len())
1205 }
1206
1207 pub fn iter(&self) -> Iter<'_, W> {
1209 Iter {
1210 warnings: self.0.iter(),
1211 }
1212 }
1213
1214 pub fn path_map(&self) -> BTreeMap<&str, &[W]> {
1220 self.0
1221 .values()
1222 .map(|group| (group.element.path.as_str(), group.warnings.as_slice()))
1223 .collect()
1224 }
1225
1226 pub fn into_path_map(self) -> BTreeMap<SmartString, Vec<W>> {
1230 self.0
1231 .into_values()
1232 .map(|group| (group.element.path, group.warnings))
1233 .collect()
1234 }
1235
1236 pub fn path_id_map(&self) -> BTreeMap<&str, Vec<SmartString>> {
1245 self.0
1246 .values()
1247 .map(|group| {
1248 let warnings = group.warnings.iter().map(Warning::id).collect();
1249 (group.element.path.as_str(), warnings)
1250 })
1251 .collect()
1252 }
1253
1254 pub fn path_msg_map(&self) -> BTreeMap<&str, Vec<String>> {
1260 self.0
1261 .values()
1262 .map(|group| {
1263 let warnings = group.warnings.iter().map(ToString::to_string).collect();
1264 (group.element.path.as_str(), warnings)
1265 })
1266 .collect()
1267 }
1268
1269 pub(crate) fn deescalate_error(&mut self, err_set: ErrorSet<W>) {
1271 let ErrorSet { error, warnings } = err_set;
1272 let Error { warning, element } = *error;
1273 self.0.extend(warnings);
1274 self.insert_warning(warning, element.id, || element);
1275 }
1276
1277 fn extend<WA>(&mut self, warnings: Set<WA>)
1281 where
1282 WA: Into<W> + Warning,
1283 {
1284 use std::collections::btree_map::Entry;
1285
1286 let Set(warnings) = warnings;
1287 let warnings = warnings
1288 .into_iter()
1289 .map(|(elem_id, group)| (elem_id, group.into_other()));
1290
1291 for (elem_id, group) in warnings {
1292 match self.0.entry(elem_id) {
1293 Entry::Vacant(entry) => {
1294 entry.insert_entry(group);
1295 }
1296 Entry::Occupied(mut entry) => {
1297 let Group {
1298 element: _,
1299 warnings,
1300 } = group;
1301 entry.get_mut().warnings.extend(warnings);
1302 }
1303 }
1304 }
1305 }
1306}
1307
1308#[derive(Debug)]
1312pub struct ErrorSet<W: Warning> {
1313 error: Box<Error<W>>,
1317
1318 warnings: BTreeMap<json::ElemId, Group<W>>,
1322}
1323
1324impl<W> ErrorSet<W>
1325where
1326 W: Warning,
1327{
1328 pub fn into_parts(self) -> (Error<W>, Set<W>) {
1330 let Self { error, warnings } = self;
1331 (*error, Set(warnings))
1332 }
1333
1334 pub(crate) fn into_other<WA>(self) -> ErrorSet<WA>
1338 where
1339 W: Into<WA>,
1340 WA: Warning,
1341 {
1342 let Self { error, warnings } = self;
1343 let warnings = warnings
1344 .into_iter()
1345 .map(|(elem_id, group)| (elem_id, group.into_other()))
1346 .collect();
1347 ErrorSet {
1348 error: Box::new(Error::into_other(*error)),
1349 warnings,
1350 }
1351 }
1352}
1353
1354#[derive(Debug)]
1359pub struct Group<W> {
1360 pub element: Element,
1362
1363 pub warnings: Vec<W>,
1365}
1366
1367impl<W> Group<W>
1368where
1369 W: Warning,
1370{
1371 fn into_other<WA>(self) -> Group<WA>
1375 where
1376 W: Into<WA>,
1377 WA: Warning,
1378 {
1379 let Self { element, warnings } = self;
1380 let warnings = warnings.into_iter().map(Into::into).collect();
1381 Group { element, warnings }
1382 }
1383}
1384
1385pub struct Iter<'caller, W>
1387where
1388 W: Warning,
1389{
1390 warnings: std::collections::btree_map::Iter<'caller, json::ElemId, Group<W>>,
1392}
1393
1394impl<W> Iter<'_, W> where W: Warning {}
1395
1396impl<'caller, W: Warning> Iterator for Iter<'caller, W> {
1397 type Item = &'caller Group<W>;
1398
1399 fn next(&mut self) -> Option<Self::Item> {
1400 let (_elem_id, group) = self.warnings.next()?;
1401 Some(group)
1402 }
1403}
1404
1405pub struct IntoIter<W>
1407where
1408 W: Warning,
1409{
1410 warnings: std::collections::btree_map::IntoIter<json::ElemId, Group<W>>,
1412}
1413
1414impl<W: Warning> Iterator for IntoIter<W> {
1415 type Item = Group<W>;
1416
1417 fn next(&mut self) -> Option<Self::Item> {
1418 let (_elem_id, group) = self.warnings.next()?;
1419 Some(group)
1420 }
1421}
1422
1423impl<W: Warning> IntoIterator for Set<W> {
1424 type Item = Group<W>;
1425 type IntoIter = IntoIter<W>;
1426
1427 fn into_iter(self) -> Self::IntoIter {
1428 let Set(warnings) = self;
1429 IntoIter {
1430 warnings: warnings.into_iter(),
1431 }
1432 }
1433}
1434
1435impl<'a, W: Warning> IntoIterator for &'a Set<W> {
1436 type Item = &'a Group<W>;
1437 type IntoIter = Iter<'a, W>;
1438
1439 fn into_iter(self) -> Self::IntoIter {
1440 self.iter()
1441 }
1442}
1443
1444#[cfg(test)]
1445pub(crate) mod test {
1446 use std::{
1447 collections::{BTreeMap, BTreeSet},
1448 fmt,
1449 };
1450
1451 use crate::{
1452 test::{ExpectValue, Expectation},
1453 SmartString,
1454 };
1455
1456 use super::{Caveat, CaveatDeferred, Error, ErrorSet, Group, Set, Verdict, Warning};
1457
1458 pub trait VerdictTestExt<T, W: Warning> {
1460 fn unwrap_only_error(self) -> Error<W>;
1462 }
1463
1464 impl<T, W: Warning> VerdictTestExt<T, W> for Verdict<T, W>
1465 where
1466 T: fmt::Debug,
1467 {
1468 fn unwrap_only_error(self) -> Error<W> {
1469 match self {
1470 Ok(c) => panic!("called `Result::unwrap_only_error` on an `Ok` value: {c:?}"),
1471 Err(set) => {
1472 let ErrorSet { error, warnings: _ } = set;
1473 *error
1474 }
1475 }
1476 }
1477 }
1478
1479 impl<T, W> Caveat<T, W>
1480 where
1481 W: Warning,
1482 {
1483 pub fn unwrap(self) -> T {
1489 let Self { value, warnings } = self;
1490 assert!(warnings.is_empty(), "{warnings:#?}");
1491 value
1492 }
1493 }
1494
1495 impl<T, W> CaveatDeferred<T, W>
1496 where
1497 W: Warning,
1498 {
1499 pub fn unwrap(self) -> T {
1505 let Self { value, warnings } = self;
1506 assert!(warnings.is_empty(), "{warnings:#?}");
1507 value
1508 }
1509 }
1510
1511 impl<W> Group<W>
1512 where
1513 W: Warning,
1514 {
1515 fn path_and_ids(&self) -> (&str, Vec<SmartString>) {
1520 let Self { element, warnings } = self;
1521 (
1522 element.path.as_str(),
1523 warnings.iter().map(Warning::id).collect(),
1524 )
1525 }
1526 }
1527
1528 pub(crate) fn assert_warnings<W>(
1532 expect_file_name: &str,
1533 warnings: &Set<W>,
1534 expected: Expectation<BTreeMap<String, Vec<String>>>,
1535 ) where
1536 W: Warning,
1537 {
1538 let Expectation::Present(ExpectValue::Some(expected)) = expected else {
1539 assert!(
1540 warnings.is_empty(),
1541 "There is no `warnings` field in the `{expect_file_name}` file but the tariff has warnings;\n{:?}",
1542 warnings.path_id_map()
1543 );
1544 return;
1545 };
1546
1547 {
1548 let warnings_grouped = warnings
1550 .iter()
1551 .map(|Group { element, warnings }| (element.path.clone(), warnings))
1552 .collect::<BTreeMap<_, _>>();
1553
1554 let mut elems_in_expect_without_warning = vec![];
1555
1556 for elem_path in expected.keys() {
1557 let elem_path = SmartString::from(elem_path);
1558 if !warnings_grouped.contains_key(&elem_path) {
1559 elems_in_expect_without_warning.push(elem_path);
1560 }
1561 }
1562
1563 assert!(elems_in_expect_without_warning.is_empty(),
1564 "The expect file `{expect_file_name}` has entries for elements that have no warnings:\n\
1565 {elems_in_expect_without_warning:#?}"
1566 );
1567 }
1568
1569 let mut elems_missing_from_expect = vec![];
1571 let mut unequal_warnings = vec![];
1574
1575 for group in warnings {
1576 let Some(warnings_expected) = expected.get(&*group.element.path) else {
1577 elems_missing_from_expect.push(group);
1578 continue;
1579 };
1580
1581 let warnings_expected = warnings_expected
1583 .iter()
1584 .map(SmartString::from)
1585 .collect::<BTreeSet<_>>();
1586 let warnings = group
1587 .warnings
1588 .iter()
1589 .map(Warning::id)
1590 .collect::<BTreeSet<_>>();
1591
1592 if warnings_expected != warnings {
1593 unequal_warnings.push(group);
1594 }
1595 }
1596
1597 if !elems_missing_from_expect.is_empty() || !unequal_warnings.is_empty() {
1598 let missing = elems_missing_from_expect
1599 .into_iter()
1600 .map(Group::path_and_ids)
1601 .collect::<BTreeMap<_, _>>();
1602
1603 let unequal = unequal_warnings
1604 .into_iter()
1605 .map(Group::path_and_ids)
1606 .collect::<BTreeMap<_, _>>();
1607
1608 match (!missing.is_empty(), !unequal.is_empty()) {
1609 (true, true) => panic!(
1610 "Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}\n\
1611 Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1612 The warnings reported are: \n{unequal:#?}"
1613 ),
1614 (true, false) => panic!("Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}"),
1615 (false, true) => panic!(
1616 "Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1617 The warnings reported are: \n{unequal:#?}"
1618 ),
1619 (false, false) => (),
1620 }
1621 }
1622 }
1623}
1624
1625#[cfg(test)]
1626mod test_group_by_elem {
1627 use std::fmt;
1628
1629 use assert_matches::assert_matches;
1630
1631 use crate::{json, test, warning};
1632
1633 use super::{Group, Set};
1634
1635 #[derive(Debug)]
1636 enum Warning {
1637 Root,
1638 One,
1639 OneAgain,
1640 Three,
1641 }
1642
1643 impl super::Warning for Warning {
1644 fn id(&self) -> crate::SmartString {
1645 match self {
1646 Warning::Root => "root".into(),
1647 Warning::One => "one".into(),
1648 Warning::OneAgain => "one_again".into(),
1649 Warning::Three => "three".into(),
1650 }
1651 }
1652 }
1653
1654 impl fmt::Display for Warning {
1655 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1656 match self {
1657 Warning::Root => write!(f, "NopeRoot"),
1658 Warning::One => write!(f, "NopeOne"),
1659 Warning::OneAgain => write!(f, "NopeOneAgain"),
1660 Warning::Three => write!(f, "NopeThree"),
1661 }
1662 }
1663 }
1664
1665 #[test]
1666 fn should_group_by_elem() {
1667 test::setup();
1668
1669 let mut warnings = Set::<Warning>::new();
1670
1671 warnings.insert_warning(Warning::Root, json::ElemId::from(0), || warning::Element {
1674 id: json::ElemId::from(0),
1675 path: "$".into(),
1676 span: json::parser::Span::default(),
1677 });
1678 warnings.insert_warning(Warning::One, json::ElemId::from(1), || warning::Element {
1679 id: json::ElemId::from(1),
1680 path: "$.field_one".into(),
1681 span: json::parser::Span::default(),
1682 });
1683 warnings.insert_warning(Warning::Three, json::ElemId::from(3), || warning::Element {
1684 id: json::ElemId::from(3),
1685 path: "$.field_three".into(),
1686 span: json::parser::Span::default(),
1687 });
1688 warnings.insert_warning(Warning::OneAgain, json::ElemId::from(1), || {
1689 warning::Element {
1690 id: json::ElemId::from(1),
1691 path: "$.field_one".into(),
1692 span: json::parser::Span::default(),
1693 }
1694 });
1695
1696 let mut iter = warnings.iter();
1697
1698 {
1699 let Group { element, warnings } = iter.next().unwrap();
1700
1701 assert_eq!(element.path, "$", "The root object should be emitted first");
1702 assert_matches!(warnings.as_slice(), [Warning::Root]);
1703 }
1704
1705 {
1706 let Group { element, warnings } = iter.next().unwrap();
1707
1708 assert_eq!(element.path, "$.field_one");
1709 assert_matches!(
1710 warnings.as_slice(),
1711 [Warning::One, Warning::OneAgain],
1712 "[`json::Element`] 1 should have two warnings"
1713 );
1714 }
1715
1716 {
1717 let Group { element, warnings } = iter.next().unwrap();
1719
1720 assert_eq!(element.path, "$.field_three");
1721 assert_matches!(warnings.as_slice(), [Warning::Three]);
1722 }
1723 }
1724
1725 #[test]
1726 fn should_into_group_by_elem() {
1727 test::setup();
1728
1729 let mut warnings = Set::<Warning>::new();
1730
1731 warnings.insert_warning(Warning::Root, json::ElemId::from(0), || warning::Element {
1734 id: json::ElemId::from(0),
1735 path: "$".into(),
1736 span: json::parser::Span::default(),
1737 });
1738 warnings.insert_warning(Warning::One, json::ElemId::from(1), || warning::Element {
1739 id: json::ElemId::from(1),
1740 path: "$.field_one".into(),
1741 span: json::parser::Span::default(),
1742 });
1743 warnings.insert_warning(Warning::Three, json::ElemId::from(3), || warning::Element {
1744 id: json::ElemId::from(3),
1745 path: "$.field_three".into(),
1746 span: json::parser::Span::default(),
1747 });
1748 warnings.insert_warning(Warning::OneAgain, json::ElemId::from(1), || {
1749 warning::Element {
1750 id: json::ElemId::from(1),
1751 path: "$.field_one".into(),
1752 span: json::parser::Span::default(),
1753 }
1754 });
1755
1756 let mut iter = warnings.iter();
1757
1758 {
1759 let Group { element, warnings } = iter.next().unwrap();
1760
1761 assert_eq!(element.path, "$");
1762 assert_matches!(warnings.as_slice(), [Warning::Root]);
1763 }
1764
1765 {
1766 let Group { element, warnings } = iter.next().unwrap();
1767
1768 assert_eq!(element.path, "$.field_one");
1769 assert_matches!(
1770 warnings.as_slice(),
1771 [Warning::One, Warning::OneAgain],
1772 "[`json::Element`] 1 should have two warnings"
1773 );
1774 }
1775
1776 {
1777 let Group { element, warnings } = iter.next().unwrap();
1779
1780 assert_eq!(element.path, "$.field_three");
1781 assert_matches!(warnings.as_slice(), [Warning::Three]);
1782 }
1783 }
1784}