rigsql_rules/layout/
lt03.rs1use rigsql_core::SegmentType;
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::violation::{LintViolation, SourceEdit};
5
6#[derive(Debug, Default)]
10pub struct RuleLT03;
11
12impl Rule for RuleLT03 {
13 fn code(&self) -> &'static str {
14 "LT03"
15 }
16 fn name(&self) -> &'static str {
17 "layout.operators"
18 }
19 fn description(&self) -> &'static str {
20 "Operators should be surrounded by single spaces."
21 }
22 fn explanation(&self) -> &'static str {
23 "Binary operators (=, <, >, +, -, etc.) should have a single space on each side \
24 for readability. 'a=b' and 'a = b' are harder to read than 'a = b'."
25 }
26 fn groups(&self) -> &[RuleGroup] {
27 &[RuleGroup::Layout]
28 }
29 fn is_fixable(&self) -> bool {
30 true
31 }
32
33 fn crawl_type(&self) -> CrawlType {
34 CrawlType::Segment(vec![
35 SegmentType::ComparisonOperator,
36 SegmentType::ArithmeticOperator,
37 ])
38 }
39
40 fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
41 let span = ctx.segment.span();
42 let mut violations = Vec::new();
43
44 if ctx.index_in_parent > 0 {
46 let prev = &ctx.siblings[ctx.index_in_parent - 1];
47 if prev.segment_type() != SegmentType::Whitespace {
48 violations.push(LintViolation::with_fix_and_msg_key(
49 self.code(),
50 "Missing space before operator.",
51 span,
52 vec![SourceEdit::insert(span.start, " ")],
53 "rules.LT03.msg.before",
54 vec![],
55 ));
56 }
57 }
58
59 if ctx.index_in_parent + 1 < ctx.siblings.len() {
61 let next = &ctx.siblings[ctx.index_in_parent + 1];
62 if next.segment_type() != SegmentType::Whitespace {
63 violations.push(LintViolation::with_fix_and_msg_key(
64 self.code(),
65 "Missing space after operator.",
66 span,
67 vec![SourceEdit::insert(span.end, " ")],
68 "rules.LT03.msg.after",
69 vec![],
70 ));
71 }
72 }
73
74 violations
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use crate::test_utils::lint_sql;
82
83 #[test]
84 fn test_lt03_flags_missing_space() {
85 let violations = lint_sql("SELECT * FROM t WHERE x=1", RuleLT03);
86 assert!(!violations.is_empty());
87 assert!(violations.iter().all(|v| v.rule_code == "LT03"));
88 }
89
90 #[test]
91 fn test_lt03_accepts_proper_spacing() {
92 let violations = lint_sql("SELECT * FROM t WHERE x = 1", RuleLT03);
93 assert_eq!(violations.len(), 0);
94 }
95}