Files
dev/src/xatu/diagnostic.rs

92 lines
2.3 KiB
Rust

use std::fmt;
use std::path::{Path, PathBuf};
const PARSE_ERROR_RULE: &str = "parse_error";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Diagnostic {
pub path: PathBuf,
pub rule: &'static str,
pub message: String,
pub line: usize,
pub column: usize,
}
impl Diagnostic {
pub(crate) fn at_offset(
path: &Path,
source: &str,
rule: &'static str,
message: String,
offset: usize,
) -> Self {
let (line, column) = line_column(source, offset);
Self {
path: path.to_path_buf(),
rule,
message,
line,
column,
}
}
}
impl fmt::Display for Diagnostic {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"{}:{}:{}: {}: {}",
self.path.display(),
self.line,
self.column,
self.rule,
self.message
)
}
}
pub(crate) fn parse_error_diagnostics(
path: &Path,
source: &str,
errors: &[rnix::ParseError],
) -> Vec<Diagnostic> {
errors
.iter()
.map(|error| {
Diagnostic::at_offset(
path,
source,
PARSE_ERROR_RULE,
error.to_string(),
parse_error_offset(error, source.len()),
)
})
.collect()
}
fn parse_error_offset(error: &rnix::ParseError, end_offset: usize) -> usize {
let range = match error {
rnix::ParseError::Unexpected(range)
| rnix::ParseError::UnexpectedExtra(range)
| rnix::ParseError::UnexpectedWanted(_, range, _)
| rnix::ParseError::UnexpectedDoubleBind(range)
| rnix::ParseError::DuplicatedArgs(range, _) => Some(range),
rnix::ParseError::UnexpectedEOF
| rnix::ParseError::UnexpectedEOFWanted(_)
| rnix::ParseError::RecursionLimitExceeded => None,
_ => None,
};
range.map_or(end_offset, |range| usize::from(range.start()))
}
fn line_column(source: &str, offset: usize) -> (usize, usize) {
let prefix = &source[..offset.min(source.len())];
let line = prefix.bytes().filter(|byte| *byte == b'\n').count() + 1;
let line_start = prefix.rfind('\n').map_or(0, |index| index + 1);
let column = prefix[line_start..].chars().count() + 1;
(line, column)
}