use std::path::PathBuf;
use anyhow::{Context, Result};
#[cfg(windows)]
const UI_BINARY: &str = "basemind-ui.exe";
#[cfg(not(windows))]
const UI_BINARY: &str = "basemind-ui";
fn resolve_ui_binary(current_exe: &std::path::Path) -> Option<PathBuf> {
let sibling = current_exe.parent()?.join(UI_BINARY);
sibling.is_file().then_some(sibling)
}
pub fn run(root: &std::path::Path, args: &[String]) -> Result<()> {
let current_exe = std::env::current_exe().context("locate the running basemind executable")?;
let program: std::ffi::OsString =
resolve_ui_binary(¤t_exe).map_or_else(|| UI_BINARY.into(), PathBuf::into_os_string);
let mut child_args: Vec<String> = Vec::with_capacity(args.len() + 2);
if !args.iter().any(|arg| arg == "--root") {
child_args.push("--root".to_string());
child_args.push(root.to_string_lossy().into_owned());
}
child_args.extend_from_slice(args);
launch_ui(&program, &child_args)
}
fn ui_launch_error(program: &std::ffi::OsStr, error: std::io::Error) -> anyhow::Error {
if error.kind() == std::io::ErrorKind::NotFound {
anyhow::anyhow!(
"the basemind desktop-UI binary ({UI_BINARY}) was not found next to `basemind` \
or on PATH; it ships in the release archive alongside `basemind`"
)
} else {
anyhow::Error::new(error).context(format!("launch {}", program.to_string_lossy()))
}
}
#[cfg(unix)]
fn launch_ui(program: &std::ffi::OsStr, args: &[String]) -> Result<()> {
use std::os::unix::process::CommandExt;
let error = std::process::Command::new(program).args(args).exec();
Err(ui_launch_error(program, error))
}
#[cfg(not(unix))]
fn launch_ui(program: &std::ffi::OsStr, args: &[String]) -> Result<()> {
match std::process::Command::new(program).args(args).status() {
Ok(status) => std::process::exit(status.code().unwrap_or(1)),
Err(error) => Err(ui_launch_error(program, error)),
}
}
#[cfg(test)]
mod tests {
use super::{UI_BINARY, resolve_ui_binary};
#[test]
fn resolves_sibling_binary_when_present() {
let dir = tempfile::tempdir().expect("tempdir");
let current_exe = dir.path().join("basemind");
std::fs::write(¤t_exe, b"").expect("write fake basemind");
let sibling = dir.path().join(UI_BINARY);
std::fs::write(&sibling, b"").expect("write fake basemind-ui");
assert_eq!(resolve_ui_binary(¤t_exe), Some(sibling));
}
#[test]
fn returns_none_when_sibling_absent() {
let dir = tempfile::tempdir().expect("tempdir");
let current_exe = dir.path().join("basemind");
std::fs::write(¤t_exe, b"").expect("write fake basemind");
assert_eq!(resolve_ui_binary(¤t_exe), None);
}
}