Files
dev/src/xatu/rules.rs

109 lines
2.5 KiB
Rust

use crate::FileId;
use crate::ast;
use crate::rules::deprecated_to_path::DeprecatedToPath;
use crate::rules::empty_inherit::EmptyInherit;
use crate::rules::empty_let_in::EmptyLetIn;
use crate::rules::unquoted_uri::UnquotedUri;
pub mod deprecated_to_path;
pub mod empty_inherit;
pub mod empty_let_in;
pub mod unquoted_uri;
#[derive(Debug)]
pub struct Diagnostic {
pub file_id: FileId,
pub finding: Finding,
}
#[derive(Debug)]
pub struct Finding {
pub rule_id: &'static str,
pub range: ast::SourceRange,
pub message: &'static str,
}
impl std::fmt::Display for Diagnostic {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
formatter,
"{}: {}: {}",
self.finding.range, self.finding.rule_id, self.finding.message,
)
}
}
pub trait NodeRule {
type Node;
const ID: &'static str;
fn select(data: &ast::NodeData) -> Option<&Self::Node>;
fn check(node: &Self::Node) -> Option<&'static str>;
}
pub trait TokenRule {
type Token;
const ID: &'static str;
fn select(data: &ast::TokenData) -> Option<&Self::Token>;
fn check(token: &Self::Token) -> Option<&'static str>;
}
pub fn run_rules(file: &ast::ParsedFile) -> Vec<Diagnostic> {
let node_diagnostics = file.nodes.iter().flat_map(|node| {
[
run_node_rule::<DeprecatedToPath>(node),
run_node_rule::<EmptyInherit>(node),
run_node_rule::<EmptyLetIn>(node),
]
.into_iter()
.flatten()
.map(|finding| Diagnostic {
file_id: file.file_id,
finding,
})
});
let token_diagnostics = file.tokens.iter().flat_map(|token| {
[run_token_rule::<UnquotedUri>(token)]
.into_iter()
.flatten()
.map(|finding| Diagnostic {
file_id: file.file_id,
finding,
})
});
node_diagnostics.chain(token_diagnostics).collect()
}
fn run_node_rule<R>(node: &ast::Node) -> Option<Finding>
where
R: NodeRule,
{
let typed_node = R::select(&node.data)?;
let message = R::check(typed_node)?;
Some(Finding {
rule_id: R::ID,
range: node.range,
message,
})
}
fn run_token_rule<R>(token: &ast::Token) -> Option<Finding>
where
R: TokenRule,
{
let typed_token = R::select(&token.data)?;
let message = R::check(typed_token)?;
Some(Finding {
rule_id: R::ID,
range: token.range,
message,
})
}