debugger/setup/
detector.rs1use std::path::Path;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum ProjectType {
10 Rust,
11 Cuda,
12 Go,
13 Python,
14 JavaScript,
15 TypeScript,
16 C,
17 Cpp,
18 CSharp,
19 Java,
20}
21
22pub fn detect_project_types(dir: &Path) -> Vec<ProjectType> {
24 let mut types = Vec::new();
25
26 if dir.join("Cargo.toml").exists() {
28 types.push(ProjectType::Rust);
29 }
30
31 if has_extension_in_dir(dir, "cu") {
33 types.push(ProjectType::Cuda);
34 }
35
36 if dir.join("go.mod").exists() || dir.join("go.sum").exists() {
38 types.push(ProjectType::Go);
39 }
40
41 if dir.join("pyproject.toml").exists()
43 || dir.join("setup.py").exists()
44 || dir.join("requirements.txt").exists()
45 || dir.join("Pipfile").exists()
46 {
47 types.push(ProjectType::Python);
48 }
49
50 if dir.join("package.json").exists() {
52 if dir.join("tsconfig.json").exists() {
54 types.push(ProjectType::TypeScript);
55 } else {
56 types.push(ProjectType::JavaScript);
57 }
58 }
59
60 if dir.join("CMakeLists.txt").exists()
62 || dir.join("Makefile").exists()
63 || dir.join("configure").exists()
64 || dir.join("meson.build").exists()
65 {
66 if has_cpp_files(dir) {
68 types.push(ProjectType::Cpp);
69 } else if has_c_files(dir) {
70 types.push(ProjectType::C);
71 } else {
72 types.push(ProjectType::Cpp);
74 }
75 }
76
77 if has_extension_in_dir(dir, "csproj") || has_extension_in_dir(dir, "sln") {
79 types.push(ProjectType::CSharp);
80 }
81
82 if dir.join("pom.xml").exists()
84 || dir.join("build.gradle").exists()
85 || dir.join("build.gradle.kts").exists()
86 {
87 types.push(ProjectType::Java);
88 }
89
90 types
91}
92
93pub fn debuggers_for_project(project: &ProjectType) -> Vec<&'static str> {
95 match project {
96 ProjectType::Rust => vec!["codelldb", "lldb"],
97 ProjectType::Cuda => vec!["cuda-gdb"],
98 ProjectType::Go => vec!["go"],
99 ProjectType::Python => vec!["python"],
100 ProjectType::JavaScript | ProjectType::TypeScript => vec![], ProjectType::C | ProjectType::Cpp => vec!["lldb", "codelldb"],
102 ProjectType::CSharp => vec![], ProjectType::Java => vec![], }
105}
106
107fn has_cpp_files(dir: &Path) -> bool {
109 has_extension_in_dir(dir, "cpp")
110 || has_extension_in_dir(dir, "cc")
111 || has_extension_in_dir(dir, "cxx")
112 || has_extension_in_dir(dir, "hpp")
113 || has_extension_in_dir(dir, "hxx")
114}
115
116fn has_c_files(dir: &Path) -> bool {
118 has_extension_in_dir(dir, "c") || has_extension_in_dir(dir, "h")
119}
120
121fn has_extension_in_dir(dir: &Path, ext: &str) -> bool {
123 if let Ok(entries) = std::fs::read_dir(dir) {
124 for entry in entries {
125 if let Ok(entry) = entry {
127 let path = entry.path();
128 if path.extension().map(|e| e == ext).unwrap_or(false) {
129 return true;
130 }
131 }
132 }
133 }
134 false
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use tempfile::tempdir;
141
142 #[test]
143 fn test_detect_rust_project() {
144 let dir = tempdir().unwrap();
145 std::fs::write(dir.path().join("Cargo.toml"), "[package]").unwrap();
146 let types = detect_project_types(dir.path());
147 assert!(types.contains(&ProjectType::Rust));
148 }
149
150 #[test]
151 fn test_detect_python_project() {
152 let dir = tempdir().unwrap();
153 std::fs::write(dir.path().join("requirements.txt"), "requests").unwrap();
154 let types = detect_project_types(dir.path());
155 assert!(types.contains(&ProjectType::Python));
156 }
157
158 #[test]
159 fn test_debuggers_for_rust() {
160 let debuggers = debuggers_for_project(&ProjectType::Rust);
161 assert!(debuggers.contains(&"codelldb"));
162 }
163}