sqrust_rules/layout/
consistent_comment_style.rs1use sqrust_core::{Diagnostic, FileContext, Rule};
2
3pub struct ConsistentCommentStyle;
4
5impl Rule for ConsistentCommentStyle {
6 fn name(&self) -> &'static str {
7 "Layout/ConsistentCommentStyle"
8 }
9
10 fn check(&self, ctx: &FileContext) -> Vec<Diagnostic> {
11 find_violations(&ctx.source, self.name())
12 }
13}
14
15#[derive(Clone, Copy, PartialEq)]
17enum CommentStyle {
18 Line, Block, }
21
22fn find_violations(source: &str, rule_name: &'static str) -> Vec<Diagnostic> {
23 let bytes = source.as_bytes();
24 let len = bytes.len();
25
26 let mut in_string = false;
27 let mut i = 0usize;
28
29 let mut occurrences: Vec<(CommentStyle, usize)> = Vec::new();
31
32 while i < len {
33 let byte = bytes[i];
34
35 if in_string {
37 if byte == b'\'' {
38 if i + 1 < len && bytes[i + 1] == b'\'' {
40 i += 2;
41 continue;
42 }
43 in_string = false;
44 }
45 i += 1;
46 continue;
47 }
48
49 if byte == b'\'' {
51 in_string = true;
52 i += 1;
53 continue;
54 }
55
56 if i + 1 < len && byte == b'-' && bytes[i + 1] == b'-' {
58 occurrences.push((CommentStyle::Line, i));
59 while i < len && bytes[i] != b'\n' {
61 i += 1;
62 }
63 continue;
64 }
65
66 if i + 1 < len && byte == b'/' && bytes[i + 1] == b'*' {
68 occurrences.push((CommentStyle::Block, i));
69 i += 2; while i < len {
72 if i + 1 < len && bytes[i] == b'*' && bytes[i + 1] == b'/' {
73 i += 2; break;
75 }
76 i += 1;
77 }
78 continue;
79 }
80
81 i += 1;
82 }
83
84 if occurrences.is_empty() {
85 return Vec::new();
86 }
87
88 let line_count = occurrences.iter().filter(|(s, _)| *s == CommentStyle::Line).count();
90 let block_count = occurrences.iter().filter(|(s, _)| *s == CommentStyle::Block).count();
91
92 if line_count == 0 || block_count == 0 {
94 return Vec::new();
95 }
96
97 let minority_style = if line_count < block_count {
101 CommentStyle::Line
103 } else if block_count < line_count {
104 CommentStyle::Block
106 } else {
107 let first_style = occurrences[0].0;
110 if first_style == CommentStyle::Line {
111 CommentStyle::Block
112 } else {
113 CommentStyle::Line
114 }
115 };
116
117 let &(_, offset) = occurrences
119 .iter()
120 .find(|(s, _)| *s == minority_style)
121 .expect("minority style has at least one occurrence");
122
123 let (line, col) = byte_offset_to_line_col(source, offset);
124 vec![Diagnostic {
125 rule: rule_name,
126 message: "Inconsistent comment style: file mixes -- and /* */ comments".to_string(),
127 line,
128 col,
129 }]
130}
131
132fn byte_offset_to_line_col(source: &str, offset: usize) -> (usize, usize) {
134 let mut line = 1usize;
135 let mut line_start = 0usize;
136 for (i, ch) in source.char_indices() {
137 if i == offset {
138 break;
139 }
140 if ch == '\n' {
141 line += 1;
142 line_start = i + 1;
143 }
144 }
145 let col = offset - line_start + 1;
146 (line, col)
147}