1use std::{
2 fmt::{self, Formatter},
3 sync::Arc,
4};
5
6use crate::{AnyError, Location, Meta, backtrace_enabled};
7
8#[derive(Debug, Copy, Clone)]
10pub enum SourceFormat {
11 OneLine,
13 MultiLine {
15 location: bool,
17 },
18}
19
20pub trait StackError: fmt::Display + fmt::Debug + Send + Sync {
25 fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static);
27
28 fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync>;
30
31 fn as_dyn(&self) -> &dyn StackError;
33
34 fn meta(&self) -> Option<&Meta>;
36
37 fn source(&self) -> Option<ErrorRef<'_>>;
39
40 fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
42
43 fn is_transparent(&self) -> bool;
45
46 fn as_ref(&self) -> ErrorRef<'_> {
50 ErrorRef::Stack(self.as_dyn())
51 }
52
53 fn stack(&self) -> Chain<'_> {
55 Chain::stack(self.as_ref())
56 }
57
58 fn sources(&self) -> Chain<'_> {
60 Chain::sources(self.as_ref())
61 }
62
63 fn report(&self) -> Report<'_> {
65 Report::new(self.as_dyn())
66 }
67}
68
69pub trait StackErrorExt: StackError + Sized + 'static {
71 #[track_caller]
75 fn into_any(self) -> AnyError {
76 AnyError::from_stack(self)
77 }
78
79 #[track_caller]
81 fn context(self, context: impl fmt::Display) -> AnyError {
82 self.into_any().context(context)
83 }
84}
85
86impl<T: StackError + Sized + 'static> StackErrorExt for T {}
87
88#[derive(Copy, Clone, Debug)]
93pub enum ErrorRef<'a> {
94 Std(&'a (dyn std::error::Error + 'static), Option<&'a Meta>),
96 Stack(&'a dyn StackError),
98}
99
100impl<'a> ErrorRef<'a> {
101 pub fn std(err: &'a (dyn std::error::Error + 'static)) -> ErrorRef<'a> {
103 ErrorRef::Std(err, None)
104 }
105
106 pub(crate) fn std_with_meta(
107 err: &'a (dyn std::error::Error + 'static),
108 meta: &'a Meta,
109 ) -> ErrorRef<'a> {
110 ErrorRef::Std(err, Some(meta))
111 }
112
113 pub fn stack(err: &dyn StackError) -> ErrorRef<'_> {
115 ErrorRef::Stack(err)
116 }
117
118 pub fn is_transparent(&self) -> bool {
120 match self {
121 ErrorRef::Std(_, _) => false,
122 ErrorRef::Stack(error) => error.is_transparent(),
123 }
124 }
125
126 pub fn as_std(self) -> &'a dyn std::error::Error {
128 match self {
129 ErrorRef::Std(error, _) => error,
130 ErrorRef::Stack(error) => error.as_std(),
131 }
132 }
133
134 pub fn source(self) -> Option<ErrorRef<'a>> {
136 match self {
137 Self::Std(error, _) => error.source().map(ErrorRef::std),
138 Self::Stack(error) => StackError::source(error),
139 }
140 }
141
142 pub fn meta(&self) -> Option<&Meta> {
144 match self {
145 ErrorRef::Std(_, meta) => *meta,
146 ErrorRef::Stack(error) => error.meta(),
147 }
148 }
149
150 pub(crate) fn fmt_location(&self, f: &mut Formatter) -> fmt::Result {
152 fmt_location(self.meta().and_then(|m| m.location()), f)
153 }
154
155 pub fn fmt_message(&self, f: &mut Formatter) -> fmt::Result {
157 match self {
158 ErrorRef::Std(error, _) => fmt::Display::fmt(error, f),
159 ErrorRef::Stack(error) => error.fmt_message(f),
160 }
161 }
162
163 pub fn downcast_ref<T: std::error::Error + 'static>(self) -> Option<&'a T> {
165 match self {
166 ErrorRef::Std(error, _) => error.downcast_ref(),
167 ErrorRef::Stack(error) => error.as_std().downcast_ref(),
168 }
169 }
170}
171
172impl<'a> fmt::Display for ErrorRef<'a> {
173 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
174 match self {
175 Self::Std(error, _) => write!(f, "{error}"),
176 Self::Stack(error) => write!(f, "{error}"),
177 }
178 }
179}
180
181#[derive(Clone, Copy)]
183pub struct Report<'a> {
184 inner: &'a dyn StackError,
185 location: bool,
186 sources: Option<SourceFormat>,
187}
188
189impl<'a> Report<'a> {
190 pub(crate) fn new(inner: &'a dyn StackError) -> Self {
191 let location = backtrace_enabled();
192 Self {
193 inner,
194 location,
195 sources: None,
196 }
197 }
198
199 pub fn full(mut self) -> Self {
201 let location = backtrace_enabled();
202 self.location = location;
203 self.sources = Some(SourceFormat::MultiLine { location });
204 self
205 }
206
207 pub fn location(mut self, value: bool) -> Self {
212 self.location = value;
213 self
214 }
215
216 pub fn sources(mut self, format: Option<SourceFormat>) -> Self {
218 self.sources = format;
219 self
220 }
221}
222
223impl<'a> fmt::Display for Report<'a> {
224 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
225 match self.sources {
226 None => {
227 let item = self
228 .inner
229 .stack()
230 .find(|item| !item.is_transparent())
231 .unwrap_or_else(|| self.inner.as_ref());
232 item.fmt_message(f)?;
233 }
234 Some(format) => {
235 let mut stack = self
236 .inner
237 .stack()
238 .filter(|s| self.location || !s.is_transparent())
239 .peekable();
240 let mut is_first = true;
241 while let Some(item) = stack.next() {
242 match format {
243 SourceFormat::OneLine => {
244 if !is_first {
245 write!(f, ": ")?;
246 }
247 item.fmt_message(f)?;
248 }
249 SourceFormat::MultiLine { location } => {
250 if !is_first {
251 write!(f, " ")?;
252 }
253 item.fmt_message(f)?;
254 if location {
255 item.fmt_location(f)?;
256 }
257 if stack.peek().is_some() {
258 writeln!(f)?;
259 if is_first {
260 writeln!(f, "Caused by:")?;
261 }
262 }
263 }
264 }
265 is_first = false;
266 }
267 }
268 }
269 Ok(())
270 }
271}
272
273fn fmt_location(location: Option<&Location>, f: &mut Formatter) -> fmt::Result {
274 if let Some(location) = location {
275 write!(f, " ({})", location)?;
276 }
277 Ok(())
278}
279
280pub struct Chain<'a> {
282 item: Option<ErrorRef<'a>>,
283 skip: bool,
284}
285
286impl<'a> Chain<'a> {
287 fn stack(item: ErrorRef<'a>) -> Self {
289 Self {
290 item: Some(item),
291 skip: false,
292 }
293 }
294
295 fn sources(item: ErrorRef<'a>) -> Self {
297 Self {
298 item: Some(item),
299 skip: true,
300 }
301 }
302}
303
304impl<'a> Iterator for Chain<'a> {
305 type Item = ErrorRef<'a>;
306 fn next(&mut self) -> Option<Self::Item> {
307 loop {
308 let item = self.item?;
309 self.item = item.source();
310 if self.skip {
311 self.skip = false;
312 } else {
313 return Some(item);
314 }
315 }
316 }
317}
318
319macro_rules! impl_stack_error_for_std_error {
320 ($ty:ty) => {
321 impl StackError for $ty {
322 fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
323 self
324 }
325
326 fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync> {
327 self
328 }
329
330 fn as_dyn(&self) -> &dyn StackError {
331 self
332 }
333
334 fn meta(&self) -> Option<&Meta> {
335 None
336 }
337
338 fn source(&self) -> Option<ErrorRef<'_>> {
339 std::error::Error::source(self).map(ErrorRef::std)
340 }
341
342 fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 fmt::Display::fmt(self, f)
344 }
345
346 fn is_transparent(&self) -> bool {
347 false
348 }
349 }
350 };
351}
352
353impl_stack_error_for_std_error!(std::io::Error);
354impl_stack_error_for_std_error!(std::fmt::Error);
355impl_stack_error_for_std_error!(std::str::Utf8Error);
356impl_stack_error_for_std_error!(std::string::FromUtf8Error);
357impl_stack_error_for_std_error!(std::net::AddrParseError);
358impl_stack_error_for_std_error!(std::array::TryFromSliceError);
359
360#[cfg(feature = "anyhow")]
361impl StackError for anyhow::Error {
362 fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
363 std::convert::AsRef::as_ref(self)
364 }
365
366 fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync> {
367 self.into_boxed_dyn_error()
368 }
369
370 fn as_dyn(&self) -> &dyn StackError {
371 self
372 }
373
374 fn meta(&self) -> Option<&Meta> {
375 None
376 }
377
378 fn source(&self) -> Option<ErrorRef<'_>> {
379 anyhow::Error::chain(self).next().map(ErrorRef::std)
380 }
381
382 fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 fmt::Display::fmt(self, f)
384 }
385
386 fn is_transparent(&self) -> bool {
387 false
388 }
389}
390
391impl<T: StackError + std::error::Error + Sized + 'static> StackError for Arc<T> {
392 fn as_std(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
393 (**self).as_std()
394 }
395
396 fn into_std(self: Box<Self>) -> Box<dyn std::error::Error + Send + Sync> {
397 self
398 }
399
400 fn as_dyn(&self) -> &dyn StackError {
401 (**self).as_dyn()
402 }
403
404 fn meta(&self) -> Option<&Meta> {
405 (**self).meta()
406 }
407
408 fn source(&self) -> Option<ErrorRef<'_>> {
409 StackError::source(&**self)
410 }
411
412 fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 (**self).fmt_message(f)
414 }
415
416 fn is_transparent(&self) -> bool {
417 (**self).is_transparent()
418 }
419}