feat(multicall): Stablize multicall

`multicall` allows you to have one binary expose itself as multiple
programs, like busybox does.  This also works well for user clap for
parsing REPLs.

Fixes #2861
This commit is contained in:
Ed Page 2022-05-03 16:51:36 -05:00
parent 1a2b7acd60
commit 686b0379ce
12 changed files with 15 additions and 55 deletions

View file

@ -64,7 +64,7 @@ default = [
"suggestions",
]
debug = ["clap_derive/debug", "backtrace"] # Enables debug messages
unstable-doc = ["derive", "cargo", "wrap_help", "yaml", "env", "unicode", "regex", "unstable-replace", "unstable-multicall", "unstable-grouped"] # for docs.rs
unstable-doc = ["derive", "cargo", "wrap_help", "yaml", "env", "unicode", "regex", "unstable-replace", "unstable-grouped"] # for docs.rs
# Used in default
std = ["indexmap/std"] # support for no_std in a backwards-compatible way
@ -81,7 +81,6 @@ unicode = ["textwrap/unicode-width", "unicase"] # Support for unicode character
# In-work features
unstable-replace = []
unstable-multicall = []
unstable-grouped = []
# note: this will always enable clap_derive, change this to `clap_derive?/unstable-v4` when MSRV is bigger than 1.60
unstable-v4 = ["clap_derive/unstable-v4"]
@ -178,17 +177,14 @@ required-features = ["derive"]
[[example]]
name = "busybox"
path = "examples/multicall-busybox.rs"
required-features = ["unstable-multicall"]
[[example]]
name = "hostname"
path = "examples/multicall-hostname.rs"
required-features = ["unstable-multicall"]
[[example]]
name = "repl"
path = "examples/repl.rs"
required-features = ["unstable-multicall"]
[[example]]
name = "01_quick"

View file

@ -15,8 +15,8 @@ MSRV?=1.56.0
_FEATURES = minimal default wasm full debug release
_FEATURES_minimal = --no-default-features --features "std"
_FEATURES_default =
_FEATURES_wasm = --features "derive cargo env unicode yaml regex unstable-replace unstable-multicall unstable-grouped"
_FEATURES_full = --features "derive cargo env unicode yaml regex unstable-replace unstable-multicall unstable-grouped wrap_help"
_FEATURES_wasm = --features "derive cargo env unicode yaml regex unstable-replace unstable-grouped"
_FEATURES_full = --features "derive cargo env unicode yaml regex unstable-replace unstable-grouped wrap_help"
_FEATURES_next = ${_FEATURES_full} --features unstable-v4
_FEATURES_debug = ${_FEATURES_full} --features debug
_FEATURES_release = ${_FEATURES_full} --release

View file

@ -152,7 +152,6 @@ Why use the procedural [Builder API](https://github.com/clap-rs/clap/blob/v3.1.1
**Warning:** These may contain breaking changes between minor releases.
* **unstable-replace**: Enable [`Command::replace`](https://github.com/clap-rs/clap/issues/2836)
* **unstable-multicall**: Enable [`Command::multicall`](https://github.com/clap-rs/clap/issues/2861)
* **unstable-grouped**: Enable [`ArgMatches::grouped_values_of`](https://github.com/clap-rs/clap/issues/2924)
* **unstable-v4**: Preview features which will be stable on the v4.0 release

View file

@ -1,5 +1,3 @@
// Note: this requires the `unstable-multicall` feature
use std::process::exit;
use clap::{Arg, Command};

View file

@ -1,5 +1,3 @@
// Note: this requires the `unstable-multicall` feature
use clap::Command;
fn main() {

View file

@ -1,5 +1,3 @@
// Note: this requires the `unstable-multicall` feature
use std::io::Write;
use clap::Command;

View file

@ -153,7 +153,6 @@ pub enum AppSettings {
since = "3.1.0",
note = "Replaced with `Command::multicall` and `Command::is_multicall_set`"
)]
#[cfg(feature = "unstable-multicall")]
Multicall,
/// Deprecated, replaced with [`Command::allow_invalid_utf8_for_external_subcommands`] and [`Command::is_allow_invalid_utf8_for_external_subcommands_set`]
@ -466,7 +465,6 @@ bitflags! {
const USE_LONG_FORMAT_FOR_HELP_SC = 1 << 42;
const INFER_LONG_ARGS = 1 << 43;
const IGNORE_ERRORS = 1 << 44;
#[cfg(feature = "unstable-multicall")]
const MULTICALL = 1 << 45;
const NO_OP = 0;
}
@ -533,7 +531,6 @@ impl_settings! { AppSettings, AppFlags,
=> Flags::HELP_REQUIRED,
Hidden
=> Flags::HIDDEN,
#[cfg(feature = "unstable-multicall")]
Multicall
=> Flags::MULTICALL,
NoAutoHelp

View file

@ -637,7 +637,6 @@ impl<'help> App<'help> {
let mut raw_args = clap_lex::RawArgs::new(itr.into_iter());
let mut cursor = raw_args.cursor();
#[cfg(feature = "unstable-multicall")]
if self.settings.is_set(AppSettings::Multicall) {
if let Some(argv0) = raw_args.next_os(&mut cursor) {
let argv0 = Path::new(&argv0);
@ -3062,7 +3061,6 @@ impl<'help> App<'help> {
/// [`App::subcommand_value_name`]: crate::Command::subcommand_value_name
/// [`App::subcommand_help_heading`]: crate::Command::subcommand_help_heading
#[inline]
#[cfg(feature = "unstable-multicall")]
pub fn multicall(self, yes: bool) -> Self {
if yes {
self.setting(AppSettings::Multicall)
@ -3715,15 +3713,9 @@ impl<'help> App<'help> {
}
/// Report whether [`Command::multicall`] is set
#[cfg(feature = "unstable-multicall")]
pub fn is_multicall_set(&self) -> bool {
self.is_set(AppSettings::Multicall)
}
#[cfg(not(feature = "unstable-multicall"))]
fn is_multicall_set(&self) -> bool {
false
}
}
/// Deprecated
@ -4109,13 +4101,10 @@ impl<'help> App<'help> {
// Make sure all the globally set flags apply to us as well
self.settings = self.settings | self.g_settings;
#[cfg(feature = "unstable-multicall")]
{
if self.is_multicall_set() {
self.settings.insert(AppSettings::SubcommandRequired.into());
self.settings.insert(AppSettings::DisableHelpFlag.into());
self.settings.insert(AppSettings::DisableVersionFlag.into());
}
if self.is_multicall_set() {
self.settings.insert(AppSettings::SubcommandRequired.into());
self.settings.insert(AppSettings::DisableHelpFlag.into());
self.settings.insert(AppSettings::DisableVersionFlag.into());
}
self._propagate();
@ -4180,7 +4169,7 @@ impl<'help> App<'help> {
mid_string.push(' ');
}
}
let is_multicall_set = cfg!(feature = "unstable-multicall") && self.is_multicall_set();
let is_multicall_set = self.is_multicall_set();
let sc = self.subcommands.iter_mut().find(|s| s.name == name)?;
@ -4263,7 +4252,7 @@ impl<'help> App<'help> {
mid_string.push(' ');
}
}
let is_multicall_set = cfg!(feature = "unstable-multicall") && self.is_multicall_set();
let is_multicall_set = self.is_multicall_set();
let self_bin_name = if is_multicall_set {
self.bin_name.as_deref().unwrap_or("")

View file

@ -60,15 +60,12 @@ pub(crate) fn assert_app(cmd: &Command) {
for arg in cmd.get_arguments() {
assert_arg(arg);
#[cfg(feature = "unstable-multicall")]
{
assert!(
!cmd.is_multicall_set(),
"Command {}: Arguments like {} cannot be set on a multicall command",
cmd.get_name(),
arg.name
);
}
assert!(
!cmd.is_multicall_set(),
"Command {}: Arguments like {} cannot be set on a multicall command",
cmd.get_name(),
arg.name
);
if let Some(s) = arg.short.as_ref() {
short_flags.push(Flag::Arg(format!("-{}", s), &*arg.name));
@ -467,7 +464,6 @@ fn assert_app_flags(cmd: &Command) {
}
checker!(is_allow_invalid_utf8_for_external_subcommands_set requires is_allow_external_subcommands_set);
#[cfg(feature = "unstable-multicall")]
checker!(is_multicall_set conflicts is_no_binary_name_set);
}

View file

@ -494,7 +494,6 @@ For more information try --help
);
}
#[cfg(feature = "unstable-multicall")]
#[test]
fn busybox_like_multicall() {
fn applet_commands() -> [Command<'static>; 2] {
@ -520,7 +519,6 @@ fn busybox_like_multicall() {
assert_eq!(m.unwrap_err().kind(), ErrorKind::UnrecognizedSubcommand);
}
#[cfg(feature = "unstable-multicall")]
#[test]
fn hostname_like_multicall() {
let mut cmd = Command::new("hostname")
@ -550,7 +548,6 @@ fn hostname_like_multicall() {
assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument);
}
#[cfg(feature = "unstable-multicall")]
#[test]
fn bad_multicall_command_error() {
let cmd = Command::new("repl")
@ -606,7 +603,6 @@ For more information try help
assert_eq!(err.kind(), ErrorKind::DisplayVersion);
}
#[cfg(feature = "unstable-multicall")]
#[test]
#[should_panic = "Command repl: Arguments like oh-no cannot be set on a multicall command"]
fn cant_have_args_with_multicall() {
@ -620,7 +616,6 @@ fn cant_have_args_with_multicall() {
cmd.build();
}
#[cfg(feature = "unstable-multicall")]
#[test]
fn multicall_help_flag() {
static EXPECTED: &str = "\
@ -644,7 +639,6 @@ OPTIONS:
utils::assert_output(cmd, "foo bar --help", EXPECTED, false);
}
#[cfg(feature = "unstable-multicall")]
#[test]
fn multicall_help_subcommand() {
static EXPECTED: &str = "\
@ -668,7 +662,6 @@ OPTIONS:
utils::assert_output(cmd, "help foo bar", EXPECTED, false);
}
#[cfg(feature = "unstable-multicall")]
#[test]
fn multicall_render_help() {
static EXPECTED: &str = "\

View file

@ -22,8 +22,6 @@ fn example_tests() {
"wrap_help",
#[cfg(feature = "unstable-replace")]
"unstable-replace",
#[cfg(feature = "unstable-multicall")]
"unstable-multicall",
#[cfg(feature = "unstable-grouped")]
"unstable-grouped",
]

View file

@ -22,8 +22,6 @@ fn ui_tests() {
"wrap_help",
#[cfg(feature = "unstable-replace")]
"unstable-replace",
#[cfg(feature = "unstable-multicall")]
"unstable-multicall",
#[cfg(feature = "unstable-grouped")]
"unstable-grouped",
]