refactor(xatu): split lint rule framework
This commit is contained in:
91
src/xatu/diagnostic.rs
Normal file
91
src/xatu/diagnostic.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user