Skip to main content

rigsql_rules/aliasing/
al06.rs

1use rigsql_core::SegmentType;
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::utils::{extract_alias_name, is_in_table_context};
5use crate::violation::LintViolation;
6
7/// AL06: Enforce table alias lengths in FROM clauses and JOIN conditions.
8///
9/// Configurable minimum and maximum alias length. Disabled by default
10/// (both min and max are 0).
11#[derive(Debug, Default)]
12pub struct RuleAL06 {
13    pub min_alias_length: usize,
14    pub max_alias_length: usize,
15}
16
17impl Rule for RuleAL06 {
18    fn code(&self) -> &'static str {
19        "AL06"
20    }
21    fn name(&self) -> &'static str {
22        "aliasing.length"
23    }
24    fn description(&self) -> &'static str {
25        "Enforce table alias lengths in FROM clauses and JOIN conditions."
26    }
27    fn explanation(&self) -> &'static str {
28        "Table aliases that are too short (like single letters) can be cryptic. \
29         Aliases that are too long defeat the purpose of aliasing. Configure \
30         min_alias_length and max_alias_length to enforce your team's standards."
31    }
32    fn groups(&self) -> &[RuleGroup] {
33        &[RuleGroup::Aliasing]
34    }
35    fn is_fixable(&self) -> bool {
36        false
37    }
38
39    fn configure(&mut self, settings: &std::collections::HashMap<String, String>) {
40        if let Some(val) = settings.get("min_alias_length") {
41            if let Ok(n) = val.parse() {
42                self.min_alias_length = n;
43            }
44        }
45        if let Some(val) = settings.get("max_alias_length") {
46            if let Ok(n) = val.parse() {
47                self.max_alias_length = n;
48            }
49        }
50    }
51
52    fn crawl_type(&self) -> CrawlType {
53        CrawlType::Segment(vec![SegmentType::AliasExpression])
54    }
55
56    fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
57        if !is_in_table_context(ctx) {
58            return vec![];
59        }
60
61        // Both 0 means disabled
62        if self.min_alias_length == 0 && self.max_alias_length == 0 {
63            return vec![];
64        }
65
66        let Some(alias_name) = extract_alias_name(ctx.segment.children()) else {
67            return vec![];
68        };
69
70        let len = alias_name.len();
71
72        if self.min_alias_length > 0 && len < self.min_alias_length {
73            return vec![LintViolation::new(
74                self.code(),
75                format!(
76                    "Alias '{}' is too short ({} chars, minimum {}).",
77                    alias_name, len, self.min_alias_length
78                ),
79                ctx.segment.span(),
80            )];
81        }
82
83        if self.max_alias_length > 0 && len > self.max_alias_length {
84            return vec![LintViolation::new(
85                self.code(),
86                format!(
87                    "Alias '{}' is too long ({} chars, maximum {}).",
88                    alias_name, len, self.max_alias_length
89                ),
90                ctx.segment.span(),
91            )];
92        }
93
94        vec![]
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use crate::test_utils::lint_sql;
102
103    #[test]
104    fn test_al06_default_no_violation() {
105        let violations = lint_sql("SELECT * FROM users AS u", RuleAL06::default());
106        assert_eq!(violations.len(), 0);
107    }
108
109    #[test]
110    fn test_al06_min_length_flags_short() {
111        let rule = RuleAL06 {
112            min_alias_length: 2,
113            max_alias_length: 0,
114        };
115        let violations = lint_sql("SELECT * FROM users AS u", rule);
116        assert_eq!(violations.len(), 1);
117    }
118
119    #[test]
120    fn test_al06_min_length_accepts_long() {
121        let rule = RuleAL06 {
122            min_alias_length: 2,
123            max_alias_length: 0,
124        };
125        let violations = lint_sql("SELECT * FROM users AS usr", rule);
126        assert_eq!(violations.len(), 0);
127    }
128}