1use crate::args_parser::{WrapperOptions, VALID_OUTPUT_FORMATS};
11
12type IdentifierSetter = fn(&mut WrapperOptions, String);
14
15type FlagSetter = fn(&mut WrapperOptions);
17
18const IDENTIFIER_OPTIONS: [(&str, IdentifierSetter); 6] = [
20 ("--status", |o, v| o.status = Some(v)),
21 ("--upload-log", |o, v| o.upload_log = Some(v)),
22 ("--stop", |o, v| o.stop = Some(v)),
23 ("--terminate", |o, v| o.terminate = Some(v)),
24 ("--attach", |o, v| o.attach = Some(v)),
25 ("--resume", |o, v| o.resume = Some(v)),
26];
27
28const FLAG_OPTIONS: [(&str, FlagSetter); 4] = [
30 ("--list", |o| o.list = true),
31 ("--running", |o| o.running = true),
32 ("--read-only", |o| o.read_only = true),
33 ("--resume-all", |o| o.resume_all = true),
34];
35
36fn parse_cleanup_option(
40 args: &[String],
41 index: usize,
42 options: &mut WrapperOptions,
43) -> Result<usize, String> {
44 let arg = args[index].as_str();
45 let (name, inline) = match arg.split_once('=') {
46 Some((name, value)) => (name, Some(value)),
47 None => (arg, None),
48 };
49 if name != "--cleanup" && name != "--cleanup-dry-run" {
50 return Ok(0);
51 }
52 options.cleanup = true;
53 options.cleanup_dry_run |= name == "--cleanup-dry-run";
54 if let Some(value) = inline {
55 if value.is_empty() {
56 return Err(format!(
57 "Option {} requires a UUID or session name argument",
58 name
59 ));
60 }
61 options.cleanup_target = Some(value.to_string());
62 return Ok(1);
63 }
64 match args.get(index + 1).filter(|next| !next.starts_with('-')) {
65 Some(next) => {
66 options.cleanup_target = Some(next.clone());
67 Ok(2)
68 }
69 None => Ok(1),
70 }
71}
72
73fn parse_naming_option(
75 args: &[String],
76 index: usize,
77 options: &mut WrapperOptions,
78) -> Result<usize, String> {
79 let arg = args[index].as_str();
80 if arg != "--alias" && arg != "--rename" {
81 return Ok(0);
82 }
83 match (args.get(index + 1), args.get(index + 2)) {
84 (Some(identifier), Some(name))
85 if !identifier.starts_with('-') && !name.starts_with('-') =>
86 {
87 if arg == "--alias" {
88 options.alias = Some(identifier.clone());
89 } else {
90 options.rename = Some(identifier.clone());
91 }
92 options.new_name = Some(name.clone());
93 Ok(3)
94 }
95 _ => Err(format!(
96 "Option {} requires a UUID or session name and a new name",
97 arg
98 )),
99 }
100}
101
102const OUTPUT_FORMAT_MODES: [&str; 4] = ["--status", "--list", "--resume", "--resume-all"];
104
105pub fn parse_query_option(
110 args: &[String],
111 index: usize,
112 options: &mut WrapperOptions,
113) -> Result<usize, String> {
114 let arg = &args[index];
115
116 let cleanup_consumed = parse_cleanup_option(args, index, options)?;
117 if cleanup_consumed > 0 {
118 return Ok(cleanup_consumed);
119 }
120
121 let naming_consumed = parse_naming_option(args, index, options)?;
122 if naming_consumed > 0 {
123 return Ok(naming_consumed);
124 }
125
126 for (name, apply) in FLAG_OPTIONS {
127 if arg == name {
128 apply(options);
129 return Ok(1);
130 }
131 }
132
133 for (name, apply) in IDENTIFIER_OPTIONS {
134 if arg == name {
135 if index + 1 < args.len() && !args[index + 1].starts_with('-') {
136 apply(options, args[index + 1].clone());
137 return Ok(2);
138 }
139 return Err(format!(
140 "Option {} requires a UUID or session name argument",
141 arg
142 ));
143 }
144
145 let prefix = format!("{}=", name);
146 if let Some(value) = arg.strip_prefix(&prefix) {
147 if value.is_empty() {
148 return Err(format!(
149 "Option {} requires a UUID or session name argument",
150 name
151 ));
152 }
153 apply(options, value.to_string());
154 return Ok(1);
155 }
156 }
157
158 if arg == "--output-format" {
159 if index + 1 < args.len() && !args[index + 1].starts_with('-') {
160 options.output_format = Some(args[index + 1].to_lowercase());
161 return Ok(2);
162 }
163 return Err(format!("Option {} requires a format argument", arg));
164 }
165
166 if arg.starts_with("--output-format=") {
167 options.output_format = Some(arg.split('=').nth(1).unwrap_or("").to_lowercase());
168 return Ok(1);
169 }
170
171 Ok(0)
172}
173
174pub fn active_query_modes(options: &WrapperOptions) -> Vec<&'static str> {
176 let mut modes = Vec::new();
177 if options.status.is_some() {
178 modes.push("--status");
179 }
180 if options.list {
181 modes.push("--list");
182 }
183 if options.upload_log.is_some() {
184 modes.push("--upload-log");
185 }
186 if options.stop.is_some() {
187 modes.push("--stop");
188 }
189 if options.terminate.is_some() {
190 modes.push("--terminate");
191 }
192 if options.attach.is_some() {
193 modes.push("--attach");
194 }
195 if options.resume.is_some() {
196 modes.push("--resume");
197 }
198 if options.resume_all {
199 modes.push("--resume-all");
200 }
201 if options.cleanup {
202 modes.push("--cleanup");
203 }
204 if options.alias.is_some() {
205 modes.push("--alias");
206 }
207 if options.rename.is_some() {
208 modes.push("--rename");
209 }
210 modes
211}
212
213pub fn has_query_mode(options: &WrapperOptions) -> bool {
215 !active_query_modes(options).is_empty()
216}
217
218pub fn validate_query_options(options: &WrapperOptions) -> Result<(), String> {
220 if let Some(ref format) = options.output_format {
221 if !VALID_OUTPUT_FORMATS.contains(&format.as_str()) {
222 return Err(format!(
223 "Invalid output format: \"{}\". Valid options are: {}",
224 format,
225 VALID_OUTPUT_FORMATS.join(", ")
226 ));
227 }
228 }
229
230 let modes = active_query_modes(options);
231
232 if modes.len() > 1 {
233 return Err(
234 "Cannot combine --status, --list, --upload-log, --stop, --terminate, --attach, --resume, --resume-all, --cleanup, --alias, or --rename in the same invocation"
235 .to_string(),
236 );
237 }
238
239 if options.output_format.is_some()
240 && !modes.iter().any(|mode| OUTPUT_FORMAT_MODES.contains(mode))
241 {
242 return Err(
243 "--output-format option is only valid with --status, --list, --resume, or --resume-all"
244 .to_string(),
245 );
246 }
247
248 if options.running && !options.list {
249 return Err("--running option is only valid with --list".to_string());
250 }
251
252 if options.read_only && options.attach.is_none() {
253 return Err("--read-only option is only valid with --attach".to_string());
254 }
255
256 Ok(())
257}