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()
39        .map(|p| p.join("codegraph").join("current").join("bin"))
40}
41
42/// Get CodeGraph CLI executable name (platform-specific).
43fn get_codegraph_exe_name() -> String {
44    if cfg!(windows) {
45        "codegraph.cmd".to_string()
46    } else {
47        "codegraph".to_string()
48    }
49}
50
51/// Check if CodeGraph CLI is installed.
52pub fn is_codegraph_installed() -> bool {
53    // Try direct command (in PATH)
54    if create_command("codegraph")
55        .arg("--version")
56        .output()
57        .is_ok()
58    {
59        return true;
60    }
61
62    // Try platform-specific installation path
63    if let Some(install_dir) = get_codegraph_install_dir() {
64        let exe_name = get_codegraph_exe_name();
65        let exe_path = install_dir.join(&exe_name);
66        if exe_path.exists()
67            && create_command(exe_path.to_str().unwrap_or("codegraph"))
68                .arg("--version")
69                .output()
70                .is_ok()
71        {
72            return true;
73        }
74    }
75
76    false
77}
78
79/// Get CodeGraph CLI path (returns the executable path or command name).
80pub fn get_codegraph_path() -> Option<String> {
81    // Try direct command first (in PATH)
82    if create_command("codegraph")
83        .arg("--version")
84        .output()
85        .is_ok()
86    {
87        return Some("codegraph".to_string());
88    }
89
90    // Try platform-specific installation path
91    if let Some(install_dir) = get_codegraph_install_dir() {
92        let exe_name = get_codegraph_exe_name();
93        let exe_path = install_dir.join(&exe_name);
94        if exe_path.exists() {
95            return Some(exe_path.to_string_lossy().to_string());
96        }
97    }
98
99    None
100}
101
102/// Auto-install CodeGraph CLI (Windows).
103pub async fn install_codegraph() -> Result<()> {
104    log::info!("Installing CodeGraph CLI...");
105
106    // Windows PowerShell installer
107    let result = create_async_command("powershell")
108        .args([
109            "-NoProfile",
110            "-Command",
111            "irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex",
112        ])
113        .output()
114        .await?;
115
116    if result.status.success() {
117        log::info!("CodeGraph CLI installed successfully");
118        Ok(())
119    } else {
120        let stderr = String::from_utf8_lossy(&result.stderr);
121        Err(anyhow::anyhow!("CodeGraph installation failed: {}", stderr))
122    }
123}
124
125/// CodeGraph installation status.
126#[derive(Debug, Clone, PartialEq, Eq)]
127#[allow(dead_code)]
128pub enum CodeGraphInstallStatus {
129    /// Already installed and available.
130    Installed(String),
131    /// Not installed, needs user approval to install.
132    NotInstalled,
133}
134
135/// Check CodeGraph installation status (no auto-install).
136#[allow(dead_code)]
137pub fn check_codegraph_status() -> CodeGraphInstallStatus {
138    match get_codegraph_path() {
139        Some(path) => CodeGraphInstallStatus::Installed(path),
140        None => CodeGraphInstallStatus::NotInstalled,
141    }
142}
143
144/// Ensure CodeGraph is available with optional auto-install.
145pub async fn ensure_codegraph() -> Result<String> {
146    if let Some(path) = get_codegraph_path() {
147        return Ok(path);
148    }
149
150    install_codegraph().await?;
151    get_codegraph_path().ok_or_else(|| anyhow::anyhow!("CodeGraph not found after installation"))
152}