Skip to main content

rigsql_rules/capitalisation/
cp04.rs

1use rigsql_core::{Segment, SegmentType, TokenKind};
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::utils::check_capitalisation;
5use crate::violation::LintViolation;
6
7/// CP04: Boolean/Null literals must be consistently capitalised.
8///
9/// By default, expects UPPER case (TRUE, FALSE, NULL).
10#[derive(Debug, Default)]
11pub struct RuleCP04;
12
13impl Rule for RuleCP04 {
14    fn code(&self) -> &'static str {
15        "CP04"
16    }
17    fn name(&self) -> &'static str {
18        "capitalisation.literals"
19    }
20    fn description(&self) -> &'static str {
21        "Boolean/Null literals must be consistently capitalised."
22    }
23    fn explanation(&self) -> &'static str {
24        "Boolean literals (TRUE, FALSE) and NULL should be consistently capitalised. \
25         Using UPPER case for these literals is the most common convention and improves readability."
26    }
27    fn groups(&self) -> &[RuleGroup] {
28        &[RuleGroup::Capitalisation]
29    }
30    fn is_fixable(&self) -> bool {
31        true
32    }
33
34    fn crawl_type(&self) -> CrawlType {
35        CrawlType::Segment(vec![SegmentType::BooleanLiteral, SegmentType::NullLiteral])
36    }
37
38    fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
39        let Segment::Token(t) = ctx.segment else {
40            return vec![];
41        };
42        if t.token.kind != TokenKind::Word {
43            return vec![];
44        }
45
46        let text = t.token.text.as_str();
47        let expected = text.to_ascii_uppercase();
48
49        check_capitalisation(
50            self.code(),
51            "Boolean/Null literals",
52            text,
53            &expected,
54            "upper",
55            t.token.span,
56        )
57        .into_iter()
58        .collect()
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use crate::test_utils::lint_sql;
66
67    #[test]
68    fn test_cp04_flags_lowercase_null() {
69        let violations = lint_sql("SELECT null", RuleCP04);
70        assert_eq!(violations.len(), 1);
71    }
72
73    #[test]
74    fn test_cp04_accepts_uppercase_null() {
75        let violations = lint_sql("SELECT NULL", RuleCP04);
76        assert_eq!(violations.len(), 0);
77    }
78
79    #[test]
80    fn test_cp04_flags_lowercase_true() {
81        let violations = lint_sql("SELECT true", RuleCP04);
82        assert_eq!(violations.len(), 1);
83    }
84
85    #[test]
86    fn test_cp04_fix_uppercases() {
87        let violations = lint_sql("SELECT null", RuleCP04);
88        assert_eq!(violations.len(), 1);
89        assert_eq!(violations[0].fixes.len(), 1);
90        assert_eq!(violations[0].fixes[0].new_text, "NULL");
91    }
92}