use crate::token::Span;
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Severity {
Warning,
Error,
}
impl std::fmt::Display for Severity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Severity::Warning => write!(f, "warning"),
Severity::Error => write!(f, "error"),
}
}
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct Replacement {
pub offset: usize,
pub length: usize,
pub new_text: String,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct Fix {
pub replacements: Vec<Replacement>,
pub is_safe: bool,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct Diagnostic {
pub rule: String,
pub message: String,
pub severity: Severity,
pub span: DiagnosticSpan,
pub file: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub fix: Option<Fix>,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct DiagnosticSpan {
pub line: usize,
pub column: usize,
}
impl From<Span> for DiagnosticSpan {
fn from(span: Span) -> Self {
Self {
line: span.line,
column: span.column,
}
}
}
impl Diagnostic {
pub fn warning(rule: &str, message: String, span: Span, file: &str) -> Self {
Self {
rule: rule.to_string(),
message,
severity: Severity::Warning,
span: span.into(),
file: file.to_string(),
fix: None,
}
}
pub fn error(rule: &str, message: String, span: Span, file: &str) -> Self {
Self {
rule: rule.to_string(),
message,
severity: Severity::Error,
span: span.into(),
file: file.to_string(),
fix: None,
}
}
pub fn with_fix(mut self, fix: Fix) -> Self {
self.fix = Some(fix);
self
}
#[allow(clippy::too_many_arguments)] pub fn warning_with_fix(
rule: &str,
message: String,
span: Span,
file: &str,
offset: usize,
length: usize,
new_text: String,
is_safe: bool,
) -> Self {
Self::warning(rule, message, span, file).with_fix(Fix {
replacements: vec![Replacement {
offset,
length,
new_text,
}],
is_safe,
})
}
}
pub fn line_byte_offset(lines: &[String], line_idx: usize) -> usize {
lines[..line_idx].iter().map(|l| l.len() + 1).sum()
}