Skip to content

Commit

Permalink
feat: Stablize multicall
Browse files Browse the repository at this point in the history
`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 clap-rs#2861
  • Loading branch information
epage committed May 3, 2022
1 parent 48c6919 commit 8ba0f64
Show file tree
Hide file tree
Showing 12 changed files with 15 additions and 55 deletions.
6 changes: 1 addition & 5 deletions Cargo.toml
Expand Up @@ -63,7 +63,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
Expand All @@ -80,7 +80,6 @@ unicode = ["textwrap/unicode-width", "unicase"] # Support for unicode character

# In-work features
unstable-replace = []
unstable-multicall = []
unstable-grouped = []
unstable-v4 = []

Expand Down Expand Up @@ -176,17 +175,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"
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Expand Up @@ -15,8 +15,8 @@ MSRV?=1.54.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
Expand Down
1 change: 0 additions & 1 deletion README.md
Expand Up @@ -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

Expand Down
2 changes: 0 additions & 2 deletions examples/multicall-busybox.rs
@@ -1,5 +1,3 @@
// Note: this requires the `unstable-multicall` feature

use std::process::exit;

use clap::{Arg, Command};
Expand Down
2 changes: 0 additions & 2 deletions examples/multicall-hostname.rs
@@ -1,5 +1,3 @@
// Note: this requires the `unstable-multicall` feature

use clap::Command;

fn main() {
Expand Down
2 changes: 0 additions & 2 deletions examples/repl.rs
@@ -1,5 +1,3 @@
// Note: this requires the `unstable-multicall` feature

use std::io::Write;

use clap::Command;
Expand Down
3 changes: 0 additions & 3 deletions src/build/app_settings.rs
Expand Up @@ -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`]
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -533,7 +531,6 @@ impl_settings! { AppSettings, AppFlags,
=> Flags::HELP_REQUIRED,
Hidden
=> Flags::HIDDEN,
#[cfg(feature = "unstable-multicall")]
Multicall
=> Flags::MULTICALL,
NoAutoHelp
Expand Down
23 changes: 6 additions & 17 deletions src/build/command.rs
Expand Up @@ -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);
Expand Down Expand Up @@ -3049,7 +3048,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)
Expand Down Expand Up @@ -3681,15 +3679,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
Expand Down Expand Up @@ -4075,13 +4067,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();
Expand Down Expand Up @@ -4146,7 +4135,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)?;

Expand Down Expand Up @@ -4229,7 +4218,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("")
Expand Down
16 changes: 6 additions & 10 deletions src/build/debug_asserts.rs
Expand Up @@ -56,15 +56,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));
Expand Down Expand Up @@ -417,7 +414,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);
}

Expand Down
7 changes: 0 additions & 7 deletions tests/builder/subcommands.rs
Expand Up @@ -494,7 +494,6 @@ For more information try --help
);
}

#[cfg(feature = "unstable-multicall")]
#[test]
fn busybox_like_multicall() {
fn applet_commands() -> [Command<'static>; 2] {
Expand All @@ -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")
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -608,7 +605,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() {
Expand All @@ -622,7 +618,6 @@ fn cant_have_args_with_multicall() {
cmd.build();
}

#[cfg(feature = "unstable-multicall")]
#[test]
fn multicall_help_flag() {
static EXPECTED: &str = "\
Expand All @@ -646,7 +641,6 @@ OPTIONS:
utils::assert_output(cmd, "foo bar --help", EXPECTED, false);
}

#[cfg(feature = "unstable-multicall")]
#[test]
fn multicall_help_subcommand() {
static EXPECTED: &str = "\
Expand All @@ -670,7 +664,6 @@ OPTIONS:
utils::assert_output(cmd, "help foo bar", EXPECTED, false);
}

#[cfg(feature = "unstable-multicall")]
#[test]
fn multicall_render_help() {
static EXPECTED: &str = "\
Expand Down
2 changes: 0 additions & 2 deletions tests/examples.rs
Expand Up @@ -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",
]
Expand Down
2 changes: 0 additions & 2 deletions tests/ui.rs
Expand Up @@ -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",
]
Expand Down

0 comments on commit 8ba0f64

Please sign in to comment.