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,
|
pub name: String,
|
||||||
|
|
||||||
/// Enable or disable the self-monitor loopback.
|
/// 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>,
|
pub loopback: Option<bool>,
|
||||||
|
|
||||||
/// Keep a mixed-in source audible in the loopback instead of isolating
|
/// Keep a mixed-in source audible in the loopback instead of isolating
|
||||||
/// it (2-node topology instead of 4).
|
/// 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>,
|
pub loopback_no_mix: Option<bool>,
|
||||||
|
|
||||||
/// Loopback volume: fraction (0.8) or percent (80).
|
/// Loopback volume: fraction (0.8) or percent (80).
|
||||||
@@ -84,7 +84,7 @@ pub struct EditArgs {
|
|||||||
pub volume: Option<f32>,
|
pub volume: Option<f32>,
|
||||||
|
|
||||||
/// Mixed-in source volume: fraction (0.8) or percent (80).
|
/// 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>,
|
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
|
/// `(long name, multi-char alias)` pairs for flags whose short form is more
|
||||||
/// `-sv` can't be a real short flag. Rewrite it to `--source-volume` before
|
/// than one character - clap's `#[arg(short)]` only supports single
|
||||||
/// clap ever sees it.
|
/// 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> {
|
fn normalize_args(args: Vec<String>) -> Vec<String> {
|
||||||
args.into_iter()
|
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()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,13 +110,8 @@ fn print_full_help() {
|
|||||||
let name = s.get_name();
|
let name = s.get_name();
|
||||||
let positionals = positional_args(s);
|
let positionals = positional_args(s);
|
||||||
let about = s.get_about().map(|a| a.to_string()).unwrap_or_default();
|
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));
|
println!(" {name:name_w$} {} {about}", pad_visual(&positionals, pos_w));
|
||||||
if !aliases.is_empty() {
|
|
||||||
print!(" | {aliases}");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let rows = &flag_rows_by_cmd[i];
|
let rows = &flag_rows_by_cmd[i];
|
||||||
for (flag, help) in rows {
|
for (flag, help) in rows {
|
||||||
@@ -120,6 +125,22 @@ fn print_full_help() {
|
|||||||
}
|
}
|
||||||
println!();
|
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:"));
|
println!("{}", ui::blue("Options:"));
|
||||||
let opt_rows: Vec<(String, String)> = cmd
|
let opt_rows: Vec<(String, String)> = cmd
|
||||||
.get_arguments()
|
.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
|
/// `-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> {
|
fn flag_names(arg: &clap::Arg) -> Option<String> {
|
||||||
match (arg.get_short(), arg.get_long()) {
|
let long = arg.get_long();
|
||||||
(Some(s), Some(l)) => Some(format!("-{s}/--{l}")),
|
let multi_char_alias = long.and_then(|l| MULTI_CHAR_ALIASES.iter().find(|(name, _)| *name == l)).map(|(_, a)| *a);
|
||||||
(Some(s), None) => Some(format!("-{s}")),
|
match (arg.get_short(), long, multi_char_alias) {
|
||||||
(None, Some(l)) => Some(format!("--{l}")),
|
(Some(s), Some(l), _) => Some(format!("-{s}/--{l}")),
|
||||||
(None, None) => None,
|
(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