sqruff_lib/rules/capitalisation/
cp03.rs

1use 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}