Files
tty7/crates/tty7-cli/src/gui.rs
T

85 lines
2.3 KiB
Rust

use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use anyhow::{Context as _, Result, bail};
pub fn launch(path: Option<&Path>) -> Result<()> {
let executable = find_executable()?;
let mut command = Command::new(&executable);
if let Some(path) = path {
command.arg("--open-path").arg(path);
}
// The CLI must not keep a caller's redirected pipes alive after it exits.
// The GUI owns its own logging and never needs this console's standard IO.
command
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.with_context(|| format!("launching {}", executable.display()))?;
Ok(())
}
/// The bundled `tty7-app` this CLI belongs to: `TTY7_APP`, then the file next
/// to this executable, then `PATH`. `gui` launches it; hook diagnosis needs the
/// same path because that is the executable a hook command names.
pub fn find_executable() -> Result<PathBuf> {
if let Some(explicit) = std::env::var_os("TTY7_APP") {
let path = PathBuf::from(explicit);
if path.is_file() {
return Ok(path);
}
bail!(
"TTY7_APP points to {}, but that file does not exist",
path.display()
);
}
let name = executable_name();
if let Ok(own) = std::env::current_exe()
&& let Some(dir) = own.parent()
{
let sibling = dir.join(name);
if sibling.is_file() {
return Ok(sibling);
}
}
if let Some(paths) = std::env::var_os("PATH") {
for dir in std::env::split_paths(&paths) {
let candidate = dir.join(name);
if candidate.is_file() {
return Ok(candidate);
}
}
}
bail!("could not find {name} next to this CLI or on PATH — install tty7-app, or set TTY7_APP")
}
fn executable_name() -> &'static str {
if cfg!(windows) {
"tty7-app.exe"
} else {
"tty7-app"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_gui_executable_name_matches_the_platform() {
assert_eq!(
executable_name(),
if cfg!(windows) {
"tty7-app.exe"
} else {
"tty7-app"
}
);
}
}