use std::borrow::Cow;
use harn_lexer::{FixEdit, Span};
use harn_parser::{DiagnosticCode, SNode};
use harn_rules::{CompiledRule, Rule as EngineRuleModel, Severity as EngineSeverity};
use crate::diagnostic::{LintDiagnostic, LintSeverity};
use crate::rule::{Rule, RuleCtx};
pub(crate) struct EngineRule {
id: String,
compiled: CompiledRule,
}
impl EngineRule {
pub(crate) fn from_toml(source: &str) -> Option<Self> {
let model = EngineRuleModel::from_toml_str(source).ok()?;
let compiled = CompiledRule::compile(&model).ok()?;
let id = compiled.id().to_string();
Some(Self { id, compiled })
}
}
fn to_lexer_span(span: &harn_rules::Span) -> Span {
Span {
start: span.start_byte,
end: span.end_byte,
line: span.start_row + 1,
column: span.start_col + 1,
end_line: span.end_row + 1,
}
}
impl Rule for EngineRule {
fn id(&self) -> &str {
&self.id
}
fn check_program(
&mut self,
_program: &[SNode],
ctx: &RuleCtx<'_>,
out: &mut Vec<LintDiagnostic>,
) {
let Some(source) = ctx.source else {
return;
};
let Ok(diagnostics) = self.compiled.diagnostics(source) else {
return;
};
for diag in diagnostics {
let span = to_lexer_span(&diag.span);
out.push(LintDiagnostic {
code: DiagnosticCode::LintRuleEngine,
rule: Cow::Owned(diag.rule_id),
message: diag.message,
span,
severity: match diag.severity {
EngineSeverity::Info => LintSeverity::Info,
EngineSeverity::Warning => LintSeverity::Warning,
EngineSeverity::Error => LintSeverity::Error,
},
suggestion: None,
fix: diag
.fix
.map(|replacement| vec![FixEdit { span, replacement }]),
});
}
}
}