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