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 let mut chain = err.into_chain();
742 let leading_markers = std::iter::successors(Some(&chain), |node| node.source.as_deref())
743 .position(|node| crate::concrete::chain::has_diagnostic(node.err.error()))
744 .unwrap_or(0);
745 for _ in 0..leading_markers {
746 chain = *chain
747 .source
748 .take()
749 .expect("a real diagnostic follows the leading markers");
750 }
751 anyhow::Error::from(chain)
752 }
753}
754
755impl<E> From<Exn<E>> for Frame
756where
757 E: Error + Send + Sync + 'static,
758{
759 fn from(err: Exn<E>) -> Self {
760 *err.frame
761 }
762}
763
764impl From<Frame> for Exn {
765 fn from(frame: Frame) -> Self {
766 Exn::from_boxed_frame(Box::new(frame))
767 }
768}
769
770impl Exn {
771 pub(crate) fn from_boxed_frame(mut frame: Box<Frame>) -> Self {
772 if !frame.source.error.is::<Untyped>() {
773 frame.source.error = Box::new(Untyped(frame.source.error));
774 }
775 Exn {
776 frame,
777 phantom: Default::default(),
778 }
779 }
780}
781
782#[cfg(all(feature = "auto-chain-error", not(feature = "tree-error")))]
783impl Exn {
784 pub(crate) fn from_chain(chain: ChainedError) -> Self {
785 let mut frames = Vec::new();
786 let mut next = Some(chain);
787 while let Some(node) = next {
788 next = node.source.map(|source| *source);
789 let frame = (!node.err.is_native_source()).then(|| Frame {
790 source: FrameSource {
791 error: node.err.into_owned_error(),
792 location: node.location,
793 children: Vec::new(),
794 },
795 });
796 frames.push((frame, node.logical_parent));
797 }
798 while let Some((frame, parent)) = frames.pop() {
800 let Some(mut frame) = frame else { continue };
801 frame.source.children.reverse();
802 match parent {
803 Some(parent) => frames[parent]
804 .0
805 .as_mut()
806 .expect("an explicit frame has an explicit parent")
807 .source
808 .children
809 .push(frame),
810 None => return frame.into(),
811 }
812 }
813 unreachable!("an error chain always contains its root frame")
814 }
815}
816
817pub struct Untyped(Box<dyn Error + Send + Sync + 'static>);
821
822impl Untyped {
823 pub(crate) fn from_boxed(error: Box<dyn Error + Send + Sync + 'static>) -> Self {
824 Untyped(error)
825 }
826}
827
828impl fmt::Display for Untyped {
829 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
830 fmt::Display::fmt(&self.0, f)
831 }
832}
833
834impl fmt::Debug for Untyped {
835 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
836 fmt::Debug::fmt(&self.0, f)
837 }
838}
839
840impl Error for Untyped {
841 fn source(&self) -> Option<&(dyn Error + 'static)> {
842 self.0.source()
843 }
844}
845
846impl<E> From<Exn<E>> for ChainedError
847where
848 E: std::error::Error + Send + Sync + 'static,
849{
850 fn from(err: Exn<E>) -> Self {
851 let flattened = flatten_error_nodes(*err.frame);
852 let mut source = None;
853 for node in flattened.into_iter().rev() {
854 source = Some(Box::new(ChainedError {
855 err: node.error,
856 location: node.location,
857 logical_parent: node.logical_parent,
858 source,
859 }));
860 }
861 *source.expect("an Exn always contains its root error")
862 }
863}
864
865struct OwnedErrorNode {
866 error: ErrorHandle,
867 location: &'static Location<'static>,
868 logical_parent: Option<usize>,
869}
870
871fn flatten_error_nodes(root: Frame) -> Vec<OwnedErrorNode> {
881 enum Pending {
882 Frame {
883 frame: Frame,
884 logical_parent: Option<usize>,
885 },
886 Source {
887 error: ErrorHandle,
888 location: &'static Location<'static>,
889 logical_parent: usize,
890 },
891 }
892
893 let mut queue = VecDeque::from([Pending::Frame {
894 frame: root,
895 logical_parent: None,
896 }]);
897 let mut out = Vec::new();
898 while let Some(node) = queue.pop_front() {
899 let node_index = out.len();
900 match node {
901 Pending::Frame {
902 frame:
903 Frame {
904 source:
905 FrameSource {
906 error,
907 location,
908 children,
909 },
910 },
911 logical_parent,
912 } => {
913 let error = ErrorHandle::new(unerase(error));
914 if let Some(source) = error.source() {
915 queue.push_back(Pending::Source {
916 error: source,
917 location,
918 logical_parent: node_index,
919 });
920 }
921 queue.extend(children.into_iter().map(|frame| Pending::Frame {
922 frame,
923 logical_parent: Some(node_index),
924 }));
925 out.push(OwnedErrorNode {
926 error,
927 location,
928 logical_parent,
929 });
930 }
931 Pending::Source {
932 error,
933 location,
934 logical_parent,
935 } => {
936 if let Some(source) = error.source() {
937 queue.push_back(Pending::Source {
938 error: source,
939 location,
940 logical_parent: node_index,
941 });
942 }
943 out.push(OwnedErrorNode {
944 error,
945 location,
946 logical_parent: Some(logical_parent),
947 });
948 }
949 }
950 }
951 out
952}
953
954fn unerase(mut error: Box<dyn Error + Send + Sync + 'static>) -> Box<dyn Error + Send + Sync + 'static> {
961 loop {
962 match error.downcast::<Untyped>() {
963 Ok(untyped) => error = untyped.0,
964 Err(typed) => return typed,
965 }
966 }
967}