1use crate::SharedString;
3use std::{any::Any, error, fmt};
4
5mod source;
6
7use source::Source;
8
9#[derive(Debug)]
11pub struct Error {
12 message: SharedString,
14 source: Option<Box<Error>>,
16 context: Option<Box<dyn Any + Send>>,
18}
19
20impl Clone for Error {
21 #[inline]
22 fn clone(&self) -> Self {
23 Self {
24 message: self.message.clone(),
25 source: self.source.clone(),
26 context: None,
27 }
28 }
29}
30
31impl PartialEq for Error {
32 #[inline]
33 fn eq(&self, other: &Self) -> bool {
34 self.message == other.message && self.source == other.source
35 }
36}
37
38impl Error {
39 #[inline]
41 pub fn new(message: impl Into<SharedString>) -> Self {
42 Self {
43 message: message.into(),
44 source: None,
45 context: None,
46 }
47 }
48
49 #[inline]
51 pub fn with_source(message: impl Into<SharedString>, source: impl Into<Error>) -> Self {
52 Self {
53 message: message.into(),
54 source: Some(Box::new(source.into())),
55 context: None,
56 }
57 }
58
59 #[inline]
61 pub fn from_error(err: impl error::Error) -> Self {
62 Self {
63 message: err.to_string().into(),
64 source: err.source().map(|err| Box::new(Self::new(err.to_string()))),
65 context: None,
66 }
67 }
68
69 #[inline]
71 pub fn wrap(self, message: impl Into<SharedString>) -> Self {
72 Self {
73 message: message.into(),
74 source: Some(Box::new(self)),
75 context: None,
76 }
77 }
78
79 #[inline]
81 pub fn set_context<T: Send + 'static>(&mut self, context: T) {
82 self.context = Some(Box::new(context));
83 }
84
85 #[inline]
87 pub fn get_context<T: Send + 'static>(&self) -> Option<&T> {
88 self.context
89 .as_ref()
90 .and_then(|ctx| ctx.downcast_ref::<T>())
91 }
92
93 #[inline]
95 pub fn get_context_mut<T: Send + 'static>(&mut self) -> Option<&mut T> {
96 self.context
97 .as_mut()
98 .and_then(|ctx| ctx.downcast_mut::<T>())
99 }
100
101 #[inline]
103 pub fn take_context<T: Send + 'static>(&mut self) -> Option<Box<T>> {
104 self.context.take().and_then(|ctx| ctx.downcast::<T>().ok())
105 }
106
107 #[inline]
109 pub fn has_context<T: Send + 'static>(&self) -> bool {
110 self.context.as_ref().is_some_and(|ctx| ctx.is::<T>())
111 }
112
113 #[inline]
115 pub fn message(&self) -> &str {
116 self.message.as_ref()
117 }
118
119 #[inline]
121 pub fn source(&self) -> Option<&Error> {
122 self.source.as_deref()
123 }
124
125 #[inline]
127 pub fn sources(&self) -> impl Iterator<Item = &Error> {
128 Source::new(self)
129 }
130
131 #[inline]
135 pub fn root_source(&self) -> Option<&Error> {
136 self.sources().last()
137 }
138}
139
140impl<E: error::Error + Send + 'static> From<E> for Error {
141 #[inline]
142 fn from(err: E) -> Self {
143 Self {
144 message: err.to_string().into(),
145 source: err.source().map(|err| Box::new(Self::new(err.to_string()))),
146 context: Some(Box::new(err)),
147 }
148 }
149}
150
151impl fmt::Display for Error {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 let message = self.message();
154 if let Some(source) = &self.source {
155 let source = source.message();
156 let root_source = self.root_source().map(|err| err.message());
157 if root_source != Some(source) {
158 tracing::error!(root_source, source, message);
159 } else {
160 tracing::error!(root_source, message);
161 }
162 } else {
163 tracing::error!(message);
164 }
165 write!(f, "{message}")
166 }
167}
168
169#[macro_export]
171macro_rules! bail {
172 ($message:literal $(,)?) => {{
173 tracing::warn!($message);
174 return Err(Error::new($message));
175 }};
176 ($err:expr $(,)?) => {{
177 tracing::warn!($err);
178 return Err(Error::from($err));
179 }};
180 ($fmt:expr, $($arg:tt)+) => {{
181 let message = format!($fmt, $($arg)+);
182 tracing::warn!(message);
183 return Err(Error::new(message));
184 }};
185}
186
187#[macro_export]
189macro_rules! warn {
190 ($message:literal $(,)?) => {{
191 tracing::warn!($message);
192 Error::new($message)
193 }};
194 ($err:expr $(,)?) => {{
195 tracing::warn!($err);
196 Error::from($err)
197 }};
198 ($fmt:expr, $($arg:tt)+) => {{
199 let message = format!($fmt, $($arg)+);
200 tracing::warn!(message);
201 Error::new(message)
202 }};
203}