use crate::ast::Span;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Severity {
Warning,
Error,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Position {
pub line: u32,
pub column: u32,
pub current_offset: u32,
pub end_offset: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Issue {
pub file_name: Option<String>,
pub position: Option<Position>,
pub description: String,
pub severity: Severity,
}
impl Issue {
pub fn line(&self) -> String {
let (line, column) = self.position.map_or((0, 0), |p| (p.line, p.column));
format!("L{line}:{column} {}", self.description)
}
}
#[derive(Debug, Clone)]
pub(crate) struct Diag {
pub span: Option<Span>,
pub message: String,
pub severity: Severity,
}
impl Diag {
pub fn error(span: Span, message: impl Into<String>) -> Self {
Self {
span: Some(span),
message: message.into(),
severity: Severity::Error,
}
}
pub fn warning(span: Span, message: impl Into<String>) -> Self {
Self {
span: Some(span),
message: message.into(),
severity: Severity::Warning,
}
}
pub fn into_issue(self, offsets: &Offsets, file_name: Option<&str>) -> Issue {
Issue {
file_name: file_name.map(str::to_owned),
position: self.span.map(|span| Position {
line: span.line,
column: span.column,
current_offset: offsets.at(span.start),
end_offset: offsets.at(span.end).max(offsets.at(span.start) + 1) - 1,
}),
description: self.message,
severity: self.severity,
}
}
}
pub(crate) struct Offsets {
len: usize,
counts: Option<Vec<u32>>,
}
impl Offsets {
pub fn new(src: &str) -> Self {
let counts = (!src.is_ascii()).then(|| {
let mut counts = Vec::with_capacity(src.len() + 1);
for (count, c) in src.chars().enumerate() {
let count = u32::try_from(count).unwrap_or(u32::MAX);
counts.extend(std::iter::repeat_n(count, c.len_utf8()));
}
counts.push(u32::try_from(src.chars().count()).unwrap_or(u32::MAX));
counts
});
Self {
len: src.len(),
counts,
}
}
fn at(&self, offset: usize) -> u32 {
let offset = offset.min(self.len);
match &self.counts {
Some(counts) => counts[offset],
None => u32::try_from(offset).unwrap_or(u32::MAX),
}
}
}
pub(crate) fn sort(diags: &mut [Diag]) {
diags.sort_by(|a, b| {
let key = |d: &Diag| d.span.map_or((0, 0), |s| (s.line, s.column));
key(a).cmp(&key(b)).then_with(|| a.message.cmp(&b.message))
});
}