app_error/
lib.rs

1//! App errors
2//!
3//! This crate provides an error type, [`AppError`], that is intended for usage in applications.
4//!
5//! It is [`Send`], [`Sync`], `'static`, and, importantly, cheaply [`Clone`]-able.
6//!
7//! To achieve this, it serializes every error it receives without owning it, meaning that
8//! you also can't retrieve the error later by downcasting it.
9//!
10//! It is also able to store multiple errors at once and provide pretty-printing of all
11//! of these them.
12//!
13//! It can carry an optional data parameter that may be retrieved later on.
14//!
15//! The inner representation is similar to `AppError = (String, Option<AppError>) | Vec<AppError>`.
16
17// Features
18#![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
29// Modules
30mod multiple;
31mod pretty;
32
33// Exports
34pub use self::{multiple::AllErrs, pretty::PrettyDisplay};
35
36// Imports
37use {
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/// Inner representation.
49#[derive(Clone)]
50enum Inner<D> {
51	/// Single error
52	Single {
53		/// Message
54		msg: Cow<'static, str>,
55
56		/// Source
57		source: Option<AppError<D>>,
58
59		/// User data
60		data: D,
61	},
62
63	/// Multiple errors
64	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			// For standard errors, just use the first source.
75			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			// With `:#?`, use a normal debug
137			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			// Otherwise, pretty print it
148			false => write!(f, "{}", PrettyDisplay::new(self)),
149		}
150	}
151}
152
153/// A reference-counted untyped error that can be created from any error type.
154///
155/// Named `AppError` as it's mostly useful in application code that don't care
156/// about the errors specifically, and instead only care to show them to users.
157pub struct AppError<D = ()> {
158	/// Inner
159	inner: Arc<Inner<D>>,
160}
161
162impl<D> AppError<D> {
163	/// Creates a new app error from an error
164	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	/// Creates a new app error from a message
179	#[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	/// Creates a new app error from a formattable message
194	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	/// Adds context to this error
209	#[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	/// Adds context to this error
224	#[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	/// Creates a new app error from multiple errors
241	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	/// Creates a new app error from multiple standard errors
251	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	/// Flattens all multiple errors recursively.
263	#[must_use]
264	pub fn flatten(self) -> Self
265	where
266		D: Clone,
267	{
268		/// Gathers all multiple errors from `err` into `flattened_errs`, recursively
269		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				// If we're a single error, recurse
283				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				// Otherwise, flatten all errors
292				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	/// Returns this type as a [`std::error::Error`]
313	#[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	/// Converts this type as into a [`std::error::Error`]
322	#[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	/// Returns an object that can be used for a pretty display of this error
331	#[must_use]
332	pub fn pretty(&self) -> PrettyDisplay<'_, D> {
333		PrettyDisplay::new(&self.inner)
334	}
335
336	/// Returns an iterator over all data in this error, recursively.
337	///
338	/// # Order
339	/// No order is guaranteed and it may change at any time.
340	#[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	/// Creates a new app error from an error and data.
351	///
352	/// `data` will be applied to all sources of `err`
353	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	/// Creates a new app error from a message
368	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	/// Creates a new app error from a formatted message
379	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	/// Adds context to this error
393	#[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	/// Adds context to this error
405	#[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 we're the same Arc, we're the same error
446		if Arc::ptr_eq(&self.inner, &other.inner) {
447			return true;
448		}
449
450		// Otherwise, perform a deep comparison
451		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/// Data iterator
482#[derive(Clone, Debug)]
483pub struct DataIter<'a, D: 'static> {
484	/// Error stack
485	errs: Vec<&'a AppError<D>>,
486
487	/// Current multiple iter
488	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			// Get the next error to check
497			let next_err = 'next_err: {
498				// If we're in the middle of an iterator, and we got a new one, then use it
499				if let Some(cur_it) = &mut self.cur_it {
500					match cur_it.next() {
501						Some(err) => break 'next_err err,
502						// Note: Remember to remove the iterator once exhausted
503						None => self.cur_it = None,
504					}
505				}
506
507				// Otherwise, use our next error from the stack
508				match self.errs.pop() {
509					Some(err) => break 'next_err err,
510					// If we have no more errors in the stack, we're done
511					None => return None,
512				}
513			};
514
515			// Then check if we can use it
516			match &*next_err.inner {
517				// If it's just a single error, yield the data and stack it's
518				// source onto the stack for next.
519				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				// Otherwise, if it's multiple
528				Inner::Multiple(errs) => match &self.cur_it {
529					// If we're already iterating, save it for later
530					Some(_) => self.errs.push(next_err),
531
532					// Otherwise, use it as our current iterator
533					None => self.cur_it = Some(errs.iter()),
534				},
535			}
536		}
537	}
538}
539
540/// Context for `Result`-like types
541pub trait Context<D> {
542	type Output;
543
544	/// Adds context to this result, if it's an error
545	fn context(self, msg: &'static str) -> Self::Output;
546
547	/// Adds context to this result lazily, if it's an error
548	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
650/// A macro that formats and creates an [`AppError`]
651pub macro app_error {
652	($msg:literal $(,)?) => {
653		// TODO: Check if it's a static string as compile time?
654		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
668/// A macro that returns an error
669pub 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
679/// A macro that returns an error if a condition is false
680pub 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	/// Error implementing `StdError` for testing.
703	#[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		// TODO: This is not correct behavior, we shouldn't display the ignored errors just because there's no multiple
1103		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}