start_command/
query_commands.rs1use crate::args_parser::WrapperOptions;
11use crate::execution_attach::{attach_execution, ExecutionAttachResult};
12use crate::execution_control::{control_execution, ControlAction, ExecutionControlResult};
13use crate::execution_resume::{resume_execution, ExecutionResumeResult};
14use crate::execution_resume_all::{resume_all_executions, ExecutionResumeAllResult};
15use crate::execution_store::{CleanupOptions, ExecutionStore};
16use crate::log_uploader::upload_execution_log;
17use crate::status_formatter::{list_executions_filtered, query_status, StatusQueryResult};
18
19pub struct QueryOutcome {
21 pub success: bool,
22 pub output: Option<String>,
23 pub error: Option<String>,
24 pub exit_code: Option<i32>,
25}
26
27impl From<StatusQueryResult> for QueryOutcome {
28 fn from(result: StatusQueryResult) -> Self {
29 QueryOutcome {
30 success: result.success,
31 output: result.output,
32 error: result.error,
33 exit_code: None,
34 }
35 }
36}
37
38impl From<ExecutionControlResult> for QueryOutcome {
39 fn from(result: ExecutionControlResult) -> Self {
40 QueryOutcome {
41 success: result.success,
42 output: result.output,
43 error: result.error,
44 exit_code: None,
45 }
46 }
47}
48
49impl From<ExecutionAttachResult> for QueryOutcome {
50 fn from(result: ExecutionAttachResult) -> Self {
51 QueryOutcome {
52 success: result.success,
53 output: result.output,
54 error: result.error,
55 exit_code: result.exit_code,
56 }
57 }
58}
59
60impl From<ExecutionResumeResult> for QueryOutcome {
61 fn from(result: ExecutionResumeResult) -> Self {
62 QueryOutcome {
63 success: result.success,
64 output: result.output,
65 error: result.error,
66 exit_code: None,
67 }
68 }
69}
70
71impl From<ExecutionResumeAllResult> for QueryOutcome {
72 fn from(result: ExecutionResumeAllResult) -> Self {
73 QueryOutcome {
74 success: result.success,
75 output: result.output,
76 error: result.error,
77 exit_code: None,
78 }
79 }
80}
81
82pub fn report_result(result: QueryOutcome) -> i32 {
84 if result.success {
85 if let Some(output) = result.output {
86 println!("{}", output);
87 }
88 return result.exit_code.unwrap_or(0);
89 }
90 if let Some(error) = result.error {
91 eprintln!("Error: {}", error);
92 }
93 result.exit_code.unwrap_or(1)
94}
95
96pub fn handle_upload_log_query(store: Option<&ExecutionStore>, identifier: &str) -> i32 {
98 match upload_execution_log(store, identifier) {
99 Ok(code) => code,
100 Err(error) => {
101 eprintln!("Error: {}", error);
102 1
103 }
104 }
105}
106
107pub fn handle_cleanup(store: Option<&ExecutionStore>, dry_run: bool) -> i32 {
111 let Some(store) = store else {
112 eprintln!("Error: Execution tracking is disabled.");
113 return 1;
114 };
115
116 let result = store.cleanup_stale(CleanupOptions {
117 dry_run,
118 ..Default::default()
119 });
120
121 for error in &result.errors {
122 eprintln!("Error: {}", error);
123 }
124
125 if result.records.is_empty() {
126 println!("No stale records found.");
127 return 0;
128 }
129
130 if dry_run {
131 println!(
132 "Found {} stale record(s) that would be cleaned up:\n",
133 result.records.len()
134 );
135 } else {
136 println!("Cleaned up {} stale record(s):\n", result.cleaned);
137 }
138
139 for record in &result.records {
140 let start_time_display = chrono::DateTime::parse_from_rfc3339(&record.start_time)
141 .map(|dt| dt.format("%Y-%m-%d %H:%M:%S").to_string())
142 .unwrap_or_else(|_| record.start_time.clone());
143
144 println!(" UUID: {}", record.uuid);
145 println!(" Command: {}", record.command);
146 println!(" Started: {}", start_time_display);
147 println!(
148 " PID: {}",
149 record
150 .pid
151 .map(|p| p.to_string())
152 .unwrap_or("N/A".to_string())
153 );
154 println!();
155 }
156
157 if dry_run {
158 println!("Run with --cleanup to actually clean up these records.");
159 }
160 0
161}
162
163pub fn dispatch_query_command(
168 options: &WrapperOptions,
169 store: Option<&ExecutionStore>,
170 command: &str,
171) -> Option<i32> {
172 let output_format = options.output_format.as_deref();
173
174 if let Some(ref identifier) = options.status {
175 return Some(report_result(
176 query_status(store, identifier, output_format).into(),
177 ));
178 }
179 if options.list {
180 return Some(report_result(
181 list_executions_filtered(store, output_format, options.running).into(),
182 ));
183 }
184 if let Some(ref identifier) = options.upload_log {
185 return Some(handle_upload_log_query(store, identifier));
186 }
187 if let Some(ref identifier) = options.stop {
188 return Some(report_result(
189 control_execution(store, identifier, ControlAction::Stop).into(),
190 ));
191 }
192 if let Some(ref identifier) = options.terminate {
193 return Some(report_result(
194 control_execution(store, identifier, ControlAction::Terminate).into(),
195 ));
196 }
197 if let Some(ref identifier) = options.attach {
198 return Some(report_result(
199 attach_execution(store, identifier, options.read_only).into(),
200 ));
201 }
202 if let Some(ref identifier) = options.resume {
203 let new_command = if command.trim().is_empty() {
204 None
205 } else {
206 Some(command)
207 };
208 return Some(report_result(
209 resume_execution(store, identifier, new_command, output_format).into(),
210 ));
211 }
212 if options.resume_all {
213 return Some(report_result(
214 resume_all_executions(store, output_format).into(),
215 ));
216 }
217 if options.cleanup {
218 return Some(handle_cleanup(store, options.cleanup_dry_run));
219 }
220
221 None
222}