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> {
116 frame: Box<Frame>,
118 phantom: PhantomData<E>,
119}
120
121#[track_caller]
123#[expect(
124 clippy::unnecessary_box_returns,
125 reason = "erasure retains the existing frame allocation"
126)]
127pub(super) fn into_frame<E: Error + Send + Sync + 'static>(error: E) -> Box<Frame> {
128 #[cfg(any(feature = "tree-error", not(feature = "auto-chain-error")))]
130 {
131 crate::ErrorExt::raise(error).into_frame()
132 }
133 #[cfg(all(feature = "auto-chain-error", not(feature = "tree-error")))]
134 {
135 Exn::new(error).frame
136 }
137}
138
139impl<E: Error + Send + Sync + 'static> From<E> for Exn<E> {
140 #[track_caller]
141 fn from(error: E) -> Self {
142 Exn::new(error)
143 }
144}
145
146impl<E: Error + Send + Sync + 'static> Exn<E> {
147 #[track_caller]
154 pub fn new(error: E) -> Self {
155 let frame = Frame {
156 source: FrameSource {
157 error: Box::new(error),
158 location: Location::caller(),
159 children: Vec::new(),
160 },
161 };
162
163 Self {
164 frame: Box::new(frame),
165 phantom: PhantomData,
166 }
167 }
168
169 #[track_caller]
170 pub(super) fn with_cause(cause: impl Error + Send + Sync + 'static, error: E) -> Self {
171 let cause = into_frame(cause);
172 let mut exn = Exn::new(error);
173 exn.frame.source.children.push(*cause);
174 exn
175 }
176
177 #[track_caller]
179 pub fn raise_all<T, I>(children: I, err: E) -> Self
180 where
181 T: Error + Send + Sync + 'static,
182 I: IntoIterator,
183 I::Item: Into<Exn<T>>,
184 {
185 let mut new_exn = Exn::new(err);
186 for exn in children {
187 let exn = exn.into();
188 new_exn.frame.source.children.push(*exn.frame);
189 }
190 new_exn
191 }
192
193 #[track_caller]
195 pub fn raise<T: Error + Send + Sync + 'static>(self, err: T) -> Exn<T> {
196 let mut new_exn = Exn::new(err);
197 new_exn.frame.source.children.push(*self.frame);
198 new_exn
199 }
200
201 #[track_caller]
203 pub fn chain<T: Error + Send + Sync + 'static>(mut self, err: impl Into<Exn<T>>) -> Exn<E> {
204 let err = err.into();
205 self.frame.source.children.push(*err.frame);
206 self
207 }
208
209 #[track_caller]
211 pub fn chain_all<T, I>(mut self, errors: I) -> Exn<E>
212 where
213 T: Error + Send + Sync + 'static,
214 I: IntoIterator,
215 I::Item: Into<Exn<T>>,
216 {
217 for err in errors {
218 let err = err.into();
219 self.frame.source.children.push(*err.frame);
220 }
221 self
222 }
223
224 pub fn drain_children(&mut self) -> impl Iterator<Item = Exn> + '_ {
229 self.frame.source.children.drain(..).map(Exn::from)
230 }
231
232 pub fn erased(self) -> Exn {
235 Exn::from_boxed_frame(self.frame)
236 }
237
238 pub fn error(&self) -> &E {
240 self.frame
241 .source
242 .error
243 .downcast_ref()
244 .expect("the owned frame always matches the compile-time error type")
245 }
246
247 pub fn into_box(self) -> Box<E> {
252 match self.frame.source.error.downcast() {
253 Ok(err) => err,
254 Err(_) => unreachable!("The type in the frame is always the type of this instance"),
255 }
256 }
257
258 pub fn into_inner(self) -> E {
263 *self.into_box()
264 }
265
266 pub fn into_error(self) -> crate::Error {
270 self.into()
271 }
272
273 pub fn into_chain(self) -> ChainedError {
278 self.into()
279 }
280
281 pub fn frame(&self) -> &Frame {
283 &self.frame
284 }
285
286 pub fn iter(&self) -> impl Iterator<Item = &Frame> {
289 self.frame().iter_frames()
290 }
291
292 pub fn iter_errors(&self) -> impl Iterator<Item = &(dyn Error + 'static)> + '_ {
296 self.frame.iter_errors_with_locations().map(|source| source.error())
297 }
298
299 pub fn metadata(&self) -> impl Iterator<Item = &Metadata> + '_ {
307 self.iter_errors()
308 .filter_map(|error| error.downcast_ref::<crate::Message>())
309 .map(|error| &error.values)
310 .filter(|values| !values.is_empty())
311 }
312
313 pub fn metadata_merged(&self) -> Metadata {
320 let mut merged = Metadata::new();
321 for values in self.metadata() {
322 merged.extend(values.iter().map(|(key, value)| (key.clone(), value.clone())));
323 }
324 merged
325 }
326
327 pub fn probable_cause(&self) -> &(dyn Error + 'static) {
332 self.frame.probable_cause().unwrap_or_else(|| self.frame.error())
333 }
334
335 pub fn downcast_any_ref<T: Error + 'static>(&self) -> Option<&T> {
340 self.iter_errors().find_map(|error| error.downcast_ref())
341 }
342}
343
344impl<E> Deref for Exn<E>
345where
346 E: Error + Send + Sync + 'static,
347{
348 type Target = E;
349
350 fn deref(&self) -> &Self::Target {
351 self.error()
352 }
353}
354
355impl<E: Error + Send + Sync + 'static> fmt::Debug for Exn<E> {
356 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357 write_frame_recursive(f, self.frame(), ErrorMode::Display)
358 }
359}
360
361impl fmt::Debug for Frame {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 write_frame_recursive(f, self, ErrorMode::Display)
364 }
365}
366
367#[derive(Copy, Clone)]
368pub(crate) enum ErrorMode {
369 Display,
370 Debug,
371}
372
373impl ErrorMode {
374 pub(crate) fn fmt(self, error: &(dyn Error + 'static), f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 if let Some(io) = error.downcast_ref::<std::io::Error>()
376 && io.get_ref().is_some()
377 {
378 return write!(f, "I/O error ({:?})", io.kind());
380 }
381 match self {
383 ErrorMode::Display => write!(f, "{error}"),
384 ErrorMode::Debug => write!(f, "{error:?}"),
385 }
386 }
387}
388
389fn write_frame_recursive(f: &mut fmt::Formatter<'_>, frame: &Frame, err_mode: ErrorMode) -> fmt::Result {
390 if crate::error::is_transparent_marker(frame.error()) {
391 let children = ErrorNode::Frame(frame).children();
392 if !children.is_empty() {
393 for (index, child) in children.into_iter().enumerate() {
394 if index != 0 {
395 writeln!(f)?;
396 }
397 write_error_node_recursive(f, child, &mut Vec::new(), &mut 0, err_mode, true)?;
398 }
399 return Ok(());
400 }
401 }
402 write_error_node_recursive(f, ErrorNode::Frame(frame), &mut Vec::new(), &mut 0, err_mode, true)
403}
404
405fn write_error_node_recursive(
406 f: &mut fmt::Formatter<'_>,
407 node: ErrorNode<'_>,
408 siblings_follow: &mut Vec<bool>,
409 number: &mut usize,
410 err_mode: ErrorMode,
411 linearize: bool,
412) -> fmt::Result {
413 let children = node.children();
414 let is_chain = children.len() == 1;
415 let continues_chain = linearize && is_chain;
416 if !siblings_follow.is_empty() {
417 f.write_str("\n ")?;
418 if let Some((has_sibling, ancestors)) = siblings_follow[1..].split_last() {
420 for has_sibling in ancestors {
421 f.write_str(if *has_sibling { "│ " } else { " " })?;
422 }
423 f.write_str(if *has_sibling || continues_chain {
424 "├─"
425 } else {
426 "└─"
427 })?;
428 }
429 if siblings_follow.len() == 1
430 || *number != 0
431 || siblings_follow.last().copied().unwrap_or(false)
432 || continues_chain
433 {
434 write!(f, "{number}: ")?;
435 } else {
436 f.write_str(" ")?;
437 }
438 }
439 err_mode.fmt(node.root_error(), f)?;
440 if !f.alternate() {
441 write_location(f, node.location())?;
442 }
443
444 if children.is_empty() {
445 return Ok(());
446 }
447 if siblings_follow.is_empty() {
448 f.write_str("\n\nCaused by:")?;
449 }
450 if continues_chain {
451 let has_sibling = siblings_follow.last().copied().unwrap_or(false);
452 let child_count = children.len();
453 for (index, child) in children.into_iter().enumerate() {
454 if siblings_follow.is_empty() {
455 siblings_follow.push(false);
456 } else {
457 *number += 1;
458 }
459 if let Some(last) = siblings_follow.last_mut() {
460 *last = has_sibling || index + 1 < child_count;
461 }
462 write_error_node_recursive(f, child, siblings_follow, number, err_mode, true)?;
463 }
464 if let Some(last) = siblings_follow.last_mut() {
465 *last = has_sibling;
466 }
467 return Ok(());
468 }
469 let child_count = children.len();
470 if siblings_follow.is_empty() {
471 siblings_follow.push(false);
473 }
474 for (mut number, child) in children.into_iter().enumerate() {
475 siblings_follow.push(number + 1 < child_count);
476 write_error_node_recursive(f, child, siblings_follow, &mut number, err_mode, is_chain)?;
477 siblings_follow.pop();
478 }
479
480 Ok(())
481}
482
483impl<E: Error + Send + Sync + 'static> fmt::Display for Exn<E> {
484 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485 fmt::Display::fmt(&self.frame, f)
486 }
487}
488
489impl<E: Error + Send + Sync + 'static> PartialEq<str> for Exn<E> {
490 fn eq(&self, other: &str) -> bool {
491 crate::root_error_eq(self.frame().error(), other)
492 }
493}
494
495impl<E: Error + Send + Sync + 'static> PartialEq<&str> for Exn<E> {
496 fn eq(&self, other: &&str) -> bool {
497 <Self as PartialEq<str>>::eq(self, other)
498 }
499}
500
501impl<E: Error + Send + Sync + 'static> PartialEq<String> for Exn<E> {
502 fn eq(&self, other: &String) -> bool {
503 <Self as PartialEq<str>>::eq(self, other)
504 }
505}
506
507impl fmt::Display for Frame {
508 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
509 if f.alternate() {
510 write_frame_recursive(f, self, ErrorMode::Debug)
512 } else {
513 if crate::error::is_transparent_marker(self.error())
514 && let Some(diagnostic) = self.iter_errors_with_locations().next()
515 {
516 return fmt::Display::fmt(diagnostic.error(), f);
517 }
518 fmt::Display::fmt(self.error(), f)
519 }
520 }
521}
522
523pub struct Frame {
525 source: FrameSource,
526}
527
528pub(crate) struct FrameSource {
531 error: Box<dyn Error + Send + Sync + 'static>,
532 location: &'static Location<'static>,
533 children: Vec<Frame>,
534}
535
536impl fmt::Display for FrameSource {
537 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
538 write!(f, "{}", self.error())
540 }
541}
542
543impl fmt::Debug for FrameSource {
544 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
545 fmt::Debug::fmt(self.error(), f)
546 }
547}
548
549impl Error for FrameSource {
550 fn source(&self) -> Option<&(dyn Error + 'static)> {
551 crate::error::native_source(self.error()).or_else(|| {
552 self.children.first().map(|frame| {
553 if frame.children().is_empty() {
554 frame.error() as &(dyn Error + 'static)
555 } else {
556 &frame.source as &(dyn Error + 'static)
557 }
558 })
559 })
560 }
561}
562
563impl FrameSource {
564 pub(crate) fn error(&self) -> &(dyn Error + Send + Sync + 'static) {
565 let mut error = &*self.error;
566 loop {
567 if let Some(erased) = error.downcast_ref::<Untyped>() {
568 error = &*erased.0;
569 } else if let Some(shared) = error.downcast_ref::<ErrorHandle>() {
570 error = shared.owned_error();
571 } else {
572 return error;
573 }
574 }
575 }
576
577 pub(crate) fn location(&self) -> &'static Location<'static> {
578 self.location
579 }
580
581 pub(crate) fn children(&self) -> &[Frame] {
582 &self.children
583 }
584}
585
586impl Frame {
587 pub fn error(&self) -> &(dyn Error + Send + Sync + 'static) {
592 self.source.error()
593 }
594
595 pub fn location(&self) -> &'static Location<'static> {
599 self.source.location()
600 }
601
602 pub fn children(&self) -> &[Frame] {
607 self.source.children()
608 }
609
610 pub(crate) fn source_frame(&self) -> &FrameSource {
611 &self.source
612 }
613}
614
615#[derive(Clone, Copy)]
621pub(crate) enum ErrorNode<'a> {
622 Frame(&'a Frame),
623 Source {
624 error: &'a (dyn Error + 'static),
625 location: &'static Location<'static>,
626 },
627 Boundary {
629 node: crate::error::Node<'a>,
630 location: &'static Location<'static>,
631 },
632}
633
634impl<'a> ErrorNode<'a> {
635 pub(crate) fn error(self) -> &'a (dyn Error + 'static) {
636 match self {
637 ErrorNode::Frame(frame) => frame.error(),
638 ErrorNode::Source { error, .. } => error,
639 ErrorNode::Boundary { node, .. } => node.display().error(),
640 }
641 }
642
643 fn root_error(self) -> &'a (dyn Error + 'static) {
644 let mut error = self.error();
645 while let Some(nested) = error.downcast_ref::<crate::Error>() {
646 error = nested.error();
647 }
648 error
649 }
650
651 pub(crate) fn location(self) -> &'static Location<'static> {
656 if let Some(nested) = self.error().downcast_ref::<crate::Error>() {
657 return ErrorNode::Boundary {
658 node: nested.iter_root(),
659 location: self.fallback_location(),
660 }
661 .location();
662 }
663 match self {
664 ErrorNode::Boundary { node, location } => node.display().location().unwrap_or(location),
665 _ => self.fallback_location(),
666 }
667 }
668
669 fn fallback_location(self) -> &'static Location<'static> {
670 match self {
671 ErrorNode::Frame(frame) => frame.location(),
672 ErrorNode::Source { location, .. } | ErrorNode::Boundary { location, .. } => location,
673 }
674 }
675
676 pub(crate) fn children(self) -> Vec<ErrorNode<'a>> {
683 let error = self.error();
684 let location = self.location();
685 let mut children = Vec::new();
686 if let Some(nested) = error.downcast_ref::<crate::Error>() {
687 children.extend(
688 ErrorNode::Boundary {
689 node: nested.iter_root(),
690 location,
691 }
692 .children(),
693 );
694 } else if let ErrorNode::Boundary { node, .. } = self {
695 children.extend(
696 node.children()
697 .into_iter()
698 .map(|node| ErrorNode::Boundary { node, location }),
699 );
700 } else if let Some(error) = crate::error::native_source(error) {
701 children.push(ErrorNode::Source { error, location });
702 }
703 if let ErrorNode::Frame(frame) = self {
704 children.extend(frame.children().iter().map(ErrorNode::Frame));
705 } else if error.is::<crate::Error>()
706 && let ErrorNode::Boundary { node, .. } = self
707 {
708 children.extend(
710 node.children()
711 .into_iter()
712 .skip(1)
713 .map(|node| ErrorNode::Boundary { node, location }),
714 );
715 }
716 let mut diagnostics = Vec::new();
717 for child in children {
718 if crate::error::is_transparent_marker(child.error()) {
719 diagnostics.extend(child.children());
720 } else {
721 diagnostics.push(child);
722 }
723 }
724 diagnostics
725 }
726}
727
728impl Frame {
730 pub fn probable_cause(&self) -> Option<&(dyn Error + 'static)> {
752 self.probable_cause_inner()
753 }
754
755 pub fn iter_frames(&self) -> impl Iterator<Item = &Frame> + '_ {
758 let mut queue = std::collections::VecDeque::new();
759 queue.push_back(self);
760 BreadthFirstFrames { queue }
761 }
762}
763
764pub struct BreadthFirstFrames<'a> {
766 queue: std::collections::VecDeque<&'a Frame>,
767}
768
769impl<'a> Iterator for BreadthFirstFrames<'a> {
770 type Item = &'a Frame;
771
772 fn next(&mut self) -> Option<Self::Item> {
773 let frame = self.queue.pop_front()?;
774 for child in frame.children() {
775 self.queue.push_back(child);
776 }
777 Some(frame)
778 }
779}
780
781impl<E> From<Exn<E>> for Box<Frame>
782where
783 E: Error + Send + Sync + 'static,
784{
785 fn from(err: Exn<E>) -> Self {
786 err.frame
787 }
788}
789
790impl<E> From<Exn<E>> for Box<dyn Error + Send + Sync + 'static>
791where
792 E: Error + Send + Sync + 'static,
793{
794 fn from(err: Exn<E>) -> Self {
795 Box::new(err.into_error())
796 }
797}
798
799#[cfg(feature = "anyhow")]
800impl<E> From<Exn<E>> for anyhow::Error
801where
802 E: Error + Send + Sync + 'static,
803{
804 fn from(err: Exn<E>) -> Self {
805 let mut chain = err.into_chain();
806 let leading_markers = std::iter::successors(Some(&chain), |node| node.source.as_deref())
807 .position(|node| crate::concrete::chain::has_diagnostic(node.err.error()))
808 .unwrap_or(0);
809 for _ in 0..leading_markers {
810 chain = *chain
811 .source
812 .take()
813 .expect("a real diagnostic follows the leading markers");
814 }
815 anyhow::Error::from(chain)
816 }
817}
818
819impl<E> From<Exn<E>> for Frame
820where
821 E: Error + Send + Sync + 'static,
822{
823 fn from(err: Exn<E>) -> Self {
824 *err.frame
825 }
826}
827
828impl From<Frame> for Exn {
829 fn from(frame: Frame) -> Self {
830 Exn::from_boxed_frame(Box::new(frame))
831 }
832}
833
834impl Exn {
835 pub(crate) fn from_boxed_frame(mut frame: Box<Frame>) -> Self {
836 if !frame.source.error.is::<Untyped>() {
837 frame.source.error = Box::new(Untyped(frame.source.error));
838 }
839 Exn {
840 frame,
841 phantom: Default::default(),
842 }
843 }
844}
845
846#[cfg(all(feature = "auto-chain-error", not(feature = "tree-error")))]
847impl Exn {
848 pub(crate) fn from_chain(chain: ChainedError) -> Self {
849 let mut frames = Vec::new();
850 let mut next = Some(chain);
851 while let Some(node) = next {
852 next = node.source.map(|source| *source);
853 let frame = (!node.err.is_native_source()).then(|| Frame {
854 source: FrameSource {
855 error: node.err.into_owned_error(),
856 location: node.location,
857 children: Vec::new(),
858 },
859 });
860 frames.push((frame, node.logical_parent));
861 }
862 while let Some((frame, parent)) = frames.pop() {
864 let Some(mut frame) = frame else { continue };
865 frame.source.children.reverse();
866 match parent {
867 Some(parent) => frames[parent]
868 .0
869 .as_mut()
870 .expect("an explicit frame has an explicit parent")
871 .source
872 .children
873 .push(frame),
874 None => return frame.into(),
875 }
876 }
877 unreachable!("an error chain always contains its root frame")
878 }
879}
880
881pub struct Untyped(Box<dyn Error + Send + Sync + 'static>);
885
886impl Untyped {
887 pub(crate) fn from_boxed(error: Box<dyn Error + Send + Sync + 'static>) -> Self {
888 Untyped(error)
889 }
890}
891
892impl fmt::Display for Untyped {
893 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
894 fmt::Display::fmt(&self.0, f)
895 }
896}
897
898impl fmt::Debug for Untyped {
899 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
900 fmt::Debug::fmt(&self.0, f)
901 }
902}
903
904impl Error for Untyped {
905 fn source(&self) -> Option<&(dyn Error + 'static)> {
906 self.0.source()
907 }
908}
909
910impl<E> From<Exn<E>> for ChainedError
911where
912 E: std::error::Error + Send + Sync + 'static,
913{
914 fn from(err: Exn<E>) -> Self {
915 let flattened = flatten_error_nodes(*err.frame);
916 let mut source = None;
917 for node in flattened.into_iter().rev() {
918 source = Some(Box::new(ChainedError {
919 err: node.error,
920 location: node.location,
921 logical_parent: node.logical_parent,
922 source,
923 }));
924 }
925 *source.expect("an Exn always contains its root error")
926 }
927}
928
929struct OwnedErrorNode {
930 error: ErrorHandle,
931 location: &'static Location<'static>,
932 logical_parent: Option<usize>,
933}
934
935fn flatten_error_nodes(root: Frame) -> Vec<OwnedErrorNode> {
945 enum Pending {
946 Frame {
947 frame: Frame,
948 logical_parent: Option<usize>,
949 },
950 Source {
951 error: ErrorHandle,
952 location: &'static Location<'static>,
953 logical_parent: usize,
954 },
955 }
956
957 let mut queue = VecDeque::from([Pending::Frame {
958 frame: root,
959 logical_parent: None,
960 }]);
961 let mut out = Vec::new();
962 while let Some(node) = queue.pop_front() {
963 let node_index = out.len();
964 match node {
965 Pending::Frame {
966 frame:
967 Frame {
968 source:
969 FrameSource {
970 error,
971 location,
972 children,
973 },
974 },
975 logical_parent,
976 } => {
977 let error = ErrorHandle::new(unerase(error));
978 if let Some(source) = error.source() {
979 queue.push_back(Pending::Source {
980 error: source,
981 location,
982 logical_parent: node_index,
983 });
984 }
985 queue.extend(children.into_iter().map(|frame| Pending::Frame {
986 frame,
987 logical_parent: Some(node_index),
988 }));
989 out.push(OwnedErrorNode {
990 error,
991 location,
992 logical_parent,
993 });
994 }
995 Pending::Source {
996 error,
997 location,
998 logical_parent,
999 } => {
1000 if let Some(source) = error.source() {
1001 queue.push_back(Pending::Source {
1002 error: source,
1003 location,
1004 logical_parent: node_index,
1005 });
1006 }
1007 out.push(OwnedErrorNode {
1008 error,
1009 location,
1010 logical_parent: Some(logical_parent),
1011 });
1012 }
1013 }
1014 }
1015 out
1016}
1017
1018fn unerase(mut error: Box<dyn Error + Send + Sync + 'static>) -> Box<dyn Error + Send + Sync + 'static> {
1025 loop {
1026 match error.downcast::<Untyped>() {
1027 Ok(untyped) => error = untyped.0,
1028 Err(typed) => return typed,
1029 }
1030 }
1031}