-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(remap): add new remap-diagnostic crate
Signed-off-by: Jean Mertz <git@jeanmertz.com>
- Loading branch information
Showing
10 changed files
with
878 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "remap-diagnostic" | ||
version = "0.1.0" | ||
authors = ["Vector Contributors <vector@timber.io>"] | ||
edition = "2018" | ||
publish = false | ||
|
||
[dependencies] | ||
codespan-reporting = "0.11" | ||
termcolor = "1" |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
use crate::{DiagnosticError, Label, Note, Severity, Span}; | ||
use codespan_reporting::diagnostic; | ||
use std::ops::{Deref, DerefMut}; | ||
|
||
#[derive(Debug, Clone, PartialEq)] | ||
pub struct Diagnostic { | ||
severity: Severity, | ||
message: String, | ||
labels: Vec<Label>, | ||
notes: Vec<Note>, | ||
} | ||
|
||
impl Diagnostic { | ||
pub fn error(message: impl ToString) -> Self { | ||
Self::new(Severity::Error, message, vec![], vec![]) | ||
} | ||
|
||
pub fn bug(message: impl ToString) -> Self { | ||
Self::new(Severity::Bug, message, vec![], vec![]) | ||
} | ||
|
||
pub fn new( | ||
severity: Severity, | ||
message: impl ToString, | ||
labels: Vec<Label>, | ||
notes: Vec<Note>, | ||
) -> Self { | ||
Self { | ||
severity, | ||
message: message.to_string(), | ||
labels, | ||
notes, | ||
} | ||
} | ||
|
||
pub fn with_primary(self, message: impl ToString, span: impl Into<Span>) -> Self { | ||
self.with_label(Label::primary(message, span.into())) | ||
} | ||
|
||
pub fn with_context(self, message: impl ToString, span: impl Into<Span>) -> Self { | ||
self.with_label(Label::context(message, span.into())) | ||
} | ||
|
||
pub fn with_label(mut self, label: Label) -> Self { | ||
self.labels.push(label); | ||
self | ||
} | ||
|
||
pub fn with_note(mut self, note: Note) -> Self { | ||
self.notes.push(note); | ||
self | ||
} | ||
|
||
pub fn severity(&self) -> Severity { | ||
self.severity | ||
} | ||
|
||
pub fn message(&self) -> &str { | ||
&self.message | ||
} | ||
|
||
pub fn notes(&self) -> &[Note] { | ||
&self.notes | ||
} | ||
|
||
pub fn labels(&self) -> &[Label] { | ||
&self.labels | ||
} | ||
|
||
/// Returns `true` if the diagnostic represents either an | ||
/// [error](Variant::Error) or [bug](Variant::Bug). | ||
#[inline] | ||
pub fn is_problem(&self) -> bool { | ||
self.severity.is_error() || self.severity.is_bug() | ||
} | ||
|
||
/// Returns `true` if the diagnostic represents a [bug](Variant::Bug). | ||
#[inline] | ||
pub fn is_bug(&self) -> bool { | ||
self.severity.is_bug() | ||
} | ||
|
||
/// Returns `true` if the diagnostic represents an [error](Variant::Error). | ||
#[inline] | ||
pub fn is_error(&self) -> bool { | ||
self.severity.is_error() | ||
} | ||
|
||
/// Returns `true` if the diagnostic represents a | ||
/// [warning](Variant::Warning). | ||
#[inline] | ||
pub fn is_warning(&self) -> bool { | ||
self.severity.is_warning() | ||
} | ||
|
||
/// Returns `true` if the diagnostic represents a [note](Variant::Note). | ||
#[inline] | ||
pub fn is_note(&self) -> bool { | ||
self.severity.is_note() | ||
} | ||
} | ||
|
||
impl From<Box<dyn DiagnosticError>> for Diagnostic { | ||
fn from(error: Box<dyn DiagnosticError>) -> Self { | ||
Self { | ||
severity: Severity::Error, | ||
message: error.message(), | ||
labels: error.labels(), | ||
notes: error.notes(), | ||
} | ||
} | ||
} | ||
|
||
impl Into<diagnostic::Diagnostic<()>> for Diagnostic { | ||
fn into(self) -> diagnostic::Diagnostic<()> { | ||
let mut notes = self.notes.to_vec(); | ||
notes.push(Note::SeeLangDocs); | ||
|
||
diagnostic::Diagnostic { | ||
severity: self.severity.into(), | ||
code: None, | ||
message: self.message.to_string(), | ||
labels: self.labels.to_vec().into_iter().map(Into::into).collect(), | ||
notes: notes.iter().map(ToString::to_string).collect(), | ||
} | ||
} | ||
} | ||
|
||
// ----------------------------------------------------------------------------- | ||
|
||
#[derive(Debug, Clone, Default, PartialEq)] | ||
pub struct DiagnosticList(Vec<Diagnostic>); | ||
|
||
impl DiagnosticList { | ||
/// Turns the diagnostic list into a result type, the `Ok` variant is | ||
/// returned if none of the diagnostics are errors or bugs. Otherwise the | ||
/// `Err` variant is returned. | ||
pub fn into_result(self) -> std::result::Result<DiagnosticList, DiagnosticList> { | ||
if self.is_err() { | ||
return Err(self); | ||
} | ||
|
||
Ok(self) | ||
} | ||
|
||
/// Returns `true` if there are any errors or bugs in the parsed source. | ||
pub fn is_err(&self) -> bool { | ||
self.0.iter().any(|d| d.is_problem()) | ||
} | ||
|
||
/// Returns the list of bug-level diagnostics. | ||
pub fn bugs(&self) -> Vec<&Diagnostic> { | ||
self.0.iter().filter(|d| d.is_bug()).collect() | ||
} | ||
|
||
/// Returns the list of error-level diagnostics. | ||
pub fn errors(&self) -> Vec<&Diagnostic> { | ||
self.0.iter().filter(|d| d.is_error()).collect() | ||
} | ||
|
||
/// Returns the list of warning-level diagnostics. | ||
pub fn warnings(&self) -> Vec<&Diagnostic> { | ||
self.0.iter().filter(|d| d.is_warning()).collect() | ||
} | ||
|
||
/// Returns the list of note-level diagnostics. | ||
pub fn notes(&self) -> Vec<&Diagnostic> { | ||
self.0.iter().filter(|d| d.is_note()).collect() | ||
} | ||
|
||
/// Returns `true` if there are any bug diagnostics. | ||
pub fn has_bugs(&self) -> bool { | ||
self.0.iter().any(|d| d.is_bug()) | ||
} | ||
|
||
/// Returns `true` if there are any error diagnostics. | ||
pub fn has_errors(&self) -> bool { | ||
self.0.iter().any(|d| d.is_error()) | ||
} | ||
|
||
/// Returns `true` if there are any warning diagnostics. | ||
pub fn has_warnings(&self) -> bool { | ||
self.0.iter().any(|d| d.is_warning()) | ||
} | ||
|
||
/// Returns `true` if there are any note diagnostics. | ||
pub fn has_notes(&self) -> bool { | ||
self.0.iter().any(|d| d.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()]) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
use crate::DiagnosticList; | ||
use std::fmt; | ||
|
||
/// A formatter to display diagnostics tied to a given source. | ||
pub struct Formatter<'a> { | ||
source: &'a str, | ||
diagnostics: DiagnosticList, | ||
color: bool, | ||
} | ||
|
||
impl<'a> Formatter<'a> { | ||
pub fn new(source: &'a str, diagnostics: impl Into<DiagnosticList>) -> Self { | ||
Self { | ||
source, | ||
diagnostics: diagnostics.into(), | ||
color: false, | ||
} | ||
} | ||
|
||
pub fn colored(mut self) -> Self { | ||
self.color = true; | ||
self | ||
} | ||
|
||
pub fn enable_colors(&mut self, color: bool) { | ||
self.color = color | ||
} | ||
} | ||
|
||
impl<'a> fmt::Display for Formatter<'a> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
use codespan_reporting::files::SimpleFile; | ||
use codespan_reporting::term; | ||
use std::str::from_utf8; | ||
use termcolor::Buffer; | ||
|
||
let file = SimpleFile::new("", self.source); | ||
let config = term::Config::default(); | ||
let mut buffer = if self.color { | ||
Buffer::ansi() | ||
} else { | ||
Buffer::no_color() | ||
}; | ||
|
||
f.write_str("\n")?; | ||
|
||
for diagnostic in self.diagnostics.iter() { | ||
term::emit(&mut buffer, &config, &file, &diagnostic.to_owned().into()) | ||
.map_err(|_| fmt::Error)?; | ||
} | ||
|
||
// Diagnostic messages can contain whitespace at the end of some lines. | ||
// This causes problems when used in our UI testing, as editors often | ||
// strip end-of-line whitespace. Removing this has no actual visual | ||
// impact. | ||
let string = from_utf8(buffer.as_slice()) | ||
.map_err(|_| fmt::Error)? | ||
.lines() | ||
.map(|line| line.trim_end()) | ||
.collect::<Vec<_>>() | ||
.join("\n"); | ||
|
||
f.write_str(&string) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
use crate::Span; | ||
use codespan_reporting::diagnostic; | ||
|
||
#[derive(Debug, PartialEq, Clone)] | ||
pub struct Label { | ||
pub message: String, | ||
pub primary: bool, | ||
pub span: Span, | ||
} | ||
|
||
impl Label { | ||
pub fn primary(message: impl ToString, span: impl Into<Span>) -> Self { | ||
Self { | ||
message: message.to_string(), | ||
primary: true, | ||
span: span.into(), | ||
} | ||
} | ||
|
||
pub fn context(message: impl ToString, span: impl Into<Span>) -> Self { | ||
Self { | ||
message: message.to_string(), | ||
primary: false, | ||
span: span.into(), | ||
} | ||
} | ||
} | ||
|
||
impl Into<diagnostic::Label<()>> for Label { | ||
fn into(self) -> diagnostic::Label<()> { | ||
let style = match self.primary { | ||
true => diagnostic::LabelStyle::Primary, | ||
false => diagnostic::LabelStyle::Secondary, | ||
}; | ||
|
||
diagnostic::Label { | ||
style, | ||
file_id: (), | ||
range: self.span.start()..self.span.end(), | ||
message: self.message, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
mod diagnostic; | ||
mod formatter; | ||
mod label; | ||
mod note; | ||
mod severity; | ||
mod span; | ||
|
||
pub use diagnostic::{Diagnostic, DiagnosticList}; | ||
pub use formatter::Formatter; | ||
pub use label::Label; | ||
pub use note::Note; | ||
pub use severity::Severity; | ||
pub use span::{span, Span}; | ||
|
||
/// A trait that can be implemented by error types to provide diagnostic | ||
/// information about the given error. | ||
pub trait DiagnosticError: std::error::Error { | ||
/// The subject message of the error. | ||
/// | ||
/// Defaults to the error message itself. | ||
fn message(&self) -> String { | ||
self.to_string() | ||
} | ||
|
||
/// One or more labels to provide more context for a given error. | ||
/// | ||
/// Defaults to no labels. | ||
fn labels(&self) -> Vec<Label> { | ||
vec![] | ||
} | ||
|
||
/// One or more notes shown at the bottom of the diagnostic message. | ||
/// | ||
/// Defaults to no notes. | ||
fn notes(&self) -> Vec<Note> { | ||
vec![] | ||
} | ||
} |
Oops, something went wrong.