matrixcode_core/tools/codegraph/
project.rs1use std::path::{Path, PathBuf};
4
5const PROJECT_ROOT_MARKERS: &[&str] = &[
7 ".git",
9 "Cargo.toml",
11 "package.json",
13 "tsconfig.json",
15 "go.mod",
17 "pyproject.toml",
19 "setup.py",
20 "requirements.txt",
21 "pom.xml",
23 "build.gradle",
24 "composer.json",
26 "Gemfile",
28];
29
30#[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
45pub fn find_project_root(start_path: &Path) -> PathBuf {
47 if let Some(git_root) = find_git_root(start_path) {
49 return git_root;
50 }
51
52 if let Some(marker_root) = find_by_markers(start_path) {
54 return marker_root;
55 }
56
57 start_path.to_path_buf()
59}
60
61fn 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
77fn find_by_markers(start_path: &Path) -> Option<PathBuf> {
79 let mut current = start_path.to_path_buf();
80
81 loop {
82 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 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}