Skip to main content

matrixcode_core/tools/codegraph/
project.rs

1//! Project root detection for CodeGraph.
2
3use std::path::{Path, PathBuf};
4
5/// Project root marker files (used to detect project root).
6const PROJECT_ROOT_MARKERS: &[&str] = &[
7    // Git
8    ".git",
9    // Rust
10    "Cargo.toml",
11    // Node.js
12    "package.json",
13    // TypeScript
14    "tsconfig.json",
15    // Go
16    "go.mod",
17    // Python
18    "pyproject.toml",
19    "setup.py",
20    "requirements.txt",
21    // Java
22    "pom.xml",
23    "build.gradle",
24    // PHP
25    "composer.json",
26    // Ruby
27    "Gemfile",
28];
29
30/// Create a command with hidden window on Windows.
31#[cfg(windows)]
32fn create_command(program: &str) -> std::process::Command {
33    use std::os::windows::process::CommandExt;
34    const CREATE_NO_WINDOW: u32 = 0x08000000;
35    let mut cmd = std::process::Command::new(program);
36    cmd.creation_flags(CREATE_NO_WINDOW);
37    cmd
38}
39
40#[cfg(not(windows))]
41fn create_command(program: &str) -> std::process::Command {
42    std::process::Command::new(program)
43}
44
45/// Find project root directory from a given starting path.
46pub fn find_project_root(start_path: &Path) -> PathBuf {
47    // Try to find by Git first
48    if let Some(git_root) = find_git_root(start_path) {
49        return git_root;
50    }
51
52    // Try to find by project markers
53    if let Some(marker_root) = find_by_markers(start_path) {
54        return marker_root;
55    }
56
57    // Fallback to current directory
58    start_path.to_path_buf()
59}
60
61/// Find Git repository root.
62fn find_git_root(start_path: &Path) -> Option<PathBuf> {
63    let output = create_command("git")
64        .args(["rev-parse", "--show-toplevel"])
65        .current_dir(start_path)
66        .output()
67        .ok()?;
68
69    if output.status.success() {
70        let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
71        Some(PathBuf::from(path))
72    } else {
73        None
74    }
75}
76
77/// Find project root by looking for marker files.
78fn find_by_markers(start_path: &Path) -> Option<PathBuf> {
79    let mut current = start_path.to_path_buf();
80
81    loop {
82        // Check for any marker file
83        for marker in PROJECT_ROOT_MARKERS {
84            let marker_path = current.join(marker);
85            if marker_path.exists() {
86                return Some(current);
87            }
88        }
89
90        // Move up one directory
91        if !current.pop() {
92            break;
93        }
94    }
95
96    None
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_find_project_root_with_git() {
105        let start = std::env::current_dir().unwrap();
106        let root = find_project_root(&start);
107        assert!(root.exists());
108    }
109}