sqruff_lib/rules/capitalisation/
cp04.rs1use ahash::AHashMap;
2use regex::Regex;
3use sqruff_lib_core::dialects::syntax::{SyntaxKind, SyntaxSet};
4
5use super::cp01::RuleCP01;
6use crate::core::config::Value;
7use crate::core::rules::base::{CloneRule, ErasedRule, LintResult, Rule, RuleGroups};
8use crate::core::rules::context::RuleContext;
9use crate::core::rules::crawlers::{Crawler, SegmentSeekerCrawler};
10
11#[derive(Clone, Debug)]
12pub struct RuleCP04 {
13 base: RuleCP01,
14}
15
16impl Default for RuleCP04 {
17 fn default() -> Self {
18 Self {
19 base: RuleCP01 {
20 skip_literals: false,
21 exclude_parent_types: &[],
22 description_elem: "Boolean/null literals",
23 ..Default::default()
24 },
25 }
26 }
27}
28
29impl Rule for RuleCP04 {
30 fn load_from_config(&self, config: &AHashMap<String, Value>) -> Result<ErasedRule, String> {
31 Ok(RuleCP04 {
32 base: RuleCP01 {
33 capitalisation_policy: config["capitalisation_policy"].as_string().unwrap().into(),
34 ignore_words: config["ignore_words"]
35 .map(|it| {
36 it.as_array()
37 .unwrap_or_default()
38 .iter()
39 .map(|it| it.as_string().unwrap().to_lowercase())
40 .collect()
41 })
42 .unwrap_or_default(),
43 ignore_words_regex: config["ignore_words_regex"]
44 .map(|it| {
45 it.as_array()
46 .unwrap_or_default()
47 .iter()
48 .map(|it| Regex::new(it.as_string().unwrap()).unwrap())
49 .collect()
50 })
51 .unwrap_or_default(),
52 ..Default::default()
53 },
54 }
55 .erased())
56 }
57
58 fn name(&self) -> &'static str {
59 "capitalisation.literals"
60 }
61
62 fn description(&self) -> &'static str {
63 "Inconsistent capitalisation of boolean/null literal."
64 }
65
66 fn long_description(&self) -> &'static str {
67 r#"
68**Anti-pattern**
69
70In this example, `null` and `false` are in lower-case whereas `TRUE` is in upper-case.
71
72```sql
73select
74 a,
75 null,
76 TRUE,
77 false
78from foo
79```
80
81**Best practice**
82
83Ensure all literal `null`/`true`/`false` literals are consistently upper or lower case
84
85```sql
86select
87 a,
88 NULL,
89 TRUE,
90 FALSE
91from foo
92
93-- Also good
94
95select
96 a,
97 null,
98 true,
99 false
100from foo
101```
102"#
103 }
104
105 fn groups(&self) -> &'static [RuleGroups] {
106 &[
107 RuleGroups::All,
108 RuleGroups::Core,
109 RuleGroups::Capitalisation,
110 ]
111 }
112 fn eval(&self, context: &RuleContext) -> Vec<LintResult> {
113 self.base.eval(context)
114 }
115
116 fn is_fix_compatible(&self) -> bool {
117 true
118 }
119
120 fn crawl_behaviour(&self) -> Crawler {
121 SegmentSeekerCrawler::new(
122 const { SyntaxSet::new(&[SyntaxKind::NullLiteral, SyntaxKind::BooleanLiteral]) },
123 )
124 .into()
125 }
126}