rigsql_rules/capitalisation/
cp04.rs1use rigsql_core::{Segment, SegmentType, TokenKind};
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::utils::check_capitalisation;
5use crate::violation::LintViolation;
6
7#[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}