mod babysit;
mod config;
mod cost;
mod deps;
mod events;
mod gate;
mod help;
mod paths;
mod playbook;
mod prompt;
mod run;
mod runner;
mod seed;
mod sensor;
mod session;
mod status;
mod surface;
mod tick;
mod util;
mod worldhash;
use anyhow::Result;
use paths::Paths;
use std::process::ExitCode;
fn main() -> ExitCode {
restore_sigpipe();
let paths = Paths::resolve();
export_env(&paths);
util::init_color();
let args: Vec<String> = std::env::args().skip(1).collect();
let cmd = args.first().map(String::as_str).unwrap_or("run");
let rest = if args.is_empty() { &[][..] } else { &args[1..] };
let result: Result<ExitCode> = match cmd {
"help" | "-h" | "--help" => {
help::print(&paths);
Ok(ExitCode::SUCCESS)
}
"version" | "--version" | "-V" => {
println!("looop {}", env!("CARGO_PKG_VERSION"));
Ok(ExitCode::SUCCESS)
}
"run" | "loop" if rest.first().map(String::as_str) == Some("--detached-id") => {
babysit::run_detached_worker(rest).map(|c| ExitCode::from(c.clamp(0, 255) as u8))
}
"run" | "loop" => deps::require_deps(&paths).and_then(|_| match rest.first() {
Some(goal) => run::cmd_run_goal(&paths, goal),
None => run::cmd_run(&paths),
}),
"tick" => deps::require_deps(&paths).and_then(|_| tick::cmd_tick(&paths)),
"ls" => deps::require_deps(&paths).and_then(|_| ls_inproc(rest)),
"status" => status::cmd_status(&paths, rest),
"start-session" => {
deps::require_deps(&paths).and_then(|_| session::cmd_start_session(&paths, rest))
}
"attach" => deps::require_deps(&paths).and_then(|_| session::cmd_attach(&paths, rest)),
"kill" => deps::require_deps(&paths).and_then(|_| session::cmd_kill(&paths, rest)),
"flag" => deps::require_deps(&paths).and_then(|_| session::cmd_flag(&paths, rest)),
"unflag" => deps::require_deps(&paths).and_then(|_| session::cmd_unflag(&paths, rest)),
"prune" => deps::require_deps(&paths).and_then(|_| session::cmd_prune(&paths, rest)),
"cost" => cost::cmd_cost(&paths, rest),
"playbook" => playbook::cmd_playbook(&paths, rest),
"_fmt" => cost::cmd_fmt(&paths),
"_cost" => cost::cmd_cost_record(&paths, rest),
other => {
eprintln!(
"looop: unknown command '{other}' (try: run, run <goal>, tick, ls, status, start-session, attach, kill, flag, unflag, prune, playbook, help)"
);
Ok(ExitCode::from(1))
}
};
match result {
Ok(code) => code,
Err(e) => {
eprintln!("{e}");
ExitCode::from(1)
}
}
}
#[cfg(unix)]
fn restore_sigpipe() {
const SIGPIPE: i32 = 13;
const SIG_DFL: usize = 0;
unsafe extern "C" {
fn signal(signum: i32, handler: usize) -> usize;
}
unsafe {
signal(SIGPIPE, SIG_DFL);
}
}
#[cfg(not(unix))]
fn restore_sigpipe() {}
fn export_env(paths: &Paths) {
let set = |k: &str, v: &std::ffi::OsStr| unsafe { std::env::set_var(k, v) };
set("LOOOP_BIN", paths.bin.as_os_str());
set("LOOOP_DATA_DIR", paths.data_dir.as_os_str());
set("CONFIG", paths.config.as_os_str());
set("CLAIMS_DIR", paths.claims_dir().as_os_str());
set("REPORTS_DIR", paths.reports_dir().as_os_str());
set("COST_LEDGER", paths.cost_ledger().as_os_str());
if let Some(bd) = &paths.babysit_dir {
set("BABYSIT_DIR", bd.as_os_str());
}
}
fn ls_inproc(rest: &[String]) -> Result<ExitCode> {
let mut json = false;
let mut watch = false;
let mut interval = "2s".to_string();
let mut it = rest.iter();
while let Some(a) = it.next() {
match a.as_str() {
"--json" => json = true,
"--watch" | "-w" => watch = true,
"--interval" | "-n" => {
if let Some(v) = it.next() {
interval = v.clone();
}
}
_ => {} }
}
match babysit::ls(json, watch, interval) {
Ok(()) => Ok(ExitCode::SUCCESS),
Err(_) => Ok(ExitCode::from(1)),
}
}