garbage_code_hunter/
analyzer.rs1use regex::Regex;
2use std::fs;
3use std::path::{Path, PathBuf};
4use syn::parse_file;
5use walkdir::WalkDir;
6
7use crate::rules::RuleEngine;
8
9#[derive(Debug, Clone)]
10pub struct CodeIssue {
11 pub file_path: PathBuf,
12 pub line: usize,
13 pub column: usize,
14 pub rule_name: String,
15 pub message: String,
16 pub severity: Severity,
17 #[allow(dead_code)]
18 pub roast_level: RoastLevel,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22pub enum Severity {
23 Mild, Spicy, Nuclear, }
27
28#[derive(Debug, Clone, PartialEq)]
29pub enum RoastLevel {
30 Gentle, Sarcastic, Savage, }
34
35pub struct CodeAnalyzer {
36 rule_engine: RuleEngine,
37 exclude_patterns: Vec<Regex>,
38}
39
40impl CodeAnalyzer {
41 pub fn new(exclude_patterns: &[String]) -> Self {
42 let patterns = exclude_patterns
43 .iter()
44 .filter_map(|pattern| {
45 let regex_pattern = pattern
47 .replace(".", r"\.")
48 .replace("*", ".*")
49 .replace("?", ".");
50 Regex::new(®ex_pattern).ok()
51 })
52 .collect();
53
54 Self {
55 rule_engine: RuleEngine::new(),
56 exclude_patterns: patterns,
57 }
58 }
59
60 fn should_exclude(&self, path: &Path) -> bool {
61 let path_str = path.to_string_lossy();
62 self.exclude_patterns
63 .iter()
64 .any(|pattern| pattern.is_match(&path_str))
65 }
66
67 pub fn analyze_path(&self, path: &Path) -> Vec<CodeIssue> {
68 let mut issues = Vec::new();
69
70 if path.is_file() {
71 if !self.should_exclude(path) {
72 if let Some(ext) = path.extension() {
73 if ext == "rs" {
74 issues.extend(self.analyze_file(path));
75 }
76 }
77 }
78 } else if path.is_dir() {
79 for entry in WalkDir::new(path)
80 .into_iter()
81 .filter_map(|e| e.ok())
82 .filter(|e| !self.should_exclude(e.path()))
83 .filter(|e| e.path().extension().map_or(false, |ext| ext == "rs"))
84 {
85 issues.extend(self.analyze_file(entry.path()));
86 }
87 }
88
89 issues
90 }
91
92 pub fn analyze_file(&self, file_path: &Path) -> Vec<CodeIssue> {
93 let content = match fs::read_to_string(file_path) {
94 Ok(content) => content,
95 Err(_) => return vec![],
96 };
97
98 let syntax_tree = match parse_file(&content) {
99 Ok(tree) => tree,
100 Err(_) => return vec![],
101 };
102
103 self.rule_engine
104 .check_file(file_path, &syntax_tree, &content)
105 }
106}