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
356pub trait VerdictExt<T, K: Kind> {
358 fn map_caveat<F, U>(self, op: F) -> Verdict<U, K>
361 where
362 F: FnOnce(T) -> U;
363}
364
365impl<T, K: Kind> VerdictExt<T, K> for Verdict<T, K>
366where
367 T: IntoCaveat,
368{
369 fn map_caveat<F, U>(self, op: F) -> Verdict<U, K>
370 where
371 F: FnOnce(T) -> U,
372 {
373 match self {
374 Ok(c) => Ok(c.map(op)),
375 Err(w) => Err(w),
376 }
377 }
378}
379
380#[macro_export]
385#[doc(hidden)]
386macro_rules! from_warning_set_to {
387 ($kind_a:path => $kind_b:path) => {
388 impl From<$crate::warning::Set<$kind_a>> for $crate::warning::Set<$kind_b> {
389 fn from(set_a: warning::Set<$kind_a>) -> Self {
390 set_a.into_set()
391 }
392 }
393 };
394}
395
396pub trait OptionExt<T, K>
398where
399 K: Kind,
400{
401 fn exit_with_warning<F>(self, warnings: Set<K>, f: F) -> Verdict<T, K>
403 where
404 F: FnOnce() -> Warning<K>;
405}
406
407impl<T, K> OptionExt<T, K> for Option<T>
408where
409 T: IntoCaveat,
410 K: Kind,
411{
412 fn exit_with_warning<F>(self, mut warnings: Set<K>, f: F) -> Verdict<T, K>
413 where
414 F: FnOnce() -> Warning<K>,
415 {
416 if let Some(v) = self {
417 Ok(v.into_caveat(warnings))
418 } else {
419 warnings.push(f());
420 Err(warnings)
421 }
422 }
423}
424
425#[derive(Debug)]
429pub struct Warning<K: Kind> {
430 kind: K,
432
433 elem_id: json::ElemId,
435}
436
437pub struct SetWriter<'caller, 'buf, K: Kind> {
451 root_elem: &'caller json::Element<'buf>,
453
454 warnings: &'caller Set<K>,
456
457 indent: &'caller str,
459}
460
461impl<'caller, 'buf, K: Kind> SetWriter<'caller, 'buf, K> {
462 pub fn new(root_elem: &'caller json::Element<'buf>, warnings: &'caller Set<K>) -> Self {
464 Self {
465 root_elem,
466 warnings,
467 indent: " - ",
468 }
469 }
470
471 pub fn with_indent(
473 root_elem: &'caller json::Element<'buf>,
474 warnings: &'caller Set<K>,
475 indent: &'caller str,
476 ) -> Self {
477 Self {
478 root_elem,
479 warnings,
480 indent,
481 }
482 }
483}
484
485impl<K: Kind> fmt::Debug for SetWriter<'_, '_, K> {
486 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487 fmt::Display::fmt(self, f)
488 }
489}
490
491impl<K: Kind> fmt::Display for SetWriter<'_, '_, K> {
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 let mut iter = self.warnings.group_by_elem(self.root_elem);
494
495 {
496 let Some(Group { element, warnings }) = iter.next() else {
498 return Ok(());
499 };
500
501 writeln!(f, "{}", element.path())?;
502
503 for warning in warnings {
504 write!(f, "{}{}", self.indent, warning)?;
505 }
506 }
507
508 for Group { element, warnings } in iter {
510 writeln!(f, "\n{}", element.path())?;
511
512 for warning in warnings {
513 write!(f, "{}{}", self.indent, warning)?;
514 }
515 }
516
517 Ok(())
518 }
519}
520
521impl<K: Kind> Warning<K> {
522 pub(crate) const fn with_elem(kind: K, elem: &json::Element<'_>) -> Warning<K> {
526 Warning {
527 kind,
528 elem_id: elem.id(),
529 }
530 }
531
532 pub fn id(&self) -> Cow<'static, str> {
534 self.kind.id()
535 }
536
537 pub fn kind(&self) -> &K {
539 &self.kind
540 }
541
542 pub fn into_kind(self) -> K {
544 self.kind
545 }
546}
547
548impl<K: Kind> fmt::Display for Warning<K> {
549 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550 write!(f, "{}", self.kind)
551 }
552}
553
554pub trait Kind: Sized + fmt::Debug + fmt::Display {
558 fn id(&self) -> Cow<'static, str>;
563}
564
565#[derive(Debug)]
567pub struct Set<K: Kind>(Vec<Warning<K>>);
568
569impl<K: Kind> Set<K> {
570 pub(crate) fn new() -> Self {
572 Self(vec![])
573 }
574
575 pub(crate) fn from_vec(warnings: Vec<Warning<K>>) -> Self {
576 Self(warnings)
577 }
578
579 fn push(&mut self, warning: Warning<K>) {
581 self.0.push(warning);
582 }
583
584 pub(crate) fn with_elem(&mut self, kind: K, elem: &json::Element<'_>) {
586 self.push(Warning::with_elem(kind, elem));
587 }
588
589 pub(crate) fn into_set<KB>(self) -> Set<KB>
593 where
594 KB: Kind + From<K>,
595 {
596 let warnings = self
597 .0
598 .into_iter()
599 .map(|warn| {
600 let Warning { kind, elem_id } = warn;
601 Warning {
602 kind: kind.into(),
603 elem_id,
604 }
605 })
606 .collect();
607
608 Set(warnings)
609 }
610
611 pub(crate) fn map_warning<KU>(self) -> Set<KU>
615 where
616 KU: Kind + From<K>,
617 {
618 let warnings = self
619 .0
620 .into_iter()
621 .map(|warn| {
622 let Warning { kind, elem_id } = warn;
623 Warning {
624 kind: kind.into(),
625 elem_id,
626 }
627 })
628 .collect();
629
630 Set(warnings)
631 }
632
633 pub(crate) fn extend<KA>(&mut self, warnings: Set<KA>)
637 where
638 KA: Into<K> + Kind,
639 {
640 self.0.extend(warnings.0.into_iter().map(|warn| {
641 let Warning { kind, elem_id } = warn;
642 Warning {
643 kind: kind.into(),
644 elem_id,
645 }
646 }));
647 }
648
649 pub fn is_empty(&self) -> bool {
651 self.0.is_empty()
652 }
653
654 pub fn len(&self) -> usize {
656 self.0.len()
657 }
658
659 pub fn group_by_elem<'caller: 'buf, 'buf>(
663 &'caller self,
664 root: &'caller json::Element<'buf>,
665 ) -> GroupByElem<'caller, 'buf, K> {
666 let mut warnings = self.0.iter().collect::<Vec<_>>();
667 warnings.sort_unstable_by_key(|warning| warning.elem_id);
668
669 GroupByElem {
670 walker: json::walk::DepthFirst::new(root),
671 warnings: warnings.into_iter().peekable(),
672 }
673 }
674
675 pub fn into_group_by_elem<'caller, 'buf>(
679 self,
680 root: &'caller json::Element<'buf>,
681 ) -> IntoGroupByElem<'caller, 'buf, K> {
682 let Self(mut warnings) = self;
683 warnings.sort_unstable_by_key(|warning| warning.elem_id);
684
685 IntoGroupByElem {
686 walker: json::walk::DepthFirst::new(root),
687 warnings: warnings.into_iter().peekable(),
688 }
689 }
690}
691
692pub struct IntoGroupByElem<'caller, 'buf, K>
694where
695 K: Kind,
696{
697 walker: json::walk::DepthFirst<'caller, 'buf>,
699
700 warnings: iter::Peekable<vec::IntoIter<Warning<K>>>,
702}
703
704#[derive(Debug)]
709pub struct IntoGroup<'caller, 'buf, K> {
710 pub element: &'caller json::Element<'buf>,
712
713 pub warnings: Vec<K>,
715}
716
717impl<'caller, 'buf, K: Kind> Iterator for IntoGroupByElem<'caller, 'buf, K> {
718 type Item = IntoGroup<'caller, 'buf, K>;
719
720 fn next(&mut self) -> Option<Self::Item> {
721 let warning = self.warnings.next()?;
723
724 let element = loop {
726 let Some(element) = self.walker.next() else {
727 error!("An Element with id: `{}` was not found", warning.elem_id);
728 return None;
729 };
730
731 if element.id() < warning.elem_id {
732 continue;
735 }
736
737 if element.id() > warning.elem_id {
738 debug_assert!(
739 element.id() <= warning.elem_id,
740 "The elements or the warnings are not sorted."
741 );
742 return None;
743 }
744
745 break element;
747 };
748
749 let mut warnings_grouped = vec![warning.into_kind()];
751
752 loop {
754 let warning = self.warnings.next_if(|w| w.elem_id == element.id());
755 let Some(warning) = warning else {
756 break;
757 };
758
759 warnings_grouped.push(warning.into_kind());
760 }
761
762 Some(IntoGroup {
763 element,
764 warnings: warnings_grouped,
765 })
766 }
767}
768
769pub struct GroupByElem<'caller, 'buf, K>
771where
772 K: Kind,
773{
774 walker: json::walk::DepthFirst<'caller, 'buf>,
776
777 warnings: iter::Peekable<vec::IntoIter<&'caller Warning<K>>>,
779}
780
781impl<K> GroupByElem<'_, '_, K>
782where
783 K: Kind,
784{
785 pub fn into_id_map(self) -> BTreeMap<String, Vec<String>> {
794 self.map(Group::into_str_ids).collect()
795 }
796
797 pub fn into_msg_map(self) -> BTreeMap<String, Vec<String>> {
803 self.map(Group::into_str_msgs).collect()
804 }
805}
806
807#[derive(Debug)]
812pub struct Group<'caller, 'buf, K> {
813 pub element: &'caller json::Element<'buf>,
815
816 pub warnings: Vec<&'caller K>,
818}
819
820impl<K> Group<'_, '_, K>
821where
822 K: Kind,
823{
824 pub fn into_str_ids(self) -> (String, Vec<String>) {
829 let Self { element, warnings } = self;
830 (
831 element.path().to_string(),
832 warnings.iter().map(|kind| kind.id().to_string()).collect(),
833 )
834 }
835
836 pub fn into_str_msgs(self) -> (String, Vec<String>) {
841 let Self { element, warnings } = self;
842 (
843 element.path().to_string(),
844 warnings.iter().map(ToString::to_string).collect(),
845 )
846 }
847}
848
849impl<'caller, 'buf, K: Kind> Iterator for GroupByElem<'caller, 'buf, K> {
850 type Item = Group<'caller, 'buf, K>;
851
852 fn next(&mut self) -> Option<Self::Item> {
853 let warning = self.warnings.next()?;
855
856 let element = loop {
858 let Some(element) = self.walker.next() else {
859 error!("An Element with id: `{}` was not found", warning.elem_id);
860 return None;
861 };
862
863 if element.id() < warning.elem_id {
864 continue;
867 }
868
869 if element.id() > warning.elem_id {
870 debug_assert!(
871 element.id() <= warning.elem_id,
872 "The elements or the warnings are not sorted."
873 );
874 return None;
875 }
876
877 break element;
879 };
880
881 let mut warnings_grouped = vec![warning.kind()];
883
884 loop {
886 let warning = self.warnings.next_if(|w| w.elem_id == element.id());
887 let Some(warning) = warning else {
888 break;
889 };
890
891 warnings_grouped.push(warning.kind());
892 }
893
894 Some(Group {
895 element,
896 warnings: warnings_grouped,
897 })
898 }
899}
900
901#[cfg(test)]
902pub(crate) mod test {
903 use std::{
904 borrow::Cow,
905 collections::{BTreeMap, BTreeSet},
906 ops, slice,
907 };
908
909 use assert_matches::assert_matches;
910
911 use crate::{
912 json,
913 test::{ExpectValue, Expectation},
914 };
915
916 use super::{Caveat, Group, Kind, Set, Warning};
917
918 impl<K: Kind> Set<K> {
919 pub fn into_vec(self) -> Vec<Warning<K>> {
921 self.0
922 }
923
924 pub fn into_parts_vec(self) -> Vec<(K, json::ElemId)> {
925 self.0
926 .into_iter()
927 .map(|Warning { kind, elem_id }| (kind, elem_id))
928 .collect()
929 }
930
931 pub fn into_kind_vec(self) -> Vec<K> {
935 self.0.into_iter().map(Warning::into_kind).collect()
936 }
937
938 pub fn as_slice(&self) -> &[Warning<K>] {
940 self.0.as_slice()
941 }
942
943 pub fn iter(&self) -> slice::Iter<'_, Warning<K>> {
945 self.0.iter()
946 }
947 }
948
949 impl<K: Kind> ops::Deref for Set<K> {
950 type Target = [Warning<K>];
951
952 fn deref(&self) -> &[Warning<K>] {
953 self.as_slice()
954 }
955 }
956
957 impl<K: Kind> IntoIterator for Set<K> {
958 type Item = Warning<K>;
959
960 type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
961
962 fn into_iter(self) -> Self::IntoIter {
963 self.0.into_iter()
964 }
965 }
966
967 impl<'a, K: Kind> IntoIterator for &'a Set<K> {
968 type Item = &'a Warning<K>;
969
970 type IntoIter = slice::Iter<'a, Warning<K>>;
971
972 fn into_iter(self) -> Self::IntoIter {
973 self.0.iter()
974 }
975 }
976
977 impl<T, K> Caveat<T, K>
978 where
979 K: Kind,
980 {
981 pub fn unwrap(self) -> T {
983 let Self { value, warnings } = self;
984 assert_matches!(warnings.into_vec().as_slice(), []);
985 value
986 }
987 }
988
989 pub(crate) fn assert_warnings<K>(
993 expect_file_name: &str,
994 root: &json::Element<'_>,
995 warnings: &Set<K>,
996 expected: Expectation<BTreeMap<String, Vec<String>>>,
997 ) where
998 K: Kind,
999 {
1000 let Expectation::Present(ExpectValue::Some(expected)) = expected else {
1001 assert!(
1002 warnings.is_empty(),
1003 "There is no `warnings` field in the `{expect_file_name}` file but the tariff has warnings;\n{:?}",
1004 warnings.group_by_elem(root).into_id_map()
1005 );
1006 return;
1007 };
1008
1009 {
1010 let warnings_grouped = warnings
1012 .group_by_elem(root)
1013 .map(|Group { element, warnings }| (element.path().to_string(), warnings))
1014 .collect::<BTreeMap<_, _>>();
1015
1016 let mut elems_in_expect_without_warning = vec![];
1017
1018 for elem_path in expected.keys() {
1019 if !warnings_grouped.contains_key(elem_path) {
1020 elems_in_expect_without_warning.push(elem_path);
1021 }
1022 }
1023
1024 assert!(elems_in_expect_without_warning.is_empty(),
1025 "The expect file `{expect_file_name}` has entries for elements that have no warnings:\n\
1026 {elems_in_expect_without_warning:#?}"
1027 );
1028 }
1029
1030 let mut elems_missing_from_expect = vec![];
1032 let mut unequal_warnings = vec![];
1035
1036 for group in warnings.group_by_elem(root) {
1037 let path_str = group.element.path().to_string();
1038 let Some(warnings_expected) = expected.get(&*path_str) else {
1039 elems_missing_from_expect.push(group);
1040 continue;
1041 };
1042
1043 let warnings_expected = warnings_expected
1045 .iter()
1046 .map(|s| Cow::Borrowed(&**s))
1047 .collect::<BTreeSet<_>>();
1048 let warnings = group
1049 .warnings
1050 .iter()
1051 .map(|w| w.id())
1052 .collect::<BTreeSet<_>>();
1053
1054 if warnings_expected != warnings {
1055 unequal_warnings.push(group);
1056 }
1057 }
1058
1059 if !elems_missing_from_expect.is_empty() || !unequal_warnings.is_empty() {
1060 let missing = elems_missing_from_expect
1061 .into_iter()
1062 .map(Group::into_str_ids)
1063 .collect::<BTreeMap<_, _>>();
1064
1065 let unequal = unequal_warnings
1066 .into_iter()
1067 .map(Group::into_str_ids)
1068 .collect::<BTreeMap<_, _>>();
1069
1070 match (!missing.is_empty(), !unequal.is_empty()) {
1071 (true, true) => panic!(
1072 "Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}\n\
1073 Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1074 The warnings reported are: \n{unequal:#?}"
1075 ),
1076 (true, false) => panic!("Elements with warnings but are not defined in the `{expect_file_name}` file:\n{missing:#?}"),
1077 (false, true) => panic!(
1078 "Elements that are in the `{expect_file_name}` file but the warnings list is not correct.\n\
1079 The warnings reported are: \n{unequal:#?}"
1080 ),
1081 (false, false) => (),
1082 }
1083 }
1084 }
1085}
1086
1087#[cfg(test)]
1088mod test_group_by_elem {
1089 use std::fmt;
1090
1091 use assert_matches::assert_matches;
1092
1093 use crate::{json, test};
1094
1095 use super::{Group, IntoGroup, Kind, Set, Warning};
1096
1097 const JSON: &str = r#"{
1098 "field_one#": "one",
1099 "field_two": "two",
1100 "field_three": "three"
1101}"#;
1102
1103 #[derive(Debug)]
1104 enum WarningKind {
1105 Root,
1106 One,
1107 OneAgain,
1108 Three,
1109 }
1110
1111 impl Kind for WarningKind {
1112 fn id(&self) -> std::borrow::Cow<'static, str> {
1113 match self {
1114 WarningKind::Root => "root".into(),
1115 WarningKind::One => "one".into(),
1116 WarningKind::OneAgain => "one_again".into(),
1117 WarningKind::Three => "three".into(),
1118 }
1119 }
1120 }
1121
1122 impl fmt::Display for WarningKind {
1123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1124 match self {
1125 WarningKind::Root => write!(f, "NopeRoot"),
1126 WarningKind::One => write!(f, "NopeOne"),
1127 WarningKind::OneAgain => write!(f, "NopeOneAgain"),
1128 WarningKind::Three => write!(f, "NopeThree"),
1129 }
1130 }
1131 }
1132
1133 #[test]
1134 fn should_group_by_elem() {
1135 test::setup();
1136
1137 let elem = parse(JSON);
1138 let mut warnings = Set::<WarningKind>::new();
1139
1140 warnings.push(Warning {
1143 kind: WarningKind::Root,
1144 elem_id: json::ElemId::from(0),
1145 });
1146 warnings.push(Warning {
1147 kind: WarningKind::One,
1148 elem_id: json::ElemId::from(1),
1149 });
1150 warnings.push(Warning {
1151 kind: WarningKind::Three,
1152 elem_id: json::ElemId::from(3),
1153 });
1154 warnings.push(Warning {
1155 kind: WarningKind::OneAgain,
1156 elem_id: json::ElemId::from(1),
1157 });
1158
1159 let mut iter = warnings.group_by_elem(&elem);
1160
1161 {
1162 let Group { element, warnings } = iter.next().unwrap();
1163
1164 assert!(
1165 element.value().is_object(),
1166 "The root object should be emitted first"
1167 );
1168 assert_eq!(
1169 element.id(),
1170 json::ElemId::from(0),
1171 "The root object should be emitted first"
1172 );
1173 assert_matches!(warnings.as_slice(), [WarningKind::Root]);
1174 }
1175
1176 {
1177 let Group { element, warnings } = iter.next().unwrap();
1178
1179 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "one");
1180 assert_eq!(element.id(), json::ElemId::from(1));
1181 assert_matches!(
1182 warnings.as_slice(),
1183 [WarningKind::One, WarningKind::OneAgain],
1184 "[`json::Element`] 1 should have two warnings"
1185 );
1186 }
1187
1188 {
1189 let Group { element, warnings } = iter.next().unwrap();
1191
1192 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "three");
1193 assert_eq!(element.id(), json::ElemId::from(3));
1194 assert_matches!(warnings.as_slice(), [WarningKind::Three]);
1195 }
1196 }
1197
1198 #[test]
1199 fn should_into_group_by_elem() {
1200 test::setup();
1201
1202 let elem = parse(JSON);
1203 let mut warnings = Set::<WarningKind>::new();
1204
1205 warnings.push(Warning {
1208 kind: WarningKind::Root,
1209 elem_id: json::ElemId::from(0),
1210 });
1211 warnings.push(Warning {
1212 kind: WarningKind::Three,
1213 elem_id: json::ElemId::from(3),
1214 });
1215 warnings.push(Warning {
1216 kind: WarningKind::One,
1217 elem_id: json::ElemId::from(1),
1218 });
1219 warnings.push(Warning {
1220 kind: WarningKind::OneAgain,
1221 elem_id: json::ElemId::from(1),
1222 });
1223
1224 let mut iter = warnings.into_group_by_elem(&elem);
1225
1226 {
1227 let IntoGroup { element, warnings } = iter.next().unwrap();
1228
1229 assert!(
1230 element.value().is_object(),
1231 "The root object should be emitted first"
1232 );
1233 assert_eq!(
1234 element.id(),
1235 json::ElemId::from(0),
1236 "The root object should be emitted first"
1237 );
1238 assert_matches!(warnings.as_slice(), [WarningKind::Root]);
1239 }
1240
1241 {
1242 let IntoGroup { element, warnings } = iter.next().unwrap();
1243
1244 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "one");
1245 assert_eq!(element.id(), json::ElemId::from(1));
1246 assert_matches!(
1247 warnings.as_slice(),
1248 [WarningKind::One, WarningKind::OneAgain],
1249 "[`json::Element`] 1 should have two warnings"
1250 );
1251 }
1252
1253 {
1254 let IntoGroup { element, warnings } = iter.next().unwrap();
1256
1257 assert_eq!(element.value().as_raw_str().unwrap().as_raw(), "three");
1258 assert_eq!(element.id(), json::ElemId::from(3));
1259 assert_matches!(warnings.as_slice(), [WarningKind::Three]);
1260 }
1261 }
1262
1263 fn parse(json: &str) -> json::Element<'_> {
1264 json::parse(json).unwrap()
1265 }
1266}