nu_lint/
rule.rs

1use crate::{
2    context::LintContext,
3    lint::{Severity, Violation},
4};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum RuleCategory {
8    Style,
9    BestPractices,
10    Performance,
11    Documentation,
12    TypeSafety,
13}
14
15impl std::fmt::Display for RuleCategory {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        match self {
18            RuleCategory::Style => write!(f, "style"),
19            RuleCategory::BestPractices => write!(f, "best-practices"),
20            RuleCategory::Performance => write!(f, "performance"),
21            RuleCategory::Documentation => write!(f, "documentation"),
22            RuleCategory::TypeSafety => write!(f, "type-safety"),
23        }
24    }
25}
26
27/// Common metadata for all rules
28pub trait RuleMetadata: Send + Sync {
29    fn id(&self) -> &str;
30    fn category(&self) -> RuleCategory;
31    fn severity(&self) -> Severity;
32    fn description(&self) -> &str;
33}
34
35/// Rules that check code using regex patterns on source text
36pub trait RegexRule: RuleMetadata {
37    fn check(&self, context: &LintContext) -> Vec<Violation>;
38}
39
40/// Rules that check code using AST traversal
41pub trait AstRule: RuleMetadata {
42    fn check(&self, context: &LintContext) -> Vec<Violation>;
43}
44
45/// Type-safe wrapper for different rule implementations
46pub enum Rule {
47    Regex(Box<dyn RegexRule>),
48    Ast(Box<dyn AstRule>),
49}
50
51impl Rule {
52    /// Check this rule against the given context
53    #[must_use]
54    pub fn check(&self, context: &LintContext) -> Vec<Violation> {
55        match self {
56            Rule::Regex(rule) => rule.check(context),
57            Rule::Ast(rule) => rule.check(context),
58        }
59    }
60
61    /// Check if this is an AST-based rule
62    #[must_use]
63    pub fn is_ast_rule(&self) -> bool {
64        matches!(self, Rule::Ast(_))
65    }
66}
67
68impl RuleMetadata for Rule {
69    fn id(&self) -> &str {
70        match self {
71            Rule::Regex(rule) => rule.id(),
72            Rule::Ast(rule) => rule.id(),
73        }
74    }
75
76    fn category(&self) -> RuleCategory {
77        match self {
78            Rule::Regex(rule) => rule.category(),
79            Rule::Ast(rule) => rule.category(),
80        }
81    }
82
83    fn severity(&self) -> Severity {
84        match self {
85            Rule::Regex(rule) => rule.severity(),
86            Rule::Ast(rule) => rule.severity(),
87        }
88    }
89
90    fn description(&self) -> &str {
91        match self {
92            Rule::Regex(rule) => rule.description(),
93            Rule::Ast(rule) => rule.description(),
94        }
95    }
96}