rigsql_rules/layout/
lt05.rs1use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
2use crate::violation::LintViolation;
3
4#[derive(Debug)]
8pub struct RuleLT05 {
9 pub max_line_length: usize,
10}
11
12impl Default for RuleLT05 {
13 fn default() -> Self {
14 Self {
15 max_line_length: 80,
16 }
17 }
18}
19
20impl Rule for RuleLT05 {
21 fn code(&self) -> &'static str {
22 "LT05"
23 }
24 fn name(&self) -> &'static str {
25 "layout.long_lines"
26 }
27 fn description(&self) -> &'static str {
28 "Line too long."
29 }
30 fn explanation(&self) -> &'static str {
31 "Long lines are harder to read and review. Keep lines under the configured \
32 maximum length (default 80 characters). Break long queries across multiple lines."
33 }
34 fn groups(&self) -> &[RuleGroup] {
35 &[RuleGroup::Layout]
36 }
37 fn is_fixable(&self) -> bool {
38 false
39 }
40
41 fn configure(&mut self, settings: &std::collections::HashMap<String, String>) {
42 if let Some(val) = settings.get("max_line_length") {
43 if let Ok(n) = val.parse() {
44 self.max_line_length = n;
45 }
46 }
47 }
48
49 fn crawl_type(&self) -> CrawlType {
50 CrawlType::RootOnly
51 }
52
53 fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
54 let mut violations = Vec::new();
55 let source = ctx.source;
56 let mut offset = 0usize;
57
58 for line in source.lines() {
59 let line_len = line.len();
60 if line_len > self.max_line_length {
61 let span = rigsql_core::Span::new(offset as u32, (offset + line_len) as u32);
62 violations.push(LintViolation::with_msg_key(
63 self.code(),
64 format!(
65 "Line is too long ({} > {} characters).",
66 line_len, self.max_line_length
67 ),
68 span,
69 "rules.LT05.msg",
70 vec![
71 ("length".to_string(), line_len.to_string()),
72 ("max".to_string(), self.max_line_length.to_string()),
73 ],
74 ));
75 }
76 offset += line_len + 1; }
78
79 violations
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::test_utils::lint_sql;
87
88 #[test]
89 fn test_lt05_flags_long_line() {
90 let long_sql = format!("SELECT {} FROM t", "a, ".repeat(30));
91 let violations = lint_sql(&long_sql, RuleLT05::default());
92 assert!(!violations.is_empty());
93 assert!(violations.iter().all(|v| v.rule_code == "LT05"));
94 }
95
96 #[test]
97 fn test_lt05_accepts_short_line() {
98 let violations = lint_sql("SELECT * FROM t", RuleLT05::default());
99 assert_eq!(violations.len(), 0);
100 }
101
102 #[test]
103 fn test_lt05_custom_max_length() {
104 let rule = RuleLT05 {
105 max_line_length: 120,
106 };
107 let sql_100_chars = format!("SELECT {} FROM t", "x".repeat(88));
108 let violations = lint_sql(&sql_100_chars, rule);
109 assert_eq!(violations.len(), 0);
110 }
111}