use crate::cli::args::{GlobalFlags, TeardownArgs};
use anyhow::Result;
use grex_core::sync::{self, HaltedContext, SyncError, SyncOptions, SyncReport, SyncStep};
use tokio_util::sync::CancellationToken;
pub fn run(args: TeardownArgs, global: &GlobalFlags, cancel: &CancellationToken) -> Result<()> {
let Some(pack_root) = args.pack_root.clone() else {
if global.json {
super::sync::emit_json_error(
"usage",
"`<pack_root>` is required (directory with `.grex/pack.yaml` or the YAML file)",
"teardown",
);
} else {
eprintln!(
"grex teardown: <pack_root> required (directory with `.grex/pack.yaml` or the YAML file)"
);
}
std::process::exit(2);
};
let opts = SyncOptions::new()
.with_dry_run(global.dry_run)
.with_validate(!args.no_validate)
.with_workspace(args.workspace.clone());
match run_impl(&pack_root, &opts, args.quiet, global.json, cancel) {
RunOutcome::Ok => Ok(()),
RunOutcome::Validation => std::process::exit(1),
RunOutcome::Exec => std::process::exit(2),
RunOutcome::Tree => std::process::exit(3),
}
}
enum RunOutcome {
Ok,
Validation,
Exec,
Tree,
}
fn run_impl(
pack_root: &std::path::Path,
opts: &SyncOptions,
quiet: bool,
json: bool,
cancel: &CancellationToken,
) -> RunOutcome {
match sync::teardown(pack_root, opts, cancel) {
Ok(report) => {
if json {
super::sync::emit_json_report(&report, opts.dry_run, "teardown");
} else {
render_report(&report, quiet);
}
if report.halted.is_some() {
return RunOutcome::Exec;
}
RunOutcome::Ok
}
Err(err) => map_sync_outcome(super::sync::classify_sync_err(err, json, "teardown")),
}
}
fn map_sync_outcome(o: super::sync::RunOutcome) -> RunOutcome {
match o {
super::sync::RunOutcome::Ok => RunOutcome::Ok,
super::sync::RunOutcome::Validation => RunOutcome::Validation,
super::sync::RunOutcome::Exec => RunOutcome::Exec,
super::sync::RunOutcome::Tree | super::sync::RunOutcome::UsageError => RunOutcome::Tree,
}
}
fn print_halted_context(ctx: &HaltedContext) {
eprintln!(
"teardown halted at pack `{}` action #{} ({}):",
ctx.pack, ctx.action_idx, ctx.action_name
);
eprintln!(" error: {}", ctx.error);
if let Some(hint) = &ctx.recovery_hint {
eprintln!(" hint: {hint}");
}
}
fn render_report(report: &SyncReport, quiet: bool) {
if !quiet {
for s in &report.steps {
print_step(s);
}
}
for w in &report.event_log_warnings {
eprintln!("warning: {w}");
}
if let Some(err) = &report.halted {
match err {
SyncError::Halted(ctx) => print_halted_context(ctx),
other => eprintln!("halted: {other}"),
}
}
}
fn print_step(s: &SyncStep) {
use grex_core::ExecResult;
let tag = match &s.exec_step.result {
ExecResult::PerformedChange => "ok",
ExecResult::WouldPerformChange => "would",
ExecResult::AlreadySatisfied => "skipped",
ExecResult::NoOp => "noop",
_ => "other",
};
println!(
"[{tag}] pack={pack} action={kind} idx={idx}",
pack = s.pack,
kind = s.exec_step.action_name,
idx = s.action_idx,
);
}