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 Box<dyn std::error::Error + Send + Sync + 'static> {
293 fn from(error: Error) -> Self {
294 Box::new(BoxedError(error))
295 }
296}
297
298#[derive(Debug, Clone, Copy, PartialEq, Eq)]
300pub enum DowncastFailure {
301 Mismatch,
303 Shared,
306}
307
308impl Display for DowncastFailure {
309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310 match self {
311 Self::Mismatch => f.write_str("the error is not an instance of the requested type"),
312 Self::Shared => f.write_str("the error is still shared with a clone"),
313 }
314 }
315}
316
317impl std::error::Error for DowncastFailure {}
318
319#[derive(Debug)]
325pub struct DowncastError {
326 failure: DowncastFailure,
327 error: Error,
328}
329
330impl DowncastError {
331 fn new(failure: DowncastFailure, error: Error) -> Self {
332 Self { failure, error }
333 }
334
335 #[must_use]
337 pub fn failure(&self) -> DowncastFailure {
338 self.failure
339 }
340
341 #[must_use]
343 pub fn error(&self) -> &Error {
344 &self.error
345 }
346
347 #[must_use]
349 pub fn into_error(self) -> Error {
350 self.error
351 }
352}
353
354trait ErrorObject: Send + Sync + 'static {
361 fn error(&self) -> &(dyn std::error::Error + Send + Sync + 'static);
363
364 fn error_mut(&mut self) -> &mut (dyn std::error::Error + Send + Sync + 'static);
366
367 fn backtrace(&self) -> &Backtrace;
369
370 fn into_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
373}
374
375struct Object<E> {
377 error: E,
378 backtrace: Backtrace,
379}
380
381impl<E> ErrorObject for Object<E>
382where
383 E: std::error::Error + Send + Sync + 'static,
384{
385 fn error(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
386 &self.error
387 }
388
389 fn error_mut(&mut self) -> &mut (dyn std::error::Error + Send + Sync + 'static) {
390 &mut self.error
391 }
392
393 fn backtrace(&self) -> &Backtrace {
394 &self.backtrace
395 }
396
397 fn into_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
398 self
399 }
400}
401
402struct Message<M>(M);
404
405impl<M: Display> Display for Message<M> {
406 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407 Display::fmt(&self.0, f)
408 }
409}
410
411impl<M: Debug> Debug for Message<M> {
412 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 Debug::fmt(&self.0, f)
414 }
415}
416
417impl<M: Display + Debug> std::error::Error for Message<M> {}
418
419struct WithContext {
422 context: Box<dyn Display + Send + Sync>,
423 error: Error,
424}
425
426impl Display for WithContext {
427 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428 Display::fmt(&self.context, f)
429 }
430}
431
432impl Debug for WithContext {
433 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434 f.debug_struct("WithContext")
435 .field("context", &format_args!("{}", self.context))
436 .field("error", &self.error)
437 .finish()
438 }
439}
440
441impl std::error::Error for WithContext {
442 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
443 Some(self.error.0.error())
444 }
445}
446
447struct Boxed(Box<dyn std::error::Error + Send + Sync + 'static>);
450
451impl Display for Boxed {
452 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453 Display::fmt(&self.0, f)
454 }
455}
456
457impl Debug for Boxed {
458 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459 Debug::fmt(&self.0, f)
460 }
461}
462
463impl std::error::Error for Boxed {
464 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
465 self.0.source()
466 }
467}
468
469struct BoxedError(Error);
474
475impl Display for BoxedError {
476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477 Display::fmt(&self.0, f)
478 }
479}
480
481impl Debug for BoxedError {
482 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483 Debug::fmt(&self.0, f)
484 }
485}
486
487impl std::error::Error for BoxedError {
488 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
489 self.0.0.error().source()
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 #[derive(Debug, Clone)]
499 struct Failure(&'static str);
500
501 impl Display for Failure {
502 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503 f.write_str(self.0)
504 }
505 }
506
507 impl std::error::Error for Failure {}
508
509 #[derive(Debug)]
511 struct WithCause {
512 cause: Failure,
513 }
514
515 impl Display for WithCause {
516 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
517 f.write_str("outer")
518 }
519 }
520
521 impl std::error::Error for WithCause {
522 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
523 Some(&self.cause)
524 }
525 }
526
527 #[test]
528 fn thiserror_transparent_forwards_display_and_source() {
529 #[derive(Debug, thiserror::Error)]
530 enum AppError {
531 #[error(transparent)]
532 Topcoat(#[from] Error),
533 }
534
535 let cause = Failure("boom");
536 let expected_cause = cause.to_string();
537 let error = Error::from(WithCause { cause });
538 let expected_message = error.to_string();
539 let app_error = AppError::from(error);
540
541 assert_eq!(app_error.to_string(), expected_message);
542 let source = std::error::Error::source(&app_error).unwrap();
543 assert!(source.is::<Failure>());
544 assert_eq!(source.to_string(), expected_cause);
545 assert!(source.source().is_none());
546
547 let app_error = AppError::from(Error::from(Failure("leaf")));
548 assert!(std::error::Error::source(&app_error).is_none());
549 }
550
551 #[test]
552 fn thiserror_from_preserves_the_stored_error_as_source() {
553 #[derive(Debug, thiserror::Error)]
554 enum AppError {
555 #[error("operation failed")]
556 Topcoat(#[from] Error),
557 }
558
559 let cause = Failure("boom");
560 let expected_cause = cause.to_string();
561 let error = Error::from(WithCause { cause });
562 let expected_message = error.to_string();
563 let app_error = AppError::from(error);
564
565 let source = std::error::Error::source(&app_error).unwrap();
566 assert!(source.is::<WithCause>());
567 assert_eq!(source.to_string(), expected_message);
568 let cause = source.source().unwrap();
569 assert!(cause.is::<Failure>());
570 assert_eq!(cause.to_string(), expected_cause);
571 assert!(cause.source().is_none());
572 }
573
574 #[test]
575 fn the_error_is_two_words_wide() {
576 assert_eq!(size_of::<Error>(), 2 * size_of::<usize>());
577 assert_eq!(size_of::<Result<()>>(), 2 * size_of::<usize>());
578 }
579
580 #[test]
581 fn clone_shares_the_underlying_error() {
582 let error = Error::from(Failure("boom"));
583 let clone = error.clone();
584
585 assert_eq!(error.to_string(), "boom");
586 assert_eq!(clone.to_string(), "boom");
587 assert_eq!(
588 clone.downcast_ref::<Failure>().map(|failure| failure.0),
589 Some("boom")
590 );
591 }
592
593 #[test]
594 fn msg_builds_an_error_from_a_message() {
595 let error = Error::msg(format!("boom {}", 1));
596 assert_eq!(error.to_string(), "boom 1");
597 assert!(error.is::<Message<String>>());
598 }
599
600 #[test]
601 fn downcast_extracts_a_unique_error() {
602 let failure = Error::from(Failure("boom")).downcast::<Failure>().unwrap();
603 assert_eq!(failure.0, "boom");
604 }
605
606 #[test]
607 fn downcast_reports_a_mismatch() {
608 let error = Error::from(std::io::Error::other("boom"));
609 let failed = error.downcast::<Failure>().unwrap_err();
610 assert_eq!(failed.failure(), DowncastFailure::Mismatch);
611 assert_eq!(failed.into_error().to_string(), "boom");
612 }
613
614 #[test]
615 fn downcast_reports_a_shared_error() {
616 let error = Error::from(Failure("boom"));
617 let clone = error.clone();
618
619 let failed = error.downcast::<Failure>().unwrap_err();
620 assert_eq!(failed.failure(), DowncastFailure::Shared);
621 let error = failed.into_error();
622 assert_eq!(error.to_string(), "boom");
623
624 drop(clone);
626 let failure = error.downcast::<Failure>().unwrap();
627 assert_eq!(failure.0, "boom");
628 }
629
630 #[test]
631 fn downcast_moves_out_of_a_context_layer() {
632 let error = Error::from(Failure("boom")).context("loading");
633 let failure = error.downcast::<Failure>().unwrap();
634 assert_eq!(failure.0, "boom");
635 }
636
637 #[test]
638 fn downcast_hands_a_context_layer_back_intact() {
639 let inner = Error::from(Failure("boom"));
640 let shared = inner.clone();
641 let error = inner.context("loading");
642
643 let failed = error.downcast::<Failure>().unwrap_err();
644 assert_eq!(failed.failure(), DowncastFailure::Shared);
645 let error = failed.into_error();
646 assert_eq!(format!("{error:#}"), "loading: boom");
647 drop(shared);
648 }
649
650 #[test]
651 fn downcast_cloned_extracts_a_unique_error() {
652 let failure = Error::from(Failure("boom"))
653 .downcast_cloned::<Failure>()
654 .unwrap();
655 assert_eq!(failure.0, "boom");
656 }
657
658 #[test]
659 fn downcast_cloned_clones_a_shared_error() {
660 let error = Error::from(Failure("boom"));
661 let clone = error.clone();
662
663 let failure = error.downcast_cloned::<Failure>().unwrap();
664 assert_eq!(failure.0, "boom");
665 assert_eq!(clone.to_string(), "boom");
666 }
667
668 #[test]
669 fn downcast_cloned_keeps_a_non_matching_error() {
670 let error = Error::from(std::io::Error::other("boom"));
671 let clone = error.clone();
672
673 let error = error.downcast_cloned::<Failure>().unwrap_err();
674 assert_eq!(error.to_string(), "boom");
675 drop(clone);
676 }
677
678 #[test]
679 fn downcast_mut_mutates_a_unique_error() {
680 let mut error = Error::from(Failure("boom"));
681 error.downcast_mut::<Failure>().unwrap().0 = "bang";
682 assert_eq!(error.to_string(), "bang");
683 }
684
685 #[test]
686 fn downcast_mut_mutates_through_a_context_layer() {
687 let mut error = Error::from(Failure("boom")).context("loading");
688 error.downcast_mut::<Failure>().unwrap().0 = "bang";
689 assert_eq!(format!("{error:#}"), "loading: bang");
690 }
691
692 #[test]
693 fn downcast_mut_reports_a_mismatch() {
694 let mut error = Error::from(std::io::Error::other("boom"));
695 assert_eq!(
696 error.downcast_mut::<Failure>().unwrap_err(),
697 DowncastFailure::Mismatch
698 );
699 }
700
701 #[test]
702 fn downcast_mut_reports_a_shared_error() {
703 let mut error = Error::from(Failure("boom"));
704 let clone = error.clone();
705
706 assert_eq!(
707 error.downcast_mut::<Failure>().unwrap_err(),
708 DowncastFailure::Shared
709 );
710
711 drop(clone);
712 assert!(error.downcast_mut::<Failure>().is_ok());
713 }
714
715 #[test]
716 fn context_wraps_the_message_and_keeps_the_error_reachable() {
717 let error = Error::from(Failure("boom")).context("loading");
718
719 assert_eq!(error.to_string(), "loading");
720 assert_eq!(format!("{error:#}"), "loading: boom");
721 assert_eq!(error.chain().count(), 2);
722 assert!(error.is::<Failure>());
723 assert_eq!(error.downcast_cloned::<Failure>().unwrap().0, "boom");
724 }
725
726 #[test]
727 fn display_and_debug_walk_the_chain() {
728 let error = Error::from(WithCause {
729 cause: Failure("boom"),
730 });
731
732 assert_eq!(error.to_string(), "outer");
733 assert_eq!(format!("{error:#}"), "outer: boom");
734 assert!(format!("{error:?}").starts_with("outer\n\nCaused by:\n boom"));
735 assert!(format!("{error:#?}").starts_with("WithCause"));
736 }
737
738 #[test]
739 fn box_conversion_keeps_the_message_and_source() {
740 let error = Error::from(WithCause {
741 cause: Failure("boom"),
742 });
743 let shared = error.clone();
744
745 let boxed: Box<dyn std::error::Error + Send + Sync> = error.into();
746 assert_eq!(boxed.to_string(), "outer");
747 assert_eq!(
748 boxed.source().map(ToString::to_string),
749 Some(String::from("boom"))
750 );
751 drop(shared);
752 }
753
754 #[test]
755 fn boxed_errors_convert_with_their_chain() {
756 let boxed: Box<dyn std::error::Error + Send + Sync> = Box::new(WithCause {
757 cause: Failure("boom"),
758 });
759 let error = Error::from_boxed(boxed);
760 assert_eq!(error.to_string(), "outer");
761 assert_eq!(format!("{error:#}"), "outer: boom");
762 }
763
764 #[cfg(feature = "anyhow")]
765 #[test]
766 fn anyhow_errors_convert_with_their_chain() {
767 let error = Error::from_anyhow(anyhow::anyhow!("boom").context("loading"));
768 assert_eq!(error.to_string(), "loading");
769 assert_eq!(format!("{error:#}"), "loading: boom");
770 }
771}