1
use core::error::Error;
2
use core::fmt::Debug;
3
use core::fmt::Display;
4

            
5
/// The Merc error type. This has a blanket [`From`] impl for any type that implements Rust's [`Error`],
6
/// meaning it can be used as a "catch all" error. Captures a backtrace that can be printed from this object.
7
pub struct MercError {
8
    inner: Box<InnerMercError>,
9
}
10

            
11
impl MercError {
12
    /// Attempts to downcast the internal error to the given type.
13
    ///
14
    /// This is useful when you need to inspect or handle a specific error type
15
    /// that may have been wrapped inside a `MercError`.
16
    ///
17
    /// # Examples
18
    ///
19
    /// ```
20
    /// use merc_utilities::MercError;
21
    /// use std::io;
22
    ///
23
    /// fn handle_error(err: MercError) {
24
    ///     // Check if the underlying error is an IO error
25
    ///     if let Some(io_err) = err.downcast_ref::<io::Error>() {
26
    ///         println!("IO error occurred: {}", io_err.kind());
27
    ///     } else {
28
    ///         println!("Some other error occurred");
29
    ///     }
30
    /// }
31
    /// ```
32
    #[must_use]
33
    pub fn downcast_ref<E: Error + 'static>(&self) -> Option<&E> {
34
        self.inner.error.downcast_ref::<E>()
35
    }
36
}
37

            
38
/// This type exists to make [`MercError`] use a "thin pointer" instead of a
39
/// "fat pointer", which reduces the size of our Result by a usize. This does
40
/// introduce an extra indirection, but error handling is a "cold path". We
41
/// don't need to optimize it to that degree.
42
struct InnerMercError {
43
    /// The underlying error
44
    error: Box<dyn Error + Send + Sync + 'static>,
45
    /// A backtrace captured at creation
46
    backtrace: std::backtrace::Backtrace,
47
}
48

            
49
// NOTE: writing the impl this way gives us From<&str>
50
impl<E> From<E> for MercError
51
where
52
    Box<dyn Error + Send + Sync + 'static>: From<E>,
53
{
54
    #[cold]
55
1208
    fn from(error: E) -> Self {
56
1208
        MercError {
57
1208
            inner: Box::new(InnerMercError {
58
1208
                error: error.into(),
59
1208
                backtrace: std::backtrace::Backtrace::capture(),
60
1208
            }),
61
1208
        }
62
1208
    }
63
}
64

            
65
impl MercError {
66
    /// Appends the captured backtrace on its own line, if one was captured.
67
    /// Shared by the `Display` and `Debug` impls.
68
    fn fmt_backtrace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69
        let backtrace = &self.inner.backtrace;
70
        if let std::backtrace::BacktraceStatus::Captured = backtrace.status() {
71
            write!(f, "\n{backtrace}")?;
72
        }
73
        Ok(())
74
    }
75
}
76

            
77
impl Display for MercError {
78
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
79
        write!(f, "{}", self.inner.error)?;
80
        self.fmt_backtrace(f)
81
    }
82
}
83

            
84
impl Debug for MercError {
85
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
86
        write!(f, "{:?}", self.inner.error)?;
87
        self.fmt_backtrace(f)
88
    }
89
}