// Copyright 2025 FastLabs Developers//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. use std::collections::VecDeque;use std::error::Error;use std::fmt;use std::marker::PhantomData;use std::ops::Deref;use std::panic::Location; use crate::{ChainedError, Exn, write_location}; impl<E: Error + Send + Sync + 'static> From<E> for Exn<E> { #[track_caller] fn from(error: E) -> Self { Exn::new(error) }} impl<E: Error + Send + Sync + 'static> Exn<E> { /// Create a new exception with the given error. /// /// This will automatically walk the [source chain of the error] and add them as children /// frames. /// /// See also [`ErrorExt::raise`](crate::ErrorExt) for a fluent way to convert an error into an `Exn` instance. /// /// Note that **sources of `error` are degenerated to their string representation** and all type information is erased. /// /// [source chain of the error]: Error::source #[track_caller] pub fn new(error: E) -> Self { fn walk_sources(error: &dyn Error, location: &'static Location<'static>) -> Vec<Frame> { if let Some(source) = error.source() { let children = vec![Frame { error: Box::new(SourceError::new(source)), location, children: walk_sources(source, location), }]; children } else { vec![] } } let location = Location::caller(); let children = walk_sources(&error, location); let frame = Frame { error: Box::new(error), location, children, }; Self { frame: Box::new(frame), phantom: PhantomData, } } /// Create a new exception with the given error and children. #[track_caller] pub fn raise_all<T, I>(children: I, err: E) -> Self where T: Error + Send + Sync + 'static, I: IntoIterator, I::Item: Into<Exn<T>>, { let mut new_exn = Exn::new(err); for exn in children { let exn = exn.into(); new_exn.frame.children.push(*exn.frame); } new_exn } /// Raise a new exception; this will make the current exception a child of the new one. #[track_caller] pub fn raise<T: Error + Send + Sync + 'static>(self, err: T) -> Exn<T> { let mut new_exn = Exn::new(err); new_exn.frame.children.push(*self.frame); new_exn } /// Use the current exception as the head of a chain, adding `err` to its children. #[track_caller] pub fn chain<T: Error + Send + Sync + 'static>(mut self, err: impl Into<Exn<T>>) -> Exn<E> { let err = err.into(); self.frame.children.push(*err.frame); self } /// Use the current exception the head of a chain, adding `errors` to its children. #[track_caller] pub fn chain_all<T, I>(mut self, errors: I) -> Exn<E> where T: Error + Send + Sync + 'static, I: IntoIterator, I::Item: Into<Exn<T>>, { for err in errors { let err = err.into(); self.frame.children.push(*err.frame); } self } /// Drain all sources of this error as untyped [`Exn`]. /// /// This is useful if one wants to re-organise errors, and the error layout is well known. pub fn drain_children(&mut self) -> impl Iterator<Item = Exn> + '_ { self.frame.children.drain(..).map(Exn::from) } /// Erase the type of this instance and turn it into a bare `Exn`. pub fn erased(self) -> Exn { let untyped_frame = { let Frame { error, location, children, } = *self.frame; // Unfortunately, we have to double-box here. // TODO: figure out tricks to make this unnecessary. let error = Untyped(error); Frame { error: Box::new(error), location, children, } }; Exn { frame: Box::new(untyped_frame), phantom: Default::default(), } } /// Return the current exception. pub fn error(&self) -> &E { self.frame .error .downcast_ref() .expect("the owned frame always matches the compile-time error type") } /// Discard all error context and return the underlying error in a Box. /// /// This is useful to retain the allocation, as internally it's also stored in a box, /// when comparing it to [`Self::into_inner()`]. pub fn into_box(self) -> Box<E> { match self.frame.error.downcast() { Ok(err) => err, Err(_) => unreachable!("The type in the frame is always the type of this instance"), } } /// Discard all error context and return the underlying error. /// /// This may be needed to obtain something that once again implements `Error`. /// Note that this destroys the internal Box and moves the value back onto the stack. pub fn into_inner(self) -> E { *self.into_box() } /// Turn ourselves into a top-level [Error] that implements [`std::error::Error`]. /// /// [Error]: crate::Error pub fn into_error(self) -> crate::Error { self.into() } /// Convert this error tree into a chain of errors, breadth first, which flattens the tree /// but retains all type dynamic type information. /// /// This is useful for inter-op with `anyhow`. pub fn into_chain(self) -> crate::ChainedError { self.into() } /// Return the underlying exception frame. pub fn frame(&self) -> &Frame { &self.frame } /// Iterate over all frames in breadth-first order. The first frame is this instance, /// followed by all of its children. pub fn iter(&self) -> impl Iterator<Item = &Frame> { self.frame().iter_frames() } /// Iterate over all frames and find one that downcasts into error of type `T`. /// Note that the search includes this instance as well. pub fn downcast_any_ref<T: Error + 'static>(&self) -> Option<&T> { self.iter().find_map(|e| e.error().downcast_ref()) }} impl<E> Deref for Exn<E>where E: Error + Send + Sync + 'static,{ type Target = E; fn deref(&self) -> &Self::Target { self.error() }} impl<E: Error + Send + Sync + 'static> fmt::Debug for Exn<E> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write_frame_recursive(f, self.frame(), "", ErrorMode::Display, TreeMode::Linearize) }} impl fmt::Debug for Frame { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write_frame_recursive(f, self, "", ErrorMode::Display, TreeMode::Linearize) }} #[derive(Copy, Clone)]enum ErrorMode { Display, Debug,} #[derive(Copy, Clone)]enum TreeMode { Linearize, Verbatim,} fn write_frame_recursive( f: &mut fmt::Formatter<'_>, frame: &Frame, prefix: &str, err_mode: ErrorMode, tree_mode: TreeMode,) -> fmt::Result { match err_mode { ErrorMode::Display => fmt::Display::fmt(frame.error(), f), ErrorMode::Debug => { write!(f, "{:?}", frame.error()) } }?; if !f.alternate() { write_location(f, frame.location)?; } let children = frame.children(); let children_len = children.len(); for (cidx, child) in children.iter().enumerate() { write!(f, "\n{prefix}|")?; write!(f, "\n{prefix}└─ ")?; let child_child_len = child.children().len(); let may_linearize_chain = matches!(tree_mode, TreeMode::Linearize) && children_len == 1 && child_child_len == 1; if may_linearize_chain { write_frame_recursive(f, child, prefix, err_mode, tree_mode)?; } else if cidx < children_len - 1 { write_frame_recursive(f, child, &format!("{prefix}| "), err_mode, tree_mode)?; } else { write_frame_recursive(f, child, &format!("{prefix} "), err_mode, tree_mode)?; } } Ok(())} impl<E: Error + Send + Sync + 'static> fmt::Display for Exn<E> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.frame, f) }} impl fmt::Display for Frame { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { // Avoid printing alternate versions of the debug info, keep it in one line, also print the tree. write_frame_recursive(f, self, "", ErrorMode::Debug, TreeMode::Verbatim) } else { fmt::Display::fmt(self.error(), f) } }} /// A frame in the exception tree.pub struct Frame { /// The error that occurred at this frame. error: Box<dyn Error + Send + Sync + 'static>, /// The source code location where this exception frame was created. location: &'static Location<'static>, /// Child exception frames that provide additional context or source errors. children: Vec<Frame>,} impl Frame { /// Return the error as a reference to [`Error`]. /// /// If the error was [erased](crate::Exn::erased), this is the original error, /// so it can still be downcast to its actual type. pub fn error(&self) -> &(dyn Error + Send + Sync + 'static) { match self.error.downcast_ref::<Untyped>() { Some(erased) => &*erased.0, None => &*self.error, } } /// Return the source code location where this exception frame was created. pub fn location(&self) -> &'static Location<'static> { self.location } /// Return a slice of the children of the exception. pub fn children(&self) -> &[Frame] { &self.children }} /// Navigationimpl Frame { /// Find the best possible cause: /// /// * in a linear chain of a single error each, it's the last-most child frame /// * in trees, find the deepest-possible frame that has the most leafs as children /// /// Return `None` if there are no children. pub fn probable_cause(&self) -> Option<&Frame> { fn walk<'a>(frame: &'a Frame, depth: usize) -> (usize, usize, &'a Frame) { if frame.children.is_empty() { return (1, depth, frame); } let mut total_leafs = 0; let mut best: Option<(usize, usize, &'a Frame)> = None; for child in &frame.children { let (leafs, d, f) = walk(child, depth + 1); total_leafs += leafs; match best { None => best = Some((leafs, d, f)), Some((bl, bd, _)) => { if leafs > bl || (leafs == bl && d > bd) { best = Some((leafs, d, f)); } } } } let self_candidate = (total_leafs, depth, frame); match best { None => self_candidate, Some(best_child) => { if total_leafs > best_child.0 || (total_leafs == best_child.0 && depth > best_child.1) { self_candidate } else { best_child } } } } // Special case: a simple error just with children would render the same as a chain, // so pretend the last child is the cause. if self.children().iter().all(|c| c.children().is_empty()) { if let Some(last) = self.children().last() { return Some(last); } } let res = walk(self, 0).2; if std::ptr::addr_eq(res, self) { None } else { Some(res) } } /// Iterate over all frames in breadth-first order. The first frame is this instance, /// followed by all of its children. pub fn iter_frames(&self) -> impl Iterator<Item = &Frame> + '_ { let mut queue = std::collections::VecDeque::new(); queue.push_back(self); BreadthFirstFrames { queue } }} /// Breadth-first iterator over `Frame`s.pub struct BreadthFirstFrames<'a> { queue: std::collections::VecDeque<&'a Frame>,} impl<'a> Iterator for BreadthFirstFrames<'a> { type Item = &'a Frame; fn next(&mut self) -> Option<Self::Item> { let frame = self.queue.pop_front()?; for child in frame.children() { self.queue.push_back(child); } Some(frame) }} impl<E> From<Exn<E>> for Box<Frame>where E: Error + Send + Sync + 'static,{ fn from(err: Exn<E>) -> Self { err.frame }} #[cfg(feature = "anyhow")]impl<E> From<Exn<E>> for anyhow::Errorwhere E: Error + Send + Sync + 'static,{ fn from(err: Exn<E>) -> Self { anyhow::Error::from(err.into_chain()) }} impl<E> From<Exn<E>> for Framewhere E: Error + Send + Sync + 'static,{ fn from(err: Exn<E>) -> Self { *err.frame }} impl From<Frame> for Exn { fn from(frame: Frame) -> Self { Exn { frame: Box::new(frame), phantom: Default::default(), } }} /// A marker to show that type information is not available,/// while storing all extractable information about the erased type./// It's the default type for [Exn].pub struct Untyped(Box<dyn Error + Send + Sync + 'static>); impl fmt::Display for Untyped { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) }} impl fmt::Debug for Untyped { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.0, f) }} impl Error for Untyped { fn source(&self) -> Option<&(dyn Error + 'static)> { self.0.source() }} /// An error that merely says that something is wrong.pub struct Something; impl fmt::Display for Something { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Something went wrong") }} impl fmt::Debug for Something { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self, f) }} impl Error for Something {} /// A way to keep all information of errors returned by `source()` chains.struct SourceError { display: String, alt_display: String, debug: String, alt_debug: String,} impl fmt::Debug for SourceError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let dbg = if f.alternate() { &self.alt_debug } else { &self.debug };Lines 1–500 of 553. Load more
// Copyright 2025 FastLabs Developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::collections::VecDeque; use std::error::Error; use std::fmt; use std::marker::PhantomData; use std::ops::Deref; use std::panic::Location; use crate::{ChainedError, Exn, write_location}; impl<E: Error + Send + Sync + 'static> From<E> for Exn<E> { #[track_caller] fn from(error: E) -> Self { Exn::new(error) } } impl<E: Error + Send + Sync + 'static> Exn<E> { /// Create a new exception with the given error. /// /// This will automatically walk the [source chain of the error] and add them as children /// frames. /// /// See also [`ErrorExt::raise`](crate::ErrorExt) for a fluent way to convert an error into an `Exn` instance. /// /// Note that **sources of `error` are degenerated to their string representation** and all type information is erased. /// /// [source chain of the error]: Error::source #[track_caller] pub fn new(error: E) -> Self { fn walk_sources(error: &dyn Error, location: &'static Location<'static>) -> Vec<Frame> { if let Some(source) = error.source() { let children = vec![Frame { error: Box::new(SourceError::new(source)), location, children: walk_sources(source, location), }]; children } else { vec![] } } let location = Location::caller(); let children = walk_sources(&error, location); let frame = Frame { error: Box::new(error), location, children, }; Self { frame: Box::new(frame), phantom: PhantomData, } } /// Create a new exception with the given error and children. #[track_caller] pub fn raise_all<T, I>(children: I, err: E) -> Self where T: Error + Send + Sync + 'static, I: IntoIterator, I::Item: Into<Exn<T>>, { let mut new_exn = Exn::new(err); for exn in children { let exn = exn.into(); new_exn.frame.children.push(*exn.frame); } new_exn } /// Raise a new exception; this will make the current exception a child of the new one. #[track_caller] pub fn raise<T: Error + Send + Sync + 'static>(self, err: T) -> Exn<T> { let mut new_exn = Exn::new(err); new_exn.frame.children.push(*self.frame); new_exn } /// Use the current exception as the head of a chain, adding `err` to its children. #[track_caller] pub fn chain<T: Error + Send + Sync + 'static>(mut self, err: impl Into<Exn<T>>) -> Exn<E> { let err = err.into(); self.frame.children.push(*err.frame); self } /// Use the current exception the head of a chain, adding `errors` to its children. #[track_caller] pub fn chain_all<T, I>(mut self, errors: I) -> Exn<E> where T: Error + Send + Sync + 'static, I: IntoIterator, I::Item: Into<Exn<T>>, { for err in errors { let err = err.into(); self.frame.children.push(*err.frame); } self } /// Drain all sources of this error as untyped [`Exn`]. /// /// This is useful if one wants to re-organise errors, and the error layout is well known. pub fn drain_children(&mut self) -> impl Iterator<Item = Exn> + '_ { self.frame.children.drain(..).map(Exn::from) } /// Erase the type of this instance and turn it into a bare `Exn`. pub fn erased(self) -> Exn { let untyped_frame = { let Frame { error, location, children, } = *self.frame; // Unfortunately, we have to double-box here. // TODO: figure out tricks to make this unnecessary. let error = Untyped(error); Frame { error: Box::new(error), location, children, } }; Exn { frame: Box::new(untyped_frame), phantom: Default::default(), } } /// Return the current exception. pub fn error(&self) -> &E { self.frame .error .downcast_ref() .expect("the owned frame always matches the compile-time error type") } /// Discard all error context and return the underlying error in a Box. /// /// This is useful to retain the allocation, as internally it's also stored in a box, /// when comparing it to [`Self::into_inner()`]. pub fn into_box(self) -> Box<E> { match self.frame.error.downcast() { Ok(err) => err, Err(_) => unreachable!("The type in the frame is always the type of this instance"), } } /// Discard all error context and return the underlying error. /// /// This may be needed to obtain something that once again implements `Error`. /// Note that this destroys the internal Box and moves the value back onto the stack. pub fn into_inner(self) -> E { *self.into_box() } /// Turn ourselves into a top-level [Error] that implements [`std::error::Error`]. /// /// [Error]: crate::Error pub fn into_error(self) -> crate::Error { self.into() } /// Convert this error tree into a chain of errors, breadth first, which flattens the tree /// but retains all type dynamic type information. /// /// This is useful for inter-op with `anyhow`. pub fn into_chain(self) -> crate::ChainedError { self.into() } /// Return the underlying exception frame. pub fn frame(&self) -> &Frame { &self.frame } /// Iterate over all frames in breadth-first order. The first frame is this instance, /// followed by all of its children. pub fn iter(&self) -> impl Iterator<Item = &Frame> { self.frame().iter_frames() } /// Iterate over all frames and find one that downcasts into error of type `T`. /// Note that the search includes this instance as well. pub fn downcast_any_ref<T: Error + 'static>(&self) -> Option<&T> { self.iter().find_map(|e| e.error().downcast_ref()) } } impl<E> Deref for Exn<E> where E: Error + Send + Sync + 'static, { type Target = E; fn deref(&self) -> &Self::Target { self.error() } } impl<E: Error + Send + Sync + 'static> fmt::Debug for Exn<E> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write_frame_recursive(f, self.frame(), "", ErrorMode::Display, TreeMode::Linearize) } } impl fmt::Debug for Frame { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write_frame_recursive(f, self, "", ErrorMode::Display, TreeMode::Linearize) } } #[derive(Copy, Clone)] enum ErrorMode { Display, Debug, } #[derive(Copy, Clone)] enum TreeMode { Linearize, Verbatim, } fn write_frame_recursive( f: &mut fmt::Formatter<'_>, frame: &Frame, prefix: &str, err_mode: ErrorMode, tree_mode: TreeMode, ) -> fmt::Result { match err_mode { ErrorMode::Display => fmt::Display::fmt(frame.error(), f), ErrorMode::Debug => { write!(f, "{:?}", frame.error()) } }?; if !f.alternate() { write_location(f, frame.location)?; } let children = frame.children(); let children_len = children.len(); for (cidx, child) in children.iter().enumerate() { write!(f, "\n{prefix}|")?; write!(f, "\n{prefix}└─ ")?; let child_child_len = child.children().len(); let may_linearize_chain = matches!(tree_mode, TreeMode::Linearize) && children_len == 1 && child_child_len == 1; if may_linearize_chain { write_frame_recursive(f, child, prefix, err_mode, tree_mode)?; } else if cidx < children_len - 1 { write_frame_recursive(f, child, &format!("{prefix}| "), err_mode, tree_mode)?; } else { write_frame_recursive(f, child, &format!("{prefix} "), err_mode, tree_mode)?; } } Ok(()) } impl<E: Error + Send + Sync + 'static> fmt::Display for Exn<E> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.frame, f) } } impl fmt::Display for Frame { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { // Avoid printing alternate versions of the debug info, keep it in one line, also print the tree. write_frame_recursive(f, self, "", ErrorMode::Debug, TreeMode::Verbatim) } else { fmt::Display::fmt(self.error(), f) } } } /// A frame in the exception tree. pub struct Frame { /// The error that occurred at this frame. error: Box<dyn Error + Send + Sync + 'static>, /// The source code location where this exception frame was created. location: &'static Location<'static>, /// Child exception frames that provide additional context or source errors. children: Vec<Frame>, } impl Frame { /// Return the error as a reference to [`Error`]. /// /// If the error was [erased](crate::Exn::erased), this is the original error, /// so it can still be downcast to its actual type. pub fn error(&self) -> &(dyn Error + Send + Sync + 'static) { match self.error.downcast_ref::<Untyped>() { Some(erased) => &*erased.0, None => &*self.error, } } /// Return the source code location where this exception frame was created. pub fn location(&self) -> &'static Location<'static> { self.location } /// Return a slice of the children of the exception. pub fn children(&self) -> &[Frame] { &self.children } } /// Navigation impl Frame { /// Find the best possible cause: /// /// * in a linear chain of a single error each, it's the last-most child frame /// * in trees, find the deepest-possible frame that has the most leafs as children /// /// Return `None` if there are no children. pub fn probable_cause(&self) -> Option<&Frame> { fn walk<'a>(frame: &'a Frame, depth: usize) -> (usize, usize, &'a Frame) { if frame.children.is_empty() { return (1, depth, frame); } let mut total_leafs = 0; let mut best: Option<(usize, usize, &'a Frame)> = None; for child in &frame.children { let (leafs, d, f) = walk(child, depth + 1); total_leafs += leafs; match best { None => best = Some((leafs, d, f)), Some((bl, bd, _)) => { if leafs > bl || (leafs == bl && d > bd) { best = Some((leafs, d, f)); } } } } let self_candidate = (total_leafs, depth, frame); match best { None => self_candidate, Some(best_child) => { if total_leafs > best_child.0 || (total_leafs == best_child.0 && depth > best_child.1) { self_candidate } else { best_child } } } } // Special case: a simple error just with children would render the same as a chain, // so pretend the last child is the cause. if self.children().iter().all(|c| c.children().is_empty()) { if let Some(last) = self.children().last() { return Some(last); } } let res = walk(self, 0).2; if std::ptr::addr_eq(res, self) { None } else { Some(res) } } /// Iterate over all frames in breadth-first order. The first frame is this instance, /// followed by all of its children. pub fn iter_frames(&self) -> impl Iterator<Item = &Frame> + '_ { let mut queue = std::collections::VecDeque::new(); queue.push_back(self); BreadthFirstFrames { queue } } } /// Breadth-first iterator over `Frame`s. pub struct BreadthFirstFrames<'a> { queue: std::collections::VecDeque<&'a Frame>, } impl<'a> Iterator for BreadthFirstFrames<'a> { type Item = &'a Frame; fn next(&mut self) -> Option<Self::Item> { let frame = self.queue.pop_front()?; for child in frame.children() { self.queue.push_back(child); } Some(frame) } } impl<E> From<Exn<E>> for Box<Frame> where E: Error + Send + Sync + 'static, { fn from(err: Exn<E>) -> Self { err.frame } } #[cfg(feature = "anyhow")] impl<E> From<Exn<E>> for anyhow::Error where E: Error + Send + Sync + 'static, { fn from(err: Exn<E>) -> Self { anyhow::Error::from(err.into_chain()) } } impl<E> From<Exn<E>> for Frame where E: Error + Send + Sync + 'static, { fn from(err: Exn<E>) -> Self { *err.frame } } impl From<Frame> for Exn { fn from(frame: Frame) -> Self { Exn { frame: Box::new(frame), phantom: Default::default(), } } } /// A marker to show that type information is not available, /// while storing all extractable information about the erased type. /// It's the default type for [Exn]. pub struct Untyped(Box<dyn Error + Send + Sync + 'static>); impl fmt::Display for Untyped { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } impl fmt::Debug for Untyped { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.0, f) } } impl Error for Untyped { fn source(&self) -> Option<&(dyn Error + 'static)> { self.0.source() } } /// An error that merely says that something is wrong. pub struct Something; impl fmt::Display for Something { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Something went wrong") } } impl fmt::Debug for Something { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self, f) } } impl Error for Something {} /// A way to keep all information of errors returned by `source()` chains. struct SourceError { display: String, alt_display: String, debug: String, alt_debug: String, } impl fmt::Debug for SourceError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let dbg = if f.alternate() { &self.alt_debug } else { &self.debug };