sqruff_lib/rules/layout/
lt04.rs

1use std::ops::Deref;
2
3use ahash::AHashMap;
4use sqruff_lib_core::dialects::syntax::{SyntaxKind, SyntaxSet};
5
6use super::lt03::RuleLT03;
7use crate::core::config::Value;
8use crate::core::rules::base::{Erased, ErasedRule, LintResult, Rule, RuleGroups};
9use crate::core::rules::context::RuleContext;
10use crate::core::rules::crawlers::{Crawler, SegmentSeekerCrawler};
11use crate::utils::reflow::sequence::{ReflowSequence, TargetSide};
12
13#[derive(Debug, Default, Clone)]
14pub struct RuleLT04 {
15    base: RuleLT03,
16}
17
18impl Rule for RuleLT04 {
19    fn load_from_config(&self, _config: &AHashMap<String, Value>) -> Result<ErasedRule, String> {
20        Ok(RuleLT04::default().erased())
21    }
22    fn name(&self) -> &'static str {
23        "layout.commas"
24    }
25
26    fn description(&self) -> &'static str {
27        "Leading/Trailing comma enforcement."
28    }
29
30    fn long_description(&self) -> &'static str {
31        r#"
32**Anti-pattern**
33
34There is a mixture of leading and trailing commas.
35
36```sql
37SELECT
38    a
39    , b,
40    c
41FROM foo
42```
43
44**Best practice**
45
46By default, sqruff prefers trailing commas. However it is configurable for leading commas. The chosen style must be used consistently throughout your SQL.
47
48```sql
49SELECT
50    a,
51    b,
52    c
53FROM foo
54
55-- Alternatively, set the configuration file to 'leading'
56-- and then the following would be acceptable:
57
58SELECT
59    a
60    , b
61    , c
62FROM foo
63```
64"#
65    }
66    fn groups(&self) -> &'static [RuleGroups] {
67        &[RuleGroups::All, RuleGroups::Layout]
68    }
69
70    fn eval(&self, context: &RuleContext) -> Vec<LintResult> {
71        let comma_positioning = context.config.raw["layout"]["type"]["comma"]["line_position"]
72            .as_string()
73            .unwrap();
74
75        if self.check_trail_lead_shortcut(
76            &context.segment,
77            context.parent_stack.last().unwrap(),
78            comma_positioning,
79        ) {
80            return vec![LintResult::new(None, Vec::new(), None, None)];
81        };
82
83        ReflowSequence::from_around_target(
84            &context.segment,
85            context.parent_stack.first().unwrap().clone(),
86            TargetSide::Both,
87            context.config,
88        )
89        .rebreak(context.tables)
90        .results()
91    }
92
93    fn is_fix_compatible(&self) -> bool {
94        true
95    }
96
97    fn crawl_behaviour(&self) -> Crawler {
98        SegmentSeekerCrawler::new(const { SyntaxSet::new(&[SyntaxKind::Comma]) }).into()
99    }
100}
101
102impl Deref for RuleLT04 {
103    type Target = RuleLT03;
104
105    fn deref(&self) -> &Self::Target {
106        &self.base
107    }
108}