git_iris/file_analyzers/
mod.rs1use regex::Regex;
2use std::path::Path;
3
4use crate::{
5 context::{ProjectMetadata, StagedFile},
6 log_debug,
7};
8
9pub trait FileAnalyzer: Send + Sync {
11 fn analyze(&self, file: &str, staged_file: &StagedFile) -> Vec<String>;
12 fn get_file_type(&self) -> &'static str;
13 fn extract_metadata(&self, file: &str, content: &str) -> ProjectMetadata;
14}
15
16mod c;
18mod cpp;
20mod gradle;
22mod java;
24mod javascript;
26mod json;
28mod kotlin;
30mod markdown;
32mod python;
34mod rust;
36mod yaml;
38
39#[allow(clippy::case_sensitive_file_extension_comparisons)] pub fn get_analyzer(file: &str) -> Box<dyn FileAnalyzer + Send + Sync> {
42 if file.ends_with(".c") || file == "Makefile" {
43 Box::new(c::CAnalyzer)
44 } else if file.ends_with(".cpp")
45 || file.ends_with(".cc")
46 || file.ends_with(".cxx")
47 || file == "CMakeLists.txt"
48 {
49 Box::new(cpp::CppAnalyzer)
50 } else if file.ends_with(".rs") {
51 Box::new(rust::RustAnalyzer)
52 } else if file.ends_with(".js") || file.ends_with(".ts") {
53 Box::new(javascript::JavaScriptAnalyzer)
54 } else if file.ends_with(".py") {
55 Box::new(python::PythonAnalyzer)
56 } else if file.ends_with(".yaml") || file.ends_with(".yml") {
57 Box::new(yaml::YamlAnalyzer)
58 } else if file.ends_with(".json") {
59 Box::new(json::JsonAnalyzer)
60 } else if file.ends_with(".md") {
61 Box::new(markdown::MarkdownAnalyzer)
62 } else if file.ends_with(".java") {
63 Box::new(java::JavaAnalyzer)
64 } else if file.ends_with(".kt") {
65 Box::new(kotlin::KotlinAnalyzer)
66 } else if file.ends_with(".gradle") || file.ends_with(".gradle.kts") {
67 Box::new(gradle::GradleAnalyzer)
68 } else {
69 Box::new(DefaultAnalyzer)
70 }
71}
72
73struct DefaultAnalyzer;
75
76impl FileAnalyzer for DefaultAnalyzer {
77 fn analyze(&self, _file: &str, _staged_file: &StagedFile) -> Vec<String> {
78 vec![]
79 }
80
81 fn get_file_type(&self) -> &'static str {
82 "Unknown file type"
83 }
84
85 fn extract_metadata(&self, _file: &str, _content: &str) -> ProjectMetadata {
86 ProjectMetadata {
87 language: Some("Unknown".to_string()),
88 ..Default::default()
89 }
90 }
91}
92
93#[allow(clippy::unwrap_used)]
103pub fn should_exclude_file(path: &str) -> bool {
104 log_debug!("Checking if file should be excluded: {}", path);
105 let exclude_patterns = vec![
106 (String::from(r"\.git"), false),
107 (String::from(r"\.svn"), false),
108 (String::from(r"\.hg"), false),
109 (String::from(r"\.DS_Store"), false),
110 (String::from(r"node_modules"), false),
111 (String::from(r"target"), false),
112 (String::from(r"build"), false),
113 (String::from(r"dist"), false),
114 (String::from(r"\.vscode"), false),
115 (String::from(r"\.idea"), false),
116 (String::from(r"\.vs"), false),
117 (String::from(r"package-lock\.json$"), true),
118 (String::from(r"\.lock$"), true),
119 (String::from(r"\.log$"), true),
120 (String::from(r"\.tmp$"), true),
121 (String::from(r"\.temp$"), true),
122 (String::from(r"\.swp$"), true),
123 (String::from(r"\.min\.js$"), true),
124 ];
125
126 let path = Path::new(path);
127
128 for (pattern, is_extension) in exclude_patterns {
129 let re = Regex::new(&pattern).expect("Could not compile regex");
130 if is_extension {
131 if let Some(file_name) = path.file_name() {
132 if re.is_match(file_name.to_str().unwrap()) {
133 log_debug!("File excluded: {}", path.display());
134 return true;
135 }
136 }
137 } else if re.is_match(path.to_str().unwrap()) {
138 log_debug!("File excluded: {}", path.display());
139 return true;
140 }
141 }
142 log_debug!("File not excluded: {}", path.display());
143 false
144}