mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 14:22:34 +00:00
Merge pull request #4792 from epage/defer
feat: Allow deferred initialization of subcommands
This commit is contained in:
commit
13534b67d0
4 changed files with 68 additions and 26 deletions
|
@ -105,6 +105,7 @@ pub struct Command {
|
||||||
subcommand_heading: Option<Str>,
|
subcommand_heading: Option<Str>,
|
||||||
external_value_parser: Option<super::ValueParser>,
|
external_value_parser: Option<super::ValueParser>,
|
||||||
long_help_exists: bool,
|
long_help_exists: bool,
|
||||||
|
deferred: Option<fn(Command) -> Command>,
|
||||||
app_ext: Extensions,
|
app_ext: Extensions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,6 +429,30 @@ impl Command {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delay initialization for parts of the `Command`
|
||||||
|
///
|
||||||
|
/// This is useful for large applications to delay definitions of subcommands until they are
|
||||||
|
/// being invoked.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use clap_builder as clap;
|
||||||
|
/// # use clap::{Command, arg};
|
||||||
|
/// Command::new("myprog")
|
||||||
|
/// .subcommand(Command::new("config")
|
||||||
|
/// .about("Controls configuration features")
|
||||||
|
/// .defer(|cmd| {
|
||||||
|
/// cmd.arg(arg!(<config> "Required configuration file to use"))
|
||||||
|
/// })
|
||||||
|
/// )
|
||||||
|
/// # ;
|
||||||
|
/// ```
|
||||||
|
pub fn defer(mut self, deferred: fn(Command) -> Command) -> Self {
|
||||||
|
self.deferred = Some(deferred);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Catch problems earlier in the development cycle.
|
/// Catch problems earlier in the development cycle.
|
||||||
///
|
///
|
||||||
/// Most error states are handled as asserts under the assumption they are programming mistake
|
/// Most error states are handled as asserts under the assumption they are programming mistake
|
||||||
|
@ -3824,6 +3849,10 @@ impl Command {
|
||||||
pub(crate) fn _build_self(&mut self, expand_help_tree: bool) {
|
pub(crate) fn _build_self(&mut self, expand_help_tree: bool) {
|
||||||
debug!("Command::_build: name={:?}", self.get_name());
|
debug!("Command::_build: name={:?}", self.get_name());
|
||||||
if !self.settings.is_set(AppSettings::Built) {
|
if !self.settings.is_set(AppSettings::Built) {
|
||||||
|
if let Some(deferred) = self.deferred.take() {
|
||||||
|
*self = (deferred)(std::mem::take(self));
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure all the globally set flags apply to us as well
|
// Make sure all the globally set flags apply to us as well
|
||||||
self.settings = self.settings | self.g_settings;
|
self.settings = self.settings | self.g_settings;
|
||||||
|
|
||||||
|
@ -4652,6 +4681,7 @@ impl Default for Command {
|
||||||
subcommand_heading: Default::default(),
|
subcommand_heading: Default::default(),
|
||||||
external_value_parser: Default::default(),
|
external_value_parser: Default::default(),
|
||||||
long_help_exists: false,
|
long_help_exists: false,
|
||||||
|
deferred: None,
|
||||||
app_ext: Default::default(),
|
app_ext: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ fn get_app() -> Command {
|
||||||
.global(true)
|
.global(true)
|
||||||
.action(ArgAction::Count),
|
.action(ArgAction::Count),
|
||||||
)
|
)
|
||||||
.subcommand(Command::new("outer").subcommand(Command::new("inner")))
|
.subcommand(Command::new("outer").defer(|cmd| cmd.subcommand(Command::new("inner"))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_matches(cmd: Command, argv: &'static str) -> ArgMatches {
|
fn get_matches(cmd: Command, argv: &'static str) -> ArgMatches {
|
||||||
|
|
|
@ -5,15 +5,15 @@ use super::utils;
|
||||||
#[test]
|
#[test]
|
||||||
fn subcommand() {
|
fn subcommand() {
|
||||||
let m = Command::new("test")
|
let m = Command::new("test")
|
||||||
.subcommand(
|
.subcommand(Command::new("some").defer(|cmd| {
|
||||||
Command::new("some").arg(
|
cmd.arg(
|
||||||
Arg::new("test")
|
Arg::new("test")
|
||||||
.short('t')
|
.short('t')
|
||||||
.long("test")
|
.long("test")
|
||||||
.action(ArgAction::Set)
|
.action(ArgAction::Set)
|
||||||
.help("testing testing"),
|
.help("testing testing"),
|
||||||
),
|
)
|
||||||
)
|
}))
|
||||||
.arg(Arg::new("other").long("other"))
|
.arg(Arg::new("other").long("other"))
|
||||||
.try_get_matches_from(vec!["myprog", "some", "--test", "testing"])
|
.try_get_matches_from(vec!["myprog", "some", "--test", "testing"])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -30,15 +30,15 @@ fn subcommand() {
|
||||||
#[test]
|
#[test]
|
||||||
fn subcommand_none_given() {
|
fn subcommand_none_given() {
|
||||||
let m = Command::new("test")
|
let m = Command::new("test")
|
||||||
.subcommand(
|
.subcommand(Command::new("some").defer(|cmd| {
|
||||||
Command::new("some").arg(
|
cmd.arg(
|
||||||
Arg::new("test")
|
Arg::new("test")
|
||||||
.short('t')
|
.short('t')
|
||||||
.long("test")
|
.long("test")
|
||||||
.action(ArgAction::Set)
|
.action(ArgAction::Set)
|
||||||
.help("testing testing"),
|
.help("testing testing"),
|
||||||
),
|
)
|
||||||
)
|
}))
|
||||||
.arg(Arg::new("other").long("other"))
|
.arg(Arg::new("other").long("other"))
|
||||||
.try_get_matches_from(vec![""])
|
.try_get_matches_from(vec![""])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -50,14 +50,16 @@ fn subcommand_none_given() {
|
||||||
fn subcommand_multiple() {
|
fn subcommand_multiple() {
|
||||||
let m = Command::new("test")
|
let m = Command::new("test")
|
||||||
.subcommands(vec![
|
.subcommands(vec![
|
||||||
Command::new("some").arg(
|
Command::new("some").defer(|cmd| {
|
||||||
Arg::new("test")
|
cmd.arg(
|
||||||
.short('t')
|
Arg::new("test")
|
||||||
.long("test")
|
.short('t')
|
||||||
.action(ArgAction::Set)
|
.long("test")
|
||||||
.help("testing testing"),
|
.action(ArgAction::Set)
|
||||||
),
|
.help("testing testing"),
|
||||||
Command::new("add").arg(Arg::new("roster").short('r')),
|
)
|
||||||
|
}),
|
||||||
|
Command::new("add").defer(|cmd| cmd.arg(Arg::new("roster").short('r'))),
|
||||||
])
|
])
|
||||||
.arg(Arg::new("other").long("other"))
|
.arg(Arg::new("other").long("other"))
|
||||||
.try_get_matches_from(vec!["myprog", "some", "--test", "testing"])
|
.try_get_matches_from(vec!["myprog", "some", "--test", "testing"])
|
||||||
|
@ -148,8 +150,9 @@ Usage: dym [COMMAND]
|
||||||
For more information, try '--help'.
|
For more information, try '--help'.
|
||||||
";
|
";
|
||||||
|
|
||||||
let cmd = Command::new("dym")
|
let cmd = Command::new("dym").subcommand(
|
||||||
.subcommand(Command::new("subcmd").arg(arg!(-s --subcmdarg <subcmdarg> "tests")));
|
Command::new("subcmd").defer(|cmd| cmd.arg(arg!(-s --subcmdarg <subcmdarg> "tests"))),
|
||||||
|
);
|
||||||
|
|
||||||
utils::assert_output(cmd, "dym --subcmarg subcmd", EXPECTED, true);
|
utils::assert_output(cmd, "dym --subcmarg subcmd", EXPECTED, true);
|
||||||
}
|
}
|
||||||
|
@ -166,8 +169,9 @@ Usage: dym [COMMAND]
|
||||||
For more information, try '--help'.
|
For more information, try '--help'.
|
||||||
";
|
";
|
||||||
|
|
||||||
let cmd = Command::new("dym")
|
let cmd = Command::new("dym").subcommand(
|
||||||
.subcommand(Command::new("subcmd").arg(arg!(-s --subcmdarg <subcmdarg> "tests")));
|
Command::new("subcmd").defer(|cmd| cmd.arg(arg!(-s --subcmdarg <subcmdarg> "tests"))),
|
||||||
|
);
|
||||||
|
|
||||||
utils::assert_output(cmd, "dym --subcmarg foo", EXPECTED, true);
|
utils::assert_output(cmd, "dym --subcmarg foo", EXPECTED, true);
|
||||||
}
|
}
|
||||||
|
@ -427,7 +431,7 @@ fn busybox_like_multicall() {
|
||||||
}
|
}
|
||||||
let cmd = Command::new("busybox")
|
let cmd = Command::new("busybox")
|
||||||
.multicall(true)
|
.multicall(true)
|
||||||
.subcommand(Command::new("busybox").subcommands(applet_commands()))
|
.subcommand(Command::new("busybox").defer(|cmd| cmd.subcommands(applet_commands())))
|
||||||
.subcommands(applet_commands());
|
.subcommands(applet_commands());
|
||||||
|
|
||||||
let m = cmd
|
let m = cmd
|
||||||
|
@ -553,7 +557,9 @@ Options:
|
||||||
.version("1.0.0")
|
.version("1.0.0")
|
||||||
.propagate_version(true)
|
.propagate_version(true)
|
||||||
.multicall(true)
|
.multicall(true)
|
||||||
.subcommand(Command::new("foo").subcommand(Command::new("bar").arg(Arg::new("value"))));
|
.subcommand(Command::new("foo").defer(|cmd| {
|
||||||
|
cmd.subcommand(Command::new("bar").defer(|cmd| cmd.arg(Arg::new("value"))))
|
||||||
|
}));
|
||||||
utils::assert_output(cmd, "foo bar --help", EXPECTED, false);
|
utils::assert_output(cmd, "foo bar --help", EXPECTED, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,7 +579,10 @@ Options:
|
||||||
.version("1.0.0")
|
.version("1.0.0")
|
||||||
.propagate_version(true)
|
.propagate_version(true)
|
||||||
.multicall(true)
|
.multicall(true)
|
||||||
.subcommand(Command::new("foo").subcommand(Command::new("bar").arg(Arg::new("value"))));
|
.subcommand(
|
||||||
|
Command::new("foo")
|
||||||
|
.defer(|cmd| cmd.subcommand(Command::new("bar").arg(Arg::new("value")))),
|
||||||
|
);
|
||||||
utils::assert_output(cmd, "help foo bar", EXPECTED, false);
|
utils::assert_output(cmd, "help foo bar", EXPECTED, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,7 +602,10 @@ Options:
|
||||||
.version("1.0.0")
|
.version("1.0.0")
|
||||||
.propagate_version(true)
|
.propagate_version(true)
|
||||||
.multicall(true)
|
.multicall(true)
|
||||||
.subcommand(Command::new("foo").subcommand(Command::new("bar").arg(Arg::new("value"))));
|
.subcommand(
|
||||||
|
Command::new("foo")
|
||||||
|
.defer(|cmd| cmd.subcommand(Command::new("bar").arg(Arg::new("value")))),
|
||||||
|
);
|
||||||
cmd.build();
|
cmd.build();
|
||||||
let subcmd = cmd.find_subcommand_mut("foo").unwrap();
|
let subcmd = cmd.find_subcommand_mut("foo").unwrap();
|
||||||
let subcmd = subcmd.find_subcommand_mut("bar").unwrap();
|
let subcmd = subcmd.find_subcommand_mut("bar").unwrap();
|
||||||
|
|
|
@ -19,7 +19,7 @@ fn with_both() -> Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_subcommand() -> Command {
|
fn with_subcommand() -> Command {
|
||||||
with_version().subcommand(Command::new("bar").subcommand(Command::new("baz")))
|
with_version().subcommand(Command::new("bar").defer(|cmd| cmd.subcommand(Command::new("baz"))))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue