use std::ops::{Deref, DerefMut};
use codespan_reporting::diagnostic;
use super::{DiagnosticMessage, Label, Note, Severity, Span};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Diagnostic {
pub severity: Severity,
pub code: usize,
pub message: String,
pub labels: Vec<Label>,
pub notes: Vec<Note>,
}
impl Diagnostic {
pub fn error(code: usize, message: impl ToString) -> Self {
Self::new(Severity::Error, code, message, vec![], vec![])
}
pub fn bug(code: usize, message: impl ToString) -> Self {
Self::new(Severity::Bug, code, message, vec![], vec![])
}
pub fn new(
severity: Severity,
code: usize,
message: impl ToString,
labels: Vec<Label>,
notes: Vec<Note>,
) -> Self {
Self {
severity,
code,
message: message.to_string(),
labels,
notes,
}
}
#[must_use]
pub fn with_primary(self, message: impl ToString, span: impl Into<Span>) -> Self {
self.with_label(Label::primary(message, span.into()))
}
#[must_use]
pub fn with_context(self, message: impl ToString, span: impl Into<Span>) -> Self {
self.with_label(Label::context(message, span.into()))
}
#[must_use]
pub fn with_label(mut self, label: Label) -> Self {
self.labels.push(label);
self
}
#[must_use]
pub fn with_note(mut self, note: Note) -> Self {
self.notes.push(note);
self
}
#[must_use]
pub fn severity(&self) -> Severity {
self.severity
}
#[must_use]
pub fn message(&self) -> &str {
&self.message
}
#[must_use]
pub fn notes(&self) -> &[Note] {
&self.notes
}
#[must_use]
pub fn labels(&self) -> &[Label] {
&self.labels
}
#[inline]
#[must_use]
pub fn is_problem(&self) -> bool {
self.severity.is_error() || self.severity.is_bug()
}
#[inline]
#[must_use]
pub fn is_bug(&self) -> bool {
self.severity.is_bug()
}
#[inline]
#[must_use]
pub fn is_error(&self) -> bool {
self.severity.is_error()
}
#[inline]
#[must_use]
pub fn is_warning(&self) -> bool {
self.severity.is_warning()
}
#[inline]
#[must_use]
pub fn is_note(&self) -> bool {
self.severity.is_note()
}
}
impl From<Box<dyn DiagnosticMessage>> for Diagnostic {
fn from(message: Box<dyn DiagnosticMessage>) -> Self {
Self {
severity: message.severity(),
code: message.code(),
message: message.message(),
labels: message.labels(),
notes: message.notes(),
}
}
}
impl From<Diagnostic> for diagnostic::Diagnostic<()> {
fn from(diag: Diagnostic) -> Self {
let mut notes = diag.notes.clone();
if diag.code >= 100 && diag.code <= 110 {
notes.push(Note::SeeCodeDocs(diag.code));
}
notes.push(Note::SeeLangDocs);
notes.push(Note::SeeRepl);
diagnostic::Diagnostic {
severity: diag.severity.into(),
code: Some(format!("E{:03}", diag.code)),
message: diag.message.clone(),
labels: diag.labels.iter().cloned().map(Into::into).collect(),
notes: notes.iter().map(ToString::to_string).collect(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct DiagnosticList(Vec<Diagnostic>);
impl DiagnosticList {
pub fn into_result(self) -> std::result::Result<DiagnosticList, DiagnosticList> {
if self.is_err() {
return Err(self);
}
Ok(self)
}
#[must_use]
pub fn is_err(&self) -> bool {
self.0.iter().any(Diagnostic::is_problem)
}
#[must_use]
pub fn bugs(&self) -> Vec<&Diagnostic> {
self.0.iter().filter(|d| d.is_bug()).collect()
}
#[must_use]
pub fn errors(&self) -> Vec<&Diagnostic> {
self.0.iter().filter(|d| d.is_error()).collect()
}
#[must_use]
pub fn warnings(&self) -> Vec<&Diagnostic> {
self.0.iter().filter(|d| d.is_warning()).collect()
}
#[must_use]
pub fn notes(&self) -> Vec<&Diagnostic> {
self.0.iter().filter(|d| d.is_note()).collect()
}
#[must_use]
pub fn has_bugs(&self) -> bool {
self.0.iter().any(Diagnostic::is_bug)
}
#[must_use]
pub fn has_errors(&self) -> bool {
self.0.iter().any(Diagnostic::is_error)
}
#[must_use]
pub fn has_warnings(&self) -> bool {
self.0.iter().any(Diagnostic::is_warning)
}
#[must_use]
pub fn has_notes(&self) -> bool {
self.0.iter().any(Diagnostic::is_note)
}
}
impl Deref for DiagnosticList {
type Target = Vec<Diagnostic>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for DiagnosticList {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl IntoIterator for DiagnosticList {
type Item = Diagnostic;
type IntoIter = std::vec::IntoIter<Diagnostic>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<T: Into<Diagnostic>> From<Vec<T>> for DiagnosticList {
fn from(diagnostics: Vec<T>) -> Self {
Self(diagnostics.into_iter().map(Into::into).collect())
}
}
impl<T: Into<Diagnostic>> From<T> for DiagnosticList {
fn from(diagnostic: T) -> Self {
Self(vec![diagnostic.into()])
}
}