Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix!: Remove unstable-replace feature flag #4805

Merged
merged 1 commit into from Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.toml
Expand Up @@ -92,7 +92,6 @@ unicode = ["clap_builder/unicode"] # Support for unicode characters in argument
string = ["clap_builder/string"] # Allow runtime generated strings

# In-work features
unstable-replace = ["clap_builder/unstable-replace"]
unstable-v5 = ["clap_builder/unstable-v5", "clap_derive?/unstable-v5", "deprecated"]

[lib]
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Expand Up @@ -15,8 +15,8 @@ MSRV?=1.64.0
_FEATURES = minimal default wasm full debug release
_FEATURES_minimal = --no-default-features --features "std"
_FEATURES_default =
_FEATURES_wasm = --no-default-features --features "std help usage error-context suggestions" --features "deprecated derive cargo env unicode string unstable-replace"
_FEATURES_full = --features "deprecated derive cargo env unicode string unstable-replace wrap_help"
_FEATURES_wasm = --no-default-features --features "std help usage error-context suggestions" --features "deprecated derive cargo env unicode string"
_FEATURES_full = --features "deprecated derive cargo env unicode string wrap_help"
_FEATURES_next = ${_FEATURES_full} --features unstable-v5
_FEATURES_debug = ${_FEATURES_full} --features debug --features clap_complete/debug
_FEATURES_release = ${_FEATURES_full} --release
Expand Down
3 changes: 1 addition & 2 deletions clap_builder/Cargo.toml
Expand Up @@ -32,7 +32,7 @@ tag-name = "v{{version}}"
[features]
default = ["std", "color", "help", "usage", "error-context", "suggestions"]
debug = ["dep:backtrace"] # Enables debug messages
unstable-doc = ["cargo", "wrap_help", "env", "unicode", "string", "unstable-replace"] # for docs.rs
unstable-doc = ["cargo", "wrap_help", "env", "unicode", "string"] # for docs.rs

# Used in default
std = [] # support for no_std in a backwards-compatible way
Expand All @@ -51,7 +51,6 @@ unicode = ["dep:unicode-width", "dep:unicase"] # Support for unicode characters
string = [] # Allow runtime generated strings

# In-work features
unstable-replace = []
unstable-v5 = ["deprecated"]

[lib]
Expand Down
130 changes: 0 additions & 130 deletions clap_builder/src/builder/command.rs
Expand Up @@ -24,7 +24,6 @@ use crate::output::fmt::Stream;
use crate::output::{fmt::Colorizer, write_help, Usage};
use crate::parser::{ArgMatcher, ArgMatches, Parser};
use crate::util::ChildGraph;
use crate::util::FlatMap;
use crate::util::{color::ColorChoice, Id};
use crate::{Error, INTERNAL_ERROR_MSG};

Expand Down Expand Up @@ -99,7 +98,6 @@ pub struct Command {
g_settings: AppFlags,
args: MKeyMap,
subcommands: Vec<Command>,
replacers: FlatMap<Str, Vec<Str>>,
groups: Vec<ArgGroup>,
current_help_heading: Option<Str>,
current_disp_ord: Option<usize>,
Expand Down Expand Up @@ -1924,129 +1922,6 @@ impl Command {
self
}

/// Replaces an argument or subcommand used on the CLI at runtime with other arguments or subcommands.
///
/// **Note:** This is gated behind [`unstable-replace`](https://github.com/clap-rs/clap/issues/2836)
///
/// When this method is used, `name` is removed from the CLI, and `target`
/// is inserted in its place. Parsing continues as if the user typed
/// `target` instead of `name`.
///
/// This can be used to create "shortcuts" for subcommands, or if a
/// particular argument has the semantic meaning of several other specific
/// arguments and values.
///
/// # Examples
///
/// We'll start with the "subcommand short" example. In this example, let's
/// assume we have a program with a subcommand `module` which can be invoked
/// via `cmd module`. Now let's also assume `module` also has a subcommand
/// called `install` which can be invoked `cmd module install`. If for some
/// reason users needed to be able to reach `cmd module install` via the
/// short-hand `cmd install`, we'd have several options.
///
/// We *could* create another sibling subcommand to `module` called
/// `install`, but then we would need to manage another subcommand and manually
/// dispatch to `cmd module install` handling code. This is error prone and
/// tedious.
///
/// We could instead use [`Command::replace`] so that, when the user types `cmd
/// install`, `clap` will replace `install` with `module install` which will
/// end up getting parsed as if the user typed the entire incantation.
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::Command;
/// let m = Command::new("cmd")
/// .subcommand(Command::new("module")
/// .subcommand(Command::new("install")))
/// .replace("install", &["module", "install"])
/// .get_matches_from(vec!["cmd", "install"]);
///
/// assert!(m.subcommand_matches("module").is_some());
/// assert!(m.subcommand_matches("module").unwrap().subcommand_matches("install").is_some());
/// ```
///
/// Now let's show an argument example!
///
/// Let's assume we have an application with two flags `--save-context` and
/// `--save-runtime`. But often users end up needing to do *both* at the
/// same time. We can add a third flag `--save-all` which semantically means
/// the same thing as `cmd --save-context --save-runtime`. To implement that,
/// we have several options.
///
/// We could create this third argument and manually check if that argument
/// and in our own consumer code handle the fact that both `--save-context`
/// and `--save-runtime` *should* have been used. But again this is error
/// prone and tedious. If we had code relying on checking `--save-context`
/// and we forgot to update that code to *also* check `--save-all` it'd mean
/// an error!
///
/// Luckily we can use [`Command::replace`] so that when the user types
/// `--save-all`, `clap` will replace that argument with `--save-context
/// --save-runtime`, and parsing will continue like normal. Now all our code
/// that was originally checking for things like `--save-context` doesn't
/// need to change!
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, ArgAction};
/// let m = Command::new("cmd")
/// .arg(Arg::new("save-context")
/// .long("save-context")
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("save-runtime")
/// .long("save-runtime")
/// .action(ArgAction::SetTrue))
/// .replace("--save-all", &["--save-context", "--save-runtime"])
/// .get_matches_from(vec!["cmd", "--save-all"]);
///
/// assert!(m.get_flag("save-context"));
/// assert!(m.get_flag("save-runtime"));
/// ```
///
/// This can also be used with options, for example if our application with
/// `--save-*` above also had a `--format=TYPE` option. Let's say it
/// accepted `txt` or `json` values. However, when `--save-all` is used,
/// only `--format=json` is allowed, or valid. We could change the example
/// above to enforce this:
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg, ArgAction};
/// let m = Command::new("cmd")
/// .arg(Arg::new("save-context")
/// .long("save-context")
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("save-runtime")
/// .long("save-runtime")
/// .action(ArgAction::SetTrue))
/// .arg(Arg::new("format")
/// .long("format")
/// .action(ArgAction::Set)
/// .value_parser(["txt", "json"]))
/// .replace("--save-all", &["--save-context", "--save-runtime", "--format=json"])
/// .get_matches_from(vec!["cmd", "--save-all"]);
///
/// assert!(m.get_flag("save-context"));
/// assert!(m.get_flag("save-runtime"));
/// assert_eq!(m.get_one::<String>("format").unwrap(), "json");
/// ```
///
/// [`Command::replace`]: Command::replace()
#[inline]
#[cfg(feature = "unstable-replace")]
#[must_use]
pub fn replace(
mut self,
name: impl Into<Str>,
target: impl IntoIterator<Item = impl Into<Str>>,
) -> Self {
self.replacers
.insert(name.into(), target.into_iter().map(Into::into).collect());
self
}

/// Exit gracefully if no arguments are present (e.g. `$ myprog`).
///
/// **NOTE:** [`subcommands`] count as arguments
Expand Down Expand Up @@ -3853,10 +3728,6 @@ impl Command {
self.max_w
}

pub(crate) fn get_replacement(&self, key: &str) -> Option<&[Str]> {
self.replacers.get(key).map(|v| v.as_slice())
}

pub(crate) fn get_keymap(&self) -> &MKeyMap {
&self.args
}
Expand Down Expand Up @@ -4749,7 +4620,6 @@ impl Default for Command {
g_settings: Default::default(),
args: Default::default(),
subcommands: Default::default(),
replacers: Default::default(),
groups: Default::default(),
current_help_heading: Default::default(),
current_disp_ord: Some(0),
Expand Down
14 changes: 0 additions & 14 deletions clap_builder/src/parser/parser.rs
Expand Up @@ -76,20 +76,6 @@ impl<'cmd> Parser<'cmd> {
let contains_last = self.cmd.get_arguments().any(|x| x.is_last_set());

while let Some(arg_os) = raw_args.next(&mut args_cursor) {
// Recover the replaced items if any.
if let Some(replaced_items) = arg_os
.to_value()
.ok()
.and_then(|a| self.cmd.get_replacement(a))
{
debug!(
"Parser::get_matches_with: found replacer: {:?}, target: {:?}",
arg_os, replaced_items
);
raw_args.insert(&args_cursor, replaced_items);
continue;
}

debug!(
"Parser::get_matches_with: Begin parsing '{:?}'",
arg_os.to_value_os(),
Expand Down
1 change: 0 additions & 1 deletion src/_features.rs
Expand Up @@ -25,5 +25,4 @@
//!
//! **Warning:** These may contain breaking changes between minor releases.
//!
//! * **unstable-replace**: Enable [`Command::replace`](https://github.com/clap-rs/clap/issues/2836)
//! * **unstable-v5**: Preview features which will be stable on the v5.0 release
18 changes: 0 additions & 18 deletions tests/builder/subcommands.rs
Expand Up @@ -225,24 +225,6 @@ Options:
utils::assert_output(cmd, "clap-test --help", INVISIBLE_ALIAS_HELP, false);
}

#[test]
#[cfg(feature = "unstable-replace")]
fn replace() {
let m = Command::new("prog")
.subcommand(
Command::new("module").subcommand(Command::new("install").about("Install module")),
)
.replace("install", ["module", "install"])
.try_get_matches_from(vec!["prog", "install"])
.unwrap();

assert_eq!(m.subcommand_name(), Some("module"));
assert_eq!(
m.subcommand_matches("module").unwrap().subcommand_name(),
Some("install")
);
}

#[test]
fn issue_1031_args_with_same_name() {
let res = Command::new("prog")
Expand Down
2 changes: 0 additions & 2 deletions tests/ui.rs
Expand Up @@ -25,8 +25,6 @@ fn ui_tests() {
"string",
#[cfg(feature = "wrap_help")]
"wrap_help",
#[cfg(feature = "unstable-replace")]
"unstable-replace",
]
.join(" ");
t.register_bins(trycmd::cargo::compile_examples(["--features", &features]).unwrap());
Expand Down