Add support for multi-character flag aliases and enhance argument handling
- Introduced `MULTI_CHAR_ALIASES` for mapping long flag names to multi-character short aliases. - Updated argument normalization to preprocess multi-character short-form flags. - Refactored help output to include alias visibility and improve alignment. - Enhanced CLI flag definition capabilities with better support for optional boolean arguments.
This commit is contained in:
@@ -71,12 +71,12 @@ pub struct EditArgs {
|
||||
pub name: String,
|
||||
|
||||
/// Enable or disable the self-monitor loopback.
|
||||
#[arg(short, long, value_name = "BOOL")]
|
||||
#[arg(short, long, num_args = 0..=1, default_missing_value = "true", value_name = "BOOL")]
|
||||
pub loopback: Option<bool>,
|
||||
|
||||
/// Keep a mixed-in source audible in the loopback instead of isolating
|
||||
/// it (2-node topology instead of 4).
|
||||
#[arg(long = "loopback-no-mix", value_name = "BOOL")]
|
||||
#[arg(long = "loopback-no-mix", num_args = 0..=1, default_missing_value = "true", value_name = "BOOL")]
|
||||
pub loopback_no_mix: Option<bool>,
|
||||
|
||||
/// Loopback volume: fraction (0.8) or percent (80).
|
||||
@@ -84,7 +84,7 @@ pub struct EditArgs {
|
||||
pub volume: Option<f32>,
|
||||
|
||||
/// Mixed-in source volume: fraction (0.8) or percent (80).
|
||||
#[arg(long = "source-volume", visible_alias = "sv", value_name = "PCT")]
|
||||
#[arg(long = "source-volume", value_name = "PCT")]
|
||||
pub source_volume: Option<f32>,
|
||||
}
|
||||
|
||||
|
||||
58
src/main.rs
58
src/main.rs
@@ -44,12 +44,22 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
/// clap's `#[arg(short)]` only supports single-character short flags, so
|
||||
/// `-sv` can't be a real short flag. Rewrite it to `--source-volume` before
|
||||
/// clap ever sees it.
|
||||
/// `(long name, multi-char alias)` pairs for flags whose short form is more
|
||||
/// than one character - clap's `#[arg(short)]` only supports single
|
||||
/// characters, so these can't be real clap short flags. Rewritten to their
|
||||
/// long form before clap ever sees them (`normalize_args`), and shown
|
||||
/// alongside the long form in the top-level help (`flag_names`) so both
|
||||
/// places stay in sync from one definition.
|
||||
const MULTI_CHAR_ALIASES: &[(&str, &str)] = &[("source-volume", "sv")];
|
||||
|
||||
fn normalize_args(args: Vec<String>) -> Vec<String> {
|
||||
args.into_iter()
|
||||
.map(|a| if a == "-sv" { "--source-volume".to_string() } else { a })
|
||||
.map(|a| {
|
||||
a.strip_prefix('-')
|
||||
.and_then(|rest| MULTI_CHAR_ALIASES.iter().find(|(_, alias)| *alias == rest))
|
||||
.map(|(long, _)| format!("--{long}"))
|
||||
.unwrap_or(a)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -100,13 +110,8 @@ fn print_full_help() {
|
||||
let name = s.get_name();
|
||||
let positionals = positional_args(s);
|
||||
let about = s.get_about().map(|a| a.to_string()).unwrap_or_default();
|
||||
let aliases = s.get_visible_aliases().collect::<Vec<_>>().join("/");
|
||||
|
||||
print!(" {name:name_w$} {} {about}", pad_visual(&positionals, pos_w));
|
||||
if !aliases.is_empty() {
|
||||
print!(" | {aliases}");
|
||||
}
|
||||
println!();
|
||||
println!(" {name:name_w$} {} {about}", pad_visual(&positionals, pos_w));
|
||||
|
||||
let rows = &flag_rows_by_cmd[i];
|
||||
for (flag, help) in rows {
|
||||
@@ -120,6 +125,22 @@ fn print_full_help() {
|
||||
}
|
||||
println!();
|
||||
|
||||
let alias_rows: Vec<(String, String)> = subcommands
|
||||
.iter()
|
||||
.filter_map(|s| {
|
||||
let aliases = s.get_visible_aliases().collect::<Vec<_>>().join(", ");
|
||||
(!aliases.is_empty()).then(|| (s.get_name().to_string(), aliases))
|
||||
})
|
||||
.collect();
|
||||
if !alias_rows.is_empty() {
|
||||
println!("{}", ui::blue("Aliases:"));
|
||||
let name_w = alias_rows.iter().map(|(n, _)| n.len()).max().unwrap_or(0);
|
||||
for (name, aliases) in &alias_rows {
|
||||
println!(" {name:name_w$} {aliases}");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
println!("{}", ui::blue("Options:"));
|
||||
let opt_rows: Vec<(String, String)> = cmd
|
||||
.get_arguments()
|
||||
@@ -167,13 +188,18 @@ fn flag_rows(cmd: &clap::Command) -> Vec<(String, String)> {
|
||||
}
|
||||
|
||||
/// `-x/--long` / `-x` / `--long` for a non-positional arg, or `None` for
|
||||
/// one with no visible flag at all.
|
||||
/// one with no visible flag at all. A long flag with an entry in
|
||||
/// `MULTI_CHAR_ALIASES` shows that alias in the short-flag position (e.g.
|
||||
/// `-sv/--source-volume`), since it works the same way in practice.
|
||||
fn flag_names(arg: &clap::Arg) -> Option<String> {
|
||||
match (arg.get_short(), arg.get_long()) {
|
||||
(Some(s), Some(l)) => Some(format!("-{s}/--{l}")),
|
||||
(Some(s), None) => Some(format!("-{s}")),
|
||||
(None, Some(l)) => Some(format!("--{l}")),
|
||||
(None, None) => None,
|
||||
let long = arg.get_long();
|
||||
let multi_char_alias = long.and_then(|l| MULTI_CHAR_ALIASES.iter().find(|(name, _)| *name == l)).map(|(_, a)| *a);
|
||||
match (arg.get_short(), long, multi_char_alias) {
|
||||
(Some(s), Some(l), _) => Some(format!("-{s}/--{l}")),
|
||||
(Some(s), None, _) => Some(format!("-{s}")),
|
||||
(None, Some(l), Some(a)) => Some(format!("-{a}/--{l}")),
|
||||
(None, Some(l), None) => Some(format!("--{l}")),
|
||||
(None, None, _) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user