mod context;
mod diagnostics;
mod rules;
#[cfg(test)]
mod tests;
pub use context::LintContext;
pub use diagnostics::{Diagnostic, FuseBlown, Severity};
pub use rules::{
BypassCapRule, DanglingReferenceRule, GhostNetRule, MalformedElementRule, PinMuxRule,
SchematicScaleRule, ShortedNetsRule, TraceClearanceRule, UnconnectedNetRule,
};
pub trait Rule {
fn name(&self) -> &'static str;
fn severity(&self) -> Severity {
Severity::Error
}
fn check(&self, ctx: &LintContext<'_>) -> Vec<Diagnostic>;
}
pub struct Linter {
rules: Vec<Box<dyn Rule>>,
}
impl Linter {
pub fn new() -> Self {
Self { rules: Vec::new() }
}
pub fn with_rule(mut self, rule: impl Rule + 'static) -> Self {
self.rules.push(Box::new(rule));
self
}
#[cfg(test)]
pub fn run(&self, circuit: &crate::parser::Circuit) -> Vec<Diagnostic> {
let netlist = crate::netlist::Netlist::from_circuit(circuit);
let ctx = LintContext::new(circuit, &netlist);
self.run_with_context(&ctx)
}
pub fn run_with_context(&self, ctx: &LintContext<'_>) -> Vec<Diagnostic> {
self.rules
.iter()
.flat_map(|rule| {
let severity = rule.severity();
rule.check(ctx)
.into_iter()
.map(move |d| Diagnostic { severity, ..d })
})
.collect()
}
}
impl Default for Linter {
fn default() -> Self {
Self::new()
.with_rule(UnconnectedNetRule)
.with_rule(MalformedElementRule)
.with_rule(GhostNetRule)
.with_rule(DanglingReferenceRule)
.with_rule(ShortedNetsRule)
.with_rule(PinMuxRule)
.with_rule(BypassCapRule)
.with_rule(TraceClearanceRule)
.with_rule(SchematicScaleRule)
}
}