1use std::collections::VecDeque;
16use std::error::Error;
17use std::fmt;
18use std::marker::PhantomData;
19use std::ops::Deref;
20use std::panic::Location;
21
22use crate::{ChainedError, Exn, write_location};
23
24impl<E: Error + Send + Sync + 'static> From<E> for Exn<E> {
25 #[track_caller]
26 fn from(error: E) -> Self {
27 Exn::new(error)
28 }
29}
30
31impl<E: Error + Send + Sync + 'static> Exn<E> {
32 #[track_caller]
43 pub fn new(error: E) -> Self {
44 fn walk_sources(error: &dyn Error, location: &'static Location<'static>) -> Vec<Frame> {
45 if let Some(source) = error.source() {
46 let children = vec![Frame {
47 error: Box::new(SourceError::new(source)),
48 location,
49 children: walk_sources(source, location),
50 }];
51 children
52 } else {
53 vec![]
54 }
55 }
56
57 let location = Location::caller();
58 let children = walk_sources(&error, location);
59 let frame = Frame {
60 error: Box::new(error),
61 location,
62 children,
63 };
64
65 Self {
66 frame: Box::new(frame),
67 phantom: PhantomData,
68 }
69 }
70
71 #[track_caller]
73 pub fn raise_all<T, I>(children: I, err: E) -> Self
74 where
75 T: Error + Send + Sync + 'static,
76 I: IntoIterator,
77 I::Item: Into<Exn<T>>,
78 {
79 let mut new_exn = Exn::new(err);
80 for exn in children {
81 let exn = exn.into();
82 new_exn.frame.children.push(*exn.frame);
83 }
84 new_exn
85 }
86
87 #[track_caller]
89 pub fn raise<T: Error + Send + Sync + 'static>(self, err: T) -> Exn<T> {
90 let mut new_exn = Exn::new(err);
91 new_exn.frame.children.push(*self.frame);
92 new_exn
93 }
94
95 #[track_caller]
97 pub fn chain<T: Error + Send + Sync + 'static>(mut self, err: impl Into<Exn<T>>) -> Exn<E> {
98 let err = err.into();
99 self.frame.children.push(*err.frame);
100 self
101 }
102
103 #[track_caller]
105 pub fn chain_all<T, I>(mut self, errors: I) -> Exn<E>
106 where
107 T: Error + Send + Sync + 'static,
108 I: IntoIterator,
109 I::Item: Into<Exn<T>>,
110 {
111 for err in errors {
112 let err = err.into();
113 self.frame.children.push(*err.frame);
114 }
115 self
116 }
117
118 pub fn drain_children(&mut self) -> impl Iterator<Item = Exn> + '_ {
122 self.frame.children.drain(..).map(Exn::from)
123 }
124
125 pub fn erased(self) -> Exn {
127 let untyped_frame = {
128 let Frame {
129 error,
130 location,
131 children,
132 } = *self.frame;
133 let error = Untyped(error);
136 Frame {
137 error: Box::new(error),
138 location,
139 children,
140 }
141 };
142 Exn {
143 frame: Box::new(untyped_frame),
144 phantom: Default::default(),
145 }
146 }
147
148 pub fn error(&self) -> &E {
150 self.frame
151 .error
152 .downcast_ref()
153 .expect("the owned frame always matches the compile-time error type")
154 }
155
156 pub fn into_box(self) -> Box<E> {
161 match self.frame.error.downcast() {
162 Ok(err) => err,
163 Err(_) => unreachable!("The type in the frame is always the type of this instance"),
164 }
165 }
166
167 pub fn into_inner(self) -> E {
172 *self.into_box()
173 }
174
175 pub fn into_error(self) -> crate::Error {
179 self.into()
180 }
181
182 pub fn into_chain(self) -> crate::ChainedError {
187 self.into()
188 }
189
190 pub fn frame(&self) -> &Frame {
192 &self.frame
193 }
194
195 pub fn iter(&self) -> impl Iterator<Item = &Frame> {
198 self.frame().iter_frames()
199 }
200
201 pub fn downcast_any_ref<T: Error + 'static>(&self) -> Option<&T> {
204 self.iter().find_map(|e| e.error.downcast_ref())
205 }
206}
207
208impl<E> Deref for Exn<E>
209where
210 E: Error + Send + Sync + 'static,
211{
212 type Target = E;
213
214 fn deref(&self) -> &Self::Target {
215 self.error()
216 }
217}
218
219impl<E: Error + Send + Sync + 'static> fmt::Debug for Exn<E> {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write_frame_recursive(f, self.frame(), "", ErrorMode::Display, TreeMode::Linearize)
222 }
223}
224
225impl fmt::Debug for Frame {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 write_frame_recursive(f, self, "", ErrorMode::Display, TreeMode::Linearize)
228 }
229}
230
231#[derive(Copy, Clone)]
232enum ErrorMode {
233 Display,
234 Debug,
235}
236
237#[derive(Copy, Clone)]
238enum TreeMode {
239 Linearize,
240 Verbatim,
241}
242
243fn write_frame_recursive(
244 f: &mut fmt::Formatter<'_>,
245 frame: &Frame,
246 prefix: &str,
247 err_mode: ErrorMode,
248 tree_mode: TreeMode,
249) -> fmt::Result {
250 match err_mode {
251 ErrorMode::Display => fmt::Display::fmt(frame.error(), f),
252 ErrorMode::Debug => {
253 write!(f, "{:?}", frame.error())
254 }
255 }?;
256 if !f.alternate() {
257 write_location(f, frame.location)?;
258 }
259
260 let children = frame.children();
261 let children_len = children.len();
262
263 for (cidx, child) in children.iter().enumerate() {
264 write!(f, "\n{prefix}|")?;
265 write!(f, "\n{prefix}└─ ")?;
266
267 let child_child_len = child.children().len();
268 let may_linearize_chain = matches!(tree_mode, TreeMode::Linearize) && children_len == 1 && child_child_len == 1;
269 if may_linearize_chain {
270 write_frame_recursive(f, child, prefix, err_mode, tree_mode)?;
271 } else if cidx < children_len - 1 {
272 write_frame_recursive(f, child, &format!("{prefix}| "), err_mode, tree_mode)?;
273 } else {
274 write_frame_recursive(f, child, &format!("{prefix} "), err_mode, tree_mode)?;
275 }
276 }
277
278 Ok(())
279}
280
281impl<E: Error + Send + Sync + 'static> fmt::Display for Exn<E> {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 fmt::Display::fmt(&self.frame, f)
284 }
285}
286
287impl fmt::Display for Frame {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 if f.alternate() {
290 write_frame_recursive(f, self, "", ErrorMode::Debug, TreeMode::Verbatim)
292 } else {
293 fmt::Display::fmt(self.error(), f)
294 }
295 }
296}
297
298pub struct Frame {
300 error: Box<dyn Error + Send + Sync + 'static>,
302 location: &'static Location<'static>,
304 children: Vec<Frame>,
306}
307
308impl Frame {
309 pub fn error(&self) -> &(dyn Error + Send + Sync + 'static) {
311 &*self.error
312 }
313
314 pub fn location(&self) -> &'static Location<'static> {
316 self.location
317 }
318
319 pub fn children(&self) -> &[Frame] {
321 &self.children
322 }
323}
324
325impl Frame {
327 pub fn probable_cause(&self) -> Option<&Frame> {
334 fn walk<'a>(frame: &'a Frame, depth: usize) -> (usize, usize, &'a Frame) {
335 if frame.children.is_empty() {
336 return (1, depth, frame);
337 }
338
339 let mut total_leafs = 0;
340 let mut best: Option<(usize, usize, &'a Frame)> = None;
341
342 for child in &frame.children {
343 let (leafs, d, f) = walk(child, depth + 1);
344 total_leafs += leafs;
345
346 match best {
347 None => best = Some((leafs, d, f)),
348 Some((bl, bd, _)) => {
349 if leafs > bl || (leafs == bl && d > bd) {
350 best = Some((leafs, d, f));
351 }
352 }
353 }
354 }
355
356 let self_candidate = (total_leafs, depth, frame);
357 match best {
358 None => self_candidate,
359 Some(best_child) => {
360 if total_leafs > best_child.0 || (total_leafs == best_child.0 && depth > best_child.1) {
361 self_candidate
362 } else {
363 best_child
364 }
365 }
366 }
367 }
368
369 if self.children().iter().all(|c| c.children().is_empty()) {
372 if let Some(last) = self.children().last() {
373 return Some(last);
374 }
375 }
376
377 let res = walk(self, 0).2;
378 if std::ptr::addr_eq(res, self) { None } else { Some(res) }
379 }
380
381 pub fn iter_frames(&self) -> impl Iterator<Item = &Frame> + '_ {
384 let mut queue = std::collections::VecDeque::new();
385 queue.push_back(self);
386 BreadthFirstFrames { queue }
387 }
388}
389
390pub struct BreadthFirstFrames<'a> {
392 queue: std::collections::VecDeque<&'a Frame>,
393}
394
395impl<'a> Iterator for BreadthFirstFrames<'a> {
396 type Item = &'a Frame;
397
398 fn next(&mut self) -> Option<Self::Item> {
399 let frame = self.queue.pop_front()?;
400 for child in frame.children() {
401 self.queue.push_back(child);
402 }
403 Some(frame)
404 }
405}
406
407impl<E> From<Exn<E>> for Box<Frame>
408where
409 E: Error + Send + Sync + 'static,
410{
411 fn from(err: Exn<E>) -> Self {
412 err.frame
413 }
414}
415
416#[cfg(feature = "anyhow")]
417impl<E> From<Exn<E>> for anyhow::Error
418where
419 E: Error + Send + Sync + 'static,
420{
421 fn from(err: Exn<E>) -> Self {
422 anyhow::Error::from(err.into_chain())
423 }
424}
425
426impl<E> From<Exn<E>> for Frame
427where
428 E: Error + Send + Sync + 'static,
429{
430 fn from(err: Exn<E>) -> Self {
431 *err.frame
432 }
433}
434
435impl From<Frame> for Exn {
436 fn from(frame: Frame) -> Self {
437 Exn {
438 frame: Box::new(frame),
439 phantom: Default::default(),
440 }
441 }
442}
443
444pub struct Untyped(Box<dyn Error + Send + Sync + 'static>);
448
449impl fmt::Display for Untyped {
450 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451 fmt::Display::fmt(&self.0, f)
452 }
453}
454
455impl fmt::Debug for Untyped {
456 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457 fmt::Debug::fmt(&self.0, f)
458 }
459}
460
461impl Error for Untyped {}
462
463pub struct Something;
465
466impl fmt::Display for Something {
467 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468 f.write_str("Something went wrong")
469 }
470}
471
472impl fmt::Debug for Something {
473 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474 fmt::Display::fmt(&self, f)
475 }
476}
477
478impl Error for Something {}
479
480struct SourceError {
482 display: String,
483 alt_display: String,
484 debug: String,
485 alt_debug: String,
486}
487
488impl fmt::Debug for SourceError {
489 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490 let dbg = if f.alternate() { &self.alt_debug } else { &self.debug };
491 f.write_str(dbg)
492 }
493}
494
495impl fmt::Display for SourceError {
496 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497 let ds = if f.alternate() {
498 &self.alt_display
499 } else {
500 &self.display
501 };
502 f.write_str(ds)
503 }
504}
505
506impl Error for SourceError {}
507
508impl SourceError {
509 fn new(err: &dyn Error) -> Self {
510 SourceError {
511 display: err.to_string(),
512 alt_display: format!("{err:#}"),
513 debug: format!("{err:?}"),
514 alt_debug: format!("{err:#?}"),
515 }
516 }
517}
518
519impl<E> From<Exn<E>> for ChainedError
520where
521 E: std::error::Error + Send + Sync + 'static,
522{
523 fn from(mut err: Exn<E>) -> Self {
524 let stack: VecDeque<_> = err.frame.children.drain(..).collect();
525 let location = err.frame.location;
526 ChainedError {
527 err: err.into_box(),
528 location,
529 source: recurse_source_frames(stack),
530 }
531 }
532}
533
534fn recurse_source_frames(mut stack: VecDeque<Frame>) -> Option<Box<ChainedError>> {
535 let frame = stack.pop_front()?;
536 stack.extend(frame.children);
537 Box::new(ChainedError {
538 err: frame.error,
539 location: frame.location,
540 source: recurse_source_frames(stack),
541 })
542 .into()
543}