use std::{
ffi::OsString,
path::{Path, PathBuf},
};
use crate::cli_runtime::CliExecutable;
#[cfg(windows)]
pub(crate) const ANTIGRAVITY_PROGRAM: &str = "agy_acp_server.exe";
#[cfg(not(windows))]
pub(crate) const ANTIGRAVITY_PROGRAM: &str = "agy_acp_server.par";
pub(crate) const HARNESS_PATH_ENV: &str = "ANTIGRAVITY_HARNESS_PATH";
#[cfg(windows)]
pub(super) const HARNESS_FILE: &str = "localharness_external.exe";
#[cfg(not(windows))]
pub(super) const HARNESS_FILE: &str = "localharness_external";
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub(crate) enum AntigravityExecutableError {
#[error(
"antigravity: {ANTIGRAVITY_PROGRAM} is not installed; run `/login antigravity` or `rho login antigravity` to install it"
)]
BinaryMissing,
}
pub(crate) fn resolve() -> Result<CliExecutable, AntigravityExecutableError> {
CliExecutable::resolve(ANTIGRAVITY_PROGRAM)
.or_else(|| {
super::install::ManagedRoot::from_env()
.ok()?
.installed_server()
.map(CliExecutable::from_path)
})
.ok_or(AntigravityExecutableError::BinaryMissing)
}
pub(crate) fn workflow_program() -> anyhow::Result<String> {
let server = std::path::absolute(resolve()?.path())?;
Ok(server.to_string_lossy().into_owned())
}
pub(crate) fn server_args() -> Vec<OsString> {
if cfg!(target_os = "linux") {
vec![OsString::from("--uid=")]
} else {
Vec::new()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum HarnessLocation {
Override(PathBuf),
OverrideMissing(PathBuf),
Beside(PathBuf),
Missing { expected: PathBuf },
}
pub(crate) fn harness_location(server: &Path, existing: Option<OsString>) -> HarnessLocation {
if let Some(path) = existing
.filter(|value| !value.is_empty())
.map(PathBuf::from)
{
return if path.is_file() {
HarnessLocation::Override(path)
} else {
HarnessLocation::OverrideMissing(path)
};
}
let server = std::fs::canonicalize(server).unwrap_or_else(|_| server.to_path_buf());
let expected = server
.parent()
.map_or_else(|| PathBuf::from(HARNESS_FILE), |dir| dir.join(HARNESS_FILE));
if expected.is_file() {
HarnessLocation::Beside(expected)
} else {
HarnessLocation::Missing { expected }
}
}
pub(crate) fn harness_env(
server: Option<&Path>,
existing: Option<OsString>,
) -> Vec<(OsString, OsString)> {
match server.map(|server| harness_location(server, existing)) {
Some(HarnessLocation::Beside(harness)) => {
vec![(HARNESS_PATH_ENV.into(), harness.into_os_string())]
}
Some(
HarnessLocation::Override(_)
| HarnessLocation::OverrideMissing(_)
| HarnessLocation::Missing { .. },
)
| None => Vec::new(),
}
}
pub(crate) fn harness_override_from_env() -> Option<OsString> {
std::env::var_os(HARNESS_PATH_ENV)
}
pub(crate) fn harness_env_for_launch(frozen: Option<&CliExecutable>) -> Vec<(OsString, OsString)> {
let server: Option<PathBuf> = match frozen {
Some(frozen) => Some(frozen.path().to_path_buf()),
None => resolve().ok().map(|found| found.path().to_path_buf()),
};
harness_env(server.as_deref(), harness_override_from_env())
}
#[cfg(test)]
#[path = "executable_tests.rs"]
mod tests;