Skip to main content

driven/sysinfo/
detectors.rs

1//! Detection utilities for system information
2//!
3//! This module provides additional detection utilities that can be used
4//! independently of the main SystemInfoProvider.
5
6use super::ProjectType;
7use std::path::Path;
8
9/// Detect project type from a directory
10pub fn detect_project_type_from_path(path: &Path) -> ProjectType {
11    super::detect_project_type(path)
12}
13
14/// Check if a path is a Rust project
15pub fn is_rust_project(path: &Path) -> bool {
16    path.join("Cargo.toml").exists()
17}
18
19/// Check if a path is a Node.js project
20pub fn is_node_project(path: &Path) -> bool {
21    path.join("package.json").exists()
22}
23
24/// Check if a path is a Python project
25pub fn is_python_project(path: &Path) -> bool {
26    path.join("pyproject.toml").exists()
27        || path.join("setup.py").exists()
28        || path.join("requirements.txt").exists()
29}
30
31/// Check if a path is a Go project
32pub fn is_go_project(path: &Path) -> bool {
33    path.join("go.mod").exists()
34}
35
36/// Check if a path is a Java project
37pub fn is_java_project(path: &Path) -> bool {
38    path.join("pom.xml").exists() || path.join("build.gradle").exists()
39}
40
41/// Check if a path has a git repository
42pub fn has_git_repo(path: &Path) -> bool {
43    path.join(".git").exists()
44}
45
46/// Get all project marker files for a given project type
47pub fn get_project_markers(project_type: ProjectType) -> Vec<&'static str> {
48    match project_type {
49        ProjectType::Rust => vec!["Cargo.toml", "Cargo.lock"],
50        ProjectType::Node => vec![
51            "package.json",
52            "package-lock.json",
53            "yarn.lock",
54            "pnpm-lock.yaml",
55        ],
56        ProjectType::Python => vec!["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"],
57        ProjectType::Go => vec!["go.mod", "go.sum"],
58        ProjectType::Java => vec!["pom.xml", "build.gradle", "build.gradle.kts"],
59        ProjectType::CSharp => vec!["*.csproj", "*.sln"],
60        ProjectType::Ruby => vec!["Gemfile", "Gemfile.lock"],
61        ProjectType::Php => vec!["composer.json", "composer.lock"],
62        ProjectType::Swift => vec!["Package.swift"],
63        ProjectType::Kotlin => vec!["build.gradle.kts"],
64        ProjectType::Cpp => vec!["CMakeLists.txt", "*.cpp", "*.hpp"],
65        ProjectType::C => vec!["Makefile", "*.c", "*.h"],
66        ProjectType::Mixed | ProjectType::Unknown => vec![],
67    }
68}
69
70/// Detect the primary language from file extensions in a directory
71pub fn detect_primary_language(path: &Path) -> Option<ProjectType> {
72    use std::collections::HashMap;
73
74    let mut counts: HashMap<ProjectType, usize> = HashMap::new();
75
76    let entries = walkdir::WalkDir::new(path)
77        .max_depth(3)
78        .into_iter()
79        .filter_map(|e| e.ok());
80
81    for entry in entries {
82        if let Some(ext) = entry.path().extension().and_then(|e| e.to_str()) {
83            let project_type = match ext {
84                "rs" => Some(ProjectType::Rust),
85                "js" | "ts" | "jsx" | "tsx" => Some(ProjectType::Node),
86                "py" => Some(ProjectType::Python),
87                "go" => Some(ProjectType::Go),
88                "java" => Some(ProjectType::Java),
89                "cs" => Some(ProjectType::CSharp),
90                "rb" => Some(ProjectType::Ruby),
91                "php" => Some(ProjectType::Php),
92                "swift" => Some(ProjectType::Swift),
93                "kt" | "kts" => Some(ProjectType::Kotlin),
94                "cpp" | "cc" | "cxx" | "hpp" => Some(ProjectType::Cpp),
95                "c" | "h" => Some(ProjectType::C),
96                _ => None,
97            };
98
99            if let Some(pt) = project_type {
100                *counts.entry(pt).or_insert(0) += 1;
101            }
102        }
103    }
104
105    counts
106        .into_iter()
107        .max_by_key(|(_, count)| *count)
108        .map(|(pt, _)| pt)
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use std::fs;
115    use tempfile::TempDir;
116
117    #[test]
118    fn test_is_rust_project() {
119        let temp = TempDir::new().unwrap();
120        fs::write(temp.path().join("Cargo.toml"), "[package]").unwrap();
121
122        assert!(is_rust_project(temp.path()));
123    }
124
125    #[test]
126    fn test_is_node_project() {
127        let temp = TempDir::new().unwrap();
128        fs::write(temp.path().join("package.json"), "{}").unwrap();
129
130        assert!(is_node_project(temp.path()));
131    }
132
133    #[test]
134    fn test_has_git_repo() {
135        let temp = TempDir::new().unwrap();
136        fs::create_dir(temp.path().join(".git")).unwrap();
137
138        assert!(has_git_repo(temp.path()));
139    }
140
141    #[test]
142    fn test_get_project_markers() {
143        let markers = get_project_markers(ProjectType::Rust);
144        assert!(markers.contains(&"Cargo.toml"));
145    }
146}