use explain_build_core::{
current_dir, detect_workspace_root, load_latest_session, load_latest_session_for_workspace,
load_session_with_previous, render_diff, render_doctor, render_session, render_why,
run_and_record, RunRequest,
};
const TOOL_NAME: &str = "cargo-explain-build";
const VERSION: &str = env!("CARGO_PKG_VERSION");
enum SessionScope {
Workspace(String),
Global,
}
fn main() {
let exit_code = match run() {
Ok(code) => code,
Err(error) => {
eprintln!("error: {error}");
1
}
};
std::process::exit(exit_code);
}
fn run() -> explain_build_core::Result<i32> {
let mut args = normalized_args().into_iter();
let Some(command) = args.next() else {
print_usage();
return Ok(1);
};
match command.as_str() {
"run" => {
let remaining = args.collect::<Vec<_>>();
if wants_help(&remaining) {
print_run_help();
return Ok(0);
}
let Some(subcommand) = remaining.first() else {
return Err(
"missing Cargo subcommand: expected one of build/check/test/run".into(),
);
};
if !matches!(subcommand.as_str(), "build" | "check" | "test" | "run") {
return Err(format!("unsupported Cargo subcommand `{subcommand}`").into());
}
let request = RunRequest {
cwd: current_dir()?,
subcommand: subcommand.clone(),
cargo_args: remaining[1..].to_vec(),
};
let result = run_and_record(request)?;
println!("{}", result.summary);
Ok(result.exit_code)
}
"last" => {
let remaining = args.collect::<Vec<_>>();
if wants_help(&remaining) {
print_last_help();
return Ok(0);
}
let (global, remaining) = parse_global_flag(&remaining)?;
if !remaining.is_empty() {
return Err(command_usage("last").into());
}
let scope = session_scope(global)?;
let Some((session, previous)) = load_latest_session_for_scope(&scope)? else {
println!("{}", no_saved_session_message(&scope));
return Ok(1);
};
println!("{}", render_session(&session, previous.as_ref()));
Ok(if session.build.success {
0
} else {
session.build.exit_code
})
}
"why" => {
let remaining = args.collect::<Vec<_>>();
if wants_help(&remaining) {
print_why_help();
return Ok(0);
}
let (global, remaining) = parse_global_flag(&remaining)?;
let Some(unit) = remaining.first() else {
return Err(
"missing unit: expected `cargo explain-build why <crate-or-target>`".into(),
);
};
if remaining.len() != 1 {
return Err(command_usage("why").into());
}
let scope = session_scope(global)?;
let Some((session, previous)) = load_latest_session_for_scope(&scope)? else {
println!("{}", no_saved_session_message(&scope));
return Ok(1);
};
println!("{}", render_why(&session, previous.as_ref(), unit));
Ok(0)
}
"diff" => {
let remaining = args.collect::<Vec<_>>();
if wants_help(&remaining) {
print_diff_help();
return Ok(0);
}
let (global, remaining) = parse_global_flag(&remaining)?;
let (left, left_previous, right, right_previous) = match remaining.as_slice() {
[] => {
let scope = session_scope(global)?;
let Some((right, right_previous)) = load_latest_session_for_scope(&scope)?
else {
println!("{}", no_saved_session_message(&scope));
return Ok(1);
};
let Some(left) = right_previous.clone() else {
println!(
"No previous workspace session exists for the latest saved session."
);
return Ok(1);
};
let (_, left_previous) = load_session_with_previous(&left.id)?
.ok_or_else(|| format!("saved session `{}` disappeared", left.id))?;
(left, left_previous, right, right_previous)
}
[left_id, right_id] => {
let Some((left, left_previous)) = load_session_with_previous(left_id)? else {
return Err(format!("unknown session `{left_id}`").into());
};
let Some((right, right_previous)) = load_session_with_previous(right_id)?
else {
return Err(format!("unknown session `{right_id}`").into());
};
(left, left_previous, right, right_previous)
}
_ => return Err(command_usage("diff").into()),
};
println!(
"{}",
render_diff(
&left,
left_previous.as_ref(),
&right,
right_previous.as_ref()
)
);
Ok(0)
}
"doctor" => {
let remaining = args.collect::<Vec<_>>();
if wants_help(&remaining) {
print_doctor_help();
return Ok(0);
}
let (global, remaining) = parse_global_flag(&remaining)?;
if !remaining.is_empty() {
return Err(command_usage("doctor").into());
}
let scope = session_scope(global)?;
let Some((session, previous)) = load_latest_session_for_scope(&scope)? else {
println!("{}", no_saved_session_message(&scope));
return Ok(1);
};
println!("{}", render_doctor(&session, previous.as_ref()));
Ok(0)
}
"--version" | "-V" | "version" => {
print_version();
Ok(0)
}
"help" => {
let remaining = args.collect::<Vec<_>>();
if remaining.is_empty() {
print_usage();
return Ok(0);
}
if remaining.len() != 1 {
return Err("usage: `cargo explain-build help [command]`".into());
}
print_help_topic(&remaining[0])?;
Ok(0)
}
"--help" | "-h" => {
print_usage();
Ok(0)
}
other => Err(format!("unknown command `{other}`; run `cargo explain-build help`").into()),
}
}
fn normalized_args() -> Vec<String> {
let args = std::env::args().skip(1).collect::<Vec<_>>();
match args.first().map(String::as_str) {
Some("explain-build") => args.into_iter().skip(1).collect(),
_ => args,
}
}
fn print_usage() {
println!("{TOOL_NAME} {VERSION}");
println!();
println!(
"Stable-first Cargo subcommand for explaining saved build, check, test, and run sessions."
);
println!();
println!("USAGE:");
println!(" cargo explain-build <command> [options]");
println!();
println!("COMMANDS:");
println!(" run record a new Cargo session and save stable evidence");
println!(" last show the latest saved session");
println!(" why explain why one workspace crate or target rebuilt");
println!(" diff compare two saved sessions or the latest pair");
println!(" doctor highlight caveats and missing evidence");
println!(" help show general help or help for one command");
println!(" version show the installed version");
println!();
println!("OPTIONS:");
println!(" -h, --help show help");
println!(" -V, --version show version");
println!();
println!("EXAMPLES:");
println!(" cargo explain-build run build");
println!(" cargo explain-build why my-crate");
println!(" cargo explain-build diff");
println!(" cargo explain-build doctor --global");
println!();
println!("NOTES:");
println!(" saved sessions live under CARGO_EXPLAIN_BUILD_HOME or ~/.cargo-explain-build");
println!(" last/why/diff/doctor prefer the current Cargo workspace unless --global is set");
println!(" non-JSON --message-format values such as `human` are rejected");
}
fn parse_global_flag(args: &[String]) -> explain_build_core::Result<(bool, Vec<String>)> {
let mut global = false;
let mut remaining = Vec::new();
for arg in args {
match arg.as_str() {
"--global" => global = true,
_ => remaining.push(arg.clone()),
}
}
Ok((global, remaining))
}
fn session_scope(global: bool) -> explain_build_core::Result<SessionScope> {
if global {
return Ok(SessionScope::Global);
}
let cwd = current_dir()?;
Ok(match detect_workspace_root(&cwd) {
Some(workspace_root) => SessionScope::Workspace(workspace_root),
None => SessionScope::Global,
})
}
fn load_latest_session_for_scope(
scope: &SessionScope,
) -> explain_build_core::Result<
Option<(
explain_build_core::model::Session,
Option<explain_build_core::model::Session>,
)>,
> {
match scope {
SessionScope::Workspace(workspace_root) => {
load_latest_session_for_workspace(workspace_root)
}
SessionScope::Global => load_latest_session(),
}
}
fn no_saved_session_message(scope: &SessionScope) -> String {
match scope {
SessionScope::Workspace(_) => {
"No saved sessions yet for this workspace. Run `cargo explain-build run build` first."
.to_string()
}
SessionScope::Global => {
"No saved sessions yet. Run `cargo explain-build run build` first.".to_string()
}
}
}
fn wants_help(args: &[String]) -> bool {
args.iter().any(|arg| arg == "--help" || arg == "-h")
}
fn print_version() {
println!("{TOOL_NAME} {VERSION}");
}
fn print_help_topic(topic: &str) -> explain_build_core::Result<()> {
match topic {
"run" => print_run_help(),
"last" => print_last_help(),
"why" => print_why_help(),
"diff" => print_diff_help(),
"doctor" => print_doctor_help(),
"help" => print_help_help(),
"version" => print_version_help(),
"--help" | "-h" => print_usage(),
"--version" | "-V" => print_version_help(),
other => return Err(format!("unknown help topic `{other}`").into()),
}
Ok(())
}
fn command_usage(command: &str) -> &'static str {
match command {
"run" => "usage: `cargo explain-build run <build|check|test|run> [cargo args...]`",
"last" => "usage: `cargo explain-build last [--global]`",
"why" => "usage: `cargo explain-build why [--global] <crate-or-target>`",
"diff" => {
"usage: `cargo explain-build diff [--global]` or `cargo explain-build diff <session-a> <session-b>`"
}
"doctor" => "usage: `cargo explain-build doctor [--global]`",
"help" => "usage: `cargo explain-build help [command]`",
"version" => "usage: `cargo explain-build version`",
_ => "usage: `cargo explain-build help`",
}
}
fn print_run_help() {
println!("{TOOL_NAME} run");
println!();
println!("USAGE:");
println!(" cargo explain-build run <build|check|test|run> [cargo args...]");
println!();
println!("EXAMPLES:");
println!(" cargo explain-build run build");
println!(" cargo explain-build run test --workspace");
println!(" cargo explain-build run build --features sqlite");
println!();
println!("NOTES:");
println!(" the wrapped Cargo command is executed from the current directory");
println!(" saved analysis requires JSON Cargo messages, so incompatible non-JSON");
println!(" --message-format values are rejected instead of silently degrading");
}
fn print_last_help() {
println!("{TOOL_NAME} last");
println!();
println!("USAGE:");
println!(" cargo explain-build last [--global]");
println!();
println!("NOTES:");
println!(" without --global, this prefers the latest session for the current workspace");
println!(" the command exits with the saved build exit code when the latest session failed");
}
fn print_why_help() {
println!("{TOOL_NAME} why");
println!();
println!("USAGE:");
println!(" cargo explain-build why [--global] <crate-or-target>");
println!();
println!("EXAMPLES:");
println!(" cargo explain-build why my-crate");
println!(" cargo explain-build why app");
println!();
println!("NOTES:");
println!(" explanations are conservative and include confidence labels");
println!(" non-workspace or filtered units can still fall back to unknown");
}
fn print_diff_help() {
println!("{TOOL_NAME} diff");
println!();
println!("USAGE:");
println!(" cargo explain-build diff [--global]");
println!(" cargo explain-build diff <session-a> <session-b>");
println!();
println!("NOTES:");
println!(" without explicit ids, diff compares the latest workspace session to its");
println!(" previous workspace session");
}
fn print_doctor_help() {
println!("{TOOL_NAME} doctor");
println!();
println!("USAGE:");
println!(" cargo explain-build doctor [--global]");
println!();
println!("NOTES:");
println!(" doctor highlights missing baseline context, check-only caveats, target");
println!(" changes, and build-script invalidation hints visible from stable evidence");
}
fn print_help_help() {
println!("{TOOL_NAME} help");
println!();
println!("USAGE:");
println!(" cargo explain-build help [command]");
println!();
println!("EXAMPLES:");
println!(" cargo explain-build help");
println!(" cargo explain-build help diff");
}
fn print_version_help() {
println!("{TOOL_NAME} version");
println!();
println!("USAGE:");
println!(" cargo explain-build version");
println!(" cargo explain-build --version");
}