start_command/
args_parser_queries.rs1use crate::args_parser::{WrapperOptions, VALID_OUTPUT_FORMATS};
10
11type IdentifierSetter = fn(&mut WrapperOptions, String);
13
14type FlagSetter = fn(&mut WrapperOptions);
16
17const IDENTIFIER_OPTIONS: [(&str, IdentifierSetter); 6] = [
19 ("--status", |o, v| o.status = Some(v)),
20 ("--upload-log", |o, v| o.upload_log = Some(v)),
21 ("--stop", |o, v| o.stop = Some(v)),
22 ("--terminate", |o, v| o.terminate = Some(v)),
23 ("--attach", |o, v| o.attach = Some(v)),
24 ("--resume", |o, v| o.resume = Some(v)),
25];
26
27const FLAG_OPTIONS: [(&str, FlagSetter); 4] = [
29 ("--list", |o| o.list = true),
30 ("--running", |o| o.running = true),
31 ("--read-only", |o| o.read_only = true),
32 ("--resume-all", |o| o.resume_all = true),
33];
34
35fn parse_cleanup_option(
39 args: &[String],
40 index: usize,
41 options: &mut WrapperOptions,
42) -> Result<usize, String> {
43 let arg = args[index].as_str();
44 let (name, inline) = match arg.split_once('=') {
45 Some((name, value)) => (name, Some(value)),
46 None => (arg, None),
47 };
48 if name != "--cleanup" && name != "--cleanup-dry-run" {
49 return Ok(0);
50 }
51 options.cleanup = true;
52 options.cleanup_dry_run |= name == "--cleanup-dry-run";
53 if let Some(value) = inline {
54 if value.is_empty() {
55 return Err(format!(
56 "Option {} requires a UUID or session name argument",
57 name
58 ));
59 }
60 options.cleanup_target = Some(value.to_string());
61 return Ok(1);
62 }
63 match args.get(index + 1).filter(|next| !next.starts_with('-')) {
64 Some(next) => {
65 options.cleanup_target = Some(next.clone());
66 Ok(2)
67 }
68 None => Ok(1),
69 }
70}
71
72const OUTPUT_FORMAT_MODES: [&str; 4] = ["--status", "--list", "--resume", "--resume-all"];
74
75pub fn parse_query_option(
80 args: &[String],
81 index: usize,
82 options: &mut WrapperOptions,
83) -> Result<usize, String> {
84 let arg = &args[index];
85
86 let cleanup_consumed = parse_cleanup_option(args, index, options)?;
87 if cleanup_consumed > 0 {
88 return Ok(cleanup_consumed);
89 }
90
91 for (name, apply) in FLAG_OPTIONS {
92 if arg == name {
93 apply(options);
94 return Ok(1);
95 }
96 }
97
98 for (name, apply) in IDENTIFIER_OPTIONS {
99 if arg == name {
100 if index + 1 < args.len() && !args[index + 1].starts_with('-') {
101 apply(options, args[index + 1].clone());
102 return Ok(2);
103 }
104 return Err(format!(
105 "Option {} requires a UUID or session name argument",
106 arg
107 ));
108 }
109
110 let prefix = format!("{}=", name);
111 if let Some(value) = arg.strip_prefix(&prefix) {
112 if value.is_empty() {
113 return Err(format!(
114 "Option {} requires a UUID or session name argument",
115 name
116 ));
117 }
118 apply(options, value.to_string());
119 return Ok(1);
120 }
121 }
122
123 if arg == "--output-format" {
124 if index + 1 < args.len() && !args[index + 1].starts_with('-') {
125 options.output_format = Some(args[index + 1].to_lowercase());
126 return Ok(2);
127 }
128 return Err(format!("Option {} requires a format argument", arg));
129 }
130
131 if arg.starts_with("--output-format=") {
132 options.output_format = Some(arg.split('=').nth(1).unwrap_or("").to_lowercase());
133 return Ok(1);
134 }
135
136 Ok(0)
137}
138
139pub fn active_query_modes(options: &WrapperOptions) -> Vec<&'static str> {
141 let mut modes = Vec::new();
142 if options.status.is_some() {
143 modes.push("--status");
144 }
145 if options.list {
146 modes.push("--list");
147 }
148 if options.upload_log.is_some() {
149 modes.push("--upload-log");
150 }
151 if options.stop.is_some() {
152 modes.push("--stop");
153 }
154 if options.terminate.is_some() {
155 modes.push("--terminate");
156 }
157 if options.attach.is_some() {
158 modes.push("--attach");
159 }
160 if options.resume.is_some() {
161 modes.push("--resume");
162 }
163 if options.resume_all {
164 modes.push("--resume-all");
165 }
166 if options.cleanup {
167 modes.push("--cleanup");
168 }
169 modes
170}
171
172pub fn has_query_mode(options: &WrapperOptions) -> bool {
174 !active_query_modes(options).is_empty()
175}
176
177pub fn validate_query_options(options: &WrapperOptions) -> Result<(), String> {
179 if let Some(ref format) = options.output_format {
180 if !VALID_OUTPUT_FORMATS.contains(&format.as_str()) {
181 return Err(format!(
182 "Invalid output format: \"{}\". Valid options are: {}",
183 format,
184 VALID_OUTPUT_FORMATS.join(", ")
185 ));
186 }
187 }
188
189 let modes = active_query_modes(options);
190
191 if modes.len() > 1 {
192 return Err(
193 "Cannot combine --status, --list, --upload-log, --stop, --terminate, --attach, --resume, --resume-all, or --cleanup in the same invocation"
194 .to_string(),
195 );
196 }
197
198 if options.output_format.is_some()
199 && !modes.iter().any(|mode| OUTPUT_FORMAT_MODES.contains(mode))
200 {
201 return Err(
202 "--output-format option is only valid with --status, --list, --resume, or --resume-all"
203 .to_string(),
204 );
205 }
206
207 if options.running && !options.list {
208 return Err("--running option is only valid with --list".to_string());
209 }
210
211 if options.read_only && options.attach.is_none() {
212 return Err("--read-only option is only valid with --attach".to_string());
213 }
214
215 Ok(())
216}