sqruff_lib/rules/capitalisation/
cp05.rs

1use ahash::AHashMap;
2use sqruff_lib_core::dialects::syntax::{SyntaxKind, SyntaxSet};
3
4use super::cp01::handle_segment;
5use crate::core::config::Value;
6use crate::core::rules::base::{Erased, ErasedRule, LintResult, Rule, RuleGroups};
7use crate::core::rules::context::RuleContext;
8use crate::core::rules::crawlers::{Crawler, SegmentSeekerCrawler};
9
10#[derive(Debug, Default, Clone)]
11pub struct RuleCP05 {
12    extended_capitalisation_policy: String,
13}
14
15impl Rule for RuleCP05 {
16    fn load_from_config(&self, config: &AHashMap<String, Value>) -> Result<ErasedRule, String> {
17        Ok(RuleCP05 {
18            extended_capitalisation_policy: config["extended_capitalisation_policy"]
19                .as_string()
20                .unwrap()
21                .to_string(),
22        }
23        .erased())
24    }
25
26    fn name(&self) -> &'static str {
27        "capitalisation.types"
28    }
29
30    fn description(&self) -> &'static str {
31        "Inconsistent capitalisation of datatypes."
32    }
33
34    fn long_description(&self) -> &'static str {
35        r#"
36**Anti-pattern**
37
38In this example, `int` and `unsigned` are in lower-case whereas `VARCHAR` is in upper-case.
39
40```sql
41CREATE TABLE t (
42    a int unsigned,
43    b VARCHAR(15)
44);
45```
46
47**Best practice**
48
49Ensure all datatypes are consistently upper or lower case
50
51```sql
52CREATE TABLE t (
53    a INT UNSIGNED,
54    b VARCHAR(15)
55);
56```
57"#
58    }
59
60    fn groups(&self) -> &'static [RuleGroups] {
61        &[
62            RuleGroups::All,
63            RuleGroups::Core,
64            RuleGroups::Capitalisation,
65        ]
66    }
67
68    fn eval(&self, context: &RuleContext) -> Vec<LintResult> {
69        let mut results = Vec::new();
70
71        if context.segment.is_type(SyntaxKind::PrimitiveType)
72            || context.segment.is_type(SyntaxKind::DatetimeTypeIdentifier)
73            || context.segment.is_type(SyntaxKind::DataType)
74        {
75            for seg in context.segment.segments() {
76                if seg.is_type(SyntaxKind::Symbol)
77                    || seg.is_type(SyntaxKind::Identifier)
78                    || seg.is_type(SyntaxKind::QuotedLiteral)
79                    || !seg.segments().is_empty()
80                {
81                    continue;
82                }
83
84                results.push(handle_segment(
85                    "Datatypes",
86                    &self.extended_capitalisation_policy,
87                    "extended_capitalisation_policy",
88                    seg.clone(),
89                    context,
90                ));
91            }
92        }
93
94        results
95    }
96
97    fn is_fix_compatible(&self) -> bool {
98        true
99    }
100
101    fn crawl_behaviour(&self) -> Crawler {
102        SegmentSeekerCrawler::new(
103            const {
104                SyntaxSet::new(&[
105                    SyntaxKind::DataTypeIdentifier,
106                    SyntaxKind::PrimitiveType,
107                    SyntaxKind::DatetimeTypeIdentifier,
108                    SyntaxKind::DataType,
109                ])
110            },
111        )
112        .into()
113    }
114}