sqrust_rules/layout/
comment_style.rs1use sqrust_core::{Diagnostic, FileContext, Rule};
2
3pub struct CommentStyle;
4
5impl Rule for CommentStyle {
6 fn name(&self) -> &'static str {
7 "Layout/CommentStyle"
8 }
9
10 fn check(&self, ctx: &FileContext) -> Vec<Diagnostic> {
11 find_violations(&ctx.source, self.name())
12 }
13}
14
15fn find_violations(source: &str, rule_name: &'static str) -> Vec<Diagnostic> {
16 let bytes = source.as_bytes();
17 let len = bytes.len();
18 let mut diags = Vec::new();
19
20 let mut i = 0usize;
21 let mut in_string = false;
22
23 while i < len {
24 let byte = bytes[i];
25
26 if in_string {
28 if byte == b'\'' {
29 if i + 1 < len && bytes[i + 1] == b'\'' {
31 i += 2;
32 continue;
33 }
34 in_string = false;
35 }
36 i += 1;
37 continue;
38 }
39
40 if byte == b'\'' {
42 in_string = true;
43 i += 1;
44 continue;
45 }
46
47 if i + 1 < len && byte == b'-' && bytes[i + 1] == b'-' {
49 while i < len && bytes[i] != b'\n' {
51 i += 1;
52 }
53 continue;
54 }
55
56 if i + 1 < len && byte == b'/' && bytes[i + 1] == b'*' {
58 let start = i;
59 i += 2; let mut has_newline = false;
62 let mut closed = false;
63
64 while i < len {
65 if bytes[i] == b'\n' {
66 has_newline = true;
67 }
68 if i + 1 < len && bytes[i] == b'*' && bytes[i + 1] == b'/' {
69 i += 2; closed = true;
71 break;
72 }
73 i += 1;
74 }
75
76 if !has_newline || !closed {
78 if !has_newline {
80 let (line, col) = byte_offset_to_line_col(source, start);
81 diags.push(Diagnostic {
82 rule: rule_name,
83 message:
84 "Single-line /* */ comment; use -- for single-line comments"
85 .to_string(),
86 line,
87 col,
88 });
89 }
90 }
91
92 continue;
93 }
94
95 i += 1;
96 }
97
98 diags
99}
100
101fn byte_offset_to_line_col(source: &str, offset: usize) -> (usize, usize) {
103 let mut line = 1usize;
104 let mut line_start = 0usize;
105 for (i, ch) in source.char_indices() {
106 if i == offset {
107 break;
108 }
109 if ch == '\n' {
110 line += 1;
111 line_start = i + 1;
112 }
113 }
114 let col = offset - line_start + 1;
115 (line, col)
116}