sqruff_lib/rules/capitalisation/
cp03.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::{Erased, ErasedRule, LintResult, Rule, RuleGroups};
8use crate::core::rules::context::RuleContext;
9use crate::core::rules::crawlers::{Crawler, SegmentSeekerCrawler};
10
11#[derive(Debug, Clone)]
12pub struct RuleCP03 {
13 base: RuleCP01,
14}
15
16impl Default for RuleCP03 {
17 fn default() -> Self {
18 Self {
19 base: RuleCP01 {
20 skip_literals: false,
21 exclude_parent_types: &[],
22 ..Default::default()
23 },
24 }
25 }
26}
27
28impl Rule for RuleCP03 {
29 fn load_from_config(&self, config: &AHashMap<String, Value>) -> Result<ErasedRule, String> {
30 Ok(RuleCP03 {
31 base: RuleCP01 {
32 capitalisation_policy: config["extended_capitalisation_policy"]
33 .as_string()
34 .unwrap()
35 .into(),
36 description_elem: "Function names",
37 ignore_words: config["ignore_words"]
38 .map(|it| {
39 it.as_array()
40 .unwrap()
41 .iter()
42 .map(|it| it.as_string().unwrap().to_lowercase())
43 .collect()
44 })
45 .unwrap_or_default(),
46 ignore_words_regex: config["ignore_words_regex"]
47 .map(|it| {
48 it.as_array()
49 .unwrap()
50 .iter()
51 .map(|it| Regex::new(it.as_string().unwrap()).unwrap())
52 .collect()
53 })
54 .unwrap_or_default(),
55
56 ..Default::default()
57 },
58 }
59 .erased())
60 }
61
62 fn name(&self) -> &'static str {
63 "capitalisation.functions"
64 }
65
66 fn description(&self) -> &'static str {
67 "Inconsistent capitalisation of function names."
68 }
69
70 fn long_description(&self) -> &'static str {
71 r#"
72**Anti-pattern**
73
74In this example, the two `SUM` functions don’t have the same capitalisation.
75
76```sql
77SELECT
78 sum(a) AS aa,
79 SUM(b) AS bb
80FROM foo
81```
82
83**Best practice**
84
85Make the case consistent.
86
87
88```sql
89SELECT
90 sum(a) AS aa,
91 sum(b) AS bb
92FROM foo
93```
94"#
95 }
96
97 fn groups(&self) -> &'static [RuleGroups] {
98 &[
99 RuleGroups::All,
100 RuleGroups::Core,
101 RuleGroups::Capitalisation,
102 ]
103 }
104
105 fn eval(&self, context: &RuleContext) -> Vec<LintResult> {
106 self.base.eval(context)
107 }
108
109 fn is_fix_compatible(&self) -> bool {
110 true
111 }
112
113 fn crawl_behaviour(&self) -> Crawler {
114 SegmentSeekerCrawler::new(const {SyntaxSet::new(&[
115 SyntaxKind::FunctionNameIdentifier,
116 SyntaxKind::BareFunction,
117 ]) })
118 .into()
119 }
120}