1#![feature(
19 decl_macro,
20 try_trait_v2,
21 extend_one,
22 debug_closure_helpers,
23 try_blocks,
24 never_type,
25 unwrap_infallible
26)]
27#![cfg_attr(test, feature(assert_matches, coverage_attribute, yeet_expr))]
28
29mod multiple;
31mod pretty;
32
33pub use self::{multiple::AllErrs, pretty::PrettyDisplay};
35
36use {
38 core::{mem, slice},
39 std::{
40 borrow::Cow,
41 error::Error as StdError,
42 fmt,
43 hash::{Hash, Hasher},
44 sync::Arc,
45 },
46};
47
48#[derive(Clone)]
50enum Inner<D> {
51 Single {
53 msg: Cow<'static, str>,
55
56 source: Option<AppError<D>>,
58
59 data: D,
61 },
62
63 Multiple(Box<[AppError<D>]>),
65}
66
67impl<D> StdError for Inner<D>
68where
69 D: fmt::Debug + 'static,
70{
71 fn source(&self) -> Option<&(dyn StdError + 'static)> {
72 match self {
73 Self::Single { source, .. } => source.as_ref().map(AppError::as_std_error),
74 Self::Multiple(errs) => errs.first().map(AppError::as_std_error),
76 }
77 }
78}
79
80impl<D> PartialEq for Inner<D>
81where
82 D: PartialEq,
83{
84 fn eq(&self, other: &Self) -> bool {
85 match (self, other) {
86 (
87 Self::Single {
88 msg: lhs_msg,
89 source: lhs_source,
90 data: lhs_data,
91 },
92 Self::Single {
93 msg: rhs_msg,
94 source: rhs_source,
95 data: rhs_data,
96 },
97 ) => lhs_msg == rhs_msg && lhs_source == rhs_source && lhs_data == rhs_data,
98 (Self::Multiple(lhs), Self::Multiple(rhs)) => lhs == rhs,
99 _ => false,
100 }
101 }
102}
103
104impl<D> Hash for Inner<D>
105where
106 D: Hash,
107{
108 fn hash<H: Hasher>(&self, state: &mut H) {
109 mem::discriminant(self).hash(state);
110 match self {
111 Self::Single { msg, source, data } => {
112 msg.hash(state);
113 source.hash(state);
114 data.hash(state);
115 },
116 Self::Multiple(errs) => errs.hash(state),
117 }
118 }
119}
120
121impl<D> fmt::Display for Inner<D> {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 match self {
124 Self::Single { msg, .. } => msg.fmt(f),
125 Self::Multiple(errs) => write!(f, "Multiple errors ({})", errs.len()),
126 }
127 }
128}
129
130impl<D> fmt::Debug for Inner<D>
131where
132 D: fmt::Debug + 'static,
133{
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 match f.alternate() {
136 true => match self {
138 Self::Single { msg, source, data } => f
139 .debug_struct("AppError")
140 .field("msg", msg)
141 .field("source", source)
142 .field("data", data)
143 .finish(),
144 Self::Multiple(errs) => f.debug_list().entries(errs).finish(),
145 },
146
147 false => write!(f, "{}", PrettyDisplay::new(self)),
149 }
150 }
151}
152
153pub struct AppError<D = ()> {
158 inner: Arc<Inner<D>>,
160}
161
162impl<D> AppError<D> {
163 pub fn new<E>(err: &E) -> Self
165 where
166 E: ?Sized + StdError,
167 D: Default,
168 {
169 Self {
170 inner: Arc::new(Inner::Single {
171 msg: err.to_string().into(),
172 source: err.source().map(Self::new),
173 data: D::default(),
174 }),
175 }
176 }
177
178 #[must_use]
180 pub fn msg(msg: &'static str) -> Self
181 where
182 D: Default,
183 {
184 Self {
185 inner: Arc::new(Inner::Single {
186 msg: msg.into(),
187 source: None,
188 data: D::default(),
189 }),
190 }
191 }
192
193 pub fn fmt<M>(msg: M) -> Self
195 where
196 M: fmt::Display,
197 D: Default,
198 {
199 Self {
200 inner: Arc::new(Inner::Single {
201 msg: msg.to_string().into(),
202 source: None,
203 data: D::default(),
204 }),
205 }
206 }
207
208 #[must_use = "Creates a new error with context, without modifying the existing one"]
210 pub fn context(&self, msg: &'static str) -> Self
211 where
212 D: Default,
213 {
214 Self {
215 inner: Arc::new(Inner::Single {
216 msg: msg.into(),
217 source: Some(self.clone()),
218 data: D::default(),
219 }),
220 }
221 }
222
223 #[must_use = "Creates a new error with context, without modifying the existing one"]
225 pub fn with_context<F, M>(&self, with_msg: F) -> Self
226 where
227 F: FnOnce() -> M,
228 M: fmt::Display,
229 D: Default,
230 {
231 Self {
232 inner: Arc::new(Inner::Single {
233 msg: with_msg().to_string().into(),
234 source: Some(self.clone()),
235 data: D::default(),
236 }),
237 }
238 }
239
240 pub fn from_multiple<Errs>(errs: Errs) -> Self
242 where
243 Errs: IntoIterator<Item = Self>,
244 {
245 Self {
246 inner: Arc::new(Inner::Multiple(errs.into_iter().collect())),
247 }
248 }
249
250 pub fn from_multiple_std<'a, Errs, E>(errs: Errs) -> Self
252 where
253 Errs: IntoIterator<Item = &'a E>,
254 E: ?Sized + StdError + 'a,
255 D: Default,
256 {
257 Self {
258 inner: Arc::new(Inner::Multiple(errs.into_iter().map(Self::new).collect())),
259 }
260 }
261
262 #[must_use]
264 pub fn flatten(self) -> Self
265 where
266 D: Clone,
267 {
268 fn flatten_into<D: Clone>(err: AppError<D>, flattened_errs: &mut Vec<AppError<D>>) {
270 let err = err.flatten();
271 match &*err.inner {
272 Inner::Single { .. } => flattened_errs.push(err),
273 Inner::Multiple(errs) =>
274 for err in errs {
275 flatten_into(err.clone(), flattened_errs);
276 },
277 }
278 }
279
280 fn flatten_inner<D: Clone>(err: AppError<D>) -> Option<AppError<D>> {
281 match Arc::unwrap_or_clone(err.inner) {
282 Inner::Single { msg, source, data } => Some(AppError {
284 inner: Arc::new(Inner::Single {
285 msg,
286 source: source.and_then(flatten_inner),
287 data,
288 }),
289 }),
290
291 Inner::Multiple(errs) => {
293 let mut flattened_errs = vec![];
294 for err in errs {
295 flatten_into(err, &mut flattened_errs);
296 }
297
298 match <[_; 0]>::try_from(flattened_errs) {
299 Ok([]) => None,
300 Err(flattened_errs) => match <[_; 1]>::try_from(flattened_errs) {
301 Ok([err]) => Some(err),
302 Err(flattened_errs) => Some(AppError::from_multiple(flattened_errs)),
303 },
304 }
305 },
306 }
307 }
308
309 flatten_inner(self).unwrap_or_else(|| Self::from_multiple([]))
310 }
311
312 #[must_use]
314 pub fn as_std_error(&self) -> &(dyn StdError + 'static)
315 where
316 D: fmt::Debug + 'static,
317 {
318 &self.inner
319 }
320
321 #[must_use]
323 pub fn into_std_error(self) -> Arc<dyn StdError + Send + Sync + 'static>
324 where
325 D: fmt::Debug + Send + Sync + 'static,
326 {
327 self.inner as Arc<_>
328 }
329
330 #[must_use]
332 pub fn pretty(&self) -> PrettyDisplay<'_, D> {
333 PrettyDisplay::new(&self.inner)
334 }
335
336 #[must_use]
341 pub fn data_iter(&self) -> DataIter<'_, D> {
342 DataIter {
343 errs: vec![self],
344 cur_it: None,
345 }
346 }
347}
348
349impl<D> AppError<D> {
350 pub fn new_with_data<E>(err: &E, data: D) -> Self
354 where
355 E: ?Sized + StdError,
356 D: Clone,
357 {
358 Self {
359 inner: Arc::new(Inner::Single {
360 msg: err.to_string().into(),
361 source: err.source().map(|source| Self::new_with_data(source, data.clone())),
362 data,
363 }),
364 }
365 }
366
367 pub fn msg_with_data(msg: &'static str, data: D) -> Self {
369 Self {
370 inner: Arc::new(Inner::Single {
371 msg: msg.into(),
372 source: None,
373 data,
374 }),
375 }
376 }
377
378 pub fn fmt_with_data<M>(msg: M, data: D) -> Self
380 where
381 M: fmt::Display,
382 {
383 Self {
384 inner: Arc::new(Inner::Single {
385 msg: msg.to_string().into(),
386 source: None,
387 data,
388 }),
389 }
390 }
391
392 #[must_use = "Creates a new error with context, without modifying the existing one"]
394 pub fn context_with_data(&self, msg: &'static str, data: D) -> Self {
395 Self {
396 inner: Arc::new(Inner::Single {
397 msg: msg.to_string().into(),
398 source: Some(self.clone()),
399 data,
400 }),
401 }
402 }
403
404 #[must_use = "Creates a new error with context, without modifying the existing one"]
406 pub fn with_context_with_data<F, M>(&self, with_msg: F, data: D) -> Self
407 where
408 F: Fn() -> M,
409 M: fmt::Display,
410 {
411 Self {
412 inner: Arc::new(Inner::Single {
413 msg: with_msg().to_string().into(),
414 source: Some(self.clone()),
415 data,
416 }),
417 }
418 }
419}
420
421impl<D> Clone for AppError<D> {
422 fn clone(&self) -> Self {
423 Self {
424 inner: Arc::clone(&self.inner),
425 }
426 }
427}
428
429
430impl<E, D> From<E> for AppError<D>
431where
432 E: StdError,
433 D: Default,
434{
435 fn from(err: E) -> Self {
436 Self::new(&err)
437 }
438}
439
440impl<D> PartialEq for AppError<D>
441where
442 D: PartialEq,
443{
444 fn eq(&self, other: &Self) -> bool {
445 if Arc::ptr_eq(&self.inner, &other.inner) {
447 return true;
448 }
449
450 self.inner == other.inner
452 }
453}
454
455impl<D> Eq for AppError<D> where D: Eq {}
456
457impl<D> Hash for AppError<D>
458where
459 D: Hash,
460{
461 fn hash<H: Hasher>(&self, state: &mut H) {
462 self.inner.hash(state);
463 }
464}
465
466impl<D> fmt::Display for AppError<D> {
467 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468 self.inner.fmt(f)
469 }
470}
471
472impl<D> fmt::Debug for AppError<D>
473where
474 D: fmt::Debug + 'static,
475{
476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477 self.inner.fmt(f)
478 }
479}
480
481#[derive(Clone, Debug)]
483pub struct DataIter<'a, D: 'static> {
484 errs: Vec<&'a AppError<D>>,
486
487 cur_it: Option<slice::Iter<'a, AppError<D>>>,
489}
490
491impl<'a, D: 'static> Iterator for DataIter<'a, D> {
492 type Item = (&'a AppError<D>, &'a D);
493
494 fn next(&mut self) -> Option<Self::Item> {
495 loop {
496 let next_err = 'next_err: {
498 if let Some(cur_it) = &mut self.cur_it {
500 match cur_it.next() {
501 Some(err) => break 'next_err err,
502 None => self.cur_it = None,
504 }
505 }
506
507 match self.errs.pop() {
509 Some(err) => break 'next_err err,
510 None => return None,
512 }
513 };
514
515 match &*next_err.inner {
517 Inner::Single { source, data, .. } => {
520 if let Some(source) = source {
521 self.errs.push(source);
522 }
523
524 break Some((next_err, data));
525 },
526
527 Inner::Multiple(errs) => match &self.cur_it {
529 Some(_) => self.errs.push(next_err),
531
532 None => self.cur_it = Some(errs.iter()),
534 },
535 }
536 }
537 }
538}
539
540pub trait Context<D> {
542 type Output;
543
544 fn context(self, msg: &'static str) -> Self::Output;
546
547 fn with_context<F, M>(self, with_msg: F) -> Self::Output
549 where
550 F: FnOnce() -> M,
551 M: fmt::Display;
552}
553
554impl<T, E, D> Context<D> for Result<T, E>
555where
556 E: StdError,
557 D: Default,
558{
559 type Output = Result<T, AppError<D>>;
560
561 fn context(self, msg: &'static str) -> Self::Output {
562 self.map_err(|err| AppError::new(&err).context(msg))
563 }
564
565 fn with_context<F, M>(self, with_msg: F) -> Self::Output
566 where
567 F: FnOnce() -> M,
568 M: fmt::Display,
569 {
570 self.map_err(|err| AppError::new(&err).with_context(with_msg))
571 }
572}
573
574impl<T, D> Context<D> for Result<T, AppError<D>>
575where
576 D: Default,
577{
578 type Output = Self;
579
580 fn context(self, msg: &'static str) -> Self::Output {
581 self.map_err(|err| err.context(msg))
582 }
583
584 fn with_context<F, M>(self, with_msg: F) -> Self::Output
585 where
586 F: FnOnce() -> M,
587 M: fmt::Display,
588 {
589 self.map_err(|err| err.with_context(with_msg))
590 }
591}
592
593impl<T, D> Context<D> for Result<T, &AppError<D>>
594where
595 D: Default,
596{
597 type Output = Result<T, AppError<D>>;
598
599 fn context(self, msg: &'static str) -> Self::Output {
600 self.map_err(|err| err.context(msg))
601 }
602
603 fn with_context<F, M>(self, with_msg: F) -> Self::Output
604 where
605 F: FnOnce() -> M,
606 M: fmt::Display,
607 {
608 self.map_err(|err| err.with_context(with_msg))
609 }
610}
611
612impl<T, D> Context<D> for Result<T, &mut AppError<D>>
613where
614 D: Default,
615{
616 type Output = Result<T, AppError<D>>;
617
618 fn context(self, msg: &'static str) -> Self::Output {
619 self.map_err(|err| err.context(msg))
620 }
621
622 fn with_context<F, M>(self, with_msg: F) -> Self::Output
623 where
624 F: FnOnce() -> M,
625 M: fmt::Display,
626 {
627 self.map_err(|err| err.with_context(with_msg))
628 }
629}
630
631impl<T, D> Context<D> for Option<T>
632where
633 D: Default,
634{
635 type Output = Result<T, AppError<D>>;
636
637 fn context(self, msg: &'static str) -> Self::Output {
638 self.ok_or_else(|| AppError::msg(msg))
639 }
640
641 fn with_context<F, M>(self, with_msg: F) -> Self::Output
642 where
643 F: FnOnce() -> M,
644 M: fmt::Display,
645 {
646 self.ok_or_else(|| AppError::fmt(with_msg()))
647 }
648}
649
650pub macro app_error {
652 ($msg:literal $(,)?) => {
653 match format_args!($msg) {
655 msg => match msg.as_str() {
656 Some(msg) => $crate::AppError::msg(msg),
657 None => $crate::AppError::fmt( ::std::fmt::format(msg) )
658 }
659 }
660
661 },
662
663 ($fmt:literal, $($arg:expr),* $(,)?) => {
664 $crate::AppError::fmt( format!($fmt, $($arg,)*) )
665 },
666}
667
668pub macro bail {
670 ($msg:literal $(,)?) => {
671 do yeet $crate::app_error!($msg)
672 },
673
674 ($fmt:literal, $($arg:expr),* $(,)?) => {
675 do yeet $crate::app_error!($fmt, $($arg),*)
676 },
677}
678
679pub macro ensure {
681 ($cond:expr, $msg:literal $(,)?) => {
682 if !$cond {
683 do yeet $crate::app_error!($msg);
684 }
685 },
686
687 ($cond:expr, $fmt:literal, $($arg:expr),* $(,)?) => {
688 if !$cond {
689 do yeet $crate::app_error!($fmt, $($arg),*);
690 }
691 },
692}
693
694#[cfg(test)]
695#[cfg_attr(test, coverage(off))]
696mod test {
697 use {
698 super::*,
699 std::{assert_matches::assert_matches, collections::HashSet},
700 };
701
702 #[derive(Clone, Debug)]
704 struct StdE {
705 msg: &'static str,
706 inner: Option<Box<Self>>,
707 }
708 impl StdError for StdE {
709 fn source(&self) -> Option<&(dyn StdError + 'static)> {
710 self.inner.as_deref().map(|err| err as &dyn StdError)
711 }
712 }
713 impl fmt::Display for StdE {
714 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
715 self.msg.fmt(f)
716 }
717 }
718
719 #[test]
720 fn std_error_source() {
721 let err = AppError::<()>::msg("A");
722 assert_matches!(err.as_std_error().source(), None);
723 assert_matches!(err.into_std_error().source(), None);
724
725 let err = AppError::<()>::msg("A").context("B");
726 assert_matches!(err.as_std_error().source(), Some(err) if err.to_string() == "A");
727 assert_matches!(err.into_std_error().source(), Some(err) if err.to_string() == "A");
728
729 let err = AppError::<()>::from_multiple([AppError::msg("A"), AppError::msg("B")]);
730 assert_matches!(err.as_std_error().source(), Some(err) if err.to_string() == "A");
731 assert_matches!(err.into_std_error().source(), Some(err) if err.to_string() == "A");
732 }
733
734 #[test]
735 fn std_error_fmt() {
736 let err = AppError::<()>::msg("A");
737 assert_eq!(err.as_std_error().to_string(), "A");
738 assert_eq!(err.into_std_error().to_string(), "A");
739
740 let err = AppError::<()>::msg("A").context("B");
741 assert_eq!(err.as_std_error().to_string(), "B");
742 assert_eq!(err.into_std_error().to_string(), "B");
743
744 let err = AppError::<()>::from_multiple([AppError::msg("A"), AppError::msg("B")]);
745 assert_eq!(err.as_std_error().to_string(), "Multiple errors (2)");
746 assert_eq!(err.into_std_error().to_string(), "Multiple errors (2)");
747 }
748
749 #[test]
750 fn from_std_error() {
751 let std_err = StdE {
752 msg: "A",
753 inner: Some(Box::new(StdE {
754 msg: "B",
755 inner: None,
756 })),
757 };
758
759 let found = AppError::<()>::new(&std_err);
760 assert_eq!(found, AppError::from(std_err));
761 let expected = AppError::msg("B").context("A");
762 assert!(
763 found == expected,
764 "Converted error was wrong.\nExpected: {}\nFound: {}",
765 expected.pretty(),
766 found.pretty()
767 );
768 }
769
770 #[test]
771 fn from_multiple_std_error() {
772 let std_errs = [
773 StdE {
774 msg: "A",
775 inner: Some(Box::new(StdE {
776 msg: "B",
777 inner: None,
778 })),
779 },
780 StdE {
781 msg: "C",
782 inner: Some(Box::new(StdE {
783 msg: "D",
784 inner: None,
785 })),
786 },
787 ];
788
789 let found = AppError::<()>::from_multiple_std(&std_errs);
790 let expected =
791 AppError::<()>::from_multiple([AppError::msg("B").context("A"), AppError::msg("D").context("C")]);
792 assert!(
793 found == expected,
794 "Converted error was wrong.\nExpected: {}\nFound: {}",
795 expected.pretty(),
796 found.pretty()
797 );
798 }
799
800 #[test]
801 fn eq() {
802 let err_a1 = AppError::<()>::msg("A");
803 assert_eq!(err_a1, err_a1);
804 assert_eq!(err_a1.clone(), err_a1);
805
806 let err_a2 = AppError::<()>::msg("A");
807 assert_eq!(err_a1, err_a2);
808
809 let err_b = AppError::<()>::msg("B");
810 assert_ne!(err_a1, err_b);
811 }
812
813 #[test]
814 fn eq_context() {
815 let err1 = AppError::<()>::msg("A").context("B");
816 assert_eq!(err1, err1);
817
818 let err2 = AppError::<()>::msg("A").context("B");
819 assert_eq!(err1, err2);
820
821 let err3 = AppError::<()>::msg("A").context("C");
822 assert_ne!(err1, err3);
823
824 let err4 = AppError::<()>::msg("B").context("C");
825 assert_ne!(err3, err4);
826 }
827
828 #[test]
829 fn eq_multiple() {
830 let err_multiple1 = AppError::<()>::from_multiple([AppError::msg("A"), AppError::msg("B")]);
831 assert_eq!(err_multiple1, err_multiple1);
832 assert_eq!(err_multiple1.clone(), err_multiple1);
833
834 let err_single = AppError::<()>::msg("A");
835 assert_ne!(err_multiple1, err_single);
836
837 let err_multiple2 = AppError::<()>::from_multiple([AppError::msg("A"), AppError::msg("B")]);
838 assert_eq!(err_multiple1, err_multiple2);
839
840 let err_multiple3 = AppError::<()>::from_multiple([AppError::msg("B"), AppError::msg("A")]);
841 assert_ne!(err_multiple1, err_multiple3);
842 }
843
844 #[test]
845 fn eq_data() {
846 #[derive(PartialEq, Debug)]
847 struct D(usize);
848
849 let err1 = AppError::msg_with_data("A", D(0));
850 assert_eq!(err1, err1);
851
852 let err2 = AppError::msg_with_data("A", D(1));
853 assert_ne!(err1, err2);
854 }
855
856 #[test]
857 fn hash() {
858 let mut errs = HashSet::new();
859 let err = AppError::<()>::msg("A");
860 assert!(errs.insert(err.clone()));
861 assert!(!errs.insert(err));
862 assert!(!errs.insert(AppError::<()>::msg("A")));
863
864 let err_multiple = AppError::<()>::from_multiple([AppError::msg("A"), AppError::msg("B")]);
865 assert!(errs.insert(err_multiple.clone()));
866 assert!(!errs.insert(err_multiple));
867 assert!(!errs.insert(AppError::<()>::from_multiple([AppError::msg("A"), AppError::msg("B")])));
868 }
869
870 #[test]
871 fn fmt_debug() {
872 let err = AppError::<()>::msg("A").context("B");
873 assert_eq!(format!("{err:?}"), "B\n└─A");
874 assert_eq!(
875 format!("{err:#?}"),
876 r#"AppError {
877 msg: "B",
878 source: Some(
879 AppError {
880 msg: "A",
881 source: None,
882 data: (),
883 },
884 ),
885 data: (),
886}"#
887 );
888
889 let err_multiple = AppError::<()>::from_multiple([AppError::msg("A"), AppError::msg("B")]);
890 assert_eq!(format!("{err_multiple:?}"), "Multiple errors:\n├─A\n└─B");
891 assert_eq!(
892 format!("{err_multiple:#?}"),
893 r#"[
894 AppError {
895 msg: "A",
896 source: None,
897 data: (),
898 },
899 AppError {
900 msg: "B",
901 source: None,
902 data: (),
903 },
904]"#
905 );
906 }
907
908 #[test]
909 fn fmt() {
910 assert_eq!(AppError::<()>::fmt("ABC").to_string(), "ABC");
911 }
912
913 #[test]
914 fn with_context() {
915 assert_eq!(
916 AppError::<()>::msg("A").context("B"),
917 AppError::<()>::msg("A").with_context(|| "B")
918 );
919 }
920
921 #[test]
922 fn ext_traits() {
923 let std_err = StdE {
924 msg: "B",
925 inner: Some(Box::new(StdE {
926 msg: "C",
927 inner: None,
928 })),
929 };
930
931 assert_eq!(
932 Err::<(), StdE>(std_err.clone()).context("A"),
933 Err(AppError::<()>::msg("C").context("B").context("A"))
934 );
935 assert_eq!(
936 Err::<(), StdE>(std_err).with_context(|| "A"),
937 Err(AppError::<()>::msg("C").context("B").context("A"))
938 );
939
940 assert_eq!(
941 Err::<(), AppError>(AppError::msg("C").context("B")).context("A"),
942 Err(AppError::<()>::msg("C").context("B").context("A"))
943 );
944 assert_eq!(
945 Err::<(), AppError>(AppError::msg("C").context("B")).with_context(|| "A"),
946 Err(AppError::<()>::msg("C").context("B").context("A"))
947 );
948
949 assert_eq!(None::<()>.context("A"), Err(AppError::<()>::msg("A")));
950 assert_eq!(None::<()>.with_context(|| "A"), Err(AppError::<()>::msg("A")));
951 }
952
953 #[test]
954 fn data_from_std() {
955 #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
956 struct D(usize);
957
958 let std_err = StdE {
959 msg: "A",
960 inner: Some(Box::new(StdE {
961 msg: "B",
962 inner: None,
963 })),
964 };
965
966 let err = AppError::<D>::new_with_data(&std_err, D(5));
967 assert_eq!(
968 err.data_iter().map(|(_, &d)| d).collect::<HashSet<_>>(),
969 [D(5), D(5)].into()
970 );
971 assert_eq!(
972 err,
973 AppError::<D>::msg_with_data("B", D(5)).with_context_with_data(|| "A", D(5))
974 );
975 }
976
977 #[test]
978 fn data() {
979 #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
980 struct D(usize);
981
982 let err = AppError::<D>::fmt_with_data("B", D(4)).context_with_data("A", D(5));
983 assert_eq!(
984 err.data_iter().map(|(_, &d)| d).collect::<HashSet<_>>(),
985 [D(5), D(4)].into()
986 );
987 }
988
989 #[test]
990 fn data_multiple() {
991 #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
992 struct D(usize);
993
994 let err = AppError::from_multiple([AppError::msg_with_data("A", D(1)), AppError::msg_with_data("B", D(2))]);
995 assert_eq!(
996 err.data_iter().map(|(_, &d)| d).collect::<HashSet<_>>(),
997 [D(1), D(2)].into()
998 );
999 }
1000
1001 #[test]
1002 fn data_empty() {
1003 let err = AppError::<!>::from_multiple([]);
1004 assert_eq!(err.data_iter().map(|(_, &d)| d).collect::<Vec<_>>(), []);
1005 }
1006
1007 #[test]
1008 fn data_complex() {
1009 #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
1010 struct D(usize);
1011
1012 let err = AppError::<D>::from_multiple([
1013 AppError::msg_with_data("B", D(1)).context_with_data("A", D(0)),
1014 AppError::from_multiple([
1015 AppError::msg_with_data("C", D(2)),
1016 AppError::msg_with_data("D", D(3)),
1017 AppError::from_multiple([AppError::msg_with_data("E", D(4)), AppError::msg_with_data("F", D(5))]),
1018 AppError::from_multiple([AppError::msg_with_data("G", D(6)), AppError::msg_with_data("H", D(7))]),
1019 ]),
1020 AppError::from_multiple([AppError::msg_with_data("I", D(8))]),
1021 AppError::msg_with_data("J", D(9)),
1022 ]);
1023 assert_eq!(
1024 err.data_iter().map(|(_, &d)| d).collect::<HashSet<_>>(),
1025 (0..=9).map(D).collect()
1026 );
1027 }
1028
1029 #[test]
1030 fn pretty() {
1031 let err = AppError::<()>::msg("A").context("B\nC").context("D");
1032 assert_eq!(
1033 format!("{:#?}", err.pretty()),
1034 r#"PrettyDisplay {
1035 root: AppError {
1036 msg: "D",
1037 source: Some(
1038 AppError {
1039 msg: "B\nC",
1040 source: Some(
1041 AppError {
1042 msg: "A",
1043 source: None,
1044 data: (),
1045 },
1046 ),
1047 data: (),
1048 },
1049 ),
1050 data: (),
1051 },
1052 ignore_err: None,
1053}"#
1054 );
1055 assert_eq!(
1056 err.pretty().to_string(),
1057 r"D
1058└─B
1059 C
1060 └─A"
1061 );
1062 assert_eq!(err.pretty().to_string(), format!("{:?}", err.pretty()));
1063
1064 let err_multiple = AppError::<()>::from_multiple([AppError::msg("A"), AppError::msg("B")]);
1065 assert_eq!(
1066 err_multiple.pretty().to_string(),
1067 r"Multiple errors:
1068├─A
1069└─B"
1070 );
1071
1072 let err_multiple_deep = AppError::<()>::from_multiple([
1073 AppError::from_multiple([AppError::msg("A\nA2"), AppError::msg("B\nB2")])
1074 .context("C\nC2")
1075 .context("D"),
1076 AppError::msg("E"),
1077 ]);
1078 assert_eq!(
1079 err_multiple_deep.pretty().to_string(),
1080 r"Multiple errors:
1081├─D
1082│ └─C
1083│ C2
1084│ └─Multiple errors:
1085│ ├─A
1086│ │ A2
1087│ └─B
1088│ B2
1089└─E"
1090 );
1091 }
1092
1093 #[test]
1094 fn pretty_ignore() {
1095 #[derive(Default)]
1096 struct D {
1097 ignore: bool,
1098 }
1099
1100 let fmt_err = |err: &AppError<D>| err.pretty().with_ignore_err(|data| data.ignore).to_string();
1101
1102 let err = AppError::<D>::msg_with_data("A", D { ignore: true }).context("B");
1104 assert_eq!(fmt_err(&err), "B\n└─A");
1105
1106 let err_multiple_deep = AppError::<D>::from_multiple([
1107 AppError::from_multiple([AppError::msg("A"), AppError::msg_with_data("B", D { ignore: true })])
1108 .context("C")
1109 .context("D"),
1110 AppError::msg_with_data("E", D { ignore: true }),
1111 AppError::msg_with_data("F", D { ignore: true }).context("G"),
1112 AppError::msg("H").context_with_data("I", D { ignore: true }),
1113 AppError::msg("J"),
1114 ]);
1115 assert_eq!(
1116 fmt_err(&err_multiple_deep),
1117 r"Multiple errors:
1118├─D
1119│ └─C
1120│ └─Multiple errors:
1121│ ├─A
1122│ └─(1 ignored errors)
1123├─J
1124└─(3 ignored errors)"
1125 );
1126 }
1127
1128 #[test]
1129 fn macros_static() {
1130 assert_eq!(app_error!("A"), AppError::<()>::msg("A"));
1131
1132 #[expect(clippy::diverging_sub_expression)]
1133 let res: Result<(), AppError> = try { bail!("A") };
1134 assert_eq!(res, Err(AppError::<()>::msg("A")));
1135
1136 let res: Result<(), AppError> = try { ensure!(true, "A") };
1137 assert_eq!(res, Ok(()));
1138
1139 let res: Result<(), AppError> = try { ensure!(false, "A") };
1140 assert_eq!(res, Err(AppError::<()>::msg("A")));
1141 }
1142
1143 #[test]
1144 fn macros_fmt() {
1145 let value = 5;
1146 assert_eq!(app_error!("A{value}"), AppError::<()>::msg("A5"));
1147
1148 #[expect(clippy::diverging_sub_expression)]
1149 let res: Result<(), AppError> = try { bail!("A{value}") };
1150 assert_eq!(res, Err(AppError::<()>::msg("A5")));
1151
1152 let res: Result<(), AppError> = try { ensure!(true, "A{value}") };
1153 assert_eq!(res, Ok(()));
1154
1155 let res: Result<(), AppError> = try { ensure!(false, "A{value}") };
1156 assert_eq!(res, Err(AppError::<()>::msg("A5")));
1157 }
1158
1159 #[test]
1160 fn flatten_simple() {
1161 let err = AppError::<()>::from_multiple([
1162 AppError::from_multiple([AppError::msg("A"), AppError::msg("B")]),
1163 AppError::msg("C"),
1164 AppError::from_multiple([
1165 AppError::from_multiple([
1166 AppError::msg("D"),
1167 AppError::from_multiple([AppError::msg("E"), AppError::msg("F")]),
1168 ]),
1169 AppError::from_multiple([
1170 AppError::from_multiple([AppError::msg("H"), AppError::msg("I")]),
1171 AppError::msg("J"),
1172 ])
1173 .context("G1")
1174 .context("G2"),
1175 ]),
1176 AppError::from_multiple([AppError::msg("K")]).context("L"),
1177 ]);
1178
1179 let found = err.flatten();
1180 let expected = AppError::from_multiple([
1181 AppError::msg("A"),
1182 AppError::msg("B"),
1183 AppError::msg("C"),
1184 AppError::msg("D"),
1185 AppError::msg("E"),
1186 AppError::msg("F"),
1187 AppError::from_multiple([AppError::msg("H"), AppError::msg("I"), AppError::msg("J")])
1188 .context("G1")
1189 .context("G2"),
1190 AppError::msg("K").context("L"),
1191 ]);
1192
1193 assert!(
1194 found == expected,
1195 "Flattened error was wrong.\nExpected: {}\nFound: {}",
1196 expected.pretty(),
1197 found.pretty()
1198 );
1199 }
1200}