diff options
Diffstat (limited to 'src/integration/env.rs')
| -rw-r--r-- | src/integration/env.rs | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/integration/env.rs b/src/integration/env.rs new file mode 100644 index 00000000..575c4c9a --- /dev/null +++ b/src/integration/env.rs @@ -0,0 +1,156 @@ +use std::io; +use std::path::PathBuf; +#[cfg(test)] +use std::sync::{Mutex, MutexGuard, OnceLock}; + +use portable_pty::CommandBuilder; + +pub(crate) const HERDR_PANE_ID_ENV_VAR: &str = "HERDR_PANE_ID"; +pub(crate) const HERDR_TAB_ID_ENV_VAR: &str = "HERDR_TAB_ID"; +pub(crate) const HERDR_WORKSPACE_ID_ENV_VAR: &str = "HERDR_WORKSPACE_ID"; + +pub(crate) const PI_CODING_AGENT_DIR_ENV_VAR: &str = "PI_CODING_AGENT_DIR"; +pub(crate) const CLAUDE_CONFIG_DIR_ENV_VAR: &str = "CLAUDE_CONFIG_DIR"; +pub(crate) const CODEX_HOME_ENV_VAR: &str = "CODEX_HOME"; +pub(crate) const KIMI_CODE_HOME_ENV_VAR: &str = "KIMI_CODE_HOME"; +pub(crate) const COPILOT_HOME_ENV_VAR: &str = "COPILOT_HOME"; +pub(crate) const QODERCLI_CONFIG_DIR_ENV_VAR: &str = "QODER_CONFIG_DIR"; +pub(crate) const CURSOR_CONFIG_DIR_ENV_VAR: &str = "CURSOR_CONFIG_DIR"; + +pub(crate) fn apply_pane_base_env(cmd: &mut CommandBuilder) { + cmd.env(crate::api::SOCKET_PATH_ENV_VAR, crate::api::socket_path()); +} + +pub(crate) fn pi_extension_dir() -> io::Result<PathBuf> { + Ok( + config_dir_from_env_or_home(PI_CODING_AGENT_DIR_ENV_VAR, &[".pi", "agent"])? + .join("extensions"), + ) +} + +pub(crate) fn omp_extension_dir() -> io::Result<PathBuf> { + Ok( + config_dir_from_env_or_home(PI_CODING_AGENT_DIR_ENV_VAR, &[".omp", "agent"])? + .join("extensions"), + ) +} + +pub(crate) fn claude_dir() -> io::Result<PathBuf> { + config_dir_from_env_or_home(CLAUDE_CONFIG_DIR_ENV_VAR, &[".claude"]) +} + +pub(crate) fn codex_dir() -> io::Result<PathBuf> { + config_dir_from_env_or_home(CODEX_HOME_ENV_VAR, &[".codex"]) +} + +pub(crate) fn kimi_dir() -> io::Result<PathBuf> { + config_dir_from_env_or_home(KIMI_CODE_HOME_ENV_VAR, &[".kimi-code"]) +} + +pub(crate) fn copilot_dir() -> io::Result<PathBuf> { + config_dir_from_env_or_home(COPILOT_HOME_ENV_VAR, &[".copilot"]) +} + +pub(crate) fn devin_dir() -> io::Result<PathBuf> { + if let Some(value) = std::env::var_os("XDG_CONFIG_HOME").filter(|value| !value.is_empty()) { + return expand_tilde_path(PathBuf::from(value)).map(|path| path.join("devin")); + } + + Ok(home_dir()?.join(".config").join("devin")) +} + +pub(crate) fn droid_dir() -> io::Result<PathBuf> { + Ok(home_dir()?.join(".factory")) +} + +pub(crate) fn config_dir_from_env_or_home( + env_var: &str, + home_relative_segments: &[&str], +) -> io::Result<PathBuf> { + if let Some(value) = std::env::var_os(env_var).filter(|value| !value.is_empty()) { + return expand_tilde_path(PathBuf::from(value)); + } + + let mut path = home_dir()?; + for segment in home_relative_segments { + path.push(segment); + } + Ok(path) +} + +pub(crate) fn expand_tilde_path(path: PathBuf) -> io::Result<PathBuf> { + let Some(raw) = path.to_str() else { + return Ok(path); + }; + + if raw == "~" { + return home_dir(); + } + + if let Some(rest) = raw + .strip_prefix("~/") + .or_else(|| raw.strip_prefix("~\\")) + .or_else(|| raw.strip_prefix('~')) + { + return Ok(home_dir()?.join(rest)); + } + + Ok(path) +} + +pub(crate) fn opencode_dir() -> io::Result<PathBuf> { + Ok(home_dir()?.join(".config/opencode")) +} + +pub(crate) fn kilo_dir() -> io::Result<PathBuf> { + Ok(home_dir()?.join(".config/kilo")) +} + +pub(crate) fn hermes_dir() -> io::Result<PathBuf> { + Ok(home_dir()?.join(".hermes")) +} + +pub(crate) fn hermes_plugin_dir() -> io::Result<PathBuf> { + Ok(hermes_dir()? + .join("plugins") + .join(super::HERMES_PLUGIN_INSTALL_NAME)) +} + +pub(crate) fn qodercli_dir() -> io::Result<PathBuf> { + config_dir_from_env_or_home(QODERCLI_CONFIG_DIR_ENV_VAR, &[".qoder"]) +} + +pub(crate) fn cursor_dir() -> io::Result<PathBuf> { + config_dir_from_env_or_home(CURSOR_CONFIG_DIR_ENV_VAR, &[".cursor"]) +} + +pub(crate) fn home_dir() -> io::Result<PathBuf> { + if let Some(home) = std::env::var_os("HOME").filter(|value| !value.is_empty()) { + return Ok(PathBuf::from(home)); + } + + #[cfg(windows)] + { + if let Some(profile) = std::env::var_os("USERPROFILE").filter(|value| !value.is_empty()) { + return Ok(PathBuf::from(profile)); + } + if let (Some(drive), Some(path)) = ( + std::env::var_os("HOMEDRIVE").filter(|value| !value.is_empty()), + std::env::var_os("HOMEPATH").filter(|value| !value.is_empty()), + ) { + let mut home = PathBuf::from(drive); + home.push(path); + return Ok(home); + } + } + + Err(io::Error::other( + "home directory is not set; cannot locate home directory", + )) +} + +#[cfg(test)] +pub(crate) fn integration_env_lock() -> MutexGuard<'static, ()> { + static LOCK: OnceLock<Mutex<()>> = OnceLock::new(); + LOCK.get_or_init(|| Mutex::new(())).lock().unwrap() +} |
