use crate::args_parser::{WrapperOptions, VALID_OUTPUT_FORMATS};
type IdentifierSetter = fn(&mut WrapperOptions, String);
type FlagSetter = fn(&mut WrapperOptions);
const IDENTIFIER_OPTIONS: [(&str, IdentifierSetter); 6] = [
("--status", |o, v| o.status = Some(v)),
("--upload-log", |o, v| o.upload_log = Some(v)),
("--stop", |o, v| o.stop = Some(v)),
("--terminate", |o, v| o.terminate = Some(v)),
("--attach", |o, v| o.attach = Some(v)),
("--resume", |o, v| o.resume = Some(v)),
];
const FLAG_OPTIONS: [(&str, FlagSetter); 4] = [
("--list", |o| o.list = true),
("--running", |o| o.running = true),
("--read-only", |o| o.read_only = true),
("--resume-all", |o| o.resume_all = true),
];
fn parse_cleanup_option(
args: &[String],
index: usize,
options: &mut WrapperOptions,
) -> Result<usize, String> {
let arg = args[index].as_str();
let (name, inline) = match arg.split_once('=') {
Some((name, value)) => (name, Some(value)),
None => (arg, None),
};
if name != "--cleanup" && name != "--cleanup-dry-run" {
return Ok(0);
}
options.cleanup = true;
options.cleanup_dry_run |= name == "--cleanup-dry-run";
if let Some(value) = inline {
if value.is_empty() {
return Err(format!(
"Option {} requires a UUID or session name argument",
name
));
}
options.cleanup_target = Some(value.to_string());
return Ok(1);
}
match args.get(index + 1).filter(|next| !next.starts_with('-')) {
Some(next) => {
options.cleanup_target = Some(next.clone());
Ok(2)
}
None => Ok(1),
}
}
fn parse_naming_option(
args: &[String],
index: usize,
options: &mut WrapperOptions,
) -> Result<usize, String> {
let arg = args[index].as_str();
if arg != "--alias" && arg != "--rename" {
return Ok(0);
}
match (args.get(index + 1), args.get(index + 2)) {
(Some(identifier), Some(name))
if !identifier.starts_with('-') && !name.starts_with('-') =>
{
if arg == "--alias" {
options.alias = Some(identifier.clone());
} else {
options.rename = Some(identifier.clone());
}
options.new_name = Some(name.clone());
Ok(3)
}
_ => Err(format!(
"Option {} requires a UUID or session name and a new name",
arg
)),
}
}
const OUTPUT_FORMAT_MODES: [&str; 4] = ["--status", "--list", "--resume", "--resume-all"];
pub fn parse_query_option(
args: &[String],
index: usize,
options: &mut WrapperOptions,
) -> Result<usize, String> {
let arg = &args[index];
let cleanup_consumed = parse_cleanup_option(args, index, options)?;
if cleanup_consumed > 0 {
return Ok(cleanup_consumed);
}
let naming_consumed = parse_naming_option(args, index, options)?;
if naming_consumed > 0 {
return Ok(naming_consumed);
}
for (name, apply) in FLAG_OPTIONS {
if arg == name {
apply(options);
return Ok(1);
}
}
for (name, apply) in IDENTIFIER_OPTIONS {
if arg == name {
if index + 1 < args.len() && !args[index + 1].starts_with('-') {
apply(options, args[index + 1].clone());
return Ok(2);
}
return Err(format!(
"Option {} requires a UUID or session name argument",
arg
));
}
let prefix = format!("{}=", name);
if let Some(value) = arg.strip_prefix(&prefix) {
if value.is_empty() {
return Err(format!(
"Option {} requires a UUID or session name argument",
name
));
}
apply(options, value.to_string());
return Ok(1);
}
}
if arg == "--output-format" {
if index + 1 < args.len() && !args[index + 1].starts_with('-') {
options.output_format = Some(args[index + 1].to_lowercase());
return Ok(2);
}
return Err(format!("Option {} requires a format argument", arg));
}
if arg.starts_with("--output-format=") {
options.output_format = Some(arg.split('=').nth(1).unwrap_or("").to_lowercase());
return Ok(1);
}
Ok(0)
}
pub fn active_query_modes(options: &WrapperOptions) -> Vec<&'static str> {
let mut modes = Vec::new();
if options.status.is_some() {
modes.push("--status");
}
if options.list {
modes.push("--list");
}
if options.upload_log.is_some() {
modes.push("--upload-log");
}
if options.stop.is_some() {
modes.push("--stop");
}
if options.terminate.is_some() {
modes.push("--terminate");
}
if options.attach.is_some() {
modes.push("--attach");
}
if options.resume.is_some() {
modes.push("--resume");
}
if options.resume_all {
modes.push("--resume-all");
}
if options.cleanup {
modes.push("--cleanup");
}
if options.alias.is_some() {
modes.push("--alias");
}
if options.rename.is_some() {
modes.push("--rename");
}
modes
}
pub fn has_query_mode(options: &WrapperOptions) -> bool {
!active_query_modes(options).is_empty()
}
pub fn validate_query_options(options: &WrapperOptions) -> Result<(), String> {
if let Some(ref format) = options.output_format {
if !VALID_OUTPUT_FORMATS.contains(&format.as_str()) {
return Err(format!(
"Invalid output format: \"{}\". Valid options are: {}",
format,
VALID_OUTPUT_FORMATS.join(", ")
));
}
}
let modes = active_query_modes(options);
if modes.len() > 1 {
return Err(
"Cannot combine --status, --list, --upload-log, --stop, --terminate, --attach, --resume, --resume-all, --cleanup, --alias, or --rename in the same invocation"
.to_string(),
);
}
if options.output_format.is_some()
&& !modes.iter().any(|mode| OUTPUT_FORMAT_MODES.contains(mode))
{
return Err(
"--output-format option is only valid with --status, --list, --resume, or --resume-all"
.to_string(),
);
}
if options.running && !options.list {
return Err("--running option is only valid with --list".to_string());
}
if options.read_only && options.attach.is_none() {
return Err("--read-only option is only valid with --attach".to_string());
}
Ok(())
}