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