garbage_code_hunter/rules/
file_structure.rs1use std::path::Path;
2use syn::{visit::Visit, File, ItemMod};
3
4use crate::analyzer::{CodeIssue, Severity};
5use crate::rules::Rule;
6use crate::utils::find_line_of_str;
7
8pub struct FileStructureRule;
10
11impl Rule for FileStructureRule {
12 fn name(&self) -> &'static str {
13 "file-structure"
14 }
15
16 fn check(
17 &self,
18 file_path: &Path,
19 _syntax_tree: &File,
20 content: &str,
21 lang: &str,
22 is_test_file: bool,
23 ) -> Vec<CodeIssue> {
24 let mut issues = Vec::new();
25 let line_count = content.lines().count();
26
27 let threshold = if is_test_file { 2000 } else { 1000 };
28 if line_count > threshold {
29 let messages_zh = [
30 "这个文件比我的毕业论文还长!建议拆分成多个模块 📚",
31 "文件长度突破天际!是想创造吉尼斯纪录吗? 🚀",
32 "这么长的文件,建议配个目录和索引 📖",
33 "文件行数比我一年的代码还多! 📈",
34 "这个文件需要电梯才能到底部 🏢",
35 "建议把这个巨型文件拆分成几个小文件,拯救一下可读性 🆘",
36 ];
37
38 let messages_en = [
39 "This file is longer than my thesis! Consider splitting into modules 📚",
40 "File length has reached the stratosphere! Going for a world record? 🚀",
41 "Such a long file needs a table of contents and index 📖",
42 "More lines than I write in a year! 📈",
43 "This file needs an elevator to reach the bottom 🏢",
44 "Please split this monster file into smaller ones, save the readability 🆘",
45 ];
46
47 let messages = if lang == "zh-CN" {
48 &messages_zh
49 } else {
50 &messages_en
51 };
52 let message = messages[line_count % messages.len()];
53
54 let severity = if line_count > 2000 {
55 Severity::Nuclear
56 } else if line_count > 1500 {
57 Severity::Spicy
58 } else {
59 Severity::Mild
60 };
61
62 issues.push(CodeIssue {
63 file_path: file_path.to_path_buf(),
64 line: threshold,
65 column: 1,
66 rule_name: "file-too-long".to_string(),
67 message: format!("{} ({}行)", message, line_count),
68 severity,
69 });
70 }
71
72 issues
73 }
74}
75
76pub struct ImportChaosRule;
78
79impl Rule for ImportChaosRule {
80 fn name(&self) -> &'static str {
81 "import-chaos"
82 }
83
84 fn check(
85 &self,
86 file_path: &Path,
87 _syntax_tree: &File,
88 content: &str,
89 lang: &str,
90 is_test_file: bool,
91 ) -> Vec<CodeIssue> {
92 if is_test_file {
93 return Vec::new();
94 }
95
96 let mut issues = Vec::new();
97
98 let use_lines: Vec<&str> = content
100 .lines()
101 .filter(|line| line.trim().starts_with("use "))
102 .collect();
103
104 if use_lines.len() > 1 {
105 let mut seen_imports = std::collections::HashSet::new();
107 for use_line in &use_lines {
108 if !seen_imports.insert(use_line) {
109 let messages = if lang == "zh-CN" {
110 [
111 "重复的 import!是想强调重要性吗? 🔄",
112 "同样的 use 语句出现了多次,建议去重 🗑️",
113 "重复 import 比我重复的话还多 💬",
114 ]
115 } else {
116 [
117 "Duplicate imports! Trying to emphasize importance? 🔄",
118 "Same use statement appears multiple times, consider deduplication 🗑️",
119 "More duplicate imports than my repeated words 💬",
120 ]
121 };
122
123 issues.push(CodeIssue {
124 file_path: file_path.to_path_buf(),
125 line: find_line_of_str(content, "use "),
126 column: 1,
127 rule_name: "duplicate-imports".to_string(),
128 message: messages[issues.len() % messages.len()].to_string(),
129 severity: Severity::Mild,
130 });
131 }
132 }
133 }
134
135 issues
136 }
137}
138
139pub struct ModuleNestingRule;
141
142impl Rule for ModuleNestingRule {
143 fn name(&self) -> &'static str {
144 "module-nesting"
145 }
146
147 fn check(
148 &self,
149 file_path: &Path,
150 syntax_tree: &File,
151 content: &str,
152 lang: &str,
153 is_test_file: bool,
154 ) -> Vec<CodeIssue> {
155 let mut visitor =
156 ModuleNestingVisitor::new(file_path.to_path_buf(), lang, is_test_file, content);
157 visitor.visit_file(syntax_tree);
158 visitor.issues
159 }
160}
161
162struct ModuleNestingVisitor {
163 file_path: std::path::PathBuf,
164 lang: String,
165 issues: Vec<CodeIssue>,
166 nesting_depth: usize,
167 max_depth: usize,
168 is_test_file: bool,
169 content: String,
170}
171
172impl ModuleNestingVisitor {
173 fn new(file_path: std::path::PathBuf, lang: &str, is_test_file: bool, content: &str) -> Self {
174 Self {
175 file_path,
176 lang: lang.to_string(),
177 issues: Vec::new(),
178 nesting_depth: 0,
179 max_depth: 0,
180 is_test_file,
181 content: content.to_string(),
182 }
183 }
184
185 fn check_nesting_depth(&mut self) {
186 let threshold = if self.is_test_file { 6 } else { 4 };
187 if self.nesting_depth > threshold {
188 let messages_zh = [
189 "模块嵌套比俄罗斯套娃还深!建议扁平化结构 🪆",
190 "这个嵌套深度需要GPS导航才能找到出口 🗺️",
191 "模块嵌套层数比我的心理防线还多 🏰",
192 "建议重新设计模块结构,当前嵌套过深 📐",
193 ];
194
195 let messages_en = [
196 "Module nesting deeper than Russian dolls! Consider flattening 🪆",
197 "This nesting depth needs GPS navigation to find the exit 🗺️",
198 "More module nesting layers than my psychological defenses 🏰",
199 "Consider redesigning module structure, current nesting too deep 📐",
200 ];
201
202 let messages = if self.lang == "zh-CN" {
203 &messages_zh
204 } else {
205 &messages_en
206 };
207 let message = messages[self.issues.len() % messages.len()];
208
209 let severity = if self.nesting_depth > threshold + 2 {
210 Severity::Spicy
211 } else {
212 Severity::Mild
213 };
214
215 self.issues.push(CodeIssue {
216 file_path: self.file_path.clone(),
217 line: find_line_of_str(&self.content, "mod "),
218 column: 1,
219 rule_name: "deep-module-nesting".to_string(),
220 message: format!("{} (深度: {})", message, self.nesting_depth),
221 severity,
222 });
223 }
224 }
225}
226
227impl<'ast> Visit<'ast> for ModuleNestingVisitor {
228 fn visit_item_mod(&mut self, module: &'ast ItemMod) {
229 self.nesting_depth += 1;
230 self.max_depth = self.max_depth.max(self.nesting_depth);
231
232 self.check_nesting_depth();
233
234 syn::visit::visit_item_mod(self, module);
235 self.nesting_depth -= 1;
236 }
237}