use std::process::ExitCode;
use clap::Parser;
use release_kit::cli::{Cli, Commands};
use release_kit::error::RkError;
use release_kit::{applog, commands, output};
fn main() -> ExitCode {
let cli = match Cli::try_parse() {
Ok(cli) => cli,
Err(err) => return parse_failure(&err),
};
let started = std::time::Instant::now();
let result = match (run(&cli), output::take_stdout_failure()) {
(Ok(()), Some(source)) => Err(RkError::Io(source)),
(outcome, _) => outcome,
};
let status = result
.as_ref()
.map_or_else(|e| e.reason().as_str(), |()| "ok");
applog::record(name(&cli.command), status, started.elapsed().as_millis());
match result {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
output::render_error(&e, wants_json(&cli.command));
ExitCode::from(e.exit_code())
}
}
}
fn run(cli: &Cli) -> Result<(), RkError> {
match &cli.command {
Commands::Method(args) => commands::read::method(args),
Commands::Binding(args) => commands::read::binding(args),
Commands::Snippet(args) => commands::read::snippet(args),
Commands::Guide(args) => commands::guide::run(args),
Commands::Forge(args) => commands::forge::run(args),
Commands::Versions(args) => commands::versions::run(args),
Commands::Payload(args) => commands::payload::run(args),
Commands::Init(args) => commands::init::run(args),
Commands::Status(args) => commands::status::run(args),
Commands::Upgrade(args) => commands::upgrade::run(args),
Commands::Adopt(args) => commands::adopt::run(args),
Commands::Setup(args) => commands::setup::run(args),
Commands::Runs(args) => commands::runs::run(args),
Commands::Skill(args) => commands::skill::run(args),
Commands::Doctor(args) => commands::doctor::run(args),
Commands::Usage => commands::usage::run(),
Commands::License => commands::license::run(),
Commands::Completions(args) => commands::completions::run(args),
}
}
fn parse_failure(err: &clap::Error) -> ExitCode {
if err.exit_code() == 0 {
let _ = err.print();
applog::record("parse", "ok", 0);
return ExitCode::SUCCESS;
}
if std::env::args_os().any(|arg| arg == std::ffi::OsStr::new("--json")) {
let message = err
.to_string()
.lines()
.next()
.unwrap_or("invalid arguments")
.trim_start_matches("error: ")
.to_owned();
output::render_error(&RkError::Usage(message), true);
} else {
let _ = err.print();
}
applog::record("parse", "usage", 0);
ExitCode::from(64)
}
const fn name(command: &Commands) -> &'static str {
match command {
Commands::Method(_) => "method",
Commands::Binding(_) => "binding",
Commands::Snippet(_) => "snippet",
Commands::Guide(_) => "guide",
Commands::Forge(_) => "forge",
Commands::Versions(_) => "versions",
Commands::Payload(_) => "payload",
Commands::Init(_) => "init",
Commands::Status(_) => "status",
Commands::Upgrade(_) => "upgrade",
Commands::Adopt(_) => "adopt",
Commands::Setup(_) => "setup",
Commands::Runs(_) => "runs",
Commands::Skill(_) => "skill",
Commands::Doctor(_) => "doctor",
Commands::Usage => "usage",
Commands::License => "license",
Commands::Completions(_) => "completions",
}
}
const fn wants_json(command: &Commands) -> bool {
use release_kit::cli::runs::RunsAction;
use release_kit::cli::setup::SetupAction;
use release_kit::cli::skill::SkillAction;
match command {
Commands::Payload(args) => args.json,
Commands::Init(args) => args.json,
Commands::Status(args) => args.json,
Commands::Upgrade(args) => args.json,
Commands::Adopt(args) => args.json,
Commands::Versions(args) => args.json,
Commands::Doctor(args) => args.json,
Commands::Setup(args) => match &args.action {
Some(SetupAction::Check { json, .. } | SetupAction::Step { json, .. }) => *json,
Some(SetupAction::Script { .. }) => false,
None => args.json,
},
Commands::Runs(args) => match &args.action {
RunsAction::List { json } | RunsAction::Show { json, .. } => *json,
RunsAction::Prune { .. } => false,
},
Commands::Skill(args) => match &args.action {
SkillAction::Install { json, .. } | SkillAction::Uninstall { json, .. } => *json,
SkillAction::List | SkillAction::Show { .. } => false,
},
_ => false,
}
}