use std::env;
use std::io::{self, Read};
use std::path::Path;
use std::process::exit;
use balls::delivery::{self, Repo, Spec, Wire};
use balls::delivery_precondition::{precondition_unmet, require_repo};
use balls::delivery_repo::{changed_task_paths, Project};
use balls::layout::Xdg;
fn main() {
let args: Vec<String> = env::args().skip(1).collect();
if args.first().map(String::as_str) == Some("protocol") {
println!("{}", delivery::PROTOCOL_JSON);
return;
}
if let Err(e) = run(&args) {
eprintln!("bl-delivery: {e}");
exit(1);
}
}
fn run(args: &[String]) -> io::Result<()> {
let op = args.first().ok_or_else(|| io::Error::other("usage: bl-delivery <op> <phase>"))?;
let phase = args.get(1).ok_or_else(|| io::Error::other("usage: bl-delivery <op> <phase>"))?;
let mut stdin = String::new();
io::stdin().read_to_string(&mut stdin)?;
let wire: Wire = serde_json::from_str(&stdin).map_err(io::Error::other)?;
delivery::ensure_safe_invocation_path(&wire.binding.invocation_path)?;
let plugin = var("BALLS_PLUGIN_NAME")?;
let home = var("HOME")?;
let xdg = Xdg::with(Path::new(&home), env::var("XDG_CONFIG_HOME").ok().as_deref(), env::var("XDG_STATE_HOME").ok().as_deref());
let invocation = &wire.binding.invocation_path;
let repo = Project::at(Path::new(invocation));
if op == "prime" {
return prime(phase, &wire, &repo);
}
let cwd = env::current_dir()?;
let id = delivery::resolve_id(wire.metadata.as_ref(), || changed_task_paths(&cwd))?;
let worktree = delivery::worktree_path(&xdg, &plugin, invocation, &id);
let branch = delivery::work_branch(&id);
let rolling_back = wire.rolling_back.is_some();
let title = wire.current_state.as_ref().map_or("", |s| s.title.as_str());
let subject = delivery::subject(title, &id);
let marker = delivery::marker(&id);
let override_msg = wire.command.as_ref().and_then(|c| c.message.as_deref());
let spec = Spec {
worktree: &worktree,
branch: &branch,
subject: &subject,
override_msg,
marker: &marker,
};
require_repo(op, phase, rolling_back, &repo, invocation)?;
delivery::dispatch(op, phase, rolling_back, &repo, &spec)?;
if let Some(line) = delivery::surfaced(op, phase, rolling_back, &worktree, worktree.is_dir()) {
println!("{line}");
}
Ok(())
}
fn prime(phase: &str, wire: &Wire, repo: &Project) -> io::Result<()> {
if wire.rolling_back.is_some() {
return Ok(());
}
if !repo.is_git_repo()? {
eprintln!("bl-delivery: {}", precondition_unmet(&wire.binding.invocation_path));
return Ok(());
}
if phase == "post" {
repo.prune()?;
}
Ok(())
}
fn var(key: &str) -> io::Result<String> {
env::var(key).map_err(|_| io::Error::other(format!("{key} is unset (set by balls per §6)")))
}