2100: Allow for nested subcommands with ZSH generation. r=pksunkara a=newAM



2106: Update the env var example for derive r=pksunkara a=rgreinho



Co-authored-by: Alex M <alexmgit@protonmail.com>
Co-authored-by: Rémy Greinhofer <remy.greinhofer@gmail.com>
This commit is contained in:
bors[bot] 2020-08-24 06:45:45 +00:00 committed by GitHub
commit 29df5c2d16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 146 additions and 10 deletions

View file

@ -1,7 +1,7 @@
//! How to use environment variable fallback an how it
//! interacts with `default_value`.
use clap::Clap;
use clap::{ArgSettings, Clap};
/// Example for allowing to specify options via environment variables.
#[derive(Clap, Debug)]
@ -18,6 +18,12 @@ struct Opt {
/// Number of retries
#[clap(long, env = "RETRIES", default_value = "5")]
retries: u32,
// If an environment variable contains a sensitive value, it can be hidden
// from the help screen with a special setting.
/// Secret token
#[clap(long, env = "SECRET_TOKEN", setting = ArgSettings::HideEnvValues)]
token: String,
}
fn main() {

View file

@ -119,7 +119,8 @@ _{bin_name_underscore}_commands() {{
}}",
bin_name_underscore = bin_name.replace(" ", "__"),
bin_name = bin_name,
subcommands_and_args = subcommands_of(parser_of(p, bin_name))
subcommands_and_args =
subcommands_of(parser_of(p, bin_name).expect(INTERNAL_ERROR_MSG))
));
}
@ -217,14 +218,20 @@ fn get_subcommands_of(p: &App) -> String {
let mut subcmds = vec![];
for &(ref name, ref bin_name) in &sc_names {
debug!(
"get_subcommands_of:iter: p={}, name={}, bin_name={}",
p.get_name(),
name,
bin_name,
);
let mut v = vec![format!("({})", name)];
let subcommand_args = get_args_of(parser_of(p, &*bin_name));
let subcommand_args = get_args_of(parser_of(p, &*bin_name).expect(INTERNAL_ERROR_MSG));
if !subcommand_args.is_empty() {
v.push(subcommand_args);
}
let subcommands = get_subcommands_of(parser_of(p, &*bin_name));
let subcommands = get_subcommands_of(parser_of(p, &*bin_name).expect(INTERNAL_ERROR_MSG));
if !subcommands.is_empty() {
v.push(subcommands);
@ -252,15 +259,24 @@ esac",
)
}
fn parser_of<'help, 'app>(p: &'app App<'help>, mut sc: &str) -> &'app App<'help> {
debug!("parser_of: sc={}", sc);
// Get the App for a given subcommand tree.
//
// Given the bin_name "a b c" and the App for "a" this returns the "c" App.
// Given the bin_name "a b c" and the App for "b" this returns the "c" App.
fn parser_of<'help, 'app>(p: &'app App<'help>, bin_name: &str) -> Option<&'app App<'help>> {
debug!("parser_of: p={}, bin_name={}", p.get_name(), bin_name);
if sc == p.get_bin_name().unwrap_or(&String::new()) {
return p;
if bin_name == p.get_bin_name().unwrap_or(&String::new()) {
return Some(p);
}
sc = sc.split(' ').last().unwrap();
p.find_subcommand(sc).expect(INTERNAL_ERROR_MSG)
for sc in p.get_subcommands() {
if let Some(ret) = parser_of(sc, bin_name) {
return Some(ret);
}
}
None
}
// Writes out the args section, which ends up being the flags, opts and positionals, and a jump to

View file

@ -712,6 +712,108 @@ _my_app_commands() {
_my_app "$@""#;
static ZSH_NESTED_SUBCOMMANDS: &str = r#"#compdef my_app
autoload -U is-at-least
_my_app() {
typeset -A opt_args
typeset -a _arguments_options
local ret=1
if is-at-least 5.2; then
_arguments_options=(-s -S -C)
else
_arguments_options=(-s -C)
fi
local context curcontext="$curcontext" state line
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
":: :_my_app_commands" \
"*::: :->first" \
&& ret=0
case $state in
(first)
words=($line[1] "${words[@]}")
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:my_app-command-$line[1]:"
case $line[1] in
(second)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
":: :_my_app__second_commands" \
"*::: :->second" \
&& ret=0
case $state in
(second)
words=($line[1] "${words[@]}")
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:my_app-second-command-$line[1]:"
case $line[1] in
(third)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
esac
;;
esac
;;
(help)
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
'-V[Prints version information]' \
'--version[Prints version information]' \
&& ret=0
;;
esac
;;
esac
}
(( $+functions[_my_app_commands] )) ||
_my_app_commands() {
local commands; commands=(
"second:" \
"help:Prints this message or the help of the given subcommand(s)" \
)
_describe -t commands 'my_app commands' commands "$@"
}
(( $+functions[_my_app__help_commands] )) ||
_my_app__help_commands() {
local commands; commands=(
)
_describe -t commands 'my_app help commands' commands "$@"
}
(( $+functions[_my_app__second_commands] )) ||
_my_app__second_commands() {
local commands; commands=(
"third:" \
)
_describe -t commands 'my_app second commands' commands "$@"
}
(( $+functions[_my_app__second__third_commands] )) ||
_my_app__second__third_commands() {
local commands; commands=(
)
_describe -t commands 'my_app second third commands' commands "$@"
}
_my_app "$@""#;
fn build_app() -> App<'static> {
build_app_with_name("myapp")
}
@ -777,6 +879,10 @@ fn build_app_special_help() -> App<'static> {
)
}
fn build_app_nested_subcommands() -> App<'static> {
App::new("first").subcommand(App::new("second").subcommand(App::new("third")))
}
pub fn common<G: Generator>(app: &mut App, name: &str, fixture: &str) {
let mut buf = vec![];
generate::<G, _>(app, name, &mut buf);
@ -856,3 +962,9 @@ fn zsh_with_special_help() {
let mut app = build_app_special_help();
common::<Zsh>(&mut app, "my_app", ZSH_SPECIAL_HELP);
}
#[test]
fn zsh_with_nested_subcommands() {
let mut app = build_app_nested_subcommands();
common::<Zsh>(&mut app, "my_app", ZSH_NESTED_SUBCOMMANDS);
}

View file

@ -263,6 +263,8 @@ impl<'help> App<'help> {
}
/// Find subcommand such that its name or one of aliases equals `name`.
///
/// This does not recurse through subcommands of subcommands.
#[inline]
pub fn find_subcommand<T>(&self, name: &T) -> Option<&App<'help>>
where