garbage_code_hunter/rules/
file_structure.rs

1use std::path::Path;
2use syn::{visit::Visit, File, ItemMod, ItemUse};
3
4use crate::analyzer::{CodeIssue, RoastLevel, Severity};
5use crate::rules::Rule;
6
7/// Detects files that are too long (>1000 lines)
8pub 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
74/// Detects chaotic import order
75pub 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        // Check for duplicate use statements
93        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            // Check for duplicate imports
107            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
119/// Detects overly deep module nesting
120pub 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}