Skip to main content

rigsql_rules/convention/
cv09.rs

1use rigsql_core::{Segment, SegmentType};
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::violation::LintViolation;
5
6/// CV09: Use of blocked words.
7///
8/// Flag identifiers that match a configurable list of blocked words.
9#[derive(Debug, Default)]
10pub struct RuleCV09 {
11    pub blocked_words: Vec<String>,
12}
13
14impl Rule for RuleCV09 {
15    fn code(&self) -> &'static str {
16        "CV09"
17    }
18    fn name(&self) -> &'static str {
19        "convention.blocked_words"
20    }
21    fn description(&self) -> &'static str {
22        "Use of blocked words."
23    }
24    fn explanation(&self) -> &'static str {
25        "Certain words may be reserved, deprecated, or disallowed by team convention. \
26         This rule flags identifiers that match a configurable list of blocked words."
27    }
28    fn groups(&self) -> &[RuleGroup] {
29        &[RuleGroup::Convention]
30    }
31    fn is_fixable(&self) -> bool {
32        false
33    }
34
35    fn configure(&mut self, settings: &std::collections::HashMap<String, String>) {
36        if let Some(val) = settings.get("blocked_words") {
37            self.blocked_words = val
38                .split(',')
39                .map(|s| s.trim().to_lowercase())
40                .filter(|s| !s.is_empty())
41                .collect();
42        }
43    }
44
45    fn crawl_type(&self) -> CrawlType {
46        CrawlType::Segment(vec![SegmentType::Identifier])
47    }
48
49    fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
50        if self.blocked_words.is_empty() {
51            return vec![];
52        }
53
54        let Segment::Token(t) = ctx.segment else {
55            return vec![];
56        };
57
58        let word = t.token.text.to_lowercase();
59        if self.blocked_words.contains(&word) {
60            return vec![LintViolation::new(
61                self.code(),
62                format!("Identifier '{}' is a blocked word.", t.token.text),
63                t.token.span,
64            )];
65        }
66
67        vec![]
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use crate::test_utils::lint_sql;
75
76    #[test]
77    fn test_cv09_no_blocked_words_no_violation() {
78        let violations = lint_sql("SELECT temp FROM t", RuleCV09::default());
79        assert_eq!(violations.len(), 0);
80    }
81
82    #[test]
83    fn test_cv09_flags_blocked_word() {
84        let rule = RuleCV09 {
85            blocked_words: vec!["temp".to_string(), "foo".to_string()],
86        };
87        let violations = lint_sql("SELECT temp FROM t", rule);
88        assert_eq!(violations.len(), 1);
89    }
90}