1use std::{
2 any::Any,
3 backtrace::{Backtrace, BacktraceStatus},
4 fmt::{self, Debug, Display},
5 ops::Deref,
6 sync::Arc,
7};
8
9pub type Result<T = (), E = Error> = ::core::result::Result<T, E>;
10
11#[derive(Clone)]
22pub struct Error(Arc<dyn ErrorObject>);
23
24impl Error {
25 pub fn msg(message: impl Display + Debug + Send + Sync + 'static) -> Self {
27 Self::from(Message(message))
28 }
29
30 #[must_use]
34 pub fn from_boxed(error: Box<dyn std::error::Error + Send + Sync + 'static>) -> Self {
35 Self::from(Boxed(error))
36 }
37
38 #[cfg(feature = "anyhow")]
42 #[must_use]
43 pub fn from_anyhow(error: anyhow::Error) -> Self {
44 Self::from_boxed(error.into())
45 }
46
47 #[must_use]
52 pub fn context(self, context: impl Display + Send + Sync + 'static) -> Self {
53 Self::from(WithContext {
54 context: Box::new(context),
55 error: self,
56 })
57 }
58
59 #[inline]
62 #[must_use]
63 pub fn is<E>(&self) -> bool
64 where
65 E: std::error::Error + Send + Sync + 'static,
66 {
67 self.downcast_ref::<E>().is_some()
68 }
69
70 pub fn downcast<E>(self) -> Result<E, DowncastError>
84 where
85 E: std::error::Error + Send + Sync + 'static,
86 {
87 if !self.is::<E>() {
88 return Err(DowncastError::new(DowncastFailure::Mismatch, self));
89 }
90 self.unwrap::<E>()
91 }
92
93 pub fn downcast_cloned<E>(self) -> Result<E, Self>
104 where
105 E: std::error::Error + Send + Sync + Clone + 'static,
106 {
107 match self.downcast::<E>() {
108 Ok(error) => Ok(error),
109 Err(failed) => {
110 let error = failed.into_error();
111 match error.downcast_ref::<E>() {
112 Some(error) => Ok(error.clone()),
113 None => Err(error),
114 }
115 }
116 }
117 }
118
119 #[must_use]
122 pub fn downcast_ref<E>(&self) -> Option<&E>
123 where
124 E: std::error::Error + Send + Sync + 'static,
125 {
126 let error = self.0.error();
127 match error.downcast_ref::<E>() {
128 Some(error) => Some(error),
129 None => error
130 .downcast_ref::<WithContext>()
131 .and_then(|context| context.error.downcast_ref::<E>()),
132 }
133 }
134
135 pub fn downcast_mut<E>(&mut self) -> Result<&mut E, DowncastFailure>
144 where
145 E: std::error::Error + Send + Sync + 'static,
146 {
147 if !self.is::<E>() {
148 return Err(DowncastFailure::Mismatch);
149 }
150 self.unwrap_mut::<E>()
151 }
152
153 fn unwrap<E>(self) -> Result<E, DowncastError>
156 where
157 E: std::error::Error + Send + Sync + 'static,
158 {
159 let any = match self.0.into_any().downcast::<Object<E>>() {
160 Ok(object) => {
161 return match Arc::try_unwrap(object) {
162 Ok(object) => Ok(object.error),
163 Err(shared) => Err(DowncastError::new(DowncastFailure::Shared, Self(shared))),
164 };
165 }
166 Err(any) => any,
167 };
168 let Ok(object) = any.downcast::<Object<WithContext>>() else {
169 unreachable!("an error holding an `E` is an `Object<E>` or a context layer");
170 };
171 match Arc::try_unwrap(object) {
172 Ok(object) => {
173 let WithContext { context, error } = object.error;
174 error.unwrap::<E>().map_err(|failed| {
175 let error = Self(Arc::new(Object {
178 error: WithContext {
179 context,
180 error: failed.error,
181 },
182 backtrace: object.backtrace,
183 }));
184 DowncastError::new(failed.failure, error)
185 })
186 }
187 Err(shared) => Err(DowncastError::new(DowncastFailure::Shared, Self(shared))),
188 }
189 }
190
191 fn unwrap_mut<E>(&mut self) -> Result<&mut E, DowncastFailure>
194 where
195 E: std::error::Error + Send + Sync + 'static,
196 {
197 let error = Arc::get_mut(&mut self.0)
198 .ok_or(DowncastFailure::Shared)?
199 .error_mut();
200 if error.is::<E>() {
201 return Ok(error
202 .downcast_mut::<E>()
203 .expect("the stored error was checked to be an `E`"));
204 }
205 error
206 .downcast_mut::<WithContext>()
207 .expect("an error holding an `E` is an `E` or a context layer")
208 .error
209 .unwrap_mut::<E>()
210 }
211
212 #[inline]
216 #[must_use]
217 pub fn backtrace(&self) -> &Backtrace {
218 self.0.backtrace()
219 }
220
221 pub fn chain(&self) -> impl Iterator<Item = &(dyn std::error::Error + 'static)> {
224 let mut next: Option<&(dyn std::error::Error + 'static)> = Some(self.0.error());
225 std::iter::from_fn(move || {
226 let current = next?;
227 next = current.source();
228 Some(current)
229 })
230 }
231}
232
233impl Display for Error {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 Display::fmt(self.0.error(), f)?;
238 if f.alternate() {
239 for cause in self.chain().skip(1) {
240 write!(f, ": {cause}")?;
241 }
242 }
243 Ok(())
244 }
245}
246
247impl Debug for Error {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 if f.alternate() {
253 return Debug::fmt(self.0.error(), f);
254 }
255 write!(f, "{}", self.0.error())?;
256 let mut causes = self.chain().skip(1).peekable();
257 if causes.peek().is_some() {
258 write!(f, "\n\nCaused by:")?;
259 for cause in causes {
260 write!(f, "\n {cause}")?;
261 }
262 }
263 let backtrace = self.backtrace();
264 if backtrace.status() == BacktraceStatus::Captured {
265 write!(f, "\n\nStack backtrace:\n{backtrace}")?;
266 }
267 Ok(())
268 }
269}
270
271impl Deref for Error {
273 type Target = dyn std::error::Error + Send + Sync + 'static;
274
275 fn deref(&self) -> &Self::Target {
276 self.0.error()
277 }
278}
279
280impl<E> From<E> for Error
281where
282 E: std::error::Error + Send + Sync + 'static,
283{
284 fn from(error: E) -> Self {
285 Self(Arc::new(Object {
286 error,
287 backtrace: Backtrace::capture(),
288 }))
289 }
290}
291
292impl From<&Error> for Error {
294 fn from(error: &Error) -> Self {
295 error.clone()
296 }
297}
298
299impl From<Error> for Box<dyn std::error::Error + Send + Sync + 'static> {
300 fn from(error: Error) -> Self {
301 Box::new(BoxedError(error))
302 }
303}
304
305#[derive(Debug, Clone, Copy, PartialEq, Eq)]
307pub enum DowncastFailure {
308 Mismatch,
310 Shared,
313}
314
315impl Display for DowncastFailure {
316 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317 match self {
318 Self::Mismatch => f.write_str("the error is not an instance of the requested type"),
319 Self::Shared => f.write_str("the error is still shared with a clone"),
320 }
321 }
322}
323
324impl std::error::Error for DowncastFailure {}
325
326#[derive(Debug)]
332pub struct DowncastError {
333 failure: DowncastFailure,
334 error: Error,
335}
336
337impl DowncastError {
338 fn new(failure: DowncastFailure, error: Error) -> Self {
339 Self { failure, error }
340 }
341
342 #[must_use]
344 pub fn failure(&self) -> DowncastFailure {
345 self.failure
346 }
347
348 #[must_use]
350 pub fn error(&self) -> &Error {
351 &self.error
352 }
353
354 #[must_use]
356 pub fn into_error(self) -> Error {
357 self.error
358 }
359}
360
361trait ErrorObject: Send + Sync + 'static {
368 fn error(&self) -> &(dyn std::error::Error + Send + Sync + 'static);
370
371 fn error_mut(&mut self) -> &mut (dyn std::error::Error + Send + Sync + 'static);
373
374 fn backtrace(&self) -> &Backtrace;
376
377 fn into_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
380}
381
382struct Object<E> {
384 error: E,
385 backtrace: Backtrace,
386}
387
388impl<E> ErrorObject for Object<E>
389where
390 E: std::error::Error + Send + Sync + 'static,
391{
392 fn error(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
393 &self.error
394 }
395
396 fn error_mut(&mut self) -> &mut (dyn std::error::Error + Send + Sync + 'static) {
397 &mut self.error
398 }
399
400 fn backtrace(&self) -> &Backtrace {
401 &self.backtrace
402 }
403
404 fn into_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
405 self
406 }
407}
408
409struct Message<M>(M);
411
412impl<M: Display> Display for Message<M> {
413 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414 Display::fmt(&self.0, f)
415 }
416}
417
418impl<M: Debug> Debug for Message<M> {
419 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
420 Debug::fmt(&self.0, f)
421 }
422}
423
424impl<M: Display + Debug> std::error::Error for Message<M> {}
425
426struct WithContext {
429 context: Box<dyn Display + Send + Sync>,
430 error: Error,
431}
432
433impl Display for WithContext {
434 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435 Display::fmt(&self.context, f)
436 }
437}
438
439impl Debug for WithContext {
440 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441 f.debug_struct("WithContext")
442 .field("context", &format_args!("{}", self.context))
443 .field("error", &self.error)
444 .finish()
445 }
446}
447
448impl std::error::Error for WithContext {
449 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
450 Some(self.error.0.error())
451 }
452}
453
454struct Boxed(Box<dyn std::error::Error + Send + Sync + 'static>);
457
458impl Display for Boxed {
459 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460 Display::fmt(&self.0, f)
461 }
462}
463
464impl Debug for Boxed {
465 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
466 Debug::fmt(&self.0, f)
467 }
468}
469
470impl std::error::Error for Boxed {
471 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
472 self.0.source()
473 }
474}
475
476struct BoxedError(Error);
481
482impl Display for BoxedError {
483 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484 Display::fmt(&self.0, f)
485 }
486}
487
488impl Debug for BoxedError {
489 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490 Debug::fmt(&self.0, f)
491 }
492}
493
494impl std::error::Error for BoxedError {
495 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
496 self.0.0.error().source()
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use super::*;
503
504 #[derive(Debug, Clone)]
506 struct Failure(&'static str);
507
508 impl Display for Failure {
509 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510 f.write_str(self.0)
511 }
512 }
513
514 impl std::error::Error for Failure {}
515
516 #[derive(Debug)]
518 struct WithCause {
519 cause: Failure,
520 }
521
522 impl Display for WithCause {
523 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
524 f.write_str("outer")
525 }
526 }
527
528 impl std::error::Error for WithCause {
529 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
530 Some(&self.cause)
531 }
532 }
533
534 #[test]
535 fn thiserror_transparent_forwards_display_and_source() {
536 #[derive(Debug, thiserror::Error)]
537 enum AppError {
538 #[error(transparent)]
539 Topcoat(#[from] Error),
540 }
541
542 let cause = Failure("boom");
543 let expected_cause = cause.to_string();
544 let error = Error::from(WithCause { cause });
545 let expected_message = error.to_string();
546 let app_error = AppError::from(error);
547
548 assert_eq!(app_error.to_string(), expected_message);
549 let source = std::error::Error::source(&app_error).unwrap();
550 assert!(source.is::<Failure>());
551 assert_eq!(source.to_string(), expected_cause);
552 assert!(source.source().is_none());
553
554 let app_error = AppError::from(Error::from(Failure("leaf")));
555 assert!(std::error::Error::source(&app_error).is_none());
556 }
557
558 #[test]
559 fn thiserror_from_preserves_the_stored_error_as_source() {
560 #[derive(Debug, thiserror::Error)]
561 enum AppError {
562 #[error("operation failed")]
563 Topcoat(#[from] Error),
564 }
565
566 let cause = Failure("boom");
567 let expected_cause = cause.to_string();
568 let error = Error::from(WithCause { cause });
569 let expected_message = error.to_string();
570 let app_error = AppError::from(error);
571
572 let source = std::error::Error::source(&app_error).unwrap();
573 assert!(source.is::<WithCause>());
574 assert_eq!(source.to_string(), expected_message);
575 let cause = source.source().unwrap();
576 assert!(cause.is::<Failure>());
577 assert_eq!(cause.to_string(), expected_cause);
578 assert!(cause.source().is_none());
579 }
580
581 #[test]
582 fn the_error_is_two_words_wide() {
583 assert_eq!(size_of::<Error>(), 2 * size_of::<usize>());
584 assert_eq!(size_of::<Result<()>>(), 2 * size_of::<usize>());
585 }
586
587 #[test]
588 fn clone_shares_the_underlying_error() {
589 let error = Error::from(Failure("boom"));
590 let clone = error.clone();
591
592 assert_eq!(error.to_string(), "boom");
593 assert_eq!(clone.to_string(), "boom");
594 assert_eq!(
595 clone.downcast_ref::<Failure>().map(|failure| failure.0),
596 Some("boom")
597 );
598 }
599
600 #[test]
601 fn msg_builds_an_error_from_a_message() {
602 let error = Error::msg(format!("boom {}", 1));
603 assert_eq!(error.to_string(), "boom 1");
604 assert!(error.is::<Message<String>>());
605 }
606
607 #[test]
608 fn downcast_extracts_a_unique_error() {
609 let failure = Error::from(Failure("boom")).downcast::<Failure>().unwrap();
610 assert_eq!(failure.0, "boom");
611 }
612
613 #[test]
614 fn downcast_reports_a_mismatch() {
615 let error = Error::from(std::io::Error::other("boom"));
616 let failed = error.downcast::<Failure>().unwrap_err();
617 assert_eq!(failed.failure(), DowncastFailure::Mismatch);
618 assert_eq!(failed.into_error().to_string(), "boom");
619 }
620
621 #[test]
622 fn downcast_reports_a_shared_error() {
623 let error = Error::from(Failure("boom"));
624 let clone = error.clone();
625
626 let failed = error.downcast::<Failure>().unwrap_err();
627 assert_eq!(failed.failure(), DowncastFailure::Shared);
628 let error = failed.into_error();
629 assert_eq!(error.to_string(), "boom");
630
631 drop(clone);
633 let failure = error.downcast::<Failure>().unwrap();
634 assert_eq!(failure.0, "boom");
635 }
636
637 #[test]
638 fn downcast_moves_out_of_a_context_layer() {
639 let error = Error::from(Failure("boom")).context("loading");
640 let failure = error.downcast::<Failure>().unwrap();
641 assert_eq!(failure.0, "boom");
642 }
643
644 #[test]
645 fn downcast_hands_a_context_layer_back_intact() {
646 let inner = Error::from(Failure("boom"));
647 let shared = inner.clone();
648 let error = inner.context("loading");
649
650 let failed = error.downcast::<Failure>().unwrap_err();
651 assert_eq!(failed.failure(), DowncastFailure::Shared);
652 let error = failed.into_error();
653 assert_eq!(format!("{error:#}"), "loading: boom");
654 drop(shared);
655 }
656
657 #[test]
658 fn downcast_cloned_extracts_a_unique_error() {
659 let failure = Error::from(Failure("boom"))
660 .downcast_cloned::<Failure>()
661 .unwrap();
662 assert_eq!(failure.0, "boom");
663 }
664
665 #[test]
666 fn downcast_cloned_clones_a_shared_error() {
667 let error = Error::from(Failure("boom"));
668 let clone = error.clone();
669
670 let failure = error.downcast_cloned::<Failure>().unwrap();
671 assert_eq!(failure.0, "boom");
672 assert_eq!(clone.to_string(), "boom");
673 }
674
675 #[test]
676 fn downcast_cloned_keeps_a_non_matching_error() {
677 let error = Error::from(std::io::Error::other("boom"));
678 let clone = error.clone();
679
680 let error = error.downcast_cloned::<Failure>().unwrap_err();
681 assert_eq!(error.to_string(), "boom");
682 drop(clone);
683 }
684
685 #[test]
686 fn downcast_mut_mutates_a_unique_error() {
687 let mut error = Error::from(Failure("boom"));
688 error.downcast_mut::<Failure>().unwrap().0 = "bang";
689 assert_eq!(error.to_string(), "bang");
690 }
691
692 #[test]
693 fn downcast_mut_mutates_through_a_context_layer() {
694 let mut error = Error::from(Failure("boom")).context("loading");
695 error.downcast_mut::<Failure>().unwrap().0 = "bang";
696 assert_eq!(format!("{error:#}"), "loading: bang");
697 }
698
699 #[test]
700 fn downcast_mut_reports_a_mismatch() {
701 let mut error = Error::from(std::io::Error::other("boom"));
702 assert_eq!(
703 error.downcast_mut::<Failure>().unwrap_err(),
704 DowncastFailure::Mismatch
705 );
706 }
707
708 #[test]
709 fn downcast_mut_reports_a_shared_error() {
710 let mut error = Error::from(Failure("boom"));
711 let clone = error.clone();
712
713 assert_eq!(
714 error.downcast_mut::<Failure>().unwrap_err(),
715 DowncastFailure::Shared
716 );
717
718 drop(clone);
719 assert!(error.downcast_mut::<Failure>().is_ok());
720 }
721
722 #[test]
723 fn context_wraps_the_message_and_keeps_the_error_reachable() {
724 let error = Error::from(Failure("boom")).context("loading");
725
726 assert_eq!(error.to_string(), "loading");
727 assert_eq!(format!("{error:#}"), "loading: boom");
728 assert_eq!(error.chain().count(), 2);
729 assert!(error.is::<Failure>());
730 assert_eq!(error.downcast_cloned::<Failure>().unwrap().0, "boom");
731 }
732
733 #[test]
734 fn display_and_debug_walk_the_chain() {
735 let error = Error::from(WithCause {
736 cause: Failure("boom"),
737 });
738
739 assert_eq!(error.to_string(), "outer");
740 assert_eq!(format!("{error:#}"), "outer: boom");
741 assert!(format!("{error:?}").starts_with("outer\n\nCaused by:\n boom"));
742 assert!(format!("{error:#?}").starts_with("WithCause"));
743 }
744
745 #[test]
746 fn box_conversion_keeps_the_message_and_source() {
747 let error = Error::from(WithCause {
748 cause: Failure("boom"),
749 });
750 let shared = error.clone();
751
752 let boxed: Box<dyn std::error::Error + Send + Sync> = error.into();
753 assert_eq!(boxed.to_string(), "outer");
754 assert_eq!(
755 boxed.source().map(ToString::to_string),
756 Some(String::from("boom"))
757 );
758 drop(shared);
759 }
760
761 #[test]
762 fn boxed_errors_convert_with_their_chain() {
763 let boxed: Box<dyn std::error::Error + Send + Sync> = Box::new(WithCause {
764 cause: Failure("boom"),
765 });
766 let error = Error::from_boxed(boxed);
767 assert_eq!(error.to_string(), "outer");
768 assert_eq!(format!("{error:#}"), "outer: boom");
769 }
770
771 #[cfg(feature = "anyhow")]
772 #[test]
773 fn anyhow_errors_convert_with_their_chain() {
774 let error = Error::from_anyhow(anyhow::anyhow!("boom").context("loading"));
775 assert_eq!(error.to_string(), "loading");
776 assert_eq!(format!("{error:#}"), "loading: boom");
777 }
778}