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::concrete::chain::ErrorHandle;
23use crate::{Metadata, types::ChainedError, write_location};
24
25pub struct Exn<E: std::error::Error + Send + Sync + 'static = Untyped> {
78 frame: Box<Frame>,
80 phantom: PhantomData<E>,
81}
82
83#[track_caller]
85#[expect(
86 clippy::unnecessary_box_returns,
87 reason = "erasure retains the existing frame allocation"
88)]
89pub(super) fn into_frame<E: Error + Send + Sync + 'static>(error: E) -> Box<Frame> {
90 #[cfg(any(feature = "tree-error", not(feature = "auto-chain-error")))]
92 {
93 crate::ErrorExt::raise(error).into_frame()
94 }
95 #[cfg(all(feature = "auto-chain-error", not(feature = "tree-error")))]
96 {
97 Exn::new(error).frame
98 }
99}
100
101impl<E: Error + Send + Sync + 'static> From<E> for Exn<E> {
102 #[track_caller]
103 fn from(error: E) -> Self {
104 Exn::new(error)
105 }
106}
107
108impl<E: Error + Send + Sync + 'static> Exn<E> {
109 #[track_caller]
116 pub fn new(error: E) -> Self {
117 let frame = Frame {
118 source: FrameSource {
119 error: Box::new(error),
120 location: Location::caller(),
121 children: Vec::new(),
122 },
123 };
124
125 Self {
126 frame: Box::new(frame),
127 phantom: PhantomData,
128 }
129 }
130
131 #[track_caller]
132 pub(super) fn with_cause(cause: impl Error + Send + Sync + 'static, error: E) -> Self {
133 let cause = into_frame(cause);
134 let mut exn = Exn::new(error);
135 exn.frame.source.children.push(*cause);
136 exn
137 }
138
139 #[track_caller]
141 pub fn raise_all<T, I>(children: I, err: E) -> Self
142 where
143 T: Error + Send + Sync + 'static,
144 I: IntoIterator,
145 I::Item: Into<Exn<T>>,
146 {
147 let mut new_exn = Exn::new(err);
148 for exn in children {
149 let exn = exn.into();
150 new_exn.frame.source.children.push(*exn.frame);
151 }
152 new_exn
153 }
154
155 #[track_caller]
157 pub fn raise<T: Error + Send + Sync + 'static>(self, err: T) -> Exn<T> {
158 let mut new_exn = Exn::new(err);
159 new_exn.frame.source.children.push(*self.frame);
160 new_exn
161 }
162
163 #[track_caller]
165 pub fn chain<T: Error + Send + Sync + 'static>(mut self, err: impl Into<Exn<T>>) -> Exn<E> {
166 let err = err.into();
167 self.frame.source.children.push(*err.frame);
168 self
169 }
170
171 #[track_caller]
173 pub fn chain_all<T, I>(mut self, errors: I) -> Exn<E>
174 where
175 T: Error + Send + Sync + 'static,
176 I: IntoIterator,
177 I::Item: Into<Exn<T>>,
178 {
179 for err in errors {
180 let err = err.into();
181 self.frame.source.children.push(*err.frame);
182 }
183 self
184 }
185
186 pub fn drain_children(&mut self) -> impl Iterator<Item = Exn> + '_ {
191 self.frame.source.children.drain(..).map(Exn::from)
192 }
193
194 pub fn erased(self) -> Exn {
197 Exn::from_boxed_frame(self.frame)
198 }
199
200 pub fn error(&self) -> &E {
202 self.frame
203 .source
204 .error
205 .downcast_ref()
206 .expect("the owned frame always matches the compile-time error type")
207 }
208
209 pub fn into_box(self) -> Box<E> {
214 match self.frame.source.error.downcast() {
215 Ok(err) => err,
216 Err(_) => unreachable!("The type in the frame is always the type of this instance"),
217 }
218 }
219
220 pub fn into_inner(self) -> E {
225 *self.into_box()
226 }
227
228 pub fn into_error(self) -> crate::Error {
232 self.into()
233 }
234
235 pub fn into_chain(self) -> ChainedError {
240 self.into()
241 }
242
243 pub fn frame(&self) -> &Frame {
245 &self.frame
246 }
247
248 pub fn iter(&self) -> impl Iterator<Item = &Frame> {
251 self.frame().iter_frames()
252 }
253
254 pub fn iter_errors(&self) -> impl Iterator<Item = &(dyn Error + 'static)> + '_ {
258 self.frame.iter_errors_with_locations().map(|source| source.error())
259 }
260
261 pub fn metadata(&self) -> impl Iterator<Item = &Metadata> + '_ {
269 self.iter_errors()
270 .filter_map(|error| error.downcast_ref::<crate::Message>())
271 .map(|error| &error.values)
272 .filter(|values| !values.is_empty())
273 }
274
275 pub fn metadata_merged(&self) -> Metadata {
282 let mut merged = Metadata::new();
283 for values in self.metadata() {
284 merged.extend(values.iter().map(|(key, value)| (key.clone(), value.clone())));
285 }
286 merged
287 }
288
289 pub fn probable_cause(&self) -> &(dyn Error + 'static) {
294 self.frame.probable_cause().unwrap_or_else(|| self.frame.error())
295 }
296
297 pub fn downcast_any_ref<T: Error + 'static>(&self) -> Option<&T> {
302 self.iter_errors().find_map(|error| error.downcast_ref())
303 }
304}
305
306impl<E> Deref for Exn<E>
307where
308 E: Error + Send + Sync + 'static,
309{
310 type Target = E;
311
312 fn deref(&self) -> &Self::Target {
313 self.error()
314 }
315}
316
317impl<E: Error + Send + Sync + 'static> fmt::Debug for Exn<E> {
318 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319 write_frame_recursive(f, self.frame(), ErrorMode::Display)
320 }
321}
322
323impl fmt::Debug for Frame {
324 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325 write_frame_recursive(f, self, ErrorMode::Display)
326 }
327}
328
329#[derive(Copy, Clone)]
330pub(crate) enum ErrorMode {
331 Display,
332 Debug,
333}
334
335impl ErrorMode {
336 pub(crate) fn fmt(self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result {
337 if let Some(io) = error.downcast_ref::<std::io::Error>()
338 && io.get_ref().is_some()
339 {
340 return write!(f, "I/O error ({:?})", io.kind());
342 }
343 match self {
345 ErrorMode::Display => write!(f, "{error}"),
346 ErrorMode::Debug => write!(f, "{error:?}"),
347 }
348 }
349}
350
351fn write_frame_recursive(f: &mut fmt::Formatter<'_>, frame: &Frame, err_mode: ErrorMode) -> fmt::Result {
352 if crate::error::is_transparent_marker(frame.error()) {
353 let children = ErrorNode::Frame(frame).children();
354 if !children.is_empty() {
355 for (index, child) in children.into_iter().enumerate() {
356 if index != 0 {
357 writeln!(f)?;
358 }
359 write_error_node_recursive(f, child, &mut Vec::new(), &mut 0, err_mode, true)?;
360 }
361 return Ok(());
362 }
363 }
364 write_error_node_recursive(f, ErrorNode::Frame(frame), &mut Vec::new(), &mut 0, err_mode, true)
365}
366
367fn write_error_node_recursive(
368 f: &mut fmt::Formatter<'_>,
369 node: ErrorNode<'_>,
370 siblings_follow: &mut Vec<bool>,
371 number: &mut usize,
372 err_mode: ErrorMode,
373 linearize: bool,
374) -> fmt::Result {
375 let children = node.children();
376 let is_chain = children.len() == 1
378 || (!children.is_empty()
379 && children
380 .iter()
381 .all(|child| matches!(child, ErrorNode::FlatSource { .. })));
382 let continues_chain = linearize && is_chain;
383 if !siblings_follow.is_empty() {
384 f.write_str("\n ")?;
385 if let Some((has_sibling, ancestors)) = siblings_follow[1..].split_last() {
387 for has_sibling in ancestors {
388 f.write_str(if *has_sibling { "│ " } else { " " })?;
389 }
390 f.write_str(if *has_sibling || continues_chain {
391 "├─"
392 } else {
393 "└─"
394 })?;
395 }
396 write!(f, "{number}: ")?;
397 }
398 err_mode.fmt(node.root_error(), f)?;
399 if !f.alternate() {
400 write_location(f, node.location())?;
401 }
402
403 if children.is_empty() {
404 return Ok(());
405 }
406 if siblings_follow.is_empty() {
407 f.write_str("\n\nCaused by:")?;
408 }
409 if continues_chain {
410 let has_sibling = siblings_follow.last().copied().unwrap_or(false);
411 let child_count = children.len();
412 for (index, child) in children.into_iter().enumerate() {
413 if siblings_follow.is_empty() {
414 siblings_follow.push(false);
415 } else {
416 *number += 1;
417 }
418 if let Some(last) = siblings_follow.last_mut() {
419 *last = has_sibling || index + 1 < child_count;
420 }
421 write_error_node_recursive(f, child, siblings_follow, number, err_mode, true)?;
422 }
423 if let Some(last) = siblings_follow.last_mut() {
424 *last = has_sibling;
425 }
426 return Ok(());
427 }
428 let child_count = children.len();
429 for (mut number, child) in children.into_iter().enumerate() {
430 siblings_follow.push(number + 1 < child_count);
431 write_error_node_recursive(f, child, siblings_follow, &mut number, err_mode, is_chain)?;
432 siblings_follow.pop();
433 }
434
435 Ok(())
436}
437
438impl<E: Error + Send + Sync + 'static> fmt::Display for Exn<E> {
439 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440 fmt::Display::fmt(&self.frame, f)
441 }
442}
443
444impl<E: Error + Send + Sync + 'static> PartialEq<str> for Exn<E> {
445 fn eq(&self, other: &str) -> bool {
446 crate::root_error_eq(self.frame().error(), other)
447 }
448}
449
450impl<E: Error + Send + Sync + 'static> PartialEq<&str> for Exn<E> {
451 fn eq(&self, other: &&str) -> bool {
452 <Self as PartialEq<str>>::eq(self, other)
453 }
454}
455
456impl<E: Error + Send + Sync + 'static> PartialEq<String> for Exn<E> {
457 fn eq(&self, other: &String) -> bool {
458 <Self as PartialEq<str>>::eq(self, other)
459 }
460}
461
462impl fmt::Display for Frame {
463 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464 if f.alternate() {
465 write_frame_recursive(f, self, ErrorMode::Debug)
467 } else {
468 if crate::error::is_transparent_marker(self.error())
469 && let Some(diagnostic) = self.iter_errors_with_locations().next()
470 {
471 return fmt::Display::fmt(diagnostic.error(), f);
472 }
473 fmt::Display::fmt(self.error(), f)
474 }
475 }
476}
477
478pub struct Frame {
480 source: FrameSource,
481}
482
483pub(crate) struct FrameSource {
486 error: Box<dyn Error + Send + Sync + 'static>,
487 location: &'static Location<'static>,
488 children: Vec<Frame>,
489}
490
491impl fmt::Display for FrameSource {
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 write!(f, "{}", self.error())
495 }
496}
497
498impl fmt::Debug for FrameSource {
499 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500 fmt::Debug::fmt(self.error(), f)
501 }
502}
503
504impl Error for FrameSource {
505 fn source(&self) -> Option<&(dyn Error + 'static)> {
506 crate::error::native_source(self.error()).or_else(|| {
507 self.children.first().map(|frame| {
508 if frame.children().is_empty() {
509 frame.error() as &(dyn Error + 'static)
510 } else {
511 &frame.source as &(dyn Error + 'static)
512 }
513 })
514 })
515 }
516}
517
518impl FrameSource {
519 pub(crate) fn error(&self) -> &(dyn Error + Send + Sync + 'static) {
520 let mut error = &*self.error;
521 loop {
522 if let Some(erased) = error.downcast_ref::<Untyped>() {
523 error = &*erased.0;
524 } else if let Some(shared) = error.downcast_ref::<ErrorHandle>() {
525 error = shared.owned_error();
526 } else {
527 return error;
528 }
529 }
530 }
531
532 pub(crate) fn location(&self) -> &'static Location<'static> {
533 self.location
534 }
535
536 pub(crate) fn children(&self) -> &[Frame] {
537 &self.children
538 }
539}
540
541impl Frame {
542 pub fn error(&self) -> &(dyn Error + Send + Sync + 'static) {
547 self.source.error()
548 }
549
550 pub fn location(&self) -> &'static Location<'static> {
554 self.source.location()
555 }
556
557 pub fn children(&self) -> &[Frame] {
562 self.source.children()
563 }
564
565 pub(crate) fn source_frame(&self) -> &FrameSource {
566 &self.source
567 }
568}
569
570#[derive(Clone, Copy)]
576pub(crate) enum ErrorNode<'a> {
577 Frame(&'a Frame),
578 Source {
579 error: &'a (dyn Error + 'static),
580 location: &'static Location<'static>,
581 },
582 FlatSource {
584 error: &'a (dyn Error + 'static),
585 location: &'static Location<'static>,
586 },
587}
588
589impl<'a> ErrorNode<'a> {
590 pub(crate) fn error(self) -> &'a (dyn Error + 'static) {
591 match self {
592 ErrorNode::Frame(frame) => frame.error(),
593 ErrorNode::Source { error, .. } | ErrorNode::FlatSource { error, .. } => error,
594 }
595 }
596
597 fn root_error(self) -> &'a (dyn Error + 'static) {
598 let mut error = self.error();
599 while let Some(nested) = error.downcast_ref::<crate::Error>() {
600 error = nested.error();
601 }
602 error
603 }
604
605 pub(crate) fn location(self) -> &'static Location<'static> {
610 match self {
611 ErrorNode::Frame(frame) => frame.location(),
612 ErrorNode::Source { location, .. } | ErrorNode::FlatSource { location, .. } => location,
613 }
614 }
615
616 pub(crate) fn children(self) -> Vec<ErrorNode<'a>> {
623 if matches!(self, ErrorNode::FlatSource { .. }) {
624 return Vec::new();
625 }
626 let error = self.error();
627 let location = self.location();
628 let mut children = Vec::new();
629 if let Some(nested) = error.downcast_ref::<crate::Error>() {
630 let root_error = self.root_error();
631 let mut skipped_root = false;
632 for source in nested
633 .iter_errors_with_locations()
634 .filter(|source| !source.error().is::<crate::Error>())
635 {
636 if !skipped_root && std::ptr::eq(source.error(), root_error) {
638 skipped_root = true;
639 continue;
640 }
641 children.push(ErrorNode::FlatSource {
642 error: source.error(),
643 location: source.location().unwrap_or(location),
644 });
645 }
646 } else if let Some(error) = crate::error::native_source(error) {
647 children.push(ErrorNode::Source { error, location });
648 }
649 if let ErrorNode::Frame(frame) = self {
650 children.extend(frame.children().iter().map(ErrorNode::Frame));
651 }
652 let mut diagnostics = Vec::new();
653 for child in children {
654 if crate::error::is_transparent_marker(child.error()) {
655 diagnostics.extend(child.children());
656 } else {
657 diagnostics.push(child);
658 }
659 }
660 diagnostics
661 }
662}
663
664impl Frame {
666 pub fn probable_cause(&self) -> Option<&(dyn Error + 'static)> {
688 self.probable_cause_inner()
689 }
690
691 pub fn iter_frames(&self) -> impl Iterator<Item = &Frame> + '_ {
694 let mut queue = std::collections::VecDeque::new();
695 queue.push_back(self);
696 BreadthFirstFrames { queue }
697 }
698}
699
700pub struct BreadthFirstFrames<'a> {
702 queue: std::collections::VecDeque<&'a Frame>,
703}
704
705impl<'a> Iterator for BreadthFirstFrames<'a> {
706 type Item = &'a Frame;
707
708 fn next(&mut self) -> Option<Self::Item> {
709 let frame = self.queue.pop_front()?;
710 for child in frame.children() {
711 self.queue.push_back(child);
712 }
713 Some(frame)
714 }
715}
716
717impl<E> From<Exn<E>> for Box<Frame>
718where
719 E: Error + Send + Sync + 'static,
720{
721 fn from(err: Exn<E>) -> Self {
722 err.frame
723 }
724}
725
726impl<E> From<Exn<E>> for Box<dyn Error + Send + Sync + 'static>
727where
728 E: Error + Send + Sync + 'static,
729{
730 fn from(err: Exn<E>) -> Self {
731 Box::new(err.into_error())
732 }
733}
734
735#[cfg(feature = "anyhow")]
736impl<E> From<Exn<E>> for anyhow::Error
737where
738 E: Error + Send + Sync + 'static,
739{
740 fn from(err: Exn<E>) -> Self {
741 anyhow::Error::from(err.into_chain())
742 }
743}
744
745impl<E> From<Exn<E>> for Frame
746where
747 E: Error + Send + Sync + 'static,
748{
749 fn from(err: Exn<E>) -> Self {
750 *err.frame
751 }
752}
753
754impl From<Frame> for Exn {
755 fn from(frame: Frame) -> Self {
756 Exn::from_boxed_frame(Box::new(frame))
757 }
758}
759
760impl Exn {
761 pub(crate) fn from_boxed_frame(mut frame: Box<Frame>) -> Self {
762 if !frame.source.error.is::<Untyped>() {
763 frame.source.error = Box::new(Untyped(frame.source.error));
764 }
765 Exn {
766 frame,
767 phantom: Default::default(),
768 }
769 }
770}
771
772#[cfg(all(feature = "auto-chain-error", not(feature = "tree-error")))]
773impl Exn {
774 pub(crate) fn from_chain(chain: ChainedError) -> Self {
775 let mut frames = Vec::new();
776 let mut next = Some(chain);
777 while let Some(node) = next {
778 next = node.source.map(|source| *source);
779 let frame = (!node.err.is_native_source()).then(|| Frame {
780 source: FrameSource {
781 error: node.err.into_owned_error(),
782 location: node.location,
783 children: Vec::new(),
784 },
785 });
786 frames.push((frame, node.logical_parent));
787 }
788 while let Some((frame, parent)) = frames.pop() {
790 let Some(mut frame) = frame else { continue };
791 frame.source.children.reverse();
792 match parent {
793 Some(parent) => frames[parent]
794 .0
795 .as_mut()
796 .expect("an explicit frame has an explicit parent")
797 .source
798 .children
799 .push(frame),
800 None => return frame.into(),
801 }
802 }
803 unreachable!("an error chain always contains its root frame")
804 }
805}
806
807pub struct Untyped(Box<dyn Error + Send + Sync + 'static>);
811
812impl Untyped {
813 pub(crate) fn from_boxed(error: Box<dyn Error + Send + Sync + 'static>) -> Self {
814 Untyped(error)
815 }
816}
817
818impl fmt::Display for Untyped {
819 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
820 fmt::Display::fmt(&self.0, f)
821 }
822}
823
824impl fmt::Debug for Untyped {
825 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
826 fmt::Debug::fmt(&self.0, f)
827 }
828}
829
830impl Error for Untyped {
831 fn source(&self) -> Option<&(dyn Error + 'static)> {
832 self.0.source()
833 }
834}
835
836impl<E> From<Exn<E>> for ChainedError
837where
838 E: std::error::Error + Send + Sync + 'static,
839{
840 fn from(err: Exn<E>) -> Self {
841 let flattened = flatten_error_nodes(*err.frame);
842 let mut source = None;
843 for node in flattened.into_iter().rev() {
844 source = Some(Box::new(ChainedError {
845 err: node.error,
846 location: node.location,
847 logical_parent: node.logical_parent,
848 source,
849 }));
850 }
851 *source.expect("an Exn always contains its root error")
852 }
853}
854
855struct OwnedErrorNode {
856 error: ErrorHandle,
857 location: &'static Location<'static>,
858 logical_parent: Option<usize>,
859}
860
861fn flatten_error_nodes(root: Frame) -> Vec<OwnedErrorNode> {
871 enum Pending {
872 Frame {
873 frame: Frame,
874 logical_parent: Option<usize>,
875 },
876 Source {
877 error: ErrorHandle,
878 location: &'static Location<'static>,
879 logical_parent: usize,
880 },
881 }
882
883 let mut queue = VecDeque::from([Pending::Frame {
884 frame: root,
885 logical_parent: None,
886 }]);
887 let mut out = Vec::new();
888 while let Some(node) = queue.pop_front() {
889 let node_index = out.len();
890 match node {
891 Pending::Frame {
892 frame:
893 Frame {
894 source:
895 FrameSource {
896 error,
897 location,
898 children,
899 },
900 },
901 logical_parent,
902 } => {
903 let error = ErrorHandle::new(unerase(error));
904 if let Some(source) = error.source() {
905 queue.push_back(Pending::Source {
906 error: source,
907 location,
908 logical_parent: node_index,
909 });
910 }
911 queue.extend(children.into_iter().map(|frame| Pending::Frame {
912 frame,
913 logical_parent: Some(node_index),
914 }));
915 out.push(OwnedErrorNode {
916 error,
917 location,
918 logical_parent,
919 });
920 }
921 Pending::Source {
922 error,
923 location,
924 logical_parent,
925 } => {
926 if let Some(source) = error.source() {
927 queue.push_back(Pending::Source {
928 error: source,
929 location,
930 logical_parent: node_index,
931 });
932 }
933 out.push(OwnedErrorNode {
934 error,
935 location,
936 logical_parent: Some(logical_parent),
937 });
938 }
939 }
940 }
941 out
942}
943
944fn unerase(mut error: Box<dyn Error + Send + Sync + 'static>) -> Box<dyn Error + Send + Sync + 'static> {
951 loop {
952 match error.downcast::<Untyped>() {
953 Ok(untyped) => error = untyped.0,
954 Err(typed) => return typed,
955 }
956 }
957}