use std::ffi::OsString;
use std::path::{Path, PathBuf};
mod completion;
pub mod gbatch;
pub mod gcancel;
pub mod gctl;
pub mod gflowd;
pub mod ginfo;
pub mod gjob;
pub mod gqueue;
pub mod gstats;
pub mod mcp;
pub fn multicall_executable() -> std::io::Result<PathBuf> {
Ok(prefer_sibling_gflow(&std::env::current_exe()?))
}
fn prefer_sibling_gflow(exe: &Path) -> PathBuf {
let Some(dir) = exe.parent() else {
return exe.to_path_buf();
};
let gflow = dir.join(format!("gflow{}", std::env::consts::EXE_SUFFIX));
if gflow.is_file() {
gflow
} else {
exe.to_path_buf()
}
}
pub async fn dispatch(argv: Vec<OsString>) -> anyhow::Result<()> {
let Some(program) = argv.first() else {
print_top_level_help();
return Ok(());
};
match program.to_string_lossy().as_ref() {
"gbatch" => gbatch::run(argv).await,
"gcancel" => gcancel::run(argv).await,
"gctl" => gctl::run(argv).await,
"gflowd" => gflowd::run(argv).await,
"ginfo" => ginfo::run(argv).await,
"gjob" => gjob::run(argv).await,
"mcp" => mcp::run(argv).await,
"gqueue" => gqueue::run(argv).await,
"gstats" => gstats::run(argv).await,
_ => {
print_top_level_help();
anyhow::bail!(
"Unknown command '{}'. Expected one of: gbatch, gcancel, gctl, gflowd, ginfo, gjob, mcp, gqueue, gstats",
program.to_string_lossy()
);
}
}
}
pub fn print_top_level_help() {
eprintln!(
"gflow (multi-call)\n\nUsage:\n gflow __multicall <command> [args...]\n gflow <command> [args...]\n\nCommands:\n gbatch\n gcancel\n gctl\n gflowd\n ginfo\n gjob\n mcp\n gqueue\n gstats\n"
);
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
fn bin_name(prefix: &str) -> String {
format!("{prefix}{}", std::env::consts::EXE_SUFFIX)
}
#[test]
fn prefer_sibling_gflow_picks_sibling_binary() {
let tmp = tempfile::tempdir().unwrap();
let gflow = tmp.path().join(bin_name("gflow"));
let gflowd = tmp.path().join(bin_name("gflowd"));
fs::write(&gflow, "").unwrap();
fs::write(&gflowd, "").unwrap();
assert_eq!(prefer_sibling_gflow(&gflowd), gflow);
assert_eq!(prefer_sibling_gflow(&gflow), gflow);
}
#[test]
fn prefer_sibling_gflow_falls_back_to_current_exe() {
let tmp = tempfile::tempdir().unwrap();
let gflowd = tmp.path().join(bin_name("gflowd"));
fs::write(&gflowd, "").unwrap();
assert_eq!(prefer_sibling_gflow(&gflowd), gflowd);
}
#[test]
fn prefer_sibling_gflow_handles_missing_parent() {
assert_eq!(
prefer_sibling_gflow(Path::new(bin_name("gflowd").as_str())),
PathBuf::from(bin_name("gflowd"))
);
}
}