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
    pub fn downcast_ref<E: Error + 'static>(&self) -> Option<&E> {
14
        self.inner.error.downcast_ref::<E>()
15
    }
16
}
17

            
18
/// This type exists to make [`MercError`] use a "thin pointer" instead of a
19
/// "fat pointer", which reduces the size of our Result by a usize. This does
20
/// introduce an extra indirection, but error handling is a "cold path". We
21
/// don't need to optimize it to that degree.
22
struct InnerMercError {
23
    /// The underlying error
24
    error: Box<dyn Error + Send + Sync + 'static>,
25
    /// A backtrace captured at creation
26
    backtrace: std::backtrace::Backtrace,
27
}
28

            
29
// NOTE: writing the impl this way gives us From<&str>
30
impl<E> From<E> for MercError
31
where
32
    Box<dyn Error + Send + Sync + 'static>: From<E>,
33
{
34
    #[cold]
35
77
    fn from(error: E) -> Self {
36
77
        MercError {
37
77
            inner: Box::new(InnerMercError {
38
77
                error: error.into(),
39
77
                backtrace: std::backtrace::Backtrace::capture(),
40
77
            }),
41
77
        }
42
77
    }
43
}
44

            
45
impl Display for MercError {
46
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
47
        writeln!(f, "{}", self.inner.error)?;
48
        Ok(())
49
    }
50
}
51

            
52
impl Debug for MercError {
53
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54
        writeln!(f, "{:?}", self.inner.error)?;
55
        {
56
            let backtrace = &self.inner.backtrace;
57
            if let std::backtrace::BacktraceStatus::Captured = backtrace.status() {
58
                writeln!(f, "{backtrace}")?;
59
            }
60
        }
61

            
62
        Ok(())
63
    }
64
}