From cfc9c23265866e0c2df8af2186479bc610ead150 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 28 Apr 2022 12:47:04 +0200 Subject: [PATCH] Add DiagnosticChain Signed-off-by: Matthias Beyer --- src/diagnostic_chain.rs | 93 +++++++++++++++++++++++++++++++++++++++ src/handlers/debug.rs | 3 ++ src/handlers/graphical.rs | 2 +- src/lib.rs | 1 + src/protocol.rs | 2 +- 5 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src/diagnostic_chain.rs diff --git a/src/diagnostic_chain.rs b/src/diagnostic_chain.rs new file mode 100644 index 0000000..1e5e0c2 --- /dev/null +++ b/src/diagnostic_chain.rs @@ -0,0 +1,93 @@ +/*! +Iterate over error `.diagnostic_source()` chains. +*/ + +use crate::protocol::Diagnostic; + +/// Iterator of a chain of cause errors. +#[derive(Clone, Default)] +#[allow(missing_debug_implementations)] +pub(crate) struct DiagnosticChain<'a> { + state: Option>, +} + +impl<'a> DiagnosticChain<'a> { + pub(crate) fn from_diagnostic(head: &'a dyn Diagnostic) -> Self { + DiagnosticChain { + state: Some(ErrorKind::Diagnostic(head)), + } + } + + pub(crate) fn from_stderror(head: &'a (dyn std::error::Error + 'static)) -> Self { + DiagnosticChain { + state: Some(ErrorKind::StdError(head)), + } + } +} + +impl<'a> Iterator for DiagnosticChain<'a> { + type Item = ErrorKind<'a>; + + fn next(&mut self) -> Option { + if let Some(err) = self.state.take() { + self.state = err.get_nested(); + Some(err) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } +} + +impl ExactSizeIterator for DiagnosticChain<'_> { + fn len(&self) -> usize { + fn depth(d: Option<&ErrorKind<'_>>) -> usize { + match d { + Some(d) => 1 + depth(d.get_nested().as_ref()), + None => 0, + } + } + + depth(self.state.as_ref()) + } +} + +#[derive(Clone)] +pub(crate) enum ErrorKind<'a> { + Diagnostic(&'a dyn Diagnostic), + StdError(&'a (dyn std::error::Error + 'static)), +} + +impl<'a> ErrorKind<'a> { + fn get_nested(&self) -> Option> { + match self { + ErrorKind::Diagnostic(d) => d + .diagnostic_source() + .map(ErrorKind::Diagnostic) + .or_else(|| d.source().map(ErrorKind::StdError)), + ErrorKind::StdError(e) => e.source().map(ErrorKind::StdError), + } + } +} + +impl<'a> std::fmt::Debug for ErrorKind<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ErrorKind::Diagnostic(d) => d.fmt(f), + ErrorKind::StdError(e) => e.fmt(f), + } + } +} + +impl<'a> std::fmt::Display for ErrorKind<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ErrorKind::Diagnostic(d) => d.fmt(f), + ErrorKind::StdError(e) => e.fmt(f), + } + } +} diff --git a/src/handlers/debug.rs b/src/handlers/debug.rs index bc245ba..a9460d1 100644 --- a/src/handlers/debug.rs +++ b/src/handlers/debug.rs @@ -51,6 +51,9 @@ impl DebugReportHandler { let labels: Vec<_> = labels.collect(); diag.field("labels", &format!("{:?}", labels)); } + if let Some(cause) = diagnostic.diagnostic_source() { + diag.field("caused by", &format!("{:?}", cause)); + } diag.finish()?; writeln!(f)?; writeln!(f, "NOTE: If you're looking for the fancy error reports, install miette with the `fancy` feature, or write your own and hook it up with miette::set_hook().") diff --git a/src/handlers/graphical.rs b/src/handlers/graphical.rs index 377922f..f870766 100644 --- a/src/handlers/graphical.rs +++ b/src/handlers/graphical.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Write}; use owo_colors::{OwoColorize, Style}; -use crate::chain::Chain; +use crate::diagnostic_chain::DiagnosticChain; use crate::handlers::theme::*; use crate::protocol::{Diagnostic, Severity}; use crate::{LabeledSpan, MietteError, ReportHandler, SourceCode, SourceSpan, SpanContents}; diff --git a/src/lib.rs b/src/lib.rs index d76f805..e397712 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -584,6 +584,7 @@ pub use panic::*; pub use protocol::*; mod chain; +mod diagnostic_chain; mod error; mod eyreish; #[cfg(feature = "fancy-no-backtrace")] diff --git a/src/protocol.rs b/src/protocol.rs index 2493346..d5cd1fa 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -61,7 +61,7 @@ pub trait Diagnostic: std::error::Error { } /// The cause of the error. - fn caused_by(&self) -> Option<&dyn Diagnostic> { + fn diagnostic_source(&self) -> Option<&dyn Diagnostic> { None } }