Skip to main content

matrixcode_core/tools/codegraph/
install.rs

1//! CodeGraph CLI installation and detection.
2
3use anyhow::Result;
4use std::path::PathBuf;
5use tokio::process::Command;
6
7/// Create a command with hidden window on Windows.
8#[cfg(windows)]
9fn create_command(program: &str) -> std::process::Command {
10    use std::os::windows::process::CommandExt;
11    const CREATE_NO_WINDOW: u32 = 0x08000000;
12    let mut cmd = std::process::Command::new(program);
13    cmd.creation_flags(CREATE_NO_WINDOW);
14    cmd
15}
16
17#[cfg(not(windows))]
18fn create_command(program: &str) -> std::process::Command {
19    std::process::Command::new(program)
20}
21
22/// Create an async command with hidden window on Windows.
23#[cfg(windows)]
24fn create_async_command(program: &str) -> Command {
25    const CREATE_NO_WINDOW: u32 = 0x08000000;
26    let mut cmd = Command::new(program);
27    cmd.creation_flags(CREATE_NO_WINDOW);
28    cmd
29}
30
31#[cfg(not(windows))]
32fn create_async_command(program: &str) -> Command {
33    Command::new(program)
34}
35
36/// Get CodeGraph installation directory (platform-specific).
37fn get_codegraph_install_dir() -> Option<PathBuf> {
38    dirs::data_local_dir().map(|p| p.join("codegraph").join("current").join("bin"))
39}
40
41/// Get CodeGraph CLI executable name (platform-specific).
42fn get_codegraph_exe_name() -> String {
43    if cfg!(windows) {
44        "codegraph.cmd".to_string()
45    } else {
46        "codegraph".to_string()
47    }
48}
49
50/// Check if CodeGraph CLI is installed.
51pub fn is_codegraph_installed() -> bool {
52    // Try direct command (in PATH)
53    if create_command("codegraph")
54        .arg("--version")
55        .output()
56        .is_ok()
57    {
58        return true;
59    }
60
61    // Try platform-specific installation path
62    if let Some(install_dir) = get_codegraph_install_dir() {
63        let exe_name = get_codegraph_exe_name();
64        let exe_path = install_dir.join(&exe_name);
65        if exe_path.exists()
66            && create_command(exe_path.to_str().unwrap_or("codegraph"))
67                .arg("--version")
68                .output()
69                .is_ok()
70        {
71            return true;
72        }
73    }
74
75    false
76}
77
78/// Get CodeGraph CLI path (returns the executable path or command name).
79pub fn get_codegraph_path() -> Option<String> {
80    // Try direct command first (in PATH)
81    if create_command("codegraph")
82        .arg("--version")
83        .output()
84        .is_ok()
85    {
86        return Some("codegraph".to_string());
87    }
88
89    // Try platform-specific installation path
90    if let Some(install_dir) = get_codegraph_install_dir() {
91        let exe_name = get_codegraph_exe_name();
92        let exe_path = install_dir.join(&exe_name);
93        if exe_path.exists() {
94            return Some(exe_path.to_string_lossy().to_string());
95        }
96    }
97
98    None
99}
100
101/// Auto-install CodeGraph CLI (Windows).
102pub async fn install_codegraph() -> Result<()> {
103    log::info!("Installing CodeGraph CLI...");
104
105    // Windows PowerShell installer
106    let result = create_async_command("powershell")
107        .args([
108            "-NoProfile",
109            "-Command",
110            "irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex",
111        ])
112        .output()
113        .await?;
114
115    if result.status.success() {
116        log::info!("CodeGraph CLI installed successfully");
117        Ok(())
118    } else {
119        let stderr = String::from_utf8_lossy(&result.stderr);
120        Err(anyhow::anyhow!("CodeGraph installation failed: {}", stderr))
121    }
122}
123
124/// CodeGraph installation status.
125#[derive(Debug, Clone, PartialEq, Eq)]
126#[allow(dead_code)]
127pub enum CodeGraphInstallStatus {
128    /// Already installed and available.
129    Installed(String),
130    /// Not installed, needs user approval to install.
131    NotInstalled,
132}
133
134/// Check CodeGraph installation status (no auto-install).
135#[allow(dead_code)]
136pub fn check_codegraph_status() -> CodeGraphInstallStatus {
137    match get_codegraph_path() {
138        Some(path) => CodeGraphInstallStatus::Installed(path),
139        None => CodeGraphInstallStatus::NotInstalled,
140    }
141}
142
143/// Ensure CodeGraph is available with optional auto-install.
144pub async fn ensure_codegraph() -> Result<String> {
145    if let Some(path) = get_codegraph_path() {
146        return Ok(path);
147    }
148
149    install_codegraph().await?;
150    get_codegraph_path().ok_or_else(|| anyhow::anyhow!("CodeGraph not found after installation"))
151}