rigsql_rules/structure/
st04.rs1use rigsql_core::SegmentType;
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::violation::LintViolation;
5
6#[derive(Debug, Default)]
10pub struct RuleST04;
11
12impl Rule for RuleST04 {
13 fn code(&self) -> &'static str {
14 "ST04"
15 }
16 fn name(&self) -> &'static str {
17 "structure.nested_case"
18 }
19 fn description(&self) -> &'static str {
20 "Nested CASE expressions should be avoided."
21 }
22 fn explanation(&self) -> &'static str {
23 "Nested CASE expressions make SQL queries difficult to read and maintain. \
24 Consider refactoring the logic using CTEs, subqueries, or separate columns \
25 to improve readability."
26 }
27 fn groups(&self) -> &[RuleGroup] {
28 &[RuleGroup::Structure]
29 }
30 fn is_fixable(&self) -> bool {
31 false
32 }
33
34 fn crawl_type(&self) -> CrawlType {
35 CrawlType::Segment(vec![SegmentType::CaseExpression])
36 }
37
38 fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
39 let mut found_nested = false;
41 let mut is_first = true;
42
43 ctx.segment.walk(&mut |seg| {
44 if is_first {
45 is_first = false;
46 return;
47 }
48 if seg.segment_type() == SegmentType::CaseExpression {
49 found_nested = true;
50 }
51 });
52
53 if found_nested {
54 return vec![LintViolation::new(
55 self.code(),
56 "Nested CASE expression found.",
57 ctx.segment.span(),
58 )];
59 }
60
61 vec![]
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::test_utils::lint_sql;
69
70 #[test]
71 fn test_st04_flags_nested_case() {
72 let violations = lint_sql(
73 "SELECT CASE WHEN x = 1 THEN CASE WHEN y = 2 THEN 'a' ELSE 'b' END ELSE 'c' END;",
74 RuleST04,
75 );
76 assert!(!violations.is_empty());
78 assert!(violations[0].message.contains("Nested CASE"));
79 }
80
81 #[test]
82 fn test_st04_accepts_simple_case() {
83 let violations = lint_sql("SELECT CASE WHEN x = 1 THEN 'a' ELSE 'b' END;", RuleST04);
84 assert_eq!(violations.len(), 0);
85 }
86}