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}
39
40impl CodeAnalyzer {
41    pub fn new(exclude_patterns: &[String]) -> Self {
42        let patterns = exclude_patterns
43            .iter()
44            .filter_map(|pattern| {
45                // Convert glob patterns to regular expressions
46                let regex_pattern = pattern
47                    .replace(".", r"\.")
48                    .replace("*", ".*")
49                    .replace("?", ".");
50                Regex::new(&regex_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}