use std::path::{Path, PathBuf};
pub const WEB_BIN_ENV: &str = "NEWT_WEB_BIN";
pub fn candidate_paths(env_override: Option<&str>, current_exe: Option<&Path>) -> Vec<PathBuf> {
let mut out = Vec::new();
if let Some(p) = env_override.map(str::trim).filter(|p| !p.is_empty()) {
out.push(PathBuf::from(p));
}
if let Some(dir) = current_exe.and_then(Path::parent) {
out.push(dir.join(format!("newt-web{}", std::env::consts::EXE_SUFFIX)));
}
out
}
pub fn resolve(
env_override: Option<&str>,
current_exe: Option<&Path>,
exists: &dyn Fn(&Path) -> bool,
) -> Option<PathBuf> {
candidate_paths(env_override, current_exe)
.into_iter()
.find(|p| exists(p))
}
pub fn not_found_message() -> String {
format!(
"newt-web is not installed (checked ${WEB_BIN_ENV}, a `newt-web` beside this \
binary, and PATH). It is a separately built crate (decision D1 — its web \
dependency tree stays out of the agent workspace): install it with \
`just install-web`, or run it in place with \
`cargo run --manifest-path newt-web/Cargo.toml`"
)
}
pub fn run(args: &[String]) -> anyhow::Result<i32> {
let env_override = std::env::var(WEB_BIN_ENV).ok();
let current_exe = std::env::current_exe().ok();
let program = resolve(
env_override.as_deref(),
current_exe.as_deref(),
&|p: &Path| p.is_file(),
)
.unwrap_or_else(|| PathBuf::from("newt-web"));
match std::process::Command::new(&program).args(args).status() {
Ok(status) => Ok(status.code().unwrap_or(1)),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
anyhow::bail!("{}", not_found_message())
}
Err(e) => Err(anyhow::anyhow!(
"could not launch {}: {e}",
program.display()
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn env_override_outranks_the_sibling_candidate() {
let exe = PathBuf::from("/opt/bin/newt");
let paths = candidate_paths(Some("/custom/newt-web"), Some(&exe));
assert_eq!(paths[0], PathBuf::from("/custom/newt-web"));
assert_eq!(
paths[1],
PathBuf::from("/opt/bin").join(format!("newt-web{}", std::env::consts::EXE_SUFFIX))
);
}
#[test]
fn blank_override_and_missing_exe_yield_no_candidates() {
assert!(candidate_paths(Some(" "), None).is_empty());
assert!(candidate_paths(None, None).is_empty());
}
#[test]
fn resolve_returns_the_first_existing_candidate_else_none() {
let exe = PathBuf::from("/opt/bin/newt");
let sibling =
PathBuf::from("/opt/bin").join(format!("newt-web{}", std::env::consts::EXE_SUFFIX));
let hit = resolve(Some("/custom/newt-web"), Some(&exe), &|p| p == sibling);
assert_eq!(hit, Some(sibling));
assert_eq!(
resolve(Some("/custom/newt-web"), Some(&exe), &|_| false),
None
);
}
#[test]
fn the_not_found_message_names_every_escape_hatch() {
let msg = not_found_message();
assert!(msg.contains(WEB_BIN_ENV));
assert!(msg.contains("just install-web"));
assert!(msg.contains("--manifest-path newt-web/Cargo.toml"));
}
}