sqruff_lib/rules/layout/
lt04.rs1use 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}