use crate::args_parser::WrapperOptions;
use crate::execution_attach::{attach_execution, ExecutionAttachResult};
use crate::execution_control::{control_execution, ControlAction, ExecutionControlResult};
use crate::execution_resume::{resume_execution_with_options, ExecutionResumeResult};
use crate::execution_resume_all::{resume_all_executions, ExecutionResumeAllResult};
use crate::execution_store::{CleanupOptions, ExecutionStore};
use crate::log_uploader::upload_execution_log;
use crate::status_formatter::{list_executions_filtered, query_status, StatusQueryResult};
pub struct QueryOutcome {
pub success: bool,
pub output: Option<String>,
pub error: Option<String>,
pub exit_code: Option<i32>,
}
impl From<StatusQueryResult> for QueryOutcome {
fn from(result: StatusQueryResult) -> Self {
QueryOutcome {
success: result.success,
output: result.output,
error: result.error,
exit_code: None,
}
}
}
impl From<ExecutionControlResult> for QueryOutcome {
fn from(result: ExecutionControlResult) -> Self {
QueryOutcome {
success: result.success,
output: result.output,
error: result.error,
exit_code: None,
}
}
}
impl From<ExecutionAttachResult> for QueryOutcome {
fn from(result: ExecutionAttachResult) -> Self {
QueryOutcome {
success: result.success,
output: result.output,
error: result.error,
exit_code: result.exit_code,
}
}
}
impl From<ExecutionResumeResult> for QueryOutcome {
fn from(result: ExecutionResumeResult) -> Self {
QueryOutcome {
success: result.success,
output: result.output,
error: result.error,
exit_code: result.exit_code,
}
}
}
impl From<ExecutionResumeAllResult> for QueryOutcome {
fn from(result: ExecutionResumeAllResult) -> Self {
QueryOutcome {
success: result.success,
output: result.output,
error: result.error,
exit_code: None,
}
}
}
pub fn report_result(result: QueryOutcome) -> i32 {
if result.success {
if let Some(output) = result.output {
println!("{}", output);
}
return result.exit_code.unwrap_or(0);
}
if let Some(error) = result.error {
eprintln!("Error: {}", error);
}
result.exit_code.unwrap_or(1)
}
pub fn handle_upload_log_query(store: Option<&ExecutionStore>, identifier: &str) -> i32 {
match upload_execution_log(store, identifier) {
Ok(code) => code,
Err(error) => {
eprintln!("Error: {}", error);
1
}
}
}
pub fn handle_cleanup(store: Option<&ExecutionStore>, dry_run: bool) -> i32 {
handle_cleanup_target(store, dry_run, None)
}
pub fn handle_cleanup_target(
store: Option<&ExecutionStore>,
dry_run: bool,
target: Option<&str>,
) -> i32 {
if let Some(target) = target {
let result = crate::execution_cleanup::cleanup_execution(
store,
target,
dry_run,
&crate::execution_control::SystemCommandRunner,
);
if !result.success {
if let Some(output) = &result.output {
println!("{}", output);
}
}
return report_result(result);
}
let Some(store) = store else {
eprintln!("Error: Execution tracking is disabled.");
return 1;
};
let result = store.cleanup_stale(CleanupOptions {
dry_run,
..Default::default()
});
for error in &result.errors {
eprintln!("Error: {}", error);
}
if result.records.is_empty() {
println!("No stale records found.");
return 0;
}
if dry_run {
println!(
"Found {} stale record(s) that would be cleaned up:\n",
result.records.len()
);
} else {
println!("Cleaned up {} stale record(s):\n", result.cleaned);
}
for record in &result.records {
let start_time_display = chrono::DateTime::parse_from_rfc3339(&record.start_time)
.map(|dt| dt.format("%Y-%m-%d %H:%M:%S").to_string())
.unwrap_or_else(|_| record.start_time.clone());
println!(" UUID: {}", record.uuid);
println!(" Command: {}", record.command);
println!(" Started: {}", start_time_display);
println!(
" PID: {}",
record
.pid
.map(|p| p.to_string())
.unwrap_or("N/A".to_string())
);
println!();
}
if dry_run {
println!("Run with --cleanup to actually clean up these records.");
}
0
}
pub fn dispatch_query_command(
options: &WrapperOptions,
store: Option<&ExecutionStore>,
command: &str,
) -> Option<i32> {
let output_format = options.output_format.as_deref();
if let Some(ref identifier) = options.status {
return Some(report_result(
query_status(store, identifier, output_format).into(),
));
}
if options.list {
return Some(report_result(
list_executions_filtered(store, output_format, options.running).into(),
));
}
if let Some(ref identifier) = options.upload_log {
return Some(
match crate::log_uploader::upload_execution_log_with_options(
store,
identifier,
options.no_sanitize,
std::env::var("START_VERBOSE").is_ok_and(|value| value == "1" || value == "true"),
) {
Ok(code) => code,
Err(error) => {
eprintln!("Error: {}", error);
1
}
},
);
}
if let Some(ref identifier) = options.stop {
return Some(report_result(
control_execution(store, identifier, ControlAction::Stop).into(),
));
}
if let Some(ref identifier) = options.terminate {
return Some(report_result(
control_execution(store, identifier, ControlAction::Terminate).into(),
));
}
if let Some(ref identifier) = options.attach {
return Some(report_result(
attach_execution(store, identifier, options.read_only).into(),
));
}
if let Some(ref identifier) = options.resume {
let new_command = if command.trim().is_empty() {
None
} else {
Some(command)
};
return Some(report_result(
resume_execution_with_options(store, identifier, new_command, output_format, options)
.into(),
));
}
if options.resume_all {
return Some(report_result(
resume_all_executions(store, output_format).into(),
));
}
if options.cleanup {
return Some(handle_cleanup_target(
store,
options.cleanup_dry_run,
options.cleanup_target.as_deref(),
));
}
None
}