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) {
314 match self.error.downcast_ref::<Untyped>() {
315 Some(erased) => &*erased.0,
316 None => &*self.error,
317 }
318 }
319
320 pub fn location(&self) -> &'static Location<'static> {
322 self.location
323 }
324
325 pub fn children(&self) -> &[Frame] {
327 &self.children
328 }
329}
330
331impl Frame {
333 pub fn probable_cause(&self) -> Option<&Frame> {
340 fn walk<'a>(frame: &'a Frame, depth: usize) -> (usize, usize, &'a Frame) {
341 if frame.children.is_empty() {
342 return (1, depth, frame);
343 }
344
345 let mut total_leafs = 0;
346 let mut best: Option<(usize, usize, &'a Frame)> = None;
347
348 for child in &frame.children {
349 let (leafs, d, f) = walk(child, depth + 1);
350 total_leafs += leafs;
351
352 match best {
353 None => best = Some((leafs, d, f)),
354 Some((bl, bd, _)) => {
355 if leafs > bl || (leafs == bl && d > bd) {
356 best = Some((leafs, d, f));
357 }
358 }
359 }
360 }
361
362 let self_candidate = (total_leafs, depth, frame);
363 match best {
364 None => self_candidate,
365 Some(best_child) => {
366 if total_leafs > best_child.0 || (total_leafs == best_child.0 && depth > best_child.1) {
367 self_candidate
368 } else {
369 best_child
370 }
371 }
372 }
373 }
374
375 if self.children().iter().all(|c| c.children().is_empty()) {
378 if let Some(last) = self.children().last() {
379 return Some(last);
380 }
381 }
382
383 let res = walk(self, 0).2;
384 if std::ptr::addr_eq(res, self) { None } else { Some(res) }
385 }
386
387 pub fn iter_frames(&self) -> impl Iterator<Item = &Frame> + '_ {
390 let mut queue = std::collections::VecDeque::new();
391 queue.push_back(self);
392 BreadthFirstFrames { queue }
393 }
394}
395
396pub struct BreadthFirstFrames<'a> {
398 queue: std::collections::VecDeque<&'a Frame>,
399}
400
401impl<'a> Iterator for BreadthFirstFrames<'a> {
402 type Item = &'a Frame;
403
404 fn next(&mut self) -> Option<Self::Item> {
405 let frame = self.queue.pop_front()?;
406 for child in frame.children() {
407 self.queue.push_back(child);
408 }
409 Some(frame)
410 }
411}
412
413impl<E> From<Exn<E>> for Box<Frame>
414where
415 E: Error + Send + Sync + 'static,
416{
417 fn from(err: Exn<E>) -> Self {
418 err.frame
419 }
420}
421
422#[cfg(feature = "anyhow")]
423impl<E> From<Exn<E>> for anyhow::Error
424where
425 E: Error + Send + Sync + 'static,
426{
427 fn from(err: Exn<E>) -> Self {
428 anyhow::Error::from(err.into_chain())
429 }
430}
431
432impl<E> From<Exn<E>> for Frame
433where
434 E: Error + Send + Sync + 'static,
435{
436 fn from(err: Exn<E>) -> Self {
437 *err.frame
438 }
439}
440
441impl From<Frame> for Exn {
442 fn from(frame: Frame) -> Self {
443 Exn {
444 frame: Box::new(frame),
445 phantom: Default::default(),
446 }
447 }
448}
449
450pub struct Untyped(Box<dyn Error + Send + Sync + 'static>);
454
455impl fmt::Display for Untyped {
456 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457 fmt::Display::fmt(&self.0, f)
458 }
459}
460
461impl fmt::Debug for Untyped {
462 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
463 fmt::Debug::fmt(&self.0, f)
464 }
465}
466
467impl Error for Untyped {
468 fn source(&self) -> Option<&(dyn Error + 'static)> {
469 self.0.source()
470 }
471}
472
473pub struct Something;
475
476impl fmt::Display for Something {
477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478 f.write_str("Something went wrong")
479 }
480}
481
482impl fmt::Debug for Something {
483 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484 fmt::Display::fmt(&self, f)
485 }
486}
487
488impl Error for Something {}
489
490struct SourceError {
492 display: String,
493 alt_display: String,
494 debug: String,
495 alt_debug: String,
496}
497
498impl fmt::Debug for SourceError {
499 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 let dbg = if f.alternate() { &self.alt_debug } else { &self.debug };
501 f.write_str(dbg)
502 }
503}
504
505impl fmt::Display for SourceError {
506 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
507 let ds = if f.alternate() {
508 &self.alt_display
509 } else {
510 &self.display
511 };
512 f.write_str(ds)
513 }
514}
515
516impl Error for SourceError {}
517
518impl SourceError {
519 fn new(err: &dyn Error) -> Self {
520 SourceError {
521 display: err.to_string(),
522 alt_display: format!("{err:#}"),
523 debug: format!("{err:?}"),
524 alt_debug: format!("{err:#?}"),
525 }
526 }
527}
528
529impl<E> From<Exn<E>> for ChainedError
530where
531 E: std::error::Error + Send + Sync + 'static,
532{
533 fn from(mut err: Exn<E>) -> Self {
534 let stack: VecDeque<_> = err.frame.children.drain(..).collect();
535 let location = err.frame.location;
536 ChainedError {
537 err: err.into_box(),
538 location,
539 source: recurse_source_frames(stack),
540 }
541 }
542}
543
544fn recurse_source_frames(mut stack: VecDeque<Frame>) -> Option<Box<ChainedError>> {
545 let frame = stack.pop_front()?;
546 stack.extend(frame.children);
547 Box::new(ChainedError {
548 err: frame.error,
549 location: frame.location,
550 source: recurse_source_frames(stack),
551 })
552 .into()
553}