Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Diagnostic for Vec<impl Diagnostic> #305

Open
gavrilikhin-d opened this issue Oct 19, 2023 · 3 comments
Open

Implement Diagnostic for Vec<impl Diagnostic> #305

gavrilikhin-d opened this issue Oct 19, 2023 · 3 comments

Comments

@gavrilikhin-d
Copy link
Contributor

... or, please, add any other way to pass multiple diagnostics at once to create a single report

@gavrilikhin-d
Copy link
Contributor Author

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:

  1. Can't create Report from &Diagnostic to provide a source code => need to clone it
  2. Can't pass &dyn SourceCode to with_source_code, because it doesn't implement (why?) SourceCode
  3. Report doesn't implement Diagnostic

@gavrilikhin-d
Copy link
Contributor Author

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 :)

@jdonszelmann
Copy link
Contributor

jdonszelmann commented Nov 9, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants