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
94pub type Verdict<T, K> = Result<Caveat<T, K>, Set<K>>;
96
97#[derive(Debug)]
101pub struct Caveat<T, K: Kind> {
102 value: T,
104
105 warnings: Set<K>,
107}
108
109impl<T, K> Caveat<T, K>
110where
111 T: IntoCaveat,
112 K: Kind,
113{
114 pub(crate) fn new(value: T, warnings: Set<K>) -> Self {
116 Self { value, warnings }
117 }
118}
119
120impl<T, K> Deref for Caveat<T, K>
132where
133 K: Kind,
134{
135 type Target = T;
136
137 fn deref(&self) -> &T {
138 &self.value
139 }
140}
141
142impl<T, K> Caveat<T, K>
143where
144 K: Kind,
145{
146 pub fn into_parts(self) -> (T, Set<K>) {
148 let Self { value, warnings } = self;
149 (value, warnings)
150 }
151
152 pub fn ignore_warnings(self) -> T {
154 self.value
155 }
156
157 pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Caveat<U, K> {
159 let Self { value, warnings } = self;
160 Caveat {
161 value: op(value),
162 warnings,
163 }
164 }
165}
166
167pub(crate) trait GatherWarnings<T, K>
172where
173 K: Kind,
174{
175 type Output;
177
178 #[must_use = "If you want to ignore the value use `let _ =`"]
180 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
181 where
182 K: Into<KA>,
183 KA: Kind;
184}
185
186impl<T, K> GatherWarnings<T, K> for Caveat<T, K>
188where
189 K: Kind,
190{
191 type Output = T;
192
193 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
195 where
196 K: Into<KA>,
197 KA: Kind,
198 {
199 let Self {
200 value,
201 warnings: inner_warnings,
202 } = self;
203
204 warnings.extend(inner_warnings);
205
206 value
207 }
208}
209
210impl<T, K> GatherWarnings<T, K> for Option<Caveat<T, K>>
212where
213 K: Kind,
214{
215 type Output = Option<T>;
216
217 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
219 where
220 K: Into<KA>,
221 KA: Kind,
222 {
223 match self {
224 Some(cv) => Some(cv.gather_warnings_into(warnings)),
225 None => None,
226 }
227 }
228}
229
230impl<T, K, E> GatherWarnings<T, K> for Result<Caveat<T, K>, E>
232where
233 K: Kind,
234 E: std::error::Error,
235{
236 type Output = Result<T, E>;
237
238 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
240 where
241 K: Into<KA>,
242 KA: Kind,
243 {
244 match self {
245 Ok(cv) => Ok(cv.gather_warnings_into(warnings)),
246 Err(err) => Err(err),
247 }
248 }
249}
250
251impl<T, K> GatherWarnings<T, K> for Verdict<T, K>
253where
254 K: Kind,
255{
256 type Output = Option<T>;
257
258 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
261 where
262 K: Into<KA>,
263 KA: Kind,
264 {
265 match self {
266 Ok(cv) => Some(cv.gather_warnings_into(warnings)),
267 Err(inner_warnings) => {
268 warnings.extend(inner_warnings);
269 None
270 }
271 }
272 }
273}
274
275impl<T, K> GatherWarnings<T, K> for Vec<Caveat<T, K>>
277where
278 K: Kind,
279{
280 type Output = Vec<T>;
281
282 fn gather_warnings_into<KA>(self, warnings: &mut Set<KA>) -> Self::Output
284 where
285 K: Into<KA>,
286 KA: Kind,
287 {
288 self.into_iter()
289 .map(|cv| cv.gather_warnings_into(warnings))
290 .collect()
291 }
292}
293
294pub trait IntoCaveat: Sized {
298 fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K>;
300}
301
302macro_rules! peel {
303 ($name:ident, $($other:ident,)*) => (into_caveat_tuple! { $($other,)* })
304}
305
306macro_rules! into_caveat_tuple {
307 () => ();
308 ( $($name:ident,)+ ) => (
309 impl<$($name),+> $crate::IntoCaveat for ($($name,)+) {
310 fn into_caveat<K: $crate::warning::Kind>(
311 self,
312 warnings: $crate::warning::Set<K>,
313 ) -> $crate::Caveat<Self, K> {
314 $crate::Caveat::new(self, warnings)
315 }
316 }
317 peel! { $($name,)+ }
318 )
319}
320
321into_caveat_tuple! { E, D, C, B, A, Z, Y, X, W, V, U, T, }
322
323into_caveat_all!(
324 (),
325 bool,
326 char,
327 u8,
328 u16,
329 u32,
330 u64,
331 u128,
332 i8,
333 i16,
334 i32,
335 i64,
336 i128
337);
338
339impl IntoCaveat for Cow<'_, str> {
341 fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K> {
342 Caveat::new(self, warnings)
343 }
344}
345
346impl<T> IntoCaveat for Option<T>
348where
349 T: IntoCaveat,
350{
351 fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K> {
352 Caveat::new(self, warnings)
353 }
354}
355
356impl<T> IntoCaveat for Vec<T>
358where
359 T: IntoCaveat,
360{
361 fn into_caveat<K: Kind>(self, warnings: Set<K>) -> Caveat<Self, K> {
362 Caveat::new(self, warnings)
363 }
364}
365
366pub trait VerdictExt<T, K: Kind> {
368 fn map_caveat<F, U>(self, op: F) -> Verdict<U, K>
371 where
372 F: FnOnce(T) -> U;
373}
374
375impl<T, K: Kind> VerdictExt<T, K> for Verdict<T, K>
376where
377 T: IntoCaveat,
378{
379 fn map_caveat<F, U>(self, op: F) -> Verdict<U, K>
380 where
381 F: FnOnce(T) -> U,
382 {
383 match self {
384 Ok(c) => Ok(c.map(op)),
385 Err(w) => Err(w),
386 }
387 }
388}
389
390#[macro_export]
395#[doc(hidden)]
396macro_rules! from_warning_set_to {
397 ($kind_a:path => $kind_b:path) => {
398 impl From<$crate::warning::Set<$kind_a>> for $crate::warning::Set<$kind_b> {
399 fn from(set_a: warning::Set<$kind_a>) -> Self {
400 set_a.into_set()
401 }
402 }
403 };
404}
405
406pub trait OptionExt<T, K>
408where
409 K: Kind,
410{
411 fn exit_with_warning<F>(self, warnings: Set<K>, f: F) -> Verdict<T, K>
413 where
414 F: FnOnce() -> Warning<K>;
415}
416
417impl<T, K> OptionExt<T, K> for Option<T>
418where
419 T: IntoCaveat,
420 K: Kind,
421{
422 fn exit_with_warning<F>(self, mut warnings: Set<K>, f: F) -> Verdict<T, K>
423 where
424 F: FnOnce() -> Warning<K>,
425 {
426 if let Some(v) = self {
427 Ok(v.into_caveat(warnings))
428 } else {
429 warnings.push(f());
430 Err(warnings)
431 }
432 }
433}
434
435#[derive(Debug)]
439pub struct Warning<K: Kind> {
440 kind: K,
442
443 elem_id: json::ElemId,
445}
446
447pub struct SetWriter<'caller, 'buf, K: Kind> {
461 root_elem: &'caller json::Element<'buf>,
463
464 warnings: &'caller Set<K>,
466
467 indent: &'caller str,
469}
470
471impl<'caller, 'buf, K: Kind> SetWriter<'caller, 'buf, K> {
472 pub fn new(root_elem: &'caller json::Element<'buf>, warnings: &'caller Set<K>) -> Self {
474 Self {
475 root_elem,
476 warnings,
477 indent: " - ",
478 }
479 }
480
481 pub fn with_indent(
483 root_elem: &'caller json::Element<'buf>,
484 warnings: &'caller Set<K>,
485 indent: &'caller str,
486 ) -> Self {
487 Self {
488 root_elem,
489 warnings,
490 indent,
491 }
492 }
493}
494
495impl<K: Kind> fmt::Debug for SetWriter<'_, '_, K> {
496 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497 fmt::Display::fmt(self, f)
498 }
499}
500
501impl<K: Kind> fmt::Display for SetWriter<'_, '_, K> {
502 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503 let mut iter = self.warnings.group_by_elem(self.root_elem);
504
505 {
506 let Some(Group { element, warnings }) = iter.next() else {
508 return Ok(());
509 };
510
511 writeln!(f, "{}", element.path())?;
512
513 for warning in warnings {
514 write!(f, "{}{}", self.indent, warning)?;
515 }
516 }
517
518 for Group { element, warnings } in iter {
520 writeln!(f, "\n{}", element.path())?;
521
522 for warning in warnings {
523 write!(f, "{}{}", self.indent, warning)?;
524 }
525 }
526
527 Ok(())
528 }
529}
530
531impl<K: Kind> Warning<K> {
532 pub(crate) const fn with_elem(kind: K, elem: &json::Element<'_>) -> Warning<K> {
536 Warning {
537 kind,
538 elem_id: elem.id(),
539 }
540 }
541
542 pub fn id(&self) -> Cow<'static, str> {
544 self.kind.id()
545 }
546
547 pub fn kind(&self) -> &K {
549 &self.kind
550 }
551
552 pub fn into_kind(self) -> K {
554 self.kind
555 }
556}
557
558impl<K: Kind> fmt::Display for Warning<K> {
559 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
560 write!(f, "{}", self.kind)
561 }
562}
563
564pub trait Kind: Sized + fmt::Debug + fmt::Display {
568 fn id(&self) -> Cow<'static, str>;
573}
574
575#[derive(Debug)]
577pub struct Set<K: Kind>(Vec<Warning<K>>);
578
579impl<K: Kind> Set<K> {
580 pub(crate) fn new() -> Self {
582 Self(vec![])
583 }
584
585 pub(crate) fn from_vec(warnings: Vec<Warning<K>>) -> Self {
586 Self(warnings)
587 }
588
589 fn push(&mut self, warning: Warning<K>) {
591 self.0.push(warning);
592 }
593
594 pub(crate) fn with_elem(&mut self, kind: K, elem: &json::Element<'_>) {
596 self.push(Warning::with_elem(kind, elem));
597 }
598
599 pub(crate) fn into_set<KB>(self) -> Set<KB>
603 where
604 KB: Kind + From<K>,
605 {
606 let warnings = self
607 .0
608 .into_iter()
609 .map(|warn| {
610 let Warning { kind, elem_id } = warn;
611 Warning {
612 kind: kind.into(),
613 elem_id,
614 }
615 })
616 .collect();
617
618 Set(warnings)
619 }
620
621 pub(crate) fn map_warning<KU>(self) -> Set<KU>
625 where
626 KU: Kind + From<K>,
627 {
628 let warnings = self
629 .0
630 .into_iter()
631 .map(|warn| {
632 let Warning { kind, elem_id } = warn;
633 Warning {
634 kind: kind.into(),
635 elem_id,
636 }
637 })
638 .collect();
639
640 Set(warnings)
641 }
642
643 pub(crate) fn extend<KA>(&mut self, warnings: Set<KA>)
647 where
648 KA: Into<K> + Kind,
649 {
650 self.0.extend(warnings.0.into_iter().map(|warn| {
651 let Warning { kind, elem_id } = warn;
652 Warning {
653 kind: kind.into(),
654 elem_id,
655 }
656 }));
657 }
658
659 pub fn is_empty(&self) -> bool {
661 self.0.is_empty()
662 }
663
664 pub fn len(&self) -> usize {
666 self.0.len()
667 }
668
669 pub fn group_by_elem<'caller: 'buf, 'buf>(
673 &'caller self,
674 root: &'caller json::Element<'buf>,
675 ) -> GroupByElem<'caller, 'buf, K> {
676 let mut warnings = self.0.iter().collect::<Vec<_>>();
677 warnings.sort_unstable_by_key(|warning| warning.elem_id);
678
679 GroupByElem {
680 walker: json::walk::DepthFirst::new(root),
681 warnings: warnings.into_iter().peekable(),
682 }
683 }
684
685 pub fn into_group_by_elem<'caller, 'buf>(
689 self,
690 root: &'caller json::Element<'buf>,
691 ) -> IntoGroupByElem<'caller, 'buf, K> {
692 let Self(mut warnings) = self;
693 warnings.sort_unstable_by_key(|warning| warning.elem_id);
694
695 IntoGroupByElem {
696 walker: json::walk::DepthFirst::new(root),
697 warnings: warnings.into_iter().peekable(),
698 }
699 }
700}
701
702pub struct IntoGroupByElem<'caller, 'buf, K>
704where
705 K: Kind,
706{
707 walker: json::walk::DepthFirst<'caller, 'buf>,
709
710 warnings: iter::Peekable<vec::IntoIter<Warning<K>>>,
712}
713
714impl<K> IntoGroupByElem<'_, '_, K>
715where
716 K: Kind,
717{
718 pub fn into_kind_map(self) -> BTreeMap<String, Vec<K>> {
720 self.map(IntoGroup::into_kinds).collect()
721 }
722
723 pub fn into_id_map(self) -> BTreeMap<String, Vec<String>> {
732 self.map(IntoGroup::into_str_ids).collect()
733 }
734
735 pub fn into_msg_map(self) -> BTreeMap<String, Vec<String>> {
741 self.map(IntoGroup::into_str_msgs).collect()
742 }
743}
744
745#[derive(Debug)]
750pub struct IntoGroup<'caller, 'buf, K> {
751 pub element: &'caller json::Element<'buf>,
753
754 pub warnings: Vec<K>,
756}
757
758impl<K> IntoGroup<'_, '_, K>
759where
760 K: Kind,
761{
762 pub fn into_kinds(self) -> (String, Vec<K>) {
767 let Self { element, warnings } = self;
768 (element.path().to_string(), warnings)
769 }
770
771 pub fn into_str_ids(self) -> (String, Vec<String>) {
776 let Self { element, warnings } = self;
777 (
778 element.path().to_string(),
779 warnings.iter().map(|kind| kind.id().to_string()).collect(),
780 )
781 }
782
783 pub fn into_str_msgs(self) -> (String, Vec<String>) {
788 let Self { element, warnings } = self;
789 (
790 element.path().to_string(),
791 warnings.iter().map(ToString::to_string).collect(),
792 )
793 }
794}
795
796impl<'caller, 'buf, K: Kind> Iterator for IntoGroupByElem<'caller, 'buf, K> {
797 type Item = IntoGroup<'caller, 'buf, K>;
798
799 fn next(&mut self) -> Option<Self::Item> {
800 let warning = self.warnings.next()?;
802
803 let element = loop {
805 let Some(element) = self.walker.next() else {
806 error!("An Element with id: `{}` was not found", warning.elem_id);
807 return None;
808 };
809
810 if element.id() < warning.elem_id {
811 continue;
814 }
815
816 if element.id() > warning.elem_id {
817 debug_assert!(
818 element.id() <= warning.elem_id,
819 "The elements or the warnings are not sorted."
820 );
821 return None;
822 }
823
824 break element;
826 };
827
828 let mut warnings_grouped = vec![warning.into_kind()];
830
831 loop {
833 let warning = self.warnings.next_if(|w| w.elem_id == element.id());
834 let Some(warning) = warning else {
835 break;
836 };
837
838 warnings_grouped.push(warning.into_kind());
839 }
840
841 Some(IntoGroup {
842 element,
843 warnings: warnings_grouped,
844 })
845 }
846}
847
848pub struct GroupByElem<'caller, 'buf, K>
850where
851 K: Kind,
852{
853 walker: json::walk::DepthFirst<'caller, 'buf>,
855
856 warnings: iter::Peekable<vec::IntoIter<&'caller Warning<K>>>,
858}
859
860impl<K> GroupByElem<'_, '_, K>
861where
862 K: Kind,
863{
864 pub fn into_id_map(self) -> BTreeMap<String, Vec<String>> {
873 self.map(Group::into_str_ids).collect()
874 }
875
876 pub fn into_msg_map(self) -> BTreeMap<String, Vec<String>> {
882 self.map(Group::into_str_msgs).collect()
883 }
884}
885
886#[derive(Debug)]
891pub struct Group<'caller, 'buf, K> {
892 pub element: &'caller json::Element<'buf>,
894
895 pub warnings: Vec<&'caller K>,
897}
898
899impl<K> Group<'_, '_, K>
900where
901 K: Kind,
902{
903 pub fn into_str_ids(self) -> (String, Vec<String>) {
908 let Self { element, warnings } = self;
909 (
910 element.path().to_string(),
911 warnings.iter().map(|kind| kind.id().to_string()).collect(),
912 )
913 }
914
915 pub fn into_str_msgs(self) -> (String, Vec<String>) {
920 let Self { element, warnings } = self;
921 (
922 element.path().to_string(),
923 warnings.iter().map(ToString::to_string).collect(),
924 )
925 }
926}
927
928impl<'caller, 'buf, K: Kind> Iterator for GroupByElem<'caller, 'buf, K> {
929 type Item = Group<'caller, 'buf, K>;
930
931 fn next(&mut self) -> Option<Self::Item> {
932 let warning = self.warnings.next()?;
934
935 let element = loop {
937 let Some(element) = self.walker.next() else {
938 error!("An Element with id: `{}` was not found", warning.elem_id);
939 return None;
940 };
941
942 if element.id() < warning.elem_id {
943 continue;
946 }
947
948 if element.id() > warning.elem_id {
949 debug_assert!(
950 element.id() <= warning.elem_id,
951 "The elements or the warnings are not sorted."
952 );
953 return None;
954 }
955
956 break element;
958 };
959
960 let mut warnings_grouped = vec![warning.kind()];
962
963 loop {
965 let warning = self.warnings.next_if(|w| w.elem_id == element.id());
966 let Some(warning) = warning else {
967 break;
968 };
969
970 warnings_grouped.push(warning.kind());
971 }
972
973 Some(Group {
974 element,
975 warnings: warnings_grouped,
976 })
977 }
978}
979
980#[cfg(test)]
981pub(crate) mod test {
982 use std::{
983 borrow::Cow,
984 collections::{BTreeMap, BTreeSet},
985 ops, slice,
986 };
987
988 use assert_matches::assert_matches;
989
990 use crate::{
991 json,
992 test::{ExpectValue, Expectation},
993 };
994
995 use super::{Caveat, Group, Kind, Set, Warning};
996
997 impl<K: Kind> Set<K> {
998 pub fn into_vec(self) -> Vec<Warning<K>> {
1000 self.0
1001 }
1002
1003 pub fn into_parts_vec(self) -> Vec<(K, json::ElemId)> {
1004 self.0
1005 .into_iter()
1006 .map(|Warning { kind, elem_id }| (kind, elem_id))
1007 .collect()
1008 }
1009
1010 pub fn into_kind_vec(self) -> Vec<K> {
1014 self.0.into_iter().map(Warning::into_kind).collect()
1015 }
1016
1017 pub fn as_slice(&self) -> &[Warning<K>] {
1019 self.0.as_slice()
1020 }
1021
1022 pub fn iter(&self) -> slice::Iter<'_, Warning<K>> {
1024 self.0.iter()
1025 }
1026 }
1027
1028 impl<K: Kind> ops::Deref for Set<K> {
1029 type Target = [Warning<K>];
1030
1031 fn deref(&self) -> &[Warning<K>] {
1032 self.as_slice()
1033 }
1034 }
1035
1036 impl<K: Kind> IntoIterator for Set<K> {
1037 type Item = Warning<K>;
1038
1039 type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
1040
1041 fn into_iter(self) -> Self::IntoIter {
1042 self.0.into_iter()
1043 }
1044 }
1045
1046 impl<'a, K: Kind> IntoIterator for &'a Set<K> {
1047 type Item = &'a Warning<K>;
1048
1049 type IntoIter = slice::Iter<'a, Warning<K>>;
1050
1051 fn into_iter(self) -> Self::IntoIter {
1052 self.0.iter()
1053 }
1054 }
1055
1056 impl<T, K> Caveat<T, K>
1057 where
1058 K: Kind,
1059 {
1060 pub fn unwrap(self) -> T {
1062 let Self { value, warnings } = self;
1063 assert_matches!(warnings.into_vec().as_slice(), []);
1064 value
1065 }
1066 }
1067
1068 pub(crate) fn assert_warnings<K>(
1072 expect_file_name: &str,
1073 root: &json::Element<'_>,
1074 warnings: &Set<K>,
1075 expected: Expectation<BTreeMap<String, Vec<String>>>,
1076 ) where
1077 K: Kind,
1078 {
1079 let Expectation::Present(ExpectValue::Some(expected)) = expected else {
1080 assert!(
1081 warnings.is_empty(),
1082 "There is no `warnings` field in the `{expect_file_name}` file but the tariff has warnings;\n{:?}",
1083 warnings.group_by_elem(root).into_id_map()
1084 );
1085 return;
1086 };
1087
1088 {
1089 let warnings_grouped = warnings
1091 .group_by_elem(root)
1092 .map(|Group { element, warnings }| (element.path().to_string(), warnings))
1093 .collect::<BTreeMap<_, _>>();
1094
1095 let mut elems_in_expect_without_warning = vec![];
1096
1097 for elem_path in expected.keys() {
1098 if !warnings_grouped.contains_key(elem_path) {
1099 elems_in_expect_without_warning.push(elem_path);
1100 }
1101 }
1102
1103 assert!(elems_in_expect_without_warning.is_empty(),
1104 "The expect file `{expect_file_name}` has entries for elements that have no warnings:\n\
1105 {elems_in_expect_without_warning:#?}"
1106 );
1107 }
1108
1109 let mut elems_missing_from_expect = vec![];
1111 let mut unequal_warnings = vec![];
1114
1115 for group in warnings.group_by_elem(root) {
1116 let path_str = group.element.path().to_string();
1117 let Some(warnings_expected) = expected.get(&*path_str) else {
1118 elems_missing_from_expect.push(group);
1119 continue;
1120 };
1121
1122 let warnings_expected = warnings_expected
1124 .iter()
1125 .map(|s| Cow::Borrowed(&**s))
1126 .collect::<BTreeSet<_>>();
1127 let warnings = group
1128 .warnings
1129 .iter()
1130 .map(|w| w.id())
1131 .collect::<BTreeSet<_>>();
1132
1133 if warnings_expected != warnings {
1134 unequal_warnings.push(group);
1135 }
1136 }
1137
1138 if !elems_missing_from_expect.is_empty() || !unequal_warnings.is_empty() {
1139 let missing = elems_missing_from_expect
1140 .into_iter()
1141 .map(Group::into_str_ids)
1142 .collect::<BTreeMap<_, _>>();
1143
1144 let unequal = unequal_warnings
1145 .into_iter()
1146 .map(Group::into_str_ids)
1147 .collect::<BTreeMap<_, _>>();
1148
1149 match (!missing.is_empty(), !unequal.is_empty()) {
1150 (true, true) => panic!(
1151 "Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}\n\
1152 Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1153 The warnings reported are: \n{unequal:#?}"
1154 ),
1155 (true, false) => panic!("Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}"),
1156 (false, true) => panic!(
1157 "Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1158 The warnings reported are: \n{unequal:#?}"
1159 ),
1160 (false, false) => (),
1161 }
1162 }
1163 }
1164}
1165
1166#[cfg(test)]
1167mod test_group_by_elem {
1168 use std::fmt;
1169
1170 use assert_matches::assert_matches;
1171
1172 use crate::{json, test};
1173
1174 use super::{Group, IntoGroup, Kind, Set, Warning};
1175
1176 const JSON: &str = r#"{
1177 "field_one#": "one",
1178 "field_two": "two",
1179 "field_three": "three"
1180}"#;
1181
1182 #[derive(Debug)]
1183 enum WarningKind {
1184 Root,
1185 One,
1186 OneAgain,
1187 Three,
1188 }
1189
1190 impl Kind for WarningKind {
1191 fn id(&self) -> std::borrow::Cow<'static, str> {
1192 match self {
1193 WarningKind::Root => "root".into(),
1194 WarningKind::One => "one".into(),
1195 WarningKind::OneAgain => "one_again".into(),
1196 WarningKind::Three => "three".into(),
1197 }
1198 }
1199 }
1200
1201 impl fmt::Display for WarningKind {
1202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1203 match self {
1204 WarningKind::Root => write!(f, "NopeRoot"),
1205 WarningKind::One => write!(f, "NopeOne"),
1206 WarningKind::OneAgain => write!(f, "NopeOneAgain"),
1207 WarningKind::Three => write!(f, "NopeThree"),
1208 }
1209 }
1210 }
1211
1212 #[test]
1213 fn should_group_by_elem() {
1214 test::setup();
1215
1216 let elem = parse(JSON);
1217 let mut warnings = Set::<WarningKind>::new();
1218
1219 warnings.push(Warning {
1222 kind: WarningKind::Root,
1223 elem_id: json::ElemId::from(0),
1224 });
1225 warnings.push(Warning {
1226 kind: WarningKind::One,
1227 elem_id: json::ElemId::from(1),
1228 });
1229 warnings.push(Warning {
1230 kind: WarningKind::Three,
1231 elem_id: json::ElemId::from(3),
1232 });
1233 warnings.push(Warning {
1234 kind: WarningKind::OneAgain,
1235 elem_id: json::ElemId::from(1),
1236 });
1237
1238 let mut iter = warnings.group_by_elem(&elem);
1239
1240 {
1241 let Group { element, warnings } = iter.next().unwrap();
1242
1243 assert!(
1244 element.value().is_object(),
1245 "The root object should be emitted first"
1246 );
1247 assert_eq!(
1248 element.id(),
1249 json::ElemId::from(0),
1250 "The root object should be emitted first"
1251 );
1252 assert_matches!(warnings.as_slice(), [WarningKind::Root]);
1253 }
1254
1255 {
1256 let Group { element, warnings } = iter.next().unwrap();
1257
1258 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "one");
1259 assert_eq!(element.id(), json::ElemId::from(1));
1260 assert_matches!(
1261 warnings.as_slice(),
1262 [WarningKind::One, WarningKind::OneAgain],
1263 "[`json::Element`] 1 should have two warnings"
1264 );
1265 }
1266
1267 {
1268 let Group { element, warnings } = iter.next().unwrap();
1270
1271 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "three");
1272 assert_eq!(element.id(), json::ElemId::from(3));
1273 assert_matches!(warnings.as_slice(), [WarningKind::Three]);
1274 }
1275 }
1276
1277 #[test]
1278 fn should_into_group_by_elem() {
1279 test::setup();
1280
1281 let elem = parse(JSON);
1282 let mut warnings = Set::<WarningKind>::new();
1283
1284 warnings.push(Warning {
1287 kind: WarningKind::Root,
1288 elem_id: json::ElemId::from(0),
1289 });
1290 warnings.push(Warning {
1291 kind: WarningKind::Three,
1292 elem_id: json::ElemId::from(3),
1293 });
1294 warnings.push(Warning {
1295 kind: WarningKind::One,
1296 elem_id: json::ElemId::from(1),
1297 });
1298 warnings.push(Warning {
1299 kind: WarningKind::OneAgain,
1300 elem_id: json::ElemId::from(1),
1301 });
1302
1303 let mut iter = warnings.into_group_by_elem(&elem);
1304
1305 {
1306 let IntoGroup { element, warnings } = iter.next().unwrap();
1307
1308 assert!(
1309 element.value().is_object(),
1310 "The root object should be emitted first"
1311 );
1312 assert_eq!(
1313 element.id(),
1314 json::ElemId::from(0),
1315 "The root object should be emitted first"
1316 );
1317 assert_matches!(warnings.as_slice(), [WarningKind::Root]);
1318 }
1319
1320 {
1321 let IntoGroup { element, warnings } = iter.next().unwrap();
1322
1323 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "one");
1324 assert_eq!(element.id(), json::ElemId::from(1));
1325 assert_matches!(
1326 warnings.as_slice(),
1327 [WarningKind::One, WarningKind::OneAgain],
1328 "[`json::Element`] 1 should have two warnings"
1329 );
1330 }
1331
1332 {
1333 let IntoGroup { element, warnings } = iter.next().unwrap();
1335
1336 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "three");
1337 assert_eq!(element.id(), json::ElemId::from(3));
1338 assert_matches!(warnings.as_slice(), [WarningKind::Three]);
1339 }
1340 }
1341
1342 fn parse(json: &str) -> json::Element<'_> {
1343 json::parse(json).unwrap()
1344 }
1345}