garbage_code_hunter/rules/
file_structure.rs1use std::path::Path;
2use syn::{visit::Visit, File, ItemMod, ItemUse};
3
4use crate::analyzer::{CodeIssue, RoastLevel, Severity};
5use crate::rules::Rule;
6
7pub struct FileStructureRule;
9
10impl Rule for FileStructureRule {
11 fn name(&self) -> &'static str {
12 "file-structure"
13 }
14
15 fn check(
16 &self,
17 file_path: &Path,
18 _syntax_tree: &File,
19 content: &str,
20 lang: &str,
21 ) -> Vec<CodeIssue> {
22 let mut issues = Vec::new();
23 let line_count = content.lines().count();
24
25 if line_count > 1000 {
26 let messages_zh = [
27 "这个文件比我的毕业论文还长!建议拆分成多个模块 📚",
28 "文件长度突破天际!是想创造吉尼斯纪录吗? 🚀",
29 "这么长的文件,建议配个目录和索引 📖",
30 "文件行数比我一年的代码还多! 📈",
31 "这个文件需要电梯才能到底部 🏢",
32 "建议把这个巨型文件拆分成几个小文件,拯救一下可读性 🆘",
33 ];
34
35 let messages_en = [
36 "This file is longer than my thesis! Consider splitting into modules 📚",
37 "File length has reached the stratosphere! Going for a world record? 🚀",
38 "Such a long file needs a table of contents and index 📖",
39 "More lines than I write in a year! 📈",
40 "This file needs an elevator to reach the bottom 🏢",
41 "Please split this monster file into smaller ones, save the readability 🆘",
42 ];
43
44 let messages = if lang == "zh-CN" {
45 &messages_zh
46 } else {
47 &messages_en
48 };
49 let message = messages[line_count % messages.len()];
50
51 let severity = if line_count > 2000 {
52 Severity::Nuclear
53 } else if line_count > 1500 {
54 Severity::Spicy
55 } else {
56 Severity::Mild
57 };
58
59 issues.push(CodeIssue {
60 file_path: file_path.to_path_buf(),
61 line: 1,
62 column: 1,
63 rule_name: "file-too-long".to_string(),
64 message: format!("{} ({}行)", message, line_count),
65 severity,
66 roast_level: RoastLevel::Sarcastic,
67 });
68 }
69
70 issues
71 }
72}
73
74pub struct ImportChaosRule;
76
77impl Rule for ImportChaosRule {
78 fn name(&self) -> &'static str {
79 "import-chaos"
80 }
81
82 fn check(
83 &self,
84 file_path: &Path,
85 syntax_tree: &File,
86 content: &str,
87 lang: &str,
88 ) -> Vec<CodeIssue> {
89 let mut visitor = ImportChaosVisitor::new(file_path.to_path_buf(), lang);
90 visitor.visit_file(syntax_tree);
91
92 let use_lines: Vec<&str> = content
94 .lines()
95 .filter(|line| line.trim().starts_with("use "))
96 .collect();
97
98 if use_lines.len() > 1 {
99 let mut sorted_uses = use_lines.clone();
100 sorted_uses.sort();
101
102 if use_lines != sorted_uses {
103 visitor.add_unordered_imports_issue();
104 }
105
106 let mut seen_imports = std::collections::HashSet::new();
108 for use_line in &use_lines {
109 if !seen_imports.insert(use_line) {
110 visitor.add_duplicate_import_issue();
111 }
112 }
113 }
114
115 visitor.issues
116 }
117}
118
119pub struct ModuleNestingRule;
121
122impl Rule for ModuleNestingRule {
123 fn name(&self) -> &'static str {
124 "module-nesting"
125 }
126
127 fn check(
128 &self,
129 file_path: &Path,
130 syntax_tree: &File,
131 _content: &str,
132 lang: &str,
133 ) -> Vec<CodeIssue> {
134 let mut visitor = ModuleNestingVisitor::new(file_path.to_path_buf(), lang);
135 visitor.visit_file(syntax_tree);
136 visitor.issues
137 }
138}
139
140struct ImportChaosVisitor {
141 file_path: std::path::PathBuf,
142 lang: String,
143 issues: Vec<CodeIssue>,
144 use_count: usize,
145}
146
147impl ImportChaosVisitor {
148 fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
149 Self {
150 file_path,
151 lang: lang.to_string(),
152 issues: Vec::new(),
153 use_count: 0,
154 }
155 }
156
157 fn add_unordered_imports_issue(&mut self) {
158 let messages_zh = [
159 "import 顺序比我的房间还乱!建议按字母顺序排列 🔤",
160 "这些 use 语句的顺序让我想起了洗牌后的扑克牌 🃏",
161 "import 排序混乱,建议使用 rustfmt 整理一下 🧹",
162 "use 语句顺序比我的作息时间还乱 ⏰",
163 ];
164
165 let messages_en = [
166 "Import order is messier than my room! Consider alphabetical sorting 🔤",
167 "These use statements remind me of shuffled playing cards 🃏",
168 "Import sorting is chaotic, consider using rustfmt 🧹",
169 "Use statement order is more chaotic than my sleep schedule ⏰",
170 ];
171
172 let messages = if self.lang == "zh-CN" {
173 &messages_zh
174 } else {
175 &messages_en
176 };
177 let message = messages[self.issues.len() % messages.len()];
178
179 self.issues.push(CodeIssue {
180 file_path: self.file_path.clone(),
181 line: 1,
182 column: 1,
183 rule_name: "unordered-imports".to_string(),
184 message: message.to_string(),
185 severity: Severity::Mild,
186 roast_level: RoastLevel::Sarcastic,
187 });
188 }
189
190 fn add_duplicate_import_issue(&mut self) {
191 let messages_zh = [
192 "重复的 import!是想强调重要性吗? 🔄",
193 "同样的 use 语句出现了多次,建议去重 🗑️",
194 "重复 import 比我重复的话还多 💬",
195 ];
196
197 let messages_en = [
198 "Duplicate imports! Trying to emphasize importance? 🔄",
199 "Same use statement appears multiple times, consider deduplication 🗑️",
200 "More duplicate imports than my repeated words 💬",
201 ];
202
203 let messages = if self.lang == "zh-CN" {
204 &messages_zh
205 } else {
206 &messages_en
207 };
208 let message = messages[self.issues.len() % messages.len()];
209
210 self.issues.push(CodeIssue {
211 file_path: self.file_path.clone(),
212 line: 1,
213 column: 1,
214 rule_name: "duplicate-imports".to_string(),
215 message: message.to_string(),
216 severity: Severity::Mild,
217 roast_level: RoastLevel::Sarcastic,
218 });
219 }
220}
221
222impl<'ast> Visit<'ast> for ImportChaosVisitor {
223 fn visit_item_use(&mut self, use_item: &'ast ItemUse) {
224 self.use_count += 1;
225 syn::visit::visit_item_use(self, use_item);
226 }
227}
228
229struct ModuleNestingVisitor {
230 file_path: std::path::PathBuf,
231 lang: String,
232 issues: Vec<CodeIssue>,
233 nesting_depth: usize,
234 max_depth: usize,
235}
236
237impl ModuleNestingVisitor {
238 fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
239 Self {
240 file_path,
241 lang: lang.to_string(),
242 issues: Vec::new(),
243 nesting_depth: 0,
244 max_depth: 0,
245 }
246 }
247
248 fn check_nesting_depth(&mut self) {
249 if self.nesting_depth > 3 {
250 let messages_zh = [
251 "模块嵌套比俄罗斯套娃还深!建议扁平化结构 🪆",
252 "这个嵌套深度需要GPS导航才能找到出口 🗺️",
253 "模块嵌套层数比我的心理防线还多 🏰",
254 "建议重新设计模块结构,当前嵌套过深 📐",
255 ];
256
257 let messages_en = [
258 "Module nesting deeper than Russian dolls! Consider flattening 🪆",
259 "This nesting depth needs GPS navigation to find the exit 🗺️",
260 "More module nesting layers than my psychological defenses 🏰",
261 "Consider redesigning module structure, current nesting too deep 📐",
262 ];
263
264 let messages = if self.lang == "zh-CN" {
265 &messages_zh
266 } else {
267 &messages_en
268 };
269 let message = messages[self.issues.len() % messages.len()];
270
271 let severity = if self.nesting_depth > 5 {
272 Severity::Spicy
273 } else {
274 Severity::Mild
275 };
276
277 self.issues.push(CodeIssue {
278 file_path: self.file_path.clone(),
279 line: 1,
280 column: 1,
281 rule_name: "deep-module-nesting".to_string(),
282 message: format!("{} (深度: {})", message, self.nesting_depth),
283 severity,
284 roast_level: RoastLevel::Sarcastic,
285 });
286 }
287 }
288}
289
290impl<'ast> Visit<'ast> for ModuleNestingVisitor {
291 fn visit_item_mod(&mut self, module: &'ast ItemMod) {
292 self.nesting_depth += 1;
293 self.max_depth = self.max_depth.max(self.nesting_depth);
294
295 self.check_nesting_depth();
296
297 syn::visit::visit_item_mod(self, module);
298 self.nesting_depth -= 1;
299 }
300}