use crate::{Error, Severity};
pub trait DiagnosticReporter {
fn finish(&mut self, result: &DiagnosticResult) -> Option<String>;
fn supports_minified_file_fallback(&self) -> bool {
true
}
fn render_error(&mut self, error: Error) -> Option<String>;
}
#[derive(Default, Debug)]
pub struct DiagnosticResult {
warnings_count: usize,
errors_count: usize,
max_warnings_exceeded: bool,
}
impl DiagnosticResult {
pub fn new(warnings_count: usize, errors_count: usize, max_warnings_exceeded: bool) -> Self {
Self { warnings_count, errors_count, max_warnings_exceeded }
}
pub fn warnings_count(&self) -> usize {
self.warnings_count
}
pub fn errors_count(&self) -> usize {
self.errors_count
}
pub fn max_warnings_exceeded(&self) -> bool {
self.max_warnings_exceeded
}
}
#[derive(Debug)]
pub struct Info {
pub start: InfoPosition,
pub end: InfoPosition,
pub filename: String,
pub message: String,
pub severity: Severity,
pub rule_id: Option<String>,
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct InfoPosition {
pub line: usize,
pub column: usize,
}
fn line_column(data: &[u8], mut offset: usize) -> Option<InfoPosition> {
if offset > data.len() {
return None;
}
if offset > 0 && offset < data.len() && data[offset - 1] == b'\r' && data[offset] == b'\n' {
offset -= 1;
}
let mut line = 1;
let mut line_start = 0;
for index in memchr::memchr2_iter(b'\r', b'\n', &data[..offset]) {
if data[index] == b'\n' && index > 0 && data[index - 1] == b'\r' {
continue;
}
line += 1;
line_start = if data[index] == b'\r' && index + 1 < offset && data[index + 1] == b'\n' {
index + 2
} else {
index + 1
};
}
Some(InfoPosition { line, column: offset - line_start + 1 })
}
impl Info {
pub fn new(diagnostic: &Error) -> Self {
let mut start = InfoPosition { line: 0, column: 0 };
let mut end = InfoPosition { line: 0, column: 0 };
let mut filename = String::new();
let mut message = String::new();
let mut severity = Severity::Warning;
let rule_id = diagnostic.code().map(|code| code.to_string());
if let Some(source) = diagnostic.source_code()
&& let Some(label) = diagnostic.labels().first()
&& let Ok(start_offset) = usize::try_from(label.offset())
&& let Some(end_offset) = label.offset().checked_add(label.len())
&& let Ok(end_offset) = usize::try_from(end_offset)
&& source.data().get(start_offset..end_offset).is_some()
&& let Some(start_position) = line_column(source.data(), start_offset)
{
start = start_position;
if let Some(end_position) = line_column(source.data(), end_offset) {
end = end_position;
}
if let Some(name) = source.name() {
filename = name.to_string();
}
if matches!(diagnostic.severity(), Some(Severity::Error)) {
severity = Severity::Error;
}
message = diagnostic.to_string();
}
Self { start, end, filename, message, severity, rule_id }
}
}