use crate::tenancy::error::TenancyError;
pub(super) fn next_value<'a, I: Iterator<Item = &'a String>>(
iter: &mut I,
flag: &str,
) -> Result<String, TenancyError> {
iter.next()
.cloned()
.ok_or_else(|| TenancyError::Validation(format!("{flag} requires a value")))
}
pub(super) fn no_args(args: &[String], verb: &str) -> Result<(), TenancyError> {
let spec = Spec {
verb,
usage: verb,
switches: &[],
valued: &[],
max_positionals: 0,
};
parse(args, &spec).map(|_| ()).map_err(Into::into)
}
pub(super) use crate::manage_interactive::{parse, Parsed, Spec};
#[cfg(feature = "postgres")]
pub(super) fn quote_ident(name: &str) -> String {
crate::sql::quote_with(name, '"')
}
pub(super) fn reject_leading_flag(
args: &[String],
verb: &str,
arg_name: &str,
usage: &str,
) -> Result<(), TenancyError> {
let Some(first) = args.first() else {
return Ok(());
};
if !first.starts_with('-') {
return Ok(());
}
if first == "--help" || first == "-h" {
return Err(TenancyError::Validation(usage.to_owned()));
}
Err(TenancyError::Validation(format!(
"{verb}: expected positional <{arg_name}> first, got flag `{first}`"
)))
}
#[cfg(test)]
mod tests {
use super::*;
const SPEC: Spec<'static> = Spec {
verb: "v",
usage: "v <a> [--on] [--k <x>]",
switches: &["--on"],
valued: &["--k"],
max_positionals: 1,
};
fn argv(a: &[&str]) -> Vec<String> {
a.iter().map(|s| (*s).to_owned()).collect()
}
#[test]
fn parse_takes_flag_values_out_of_the_positionals() {
let p = parse(&argv(&["--k", "false", "acme", "--on"]), &SPEC).expect("ok");
assert_eq!(p.positional(0).map(String::as_str), Some("acme"));
assert!(p.has("--on"));
assert_eq!(p.value("--k").unwrap(), Some("false"));
}
#[test]
fn parse_refuses_unknown_flags_extra_positionals_and_help() {
for bad in [&["--rol"][..], &["a", "b"], &["--help"], &["-x"]] {
assert!(parse(&argv(bad), &SPEC).is_err(), "{bad:?}");
}
assert!(parse(&argv(&["--k", "1", "--k", "2"]), &SPEC)
.unwrap()
.value("--k")
.is_err());
}
#[test]
fn reject_leading_flag_passes_normal_positional() {
let args = vec!["acme".to_owned()];
assert!(reject_leading_flag(&args, "create-tenant", "slug", "USAGE").is_ok());
}
#[test]
fn reject_leading_flag_passes_empty() {
assert!(reject_leading_flag(&[], "create-tenant", "slug", "USAGE").is_ok());
}
#[test]
fn reject_leading_flag_emits_help_for_help_flag() {
let args = vec!["--help".to_owned()];
let err =
reject_leading_flag(&args, "create-tenant", "slug", "USAGE\nFLAGS\n").unwrap_err();
let msg = err.to_string();
assert!(msg.contains("USAGE"), "expected usage in `{msg}`");
}
#[test]
fn reject_leading_flag_emits_help_for_short_help_flag() {
let args = vec!["-h".to_owned()];
assert!(reject_leading_flag(&args, "create-tenant", "slug", "USAGE").is_err());
}
#[test]
fn reject_leading_flag_rejects_unknown_flag_with_clear_message() {
let args = vec!["--mode".to_owned(), "schema".to_owned()];
let err = reject_leading_flag(&args, "create-tenant", "slug", "USAGE").unwrap_err();
let msg = err.to_string();
assert!(msg.contains("expected positional <slug>"), "{msg}");
assert!(msg.contains("--mode"), "{msg}");
}
}