use std::env;
use std::path::PathBuf;
use std::process::{Command, ExitCode};
fn main() -> ExitCode {
let forwarded: Vec<String> = env::args().skip(1).collect();
let invoked = env::args().next().unwrap_or_else(|| "athena".to_string());
match resolve_and_run(&forwarded, &invoked) {
Ok(exit_code) => exit_code,
Err(err) => {
eprintln!("{}: {}", invoked, err);
eprintln!();
eprintln!("Hints:");
eprintln!(" • Build the runtime + CLI together:");
eprintln!(" cargo build --bin athena --bin athena_rs --bin athena-cli --bin athena-server");
eprintln!(" • Dev:");
eprintln!(" cargo run --bin athena -- --help");
eprintln!(" cargo run --bin athena_rs -- server");
eprintln!(" • The `athena` CLI is the recommended interface to Athena surfaces.");
ExitCode::from(1)
}
}
}
fn resolve_and_run(args: &[String], invoked: &str) -> std::io::Result<ExitCode> {
let athena = find_athena_binary()?;
if std::env::var("ATHENA_LAUNCHER_QUIET").is_err() && !is_sibling_path(&athena) {
let label = std::path::Path::new(invoked)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("athena-cli");
eprintln!("[{}] → {}", label, athena.display());
}
let status = Command::new(&athena)
.args(args)
.status()?;
match status.code() {
Some(code) if code >= 0 && code <= 255 => Ok(ExitCode::from(code as u8)),
Some(_) => Ok(ExitCode::from(1)),
None => Ok(ExitCode::from(1)), }
}
fn is_sibling_path(p: &std::path::Path) -> bool {
if let Ok(me) = std::env::current_exe() {
if let (Some(my_dir), Some(target_dir)) = (me.parent(), p.parent()) {
return my_dir == target_dir;
}
}
false
}
fn find_athena_binary() -> std::io::Result<PathBuf> {
if let Some(sib) = sibling_candidate() {
if sib.is_file() {
return Ok(sib);
}
}
if let Ok(p) = which::which("athena_rs") {
return Ok(p);
}
if let Ok(p) = which::which("athena") {
return Ok(p);
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"could not find 'athena_rs' (the Athena runtime) or 'athena'. \
Build both with: cargo build --bin athena --bin athena_rs",
))
}
fn sibling_candidate() -> Option<PathBuf> {
let current = env::current_exe().ok()?;
let dir = current.parent()?;
let names = if cfg!(windows) {
["athena_rs.exe", "athena.exe"]
} else {
["athena_rs", "athena"]
};
for name in names {
let p = dir.join(name);
if p.is_file() {
return Some(p);
}
}
None
}