use std::env;
use std::ffi::OsStr;
use std::process::Command;
use crate::toolchain::{ToolchainSpec, resolve};
pub fn build_command<S: AsRef<OsStr>>(
toolchain: ToolchainSpec,
command: Vec<S>,
) -> anyhow::Result<Command> {
let exe_name = command[0].as_ref();
let mut bin_dir = toolchain.install_path();
bin_dir.push("bin");
if !bin_dir.exists() {
return Err(anyhow::anyhow!("Toolchain '{toolchain}' is not installed"));
}
let err_msg = anyhow::anyhow!(
"Command '{}' not found in toolchain '{toolchain}'",
exe_name.to_string_lossy()
);
let internal_bin_dir = bin_dir.join("internal");
let paths = env::join_paths([&bin_dir, &internal_bin_dir])
.map_err(|e| anyhow::anyhow!("Failed to build PATH environment variable: {}", e))?;
let mut cmd = if let Some(exe_resolved) = resolve::resolve_exe(exe_name, &paths) {
tracing::debug!(
"Resolved executable for '{}': {}",
exe_name.to_string_lossy(),
exe_resolved.display()
);
Command::new(exe_resolved)
} else if exe_name == "moon-lsp" {
let host_paths = std::env::var_os("PATH")
.ok_or(anyhow::anyhow!("Failed to get PATH environment variable"))?;
let lsp_exe = resolve::resolve_exe("moonbit-lsp", &paths)
.or_else(|| resolve::resolve_file("moonbit-lsp", &paths))
.or_else(|| resolve::resolve_file("lsp-server.js", &paths))
.ok_or(err_msg)?;
tracing::debug!("Resolved LSP server executable: {}", lsp_exe.display());
let runtime = resolve::resolve_exe("bun", &host_paths)
.or_else(|| resolve::resolve_exe("node", &host_paths))
.ok_or(anyhow::anyhow!(
"No JavaScript runtime ('bun' or 'node') found for running LSP server"
))?;
tracing::debug!(
"Resolved JavaScript runtime for LSP server: {}",
runtime.display()
);
let mut cmd = Command::new(runtime);
cmd.arg(lsp_exe);
let mut toolchain_root = bin_dir.clone();
toolchain_root.pop();
cmd.env("MOON_HOME", toolchain_root.into_os_string());
cmd
} else {
return Err(err_msg);
};
cmd.args(&command[1..]);
let spec = std::ffi::OsStr::new(toolchain.as_str());
cmd.env(
"MOONUP_TOOLCHAIN_SPEC",
env::var_os("MOONUP_TOOLCHAIN_SPEC").unwrap_or(spec.into()),
);
if exe_name == "moon" {
let mut libcore = bin_dir.clone();
libcore.pop();
libcore.push("lib");
libcore.push("core");
cmd.env(
"MOON_CORE_OVERRIDE",
env::var_os("MOON_CORE_OVERRIDE").unwrap_or(libcore.into_os_string()),
);
}
tracing::debug!("build command: {:?}", cmd);
Ok(cmd)
}