Open gavrilikhin-d opened 11 months ago
I tried to use a custom ErrVec
error and special report handler, to no avail.
There are 3 problems I've faced while doing it:
Report
from &Diagnostic
to provide a source code => need to clone it&dyn SourceCode
to with_source_code
, because it doesn't implement (why?) SourceCode
Report
doesn't implement Diagnostic
Ok, I've managed to do this, using a custom WithSourceCode
use std::fmt::{self, Display};
use miette::{Diagnostic, LabeledSpan, MietteHandler, ReportHandler, SourceCode};
/// Struct to report errors
pub struct Reporter;
impl Default for Reporter {
fn default() -> Self {
Self
}
}
impl ReportHandler for Reporter {
fn debug(&self, error: &(dyn miette::Diagnostic), f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
return fmt::Debug::fmt(error, f);
}
let handler = MietteHandler::default();
// Check that this is an error vector.
// We want to threat it as just a collection of unrelated errors
if error.to_string().is_empty() {
if let Some(source_code) = error.source_code() {
for e in error.related().unwrap() {
handler.debug(
&WithSourceCode {
diagnostic: e,
source_code,
},
f,
)?;
}
} else {
for e in error.related().unwrap() {
handler.debug(e, f)?;
}
}
Ok(())
} else {
handler.debug(error, f)
}
}
}
struct WithSourceCode<'d, 's> {
diagnostic: &'d dyn Diagnostic,
source_code: &'s dyn SourceCode,
}
impl Display for WithSourceCode<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.diagnostic)
}
}
impl fmt::Debug for WithSourceCode<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
std::fmt::Debug::fmt(&self.diagnostic, f)
}
}
impl std::error::Error for WithSourceCode<'_, '_> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.diagnostic.source()
}
}
impl Diagnostic for WithSourceCode<'_, '_> {
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.diagnostic.code()
}
fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
self.diagnostic.diagnostic_source()
}
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.diagnostic.help()
}
fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
self.diagnostic.labels()
}
fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
self.diagnostic.related()
}
fn severity(&self) -> Option<miette::Severity> {
self.diagnostic.severity()
}
fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
self.diagnostic.url()
}
fn source_code(&self) -> Option<&dyn SourceCode> {
self.diagnostic.source_code().or(Some(self.source_code))
}
}
I think, that &dyn/impl SourceCode
and &dyn/impl Diagnostic
should implement corresponding traits.
And Report should be a Diagnostic
:)
I currently do this by putting #[related]
on a Vec<impl Diagnostic>
, in a separate wrapper error that holds each of the individual errors. However, the meaning of that is subtly different I'd say.
... or, please, add any other way to pass multiple diagnostics at once to create a single report