Skip to main content

garbage_code_hunter/rules/
file_structure.rs

1use 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
8/// Detects files that are too long (>1000 lines)
9pub 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
76/// Detects chaotic import order
77pub 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        // Check for duplicate use statements
99        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            // Check for duplicate imports
106            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
139/// Detects overly deep module nesting
140pub 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}