garbage_code_hunter/
analyzer.rs

1use 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,    // Minor issues
24    Spicy,   // Medium issues
25    Nuclear, // Serious issues
26}
27
28#[derive(Debug, Clone, PartialEq)]
29pub enum RoastLevel {
30    Gentle,    // Gentle roasting
31    Sarcastic, // Sarcastic comments
32    Savage,    // Brutal honesty
33}
34
35pub struct CodeAnalyzer {
36    rule_engine: RuleEngine,
37    exclude_patterns: Vec<Regex>,
38    lang: String,
39}
40
41impl CodeAnalyzer {
42    pub fn new(exclude_patterns: &[String], lang: &str) -> Self {
43        let patterns = exclude_patterns
44            .iter()
45            .filter_map(|pattern| {
46                // Convert glob patterns to regular expressions
47                let regex_pattern = pattern
48                    .replace(".", r"\.")
49                    .replace("*", ".*")
50                    .replace("?", ".");
51                Regex::new(&regex_pattern).ok()
52            })
53            .collect();
54
55        Self {
56            rule_engine: RuleEngine::new(),
57            exclude_patterns: patterns,
58            lang: lang.to_string(),
59        }
60    }
61
62    fn should_exclude(&self, path: &Path) -> bool {
63        let path_str = path.to_string_lossy();
64        self.exclude_patterns
65            .iter()
66            .any(|pattern| pattern.is_match(&path_str))
67    }
68
69    pub fn analyze_path(&self, path: &Path) -> Vec<CodeIssue> {
70        let mut issues = Vec::new();
71
72        if path.is_file() {
73            if !self.should_exclude(path) {
74                if let Some(ext) = path.extension() {
75                    if ext == "rs" {
76                        issues.extend(self.analyze_file(path));
77                    }
78                }
79            }
80        } else if path.is_dir() {
81            for entry in WalkDir::new(path)
82                .into_iter()
83                .filter_map(|e| e.ok())
84                .filter(|e| !self.should_exclude(e.path()))
85                .filter(|e| e.path().extension().is_some_and(|ext| ext == "rs"))
86            {
87                issues.extend(self.analyze_file(entry.path()));
88            }
89        }
90
91        issues
92    }
93
94    pub fn analyze_file(&self, file_path: &Path) -> Vec<CodeIssue> {
95        let content = match fs::read_to_string(file_path) {
96            Ok(content) => content,
97            Err(_) => return vec![],
98        };
99
100        let syntax_tree = match parse_file(&content) {
101            Ok(tree) => tree,
102            Err(_) => return vec![],
103        };
104
105        self.rule_engine
106            .check_file(file_path, &syntax_tree, &content, &self.lang)
107    }
108}