1use std::{
4 backtrace::{Backtrace, BacktraceStatus},
5 fmt::{self, Display, Formatter},
6};
7
8#[macro_export]
10macro_rules! err {
11 (with: $error:expr, $($arg:tt)*) => {{
12 let error = format!($($arg)*);
13 let source = Box::new($error);
14 $crate::error::Error::new(error, Some(source))
15 }};
16 ($($arg:tt)*) => {{
17 let error = format!($($arg)*);
18 $crate::error::Error::new(error, None)
19 }};
20}
21
22#[derive(Debug)]
24pub struct Error {
25 pub error: String,
27 pub source: Option<Box<dyn std::error::Error + Send>>,
29 pub backtrace: Backtrace,
31}
32impl Error {
33 pub fn new(error: String, source: Option<Box<dyn std::error::Error + Send>>) -> Self {
35 let backtrace = Backtrace::capture();
36 Self { error, source, backtrace }
37 }
38
39 pub fn has_backtrace(&self) -> bool {
41 self.backtrace.status() == BacktraceStatus::Captured
42 }
43}
44impl Display for Error {
45 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
46 writeln!(f, "{}", self.error)?;
48
49 if let Some(source) = &self.source {
51 writeln!(f, " caused by: {source}")?;
52 }
53 Ok(())
54 }
55}
56impl std::error::Error for Error {
57 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
58 #[allow(clippy::borrowed_box, reason = "Type gymnastics to remove the `Send`")]
60 let source: &Box<dyn std::error::Error + Send> = self.source.as_ref()?;
61 let source: &dyn std::error::Error = source.as_ref();
62 Some(source)
63 }
64}