1use std::{borrow::Cow, collections::BTreeMap, fmt, iter, ops::Deref, vec};
47
48use tracing::error;
49
50use crate::json;
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<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}
77
78#[doc(hidden)]
80#[macro_export]
81macro_rules! into_caveat_all {
82 ($($kind:ty),+) => {
83 $(impl $crate::IntoCaveat for $kind {
84 fn into_caveat<K: $crate::warning::Kind>(
85 self,
86 warnings: $crate::warning::Set<K>,
87 ) -> $crate::Caveat<Self, K> {
88 $crate::Caveat::new(self, warnings)
89 }
90 })+
91 };
92}
93
94#[doc(hidden)]
95#[macro_export]
96macro_rules! from_warning_all {
97 ($($source_kind:path => $target_kind:ident::$target_variant:ident),+) => {
98 $(
99 impl From<$source_kind> for $target_kind {
101 fn from(warn_kind: $source_kind) -> Self {
102 $target_kind::$target_variant(warn_kind)
103 }
104 }
105
106 impl From<$crate::warning::Set<$source_kind>> for $crate::warning::Set<$target_kind> {
111 fn from(set_a: warning::Set<$source_kind>) -> Self {
112 set_a.into_set()
113 }
114 }
115 )+
116 };
117}
118
119pub type Verdict<T, K> = Result<Caveat<T, K>, Set<K>>;
121
122pub type VerdictX<T, K> = Result<Caveat<T, K>, Vec<K>>;
123
124#[derive(Debug)]
128pub struct Caveat<T, K: Kind> {
129 value: T,
131
132 warnings: Set<K>,
134}
135
136impl<T, K> Caveat<T, K>
137where
138 T: IntoCaveat,
139 K: Kind,
140{
141 pub(crate) fn new(value: T, warnings: Set<K>) -> Self {
143 Self { value, warnings }
144 }
145}
146
147impl<T, K> Deref for Caveat<T, K>
159where
160 K: Kind,
161{
162 type Target = T;
163
164 fn deref(&self) -> &T {
165 &self.value
166 }
167}
168
169impl<T, K> Caveat<T, K>
170where
171 K: Kind,
172{
173 pub fn into_parts(self) -> (T, Set<K>) {
175 let Self { value, warnings } = self;
176 (value, warnings)
177 }
178
179 pub fn ignore_warnings(self) -> T {
181 self.value
182 }
183
184 pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Caveat<U, K> {
186 let Self { value, warnings } = self;
187 Caveat {
188 value: op(value),
189 warnings,
190 }
191 }
192}
193
194pub(crate) trait GatherWarnings<T, K>
199where
200 K: Kind,
201{
202 type Output;
204
205 #[must_use = "If you want to ignore the value use `let _ =`"]
207 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
208 where
209 K: Into<KA>,
210 KA: Kind;
211}
212
213impl<T, K> GatherWarnings<T, K> for Caveat<T, K>
215where
216 K: Kind,
217{
218 type Output = T;
219
220 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
222 where
223 K: Into<KA>,
224 KA: Kind,
225 {
226 let Self {
227 value,
228 warnings: inner_warnings,
229 } = self;
230
231 warnings.extend(inner_warnings);
232
233 value
234 }
235}
236
237impl<T, K> GatherWarnings<T, K> for Option<Caveat<T, K>>
239where
240 K: Kind,
241{
242 type Output = Option<T>;
243
244 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
246 where
247 K: Into<KA>,
248 KA: Kind,
249 {
250 match self {
251 Some(cv) => Some(cv.gather_warnings_into(warnings)),
252 None => None,
253 }
254 }
255}
256
257impl<T, K, E> GatherWarnings<T, K> for Result<Caveat<T, K>, E>
259where
260 K: Kind,
261 E: std::error::Error,
262{
263 type Output = Result<T, E>;
264
265 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
267 where
268 K: Into<KA>,
269 KA: Kind,
270 {
271 match self {
272 Ok(cv) => Ok(cv.gather_warnings_into(warnings)),
273 Err(err) => Err(err),
274 }
275 }
276}
277
278impl<T, K> GatherWarnings<T, K> for Verdict<T, K>
280where
281 K: Kind,
282{
283 type Output = Option<T>;
284
285 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
288 where
289 K: Into<KA>,
290 KA: Kind,
291 {
292 match self {
293 Ok(cv) => Some(cv.gather_warnings_into(warnings)),
294 Err(inner_warnings) => {
295 warnings.extend(inner_warnings);
296 None
297 }
298 }
299 }
300}
301
302impl<T, K> GatherWarnings<T, K> for Vec<Caveat<T, K>>
304where
305 K: Kind,
306{
307 type Output = Vec<T>;
308
309 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
311 where
312 K: Into<KA>,
313 KA: Kind,
314 {
315 self.into_iter()
316 .map(|cv| cv.gather_warnings_into(warnings))
317 .collect()
318 }
319}
320
321pub trait IntoCaveat: Sized {
325 fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K>;
327}
328
329macro_rules! peel {
330 ($name:ident, $($other:ident,)*) => (into_caveat_tuple! { $($other,)* })
331}
332
333macro_rules! into_caveat_tuple {
334 () => ();
335 ( $($name:ident,)+ ) => (
336 impl<$($name),+> $crate::IntoCaveat for ($($name,)+) {
337 fn into_caveat<K: $crate::warning::Kind>(
338 self,
339 warnings: $crate::warning::Set<K>,
340 ) -> $crate::Caveat<Self, K> {
341 $crate::Caveat::new(self, warnings)
342 }
343 }
344 peel! { $($name,)+ }
345 )
346}
347
348into_caveat_tuple! { E, D, C, B, A, Z, Y, X, W, V, U, T, }
349
350into_caveat_all!(
351 (),
352 bool,
353 char,
354 u8,
355 u16,
356 u32,
357 u64,
358 u128,
359 i8,
360 i16,
361 i32,
362 i64,
363 i128
364);
365
366impl IntoCaveat for Cow<'_, str> {
368 fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K> {
369 Caveat::new(self, warnings)
370 }
371}
372
373impl<T> IntoCaveat for Option<T>
375where
376 T: IntoCaveat,
377{
378 fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K> {
379 Caveat::new(self, warnings)
380 }
381}
382
383impl<T> IntoCaveat for Vec<T>
385where
386 T: IntoCaveat,
387{
388 fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K> {
389 Caveat::new(self, warnings)
390 }
391}
392
393pub trait VerdictExt<T, K: Kind> {
395 fn map_caveat<F, U>(self, op: F) -> Verdict<U, K>
398 where
399 F: FnOnce(T) -> U;
400}
401
402impl<T, K: Kind> VerdictExt<T, K> for Verdict<T, K>
403where
404 T: IntoCaveat,
405{
406 fn map_caveat<F, U>(self, op: F) -> Verdict<U, K>
407 where
408 F: FnOnce(T) -> U,
409 {
410 match self {
411 Ok(c) => Ok(c.map(op)),
412 Err(w) => Err(w),
413 }
414 }
415}
416
417pub trait OptionExt<T, K>
419where
420 K: Kind,
421{
422 fn exit_with_warning<F>(self, warnings: Set<K>, f: F) -> Verdict<T, K>
424 where
425 F: FnOnce() -> Warning<K>;
426}
427
428impl<T, K> OptionExt<T, K> for Option<T>
429where
430 T: IntoCaveat,
431 K: Kind,
432{
433 fn exit_with_warning<F>(self, mut warnings: Set<K>, f: F) -> Verdict<T, K>
434 where
435 F: FnOnce() -> Warning<K>,
436 {
437 if let Some(v) = self {
438 Ok(v.into_caveat(warnings))
439 } else {
440 warnings.push(f());
441 Err(warnings)
442 }
443 }
444}
445
446#[derive(Debug)]
450pub struct Warning<K: Kind> {
451 kind: K,
453
454 elem_id: json::ElemId,
456}
457
458pub struct SetWriter<'caller, 'buf, K: Kind> {
472 root_elem: &'caller json::Element<'buf>,
474
475 warnings: &'caller Set<K>,
477
478 indent: &'caller str,
480}
481
482impl<'caller, 'buf, K: Kind> SetWriter<'caller, 'buf, K> {
483 pub fn new(root_elem: &'caller json::Element<'buf>, warnings: &'caller Set<K>) -> Self {
485 Self {
486 root_elem,
487 warnings,
488 indent: " - ",
489 }
490 }
491
492 pub fn with_indent(
494 root_elem: &'caller json::Element<'buf>,
495 warnings: &'caller Set<K>,
496 indent: &'caller str,
497 ) -> Self {
498 Self {
499 root_elem,
500 warnings,
501 indent,
502 }
503 }
504}
505
506impl<K: Kind> fmt::Debug for SetWriter<'_, '_, K> {
507 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508 fmt::Display::fmt(self, f)
509 }
510}
511
512impl<K: Kind> fmt::Display for SetWriter<'_, '_, K> {
513 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
514 let mut iter = self.warnings.group_by_elem(self.root_elem);
515
516 {
517 let Some(Group { element, warnings }) = iter.next() else {
519 return Ok(());
520 };
521
522 writeln!(f, "{}", element.path())?;
523
524 for warning in warnings {
525 write!(f, "{}{}", self.indent, warning)?;
526 }
527 }
528
529 for Group { element, warnings } in iter {
531 writeln!(f, "\n{}", element.path())?;
532
533 for warning in warnings {
534 write!(f, "{}{}", self.indent, warning)?;
535 }
536 }
537
538 Ok(())
539 }
540}
541
542impl<K: Kind> Warning<K> {
543 pub(crate) const fn with_elem(kind: K, elem: &json::Element<'_>) -> Warning<K> {
547 Warning {
548 kind,
549 elem_id: elem.id(),
550 }
551 }
552
553 pub fn id(&self) -> Cow<'static, str> {
555 self.kind.id()
556 }
557
558 pub fn kind(&self) -> &K {
560 &self.kind
561 }
562
563 pub fn into_kind(self) -> K {
565 self.kind
566 }
567}
568
569impl<K: Kind> fmt::Display for Warning<K> {
570 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
571 write!(f, "{}", self.kind)
572 }
573}
574
575pub trait Kind: Sized + fmt::Debug + fmt::Display {
579 fn id(&self) -> Cow<'static, str>;
584}
585
586#[derive(Debug)]
588pub struct Set<K: Kind>(Vec<Warning<K>>);
589
590impl<K: Kind> Set<K> {
591 pub(crate) fn new() -> Self {
593 Self(vec![])
594 }
595
596 pub(crate) fn from_vec(warnings: Vec<Warning<K>>) -> Self {
598 Self(warnings)
599 }
600
601 fn push(&mut self, warning: Warning<K>) {
603 self.0.push(warning);
604 }
605
606 pub(crate) fn with_elem(&mut self, kind: K, elem: &json::Element<'_>) {
608 self.push(Warning::with_elem(kind, elem));
609 }
610
611 pub(crate) fn bail<T>(mut self, kind: K, elem: &json::Element<'_>) -> Verdict<T, K> {
615 self.0.push(Warning::with_elem(kind, elem));
616 Err(self)
617 }
618
619 pub(crate) fn into_set<KB>(self) -> Set<KB>
623 where
624 KB: Kind + From<K>,
625 {
626 let warnings = self
627 .0
628 .into_iter()
629 .map(|warn| {
630 let Warning { kind, elem_id } = warn;
631 Warning {
632 kind: kind.into(),
633 elem_id,
634 }
635 })
636 .collect();
637
638 Set(warnings)
639 }
640
641 pub(crate) fn map_warning<KU>(self) -> Set<KU>
645 where
646 KU: Kind + From<K>,
647 {
648 let warnings = self
649 .0
650 .into_iter()
651 .map(|warn| {
652 let Warning { kind, elem_id } = warn;
653 Warning {
654 kind: kind.into(),
655 elem_id,
656 }
657 })
658 .collect();
659
660 Set(warnings)
661 }
662
663 pub(crate) fn extend<KA>(&mut self, warnings: Set<KA>)
667 where
668 KA: Into<K> + Kind,
669 {
670 self.0.extend(warnings.0.into_iter().map(|warn| {
671 let Warning { kind, elem_id } = warn;
672 Warning {
673 kind: kind.into(),
674 elem_id,
675 }
676 }));
677 }
678
679 pub fn is_empty(&self) -> bool {
681 self.0.is_empty()
682 }
683
684 pub fn len(&self) -> usize {
686 self.0.len()
687 }
688
689 pub fn group_by_elem<'caller: 'buf, 'buf>(
693 &'caller self,
694 root: &'caller json::Element<'buf>,
695 ) -> GroupByElem<'caller, 'buf, K> {
696 let mut warnings = self.0.iter().collect::<Vec<_>>();
697 warnings.sort_unstable_by_key(|warning| warning.elem_id);
698
699 GroupByElem {
700 walker: json::walk::DepthFirst::new(root),
701 warnings: warnings.into_iter().peekable(),
702 }
703 }
704
705 pub fn into_group_by_elem<'caller, 'buf>(
709 self,
710 root: &'caller json::Element<'buf>,
711 ) -> IntoGroupByElem<'caller, 'buf, K> {
712 let Self(mut warnings) = self;
713 warnings.sort_unstable_by_key(|warning| warning.elem_id);
714
715 IntoGroupByElem {
716 walker: json::walk::DepthFirst::new(root),
717 warnings: warnings.into_iter().peekable(),
718 }
719 }
720}
721
722pub struct IntoGroupByElem<'caller, 'buf, K>
724where
725 K: Kind,
726{
727 walker: json::walk::DepthFirst<'caller, 'buf>,
729
730 warnings: iter::Peekable<vec::IntoIter<Warning<K>>>,
732}
733
734impl<K> IntoGroupByElem<'_, '_, K>
735where
736 K: Kind,
737{
738 pub fn into_kind_map(self) -> BTreeMap<String, Vec<K>> {
740 self.map(IntoGroup::into_kinds).collect()
741 }
742
743 pub fn into_id_map(self) -> BTreeMap<String, Vec<String>> {
752 self.map(IntoGroup::into_str_ids).collect()
753 }
754
755 pub fn into_msg_map(self) -> BTreeMap<String, Vec<String>> {
761 self.map(IntoGroup::into_str_msgs).collect()
762 }
763}
764
765#[derive(Debug)]
770pub struct IntoGroup<'caller, 'buf, K> {
771 pub element: &'caller json::Element<'buf>,
773
774 pub warnings: Vec<K>,
776}
777
778impl<K> IntoGroup<'_, '_, K>
779where
780 K: Kind,
781{
782 pub fn into_kinds(self) -> (String, Vec<K>) {
787 let Self { element, warnings } = self;
788 (element.path().to_string(), warnings)
789 }
790
791 pub fn into_str_ids(self) -> (String, Vec<String>) {
796 let Self { element, warnings } = self;
797 (
798 element.path().to_string(),
799 warnings.iter().map(|kind| kind.id().to_string()).collect(),
800 )
801 }
802
803 pub fn into_str_msgs(self) -> (String, Vec<String>) {
808 let Self { element, warnings } = self;
809 (
810 element.path().to_string(),
811 warnings.iter().map(ToString::to_string).collect(),
812 )
813 }
814}
815
816impl<'caller, 'buf, K: Kind> Iterator for IntoGroupByElem<'caller, 'buf, K> {
817 type Item = IntoGroup<'caller, 'buf, K>;
818
819 fn next(&mut self) -> Option<Self::Item> {
820 let warning = self.warnings.next()?;
822
823 let element = loop {
825 let Some(element) = self.walker.next() else {
826 error!("An Element with id: `{}` was not found", warning.elem_id);
827 return None;
828 };
829
830 if element.id() < warning.elem_id {
831 continue;
834 }
835
836 if element.id() > warning.elem_id {
837 debug_assert!(
838 element.id() <= warning.elem_id,
839 "The elements or the warnings are not sorted."
840 );
841 return None;
842 }
843
844 break element;
846 };
847
848 let mut warnings_grouped = vec![warning.into_kind()];
850
851 loop {
853 let warning = self.warnings.next_if(|w| w.elem_id == element.id());
854 let Some(warning) = warning else {
855 break;
856 };
857
858 warnings_grouped.push(warning.into_kind());
859 }
860
861 Some(IntoGroup {
862 element,
863 warnings: warnings_grouped,
864 })
865 }
866}
867
868pub struct GroupByElem<'caller, 'buf, K>
870where
871 K: Kind,
872{
873 walker: json::walk::DepthFirst<'caller, 'buf>,
875
876 warnings: iter::Peekable<vec::IntoIter<&'caller Warning<K>>>,
878}
879
880impl<K> GroupByElem<'_, '_, K>
881where
882 K: Kind,
883{
884 pub fn into_id_map(self) -> BTreeMap<String, Vec<String>> {
893 self.map(Group::into_str_ids).collect()
894 }
895
896 pub fn into_msg_map(self) -> BTreeMap<String, Vec<String>> {
902 self.map(Group::into_str_msgs).collect()
903 }
904}
905
906#[derive(Debug)]
911pub struct Group<'caller, 'buf, K> {
912 pub element: &'caller json::Element<'buf>,
914
915 pub warnings: Vec<&'caller K>,
917}
918
919impl<K> Group<'_, '_, K>
920where
921 K: Kind,
922{
923 pub fn into_str_ids(self) -> (String, Vec<String>) {
928 let Self { element, warnings } = self;
929 (
930 element.path().to_string(),
931 warnings.iter().map(|kind| kind.id().to_string()).collect(),
932 )
933 }
934
935 pub fn into_str_msgs(self) -> (String, Vec<String>) {
940 let Self { element, warnings } = self;
941 (
942 element.path().to_string(),
943 warnings.iter().map(ToString::to_string).collect(),
944 )
945 }
946}
947
948impl<'caller, 'buf, K: Kind> Iterator for GroupByElem<'caller, 'buf, K> {
949 type Item = Group<'caller, 'buf, K>;
950
951 fn next(&mut self) -> Option<Self::Item> {
952 let warning = self.warnings.next()?;
954
955 let element = loop {
957 let Some(element) = self.walker.next() else {
958 error!("An Element with id: `{}` was not found", warning.elem_id);
959 return None;
960 };
961
962 if element.id() < warning.elem_id {
963 continue;
966 }
967
968 if element.id() > warning.elem_id {
969 debug_assert!(
970 element.id() <= warning.elem_id,
971 "The elements or the warnings are not sorted."
972 );
973 return None;
974 }
975
976 break element;
978 };
979
980 let mut warnings_grouped = vec![warning.kind()];
982
983 loop {
985 let warning = self.warnings.next_if(|w| w.elem_id == element.id());
986 let Some(warning) = warning else {
987 break;
988 };
989
990 warnings_grouped.push(warning.kind());
991 }
992
993 Some(Group {
994 element,
995 warnings: warnings_grouped,
996 })
997 }
998}
999
1000#[cfg(test)]
1001pub(crate) mod test {
1002 use std::{
1003 borrow::Cow,
1004 collections::{BTreeMap, BTreeSet},
1005 ops, slice,
1006 };
1007
1008 use assert_matches::assert_matches;
1009
1010 use crate::{
1011 json,
1012 test::{ExpectValue, Expectation},
1013 };
1014
1015 use super::{Caveat, Group, Kind, Set, Warning};
1016
1017 impl<K: Kind> Set<K> {
1018 pub fn into_vec(self) -> Vec<Warning<K>> {
1020 self.0
1021 }
1022
1023 pub fn into_parts_vec(self) -> Vec<(K, json::ElemId)> {
1024 self.0
1025 .into_iter()
1026 .map(|Warning { kind, elem_id }| (kind, elem_id))
1027 .collect()
1028 }
1029
1030 pub fn into_kind_vec(self) -> Vec<K> {
1034 self.0.into_iter().map(Warning::into_kind).collect()
1035 }
1036
1037 pub fn as_slice(&self) -> &[Warning<K>] {
1039 self.0.as_slice()
1040 }
1041
1042 pub fn iter(&self) -> slice::Iter<'_, Warning<K>> {
1044 self.0.iter()
1045 }
1046 }
1047
1048 impl<K: Kind> ops::Deref for Set<K> {
1049 type Target = [Warning<K>];
1050
1051 fn deref(&self) -> &[Warning<K>] {
1052 self.as_slice()
1053 }
1054 }
1055
1056 impl<K: Kind> IntoIterator for Set<K> {
1057 type Item = Warning<K>;
1058
1059 type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
1060
1061 fn into_iter(self) -> Self::IntoIter {
1062 self.0.into_iter()
1063 }
1064 }
1065
1066 impl<'a, K: Kind> IntoIterator for &'a Set<K> {
1067 type Item = &'a Warning<K>;
1068
1069 type IntoIter = slice::Iter<'a, Warning<K>>;
1070
1071 fn into_iter(self) -> Self::IntoIter {
1072 self.0.iter()
1073 }
1074 }
1075
1076 impl<T, K> Caveat<T, K>
1077 where
1078 K: Kind,
1079 {
1080 pub fn unwrap(self) -> T {
1082 let Self { value, warnings } = self;
1083 assert_matches!(warnings.into_vec().as_slice(), []);
1084 value
1085 }
1086 }
1087
1088 pub(crate) fn assert_warnings<K>(
1092 expect_file_name: &str,
1093 root: &json::Element<'_>,
1094 warnings: &Set<K>,
1095 expected: Expectation<BTreeMap<String, Vec<String>>>,
1096 ) where
1097 K: Kind,
1098 {
1099 let Expectation::Present(ExpectValue::Some(expected)) = expected else {
1100 assert!(
1101 warnings.is_empty(),
1102 "There is no `warnings` field in the `{expect_file_name}` file but the tariff has warnings;\n{:?}",
1103 warnings.group_by_elem(root).into_id_map()
1104 );
1105 return;
1106 };
1107
1108 {
1109 let warnings_grouped = warnings
1111 .group_by_elem(root)
1112 .map(|Group { element, warnings }| (element.path().to_string(), warnings))
1113 .collect::<BTreeMap<_, _>>();
1114
1115 let mut elems_in_expect_without_warning = vec![];
1116
1117 for elem_path in expected.keys() {
1118 if !warnings_grouped.contains_key(elem_path) {
1119 elems_in_expect_without_warning.push(elem_path);
1120 }
1121 }
1122
1123 assert!(elems_in_expect_without_warning.is_empty(),
1124 "The expect file `{expect_file_name}` has entries for elements that have no warnings:\n\
1125 {elems_in_expect_without_warning:#?}"
1126 );
1127 }
1128
1129 let mut elems_missing_from_expect = vec![];
1131 let mut unequal_warnings = vec![];
1134
1135 for group in warnings.group_by_elem(root) {
1136 let path_str = group.element.path().to_string();
1137 let Some(warnings_expected) = expected.get(&*path_str) else {
1138 elems_missing_from_expect.push(group);
1139 continue;
1140 };
1141
1142 let warnings_expected = warnings_expected
1144 .iter()
1145 .map(|s| Cow::Borrowed(&**s))
1146 .collect::<BTreeSet<_>>();
1147 let warnings = group
1148 .warnings
1149 .iter()
1150 .map(|w| w.id())
1151 .collect::<BTreeSet<_>>();
1152
1153 if warnings_expected != warnings {
1154 unequal_warnings.push(group);
1155 }
1156 }
1157
1158 if !elems_missing_from_expect.is_empty() || !unequal_warnings.is_empty() {
1159 let missing = elems_missing_from_expect
1160 .into_iter()
1161 .map(Group::into_str_ids)
1162 .collect::<BTreeMap<_, _>>();
1163
1164 let unequal = unequal_warnings
1165 .into_iter()
1166 .map(Group::into_str_ids)
1167 .collect::<BTreeMap<_, _>>();
1168
1169 match (!missing.is_empty(), !unequal.is_empty()) {
1170 (true, true) => panic!(
1171 "Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}\n\
1172 Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1173 The warnings reported are: \n{unequal:#?}"
1174 ),
1175 (true, false) => panic!("Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}"),
1176 (false, true) => panic!(
1177 "Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1178 The warnings reported are: \n{unequal:#?}"
1179 ),
1180 (false, false) => (),
1181 }
1182 }
1183 }
1184}
1185
1186#[cfg(test)]
1187mod test_group_by_elem {
1188 use std::fmt;
1189
1190 use assert_matches::assert_matches;
1191
1192 use crate::{json, test};
1193
1194 use super::{Group, IntoGroup, Kind, Set, Warning};
1195
1196 const JSON: &str = r#"{
1197 "field_one#": "one",
1198 "field_two": "two",
1199 "field_three": "three"
1200}"#;
1201
1202 #[derive(Debug)]
1203 enum WarningKind {
1204 Root,
1205 One,
1206 OneAgain,
1207 Three,
1208 }
1209
1210 impl Kind for WarningKind {
1211 fn id(&self) -> std::borrow::Cow<'static, str> {
1212 match self {
1213 WarningKind::Root => "root".into(),
1214 WarningKind::One => "one".into(),
1215 WarningKind::OneAgain => "one_again".into(),
1216 WarningKind::Three => "three".into(),
1217 }
1218 }
1219 }
1220
1221 impl fmt::Display for WarningKind {
1222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1223 match self {
1224 WarningKind::Root => write!(f, "NopeRoot"),
1225 WarningKind::One => write!(f, "NopeOne"),
1226 WarningKind::OneAgain => write!(f, "NopeOneAgain"),
1227 WarningKind::Three => write!(f, "NopeThree"),
1228 }
1229 }
1230 }
1231
1232 #[test]
1233 fn should_group_by_elem() {
1234 test::setup();
1235
1236 let elem = parse(JSON);
1237 let mut warnings = Set::<WarningKind>::new();
1238
1239 warnings.push(Warning {
1242 kind: WarningKind::Root,
1243 elem_id: json::ElemId::from(0),
1244 });
1245 warnings.push(Warning {
1246 kind: WarningKind::One,
1247 elem_id: json::ElemId::from(1),
1248 });
1249 warnings.push(Warning {
1250 kind: WarningKind::Three,
1251 elem_id: json::ElemId::from(3),
1252 });
1253 warnings.push(Warning {
1254 kind: WarningKind::OneAgain,
1255 elem_id: json::ElemId::from(1),
1256 });
1257
1258 let mut iter = warnings.group_by_elem(&elem);
1259
1260 {
1261 let Group { element, warnings } = iter.next().unwrap();
1262
1263 assert!(
1264 element.value().is_object(),
1265 "The root object should be emitted first"
1266 );
1267 assert_eq!(
1268 element.id(),
1269 json::ElemId::from(0),
1270 "The root object should be emitted first"
1271 );
1272 assert_matches!(warnings.as_slice(), [WarningKind::Root]);
1273 }
1274
1275 {
1276 let Group { element, warnings } = iter.next().unwrap();
1277
1278 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "one");
1279 assert_eq!(element.id(), json::ElemId::from(1));
1280 assert_matches!(
1281 warnings.as_slice(),
1282 [WarningKind::One, WarningKind::OneAgain],
1283 "[`json::Element`] 1 should have two warnings"
1284 );
1285 }
1286
1287 {
1288 let Group { element, warnings } = iter.next().unwrap();
1290
1291 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "three");
1292 assert_eq!(element.id(), json::ElemId::from(3));
1293 assert_matches!(warnings.as_slice(), [WarningKind::Three]);
1294 }
1295 }
1296
1297 #[test]
1298 fn should_into_group_by_elem() {
1299 test::setup();
1300
1301 let elem = parse(JSON);
1302 let mut warnings = Set::<WarningKind>::new();
1303
1304 warnings.push(Warning {
1307 kind: WarningKind::Root,
1308 elem_id: json::ElemId::from(0),
1309 });
1310 warnings.push(Warning {
1311 kind: WarningKind::Three,
1312 elem_id: json::ElemId::from(3),
1313 });
1314 warnings.push(Warning {
1315 kind: WarningKind::One,
1316 elem_id: json::ElemId::from(1),
1317 });
1318 warnings.push(Warning {
1319 kind: WarningKind::OneAgain,
1320 elem_id: json::ElemId::from(1),
1321 });
1322
1323 let mut iter = warnings.into_group_by_elem(&elem);
1324
1325 {
1326 let IntoGroup { element, warnings } = iter.next().unwrap();
1327
1328 assert!(
1329 element.value().is_object(),
1330 "The root object should be emitted first"
1331 );
1332 assert_eq!(
1333 element.id(),
1334 json::ElemId::from(0),
1335 "The root object should be emitted first"
1336 );
1337 assert_matches!(warnings.as_slice(), [WarningKind::Root]);
1338 }
1339
1340 {
1341 let IntoGroup { element, warnings } = iter.next().unwrap();
1342
1343 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "one");
1344 assert_eq!(element.id(), json::ElemId::from(1));
1345 assert_matches!(
1346 warnings.as_slice(),
1347 [WarningKind::One, WarningKind::OneAgain],
1348 "[`json::Element`] 1 should have two warnings"
1349 );
1350 }
1351
1352 {
1353 let IntoGroup { element, warnings } = iter.next().unwrap();
1355
1356 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "three");
1357 assert_eq!(element.id(), json::ElemId::from(3));
1358 assert_matches!(warnings.as_slice(), [WarningKind::Three]);
1359 }
1360 }
1361
1362 fn parse(json: &str) -> json::Element<'_> {
1363 json::parse(json).unwrap()
1364 }
1365}