mirror of
https://github.com/clap-rs/clap
synced 2024-12-13 22:32:33 +00:00
Merge pull request #4032 from epage/flag
fix!: Make ArgAction::Set the default
This commit is contained in:
commit
179faa6bb2
17 changed files with 259 additions and 108 deletions
|
@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||
- `arg!` now sets `ArgAction::SetTrue`, `ArgAction::Count`, `ArgAction::Set`, or `ArgAction::Append` as appropriate (#3795)
|
||||
- Default actions are now `Set` and `SetTrue`
|
||||
- Removed `PartialEq` and `Eq` from `Command`
|
||||
- By default, an `Arg`s default action is `ArgAction::Set`, rather than `ArgAction::SetTrue`
|
||||
- Replace `Arg::number_of_values` (average across occurrences) with `Arg::num_args` (per occurrence, raw CLI args, not parsed values)
|
||||
- `num_args(0)` no longer implies `takes_value(true).multiple_values(true)`
|
||||
- `num_args(1)` no longer implies `multiple_values(true)`
|
||||
|
|
|
@ -67,12 +67,13 @@ pub fn build_from_builder(c: &mut Criterion) {
|
|||
.help("tests flags")
|
||||
.long("flag")
|
||||
.global(true)
|
||||
.action(ArgAction::Append),
|
||||
.action(ArgAction::Count),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("flag2")
|
||||
.short('F')
|
||||
.help("tests flags with exclusions")
|
||||
.action(ArgAction::SetTrue)
|
||||
.conflicts_with("flag")
|
||||
.requires("option2"),
|
||||
)
|
||||
|
|
|
@ -42,7 +42,8 @@ fn app_example3<'c>() -> Command<'c> {
|
|||
.arg(
|
||||
Arg::new("debug")
|
||||
.help("turn on debugging information")
|
||||
.short('d'),
|
||||
.short('d')
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.args(&[
|
||||
Arg::new("config")
|
||||
|
@ -73,6 +74,7 @@ fn app_example4<'c>() -> Command<'c> {
|
|||
Arg::new("debug")
|
||||
.help("turn on debugging information")
|
||||
.short('d')
|
||||
.action(ArgAction::SetTrue)
|
||||
.long("debug"),
|
||||
)
|
||||
.arg(
|
||||
|
|
|
@ -299,7 +299,7 @@ where
|
|||
F: Fn(&'static str) -> &'static str,
|
||||
{
|
||||
let arg = |name| Arg::new(name).help(doc(name));
|
||||
let flag = |name| arg(name).long(name);
|
||||
let flag = |name| arg(name).long(name).action(ArgAction::SetTrue);
|
||||
|
||||
Command::new("ripgrep")
|
||||
.author("BurntSushi") // simulating since it's only a bench
|
||||
|
@ -379,18 +379,21 @@ where
|
|||
.arg(
|
||||
flag("after-context")
|
||||
.short('A')
|
||||
.action(ArgAction::Set)
|
||||
.value_name("NUM")
|
||||
.value_parser(value_parser!(usize)),
|
||||
)
|
||||
.arg(
|
||||
flag("before-context")
|
||||
.short('B')
|
||||
.action(ArgAction::Set)
|
||||
.value_name("NUM")
|
||||
.value_parser(value_parser!(usize)),
|
||||
)
|
||||
.arg(
|
||||
flag("context")
|
||||
.short('C')
|
||||
.action(ArgAction::Set)
|
||||
.value_name("NUM")
|
||||
.value_parser(value_parser!(usize)),
|
||||
)
|
||||
|
@ -419,11 +422,13 @@ where
|
|||
.arg(
|
||||
flag("max-count")
|
||||
.short('m')
|
||||
.action(ArgAction::Set)
|
||||
.value_name("NUM")
|
||||
.value_parser(value_parser!(usize)),
|
||||
)
|
||||
.arg(
|
||||
flag("maxdepth")
|
||||
.action(ArgAction::Set)
|
||||
.value_name("NUM")
|
||||
.value_parser(value_parser!(usize)),
|
||||
)
|
||||
|
@ -436,13 +441,19 @@ where
|
|||
.arg(flag("null"))
|
||||
.arg(flag("path-separator").value_name("SEPARATOR"))
|
||||
.arg(flag("pretty").short('p'))
|
||||
.arg(flag("replace").short('r').value_name("ARG"))
|
||||
.arg(
|
||||
flag("replace")
|
||||
.short('r')
|
||||
.action(ArgAction::Set)
|
||||
.value_name("ARG"),
|
||||
)
|
||||
.arg(flag("case-sensitive").short('s'))
|
||||
.arg(flag("smart-case").short('S'))
|
||||
.arg(flag("sort-files"))
|
||||
.arg(
|
||||
flag("threads")
|
||||
.short('j')
|
||||
.action(ArgAction::Set)
|
||||
.value_name("ARG")
|
||||
.value_parser(value_parser!(usize)),
|
||||
)
|
||||
|
|
|
@ -30,7 +30,8 @@ fn build_cli() -> Command<'static> {
|
|||
Arg::new("verbose")
|
||||
.help("Enable verbose output")
|
||||
.short('v')
|
||||
.long("verbose"),
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("show")
|
||||
|
@ -53,6 +54,7 @@ fn build_cli() -> Command<'static> {
|
|||
Arg::new("no-self-update")
|
||||
.help("Don't perform self update when running the `rustup` command")
|
||||
.long("no-self-update")
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true),
|
||||
),
|
||||
)
|
||||
|
@ -210,6 +212,7 @@ fn build_cli() -> Command<'static> {
|
|||
.arg(
|
||||
Arg::new("nonexistent")
|
||||
.long("nonexistent")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Remove override toolchain for all nonexistent directories"),
|
||||
),
|
||||
)
|
||||
|
@ -226,6 +229,7 @@ fn build_cli() -> Command<'static> {
|
|||
.arg(
|
||||
Arg::new("nonexistent")
|
||||
.long("nonexistent")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Remove override toolchain for all nonexistent directories"),
|
||||
),
|
||||
),
|
||||
|
@ -250,11 +254,13 @@ fn build_cli() -> Command<'static> {
|
|||
.arg(
|
||||
Arg::new("book")
|
||||
.long("book")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("The Rust Programming Language book"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("std")
|
||||
.long("std")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Standard library API documentation"),
|
||||
)
|
||||
.group(ArgGroup::new("page").args(&["book", "std"])),
|
||||
|
@ -276,7 +282,7 @@ fn build_cli() -> Command<'static> {
|
|||
.subcommand(
|
||||
Command::new("uninstall")
|
||||
.about("Uninstall rustup.")
|
||||
.arg(Arg::new("no-prompt").short('y')),
|
||||
.arg(Arg::new("no-prompt").short('y').action(ArgAction::SetTrue)),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("upgrade-data").about("Upgrade the internal data format."),
|
||||
|
|
|
@ -623,13 +623,14 @@ fn write_positionals_of(p: &Command) -> String {
|
|||
debug!("write_positionals_of:iter: arg={}", arg.get_id());
|
||||
|
||||
let num_args = arg.get_num_args().expect("built");
|
||||
let cardinality = if num_args != 1.into() && num_args != 0.into() {
|
||||
"*:"
|
||||
} else if !arg.is_required_set() {
|
||||
":"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let cardinality =
|
||||
if num_args != builder::ValueRange::EMPTY && num_args != builder::ValueRange::SINGLE {
|
||||
"*:"
|
||||
} else if !arg.is_required_set() {
|
||||
":"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let a = format!(
|
||||
"'{cardinality}:{name}{help}:{value_completion}' \\",
|
||||
|
|
|
@ -335,7 +335,7 @@ fn gen_args(arg: &Arg, indent: usize) -> String {
|
|||
));
|
||||
|
||||
let num_args = arg.get_num_args().expect("built");
|
||||
if num_args != 0.into() && num_args != 1.into() {
|
||||
if num_args != builder::ValueRange::EMPTY && num_args != builder::ValueRange::SINGLE {
|
||||
buffer.push_str(&format!(
|
||||
"{:indent$}isVariadic: true,\n",
|
||||
"",
|
||||
|
|
|
@ -13,7 +13,7 @@ use std::{env, ffi::OsString};
|
|||
use super::{ArgFlags, ArgSettings};
|
||||
use crate::builder::ArgPredicate;
|
||||
use crate::builder::PossibleValue;
|
||||
use crate::builder::ValuesRange;
|
||||
use crate::builder::ValueRange;
|
||||
use crate::util::{Id, Key};
|
||||
use crate::ArgAction;
|
||||
use crate::ValueHint;
|
||||
|
@ -71,7 +71,7 @@ pub struct Arg<'help> {
|
|||
pub(crate) short_aliases: Vec<(char, bool)>, // (name, visible)
|
||||
pub(crate) disp_ord: Option<usize>,
|
||||
pub(crate) val_names: Vec<&'help str>,
|
||||
pub(crate) num_vals: Option<ValuesRange>,
|
||||
pub(crate) num_vals: Option<ValueRange>,
|
||||
pub(crate) val_delim: Option<char>,
|
||||
pub(crate) default_vals: Vec<&'help OsStr>,
|
||||
pub(crate) default_vals_ifs: Vec<(Id, ArgPredicate<'help>, Option<&'help OsStr>)>,
|
||||
|
@ -778,16 +778,6 @@ impl<'help> Arg<'help> {
|
|||
|
||||
/// # Value Handling
|
||||
impl<'help> Arg<'help> {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn takes_value(self, yes: bool) -> Self {
|
||||
if yes {
|
||||
self.setting(ArgSettings::TakesValue)
|
||||
} else {
|
||||
self.unset_setting(ArgSettings::TakesValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specify the behavior when parsing an argument
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -1021,10 +1011,10 @@ impl<'help> Arg<'help> {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn num_args(mut self, qty: impl Into<ValuesRange>) -> Self {
|
||||
pub fn num_args(mut self, qty: impl Into<ValueRange>) -> Self {
|
||||
let qty = qty.into();
|
||||
self.num_vals = Some(qty);
|
||||
self.takes_value(qty.takes_values())
|
||||
self
|
||||
}
|
||||
|
||||
/// Placeholder for the argument's value in the help message / usage.
|
||||
|
@ -1134,7 +1124,7 @@ impl<'help> Arg<'help> {
|
|||
#[must_use]
|
||||
pub fn value_names(mut self, names: &[&'help str]) -> Self {
|
||||
self.val_names = names.to_vec();
|
||||
self.takes_value(true)
|
||||
self
|
||||
}
|
||||
|
||||
/// Provide the shell a hint about how to complete this argument.
|
||||
|
@ -1169,7 +1159,7 @@ impl<'help> Arg<'help> {
|
|||
#[must_use]
|
||||
pub fn value_hint(mut self, value_hint: ValueHint) -> Self {
|
||||
self.value_hint = Some(value_hint);
|
||||
self.takes_value(true)
|
||||
self
|
||||
}
|
||||
|
||||
/// Match values against [`PossibleValuesParser`][crate::builder::PossibleValuesParser] without matching case.
|
||||
|
@ -1370,7 +1360,7 @@ impl<'help> Arg<'help> {
|
|||
#[must_use]
|
||||
pub fn value_delimiter(mut self, d: impl Into<Option<char>>) -> Self {
|
||||
self.val_delim = d.into();
|
||||
self.takes_value(true)
|
||||
self
|
||||
}
|
||||
|
||||
/// Sentinel to **stop** parsing multiple values of a given argument.
|
||||
|
@ -1423,7 +1413,7 @@ impl<'help> Arg<'help> {
|
|||
#[must_use]
|
||||
pub fn value_terminator(mut self, term: &'help str) -> Self {
|
||||
self.terminator = Some(term);
|
||||
self.takes_value(true)
|
||||
self
|
||||
}
|
||||
|
||||
/// Consume all following arguments.
|
||||
|
@ -1451,8 +1441,10 @@ impl<'help> Arg<'help> {
|
|||
#[inline]
|
||||
#[must_use]
|
||||
pub fn raw(mut self, yes: bool) -> Self {
|
||||
self.num_vals.get_or_insert_with(|| (1..).into());
|
||||
self.takes_value(yes).allow_hyphen_values(yes).last(yes)
|
||||
if yes {
|
||||
self.num_vals.get_or_insert_with(|| (1..).into());
|
||||
}
|
||||
self.allow_hyphen_values(yes).last(yes)
|
||||
}
|
||||
|
||||
/// Value for the argument when not present.
|
||||
|
@ -1549,7 +1541,7 @@ impl<'help> Arg<'help> {
|
|||
#[must_use]
|
||||
pub fn default_values_os(mut self, vals: &[&'help OsStr]) -> Self {
|
||||
self.default_vals = vals.to_vec();
|
||||
self.takes_value(true)
|
||||
self
|
||||
}
|
||||
|
||||
/// Value for the argument when the flag is present but no value is specified.
|
||||
|
@ -1681,7 +1673,7 @@ impl<'help> Arg<'help> {
|
|||
#[must_use]
|
||||
pub fn default_missing_values_os(mut self, vals: &[&'help OsStr]) -> Self {
|
||||
self.default_missing_vals = vals.to_vec();
|
||||
self.takes_value(true)
|
||||
self
|
||||
}
|
||||
|
||||
/// Read from `name` environment variable when argument is not present.
|
||||
|
@ -1733,7 +1725,7 @@ impl<'help> Arg<'help> {
|
|||
///
|
||||
/// ```rust
|
||||
/// # use std::env;
|
||||
/// # use clap::{Command, Arg};
|
||||
/// # use clap::{Command, Arg, ArgAction};
|
||||
/// # use clap::builder::FalseyValueParser;
|
||||
///
|
||||
/// env::set_var("TRUE_FLAG", "true");
|
||||
|
@ -1742,14 +1734,17 @@ impl<'help> Arg<'help> {
|
|||
/// let m = Command::new("prog")
|
||||
/// .arg(Arg::new("true_flag")
|
||||
/// .long("true_flag")
|
||||
/// .action(ArgAction::SetTrue)
|
||||
/// .value_parser(FalseyValueParser::new())
|
||||
/// .env("TRUE_FLAG"))
|
||||
/// .arg(Arg::new("false_flag")
|
||||
/// .long("false_flag")
|
||||
/// .action(ArgAction::SetTrue)
|
||||
/// .value_parser(FalseyValueParser::new())
|
||||
/// .env("FALSE_FLAG"))
|
||||
/// .arg(Arg::new("absent_flag")
|
||||
/// .long("absent_flag")
|
||||
/// .action(ArgAction::SetTrue)
|
||||
/// .value_parser(FalseyValueParser::new())
|
||||
/// .env("ABSENT_FLAG"))
|
||||
/// .get_matches_from(vec![
|
||||
|
@ -2621,7 +2616,7 @@ impl<'help> Arg<'help> {
|
|||
) -> Self {
|
||||
self.default_vals_ifs
|
||||
.push((arg_id.into(), val.into(), default));
|
||||
self.takes_value(true)
|
||||
self
|
||||
}
|
||||
|
||||
/// Specifies multiple values and conditions in the same manner as [`Arg::default_value_if`].
|
||||
|
@ -3401,7 +3396,8 @@ impl<'help> Arg<'help> {
|
|||
/// .conflicts_with("debug")
|
||||
/// .long("config"))
|
||||
/// .arg(Arg::new("debug")
|
||||
/// .long("debug"))
|
||||
/// .long("debug")
|
||||
/// .action(ArgAction::SetTrue))
|
||||
/// .try_get_matches_from(vec![
|
||||
/// "prog", "--debug", "--config", "file.conf"
|
||||
/// ]);
|
||||
|
@ -3691,7 +3687,7 @@ impl<'help> Arg<'help> {
|
|||
|
||||
/// Get the number of values for this argument.
|
||||
#[inline]
|
||||
pub fn get_num_args(&self) -> Option<ValuesRange> {
|
||||
pub fn get_num_args(&self) -> Option<ValueRange> {
|
||||
self.num_vals
|
||||
}
|
||||
|
||||
|
@ -3782,7 +3778,7 @@ impl<'help> Arg<'help> {
|
|||
}
|
||||
|
||||
pub(crate) fn is_takes_value_set(&self) -> bool {
|
||||
self.is_set(ArgSettings::TakesValue)
|
||||
self.get_action().takes_values()
|
||||
}
|
||||
|
||||
/// Report whether [`Arg::allow_hyphen_values`] is set
|
||||
|
@ -3891,26 +3887,31 @@ impl<'help> Arg<'help> {
|
|||
/// # Internally used only
|
||||
impl<'help> Arg<'help> {
|
||||
pub(crate) fn _build(&mut self) {
|
||||
if self.is_positional() {
|
||||
self.settings.set(ArgSettings::TakesValue);
|
||||
}
|
||||
if self.action.is_none() {
|
||||
if self.get_id() == "help" && !self.is_takes_value_set() {
|
||||
if self.get_id() == "help"
|
||||
&& matches!(self.get_num_args(), Some(ValueRange::EMPTY) | None)
|
||||
{
|
||||
let action = super::ArgAction::Help;
|
||||
self.action = Some(action);
|
||||
} else if self.get_id() == "version" && !self.is_takes_value_set() {
|
||||
} else if self.get_id() == "version"
|
||||
&& matches!(self.get_num_args(), Some(ValueRange::EMPTY) | None)
|
||||
{
|
||||
let action = super::ArgAction::Version;
|
||||
self.action = Some(action);
|
||||
} else if self.is_takes_value_set() {
|
||||
let action = if self.is_positional() && self.is_multiple_values_set() {
|
||||
// Allow collecting arguments interleaved with flags
|
||||
super::ArgAction::Append
|
||||
} else {
|
||||
super::ArgAction::Set
|
||||
};
|
||||
} else if self.num_vals == Some(ValueRange::EMPTY) {
|
||||
let action = super::ArgAction::SetTrue;
|
||||
self.action = Some(action);
|
||||
} else {
|
||||
let action = super::ArgAction::SetTrue;
|
||||
let action =
|
||||
if self.is_positional() && self.num_vals.unwrap_or_default().is_unbounded() {
|
||||
// Allow collecting arguments interleaved with flags
|
||||
//
|
||||
// Bounded values are probably a group and the user should explicitly opt-in to
|
||||
// Append
|
||||
super::ArgAction::Append
|
||||
} else {
|
||||
super::ArgAction::Set
|
||||
};
|
||||
self.action = Some(action);
|
||||
}
|
||||
}
|
||||
|
@ -3925,11 +3926,6 @@ impl<'help> Arg<'help> {
|
|||
self.default_missing_vals = vec![default_value];
|
||||
}
|
||||
}
|
||||
if action.takes_values() {
|
||||
self.settings.set(ArgSettings::TakesValue);
|
||||
} else {
|
||||
self.settings.unset(ArgSettings::TakesValue);
|
||||
}
|
||||
}
|
||||
|
||||
if self.value_parser.is_none() {
|
||||
|
@ -3944,11 +3940,12 @@ impl<'help> Arg<'help> {
|
|||
if val_names_len > 1 {
|
||||
self.num_vals.get_or_insert(val_names_len.into());
|
||||
} else {
|
||||
if self.is_takes_value_set() {
|
||||
self.num_vals.get_or_insert(1.into());
|
||||
let nargs = if self.get_action().takes_values() {
|
||||
ValueRange::SINGLE
|
||||
} else {
|
||||
self.num_vals.get_or_insert(0.into());
|
||||
}
|
||||
ValueRange::EMPTY
|
||||
};
|
||||
self.num_vals.get_or_insert(nargs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ pub(crate) enum ArgSettings {
|
|||
Required,
|
||||
Global,
|
||||
Hidden,
|
||||
TakesValue,
|
||||
NextLineHelp,
|
||||
HidePossibleValues,
|
||||
AllowHyphenValues,
|
||||
|
@ -52,7 +51,6 @@ bitflags! {
|
|||
const REQUIRED = 1;
|
||||
const GLOBAL = 1 << 3;
|
||||
const HIDDEN = 1 << 4;
|
||||
const TAKES_VAL = 1 << 5;
|
||||
const NEXT_LINE_HELP = 1 << 7;
|
||||
const DELIM_NOT_SET = 1 << 10;
|
||||
const HIDE_POS_VALS = 1 << 11;
|
||||
|
@ -76,7 +74,6 @@ impl_settings! { ArgSettings, ArgFlags,
|
|||
Required => Flags::REQUIRED,
|
||||
Global => Flags::GLOBAL,
|
||||
Hidden => Flags::HIDDEN,
|
||||
TakesValue => Flags::TAKES_VAL,
|
||||
NextLineHelp => Flags::NEXT_LINE_HELP,
|
||||
HidePossibleValues => Flags::HIDE_POS_VALS,
|
||||
AllowHyphenValues => Flags::ALLOW_TAC_VALS,
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::cmp::Ordering;
|
|||
use clap_lex::RawOsStr;
|
||||
|
||||
use crate::builder::arg::ArgProvider;
|
||||
use crate::builder::ValueRange;
|
||||
use crate::mkeymap::KeyType;
|
||||
use crate::ArgAction;
|
||||
use crate::INTERNAL_ERROR_MSG;
|
||||
|
@ -693,7 +694,7 @@ fn assert_arg(arg: &Arg) {
|
|||
|
||||
let num_vals = arg.get_num_args().expect(INTERNAL_ERROR_MSG);
|
||||
// This can be the cause of later asserts, so put this first
|
||||
if num_vals != 0.into() {
|
||||
if num_vals != ValueRange::EMPTY {
|
||||
// HACK: Don't check for flags to make the derive easier
|
||||
let num_val_names = arg.get_value_names().unwrap_or(&[]).len();
|
||||
if num_vals.max_values() < num_val_names {
|
||||
|
@ -728,7 +729,7 @@ fn assert_arg(arg: &Arg) {
|
|||
);
|
||||
}
|
||||
|
||||
if num_vals == 1.into() {
|
||||
if num_vals == ValueRange::SINGLE {
|
||||
assert!(
|
||||
!arg.is_multiple_values_set(),
|
||||
"Argument {}: mismatch between `num_args` and `multiple_values`",
|
||||
|
|
|
@ -23,7 +23,7 @@ pub use arg::Arg;
|
|||
pub use arg_group::ArgGroup;
|
||||
pub use command::Command;
|
||||
pub use possible_value::PossibleValue;
|
||||
pub use range::ValuesRange;
|
||||
pub use range::ValueRange;
|
||||
pub use value_hint::ValueHint;
|
||||
pub use value_parser::_AutoValueParser;
|
||||
pub use value_parser::via_prelude;
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
/// Values per occurrence for an argument
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ValuesRange {
|
||||
pub struct ValueRange {
|
||||
start_inclusive: usize,
|
||||
end_inclusive: usize,
|
||||
}
|
||||
|
||||
impl ValuesRange {
|
||||
impl ValueRange {
|
||||
/// Nor argument values, or a flag
|
||||
pub const EMPTY: Self = Self {
|
||||
start_inclusive: 0,
|
||||
end_inclusive: 0,
|
||||
};
|
||||
|
||||
/// A single argument value, the most common case for options
|
||||
pub const SINGLE: Self = Self {
|
||||
start_inclusive: 1,
|
||||
end_inclusive: 1,
|
||||
};
|
||||
|
||||
/// Create a range
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -15,19 +27,19 @@ impl ValuesRange {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use clap::builder::ValuesRange;
|
||||
/// let range = ValuesRange::new(5);
|
||||
/// let range = ValuesRange::new(5..10);
|
||||
/// let range = ValuesRange::new(5..=10);
|
||||
/// let range = ValuesRange::new(5..);
|
||||
/// let range = ValuesRange::new(..10);
|
||||
/// let range = ValuesRange::new(..=10);
|
||||
/// # use clap::builder::ValueRange;
|
||||
/// let range = ValueRange::new(5);
|
||||
/// let range = ValueRange::new(5..10);
|
||||
/// let range = ValueRange::new(5..=10);
|
||||
/// let range = ValueRange::new(5..);
|
||||
/// let range = ValueRange::new(..10);
|
||||
/// let range = ValueRange::new(..=10);
|
||||
/// ```
|
||||
///
|
||||
/// While this will panic:
|
||||
/// ```should_panic
|
||||
/// # use clap::builder::ValuesRange;
|
||||
/// let range = ValuesRange::new(10..5); // Panics!
|
||||
/// # use clap::builder::ValueRange;
|
||||
/// let range = ValueRange::new(10..5); // Panics!
|
||||
/// ```
|
||||
pub fn new(range: impl Into<Self>) -> Self {
|
||||
range.into()
|
||||
|
@ -56,17 +68,21 @@ impl ValuesRange {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use clap::builder::ValuesRange;
|
||||
/// let range = ValuesRange::new(5);
|
||||
/// # use clap::builder::ValueRange;
|
||||
/// let range = ValueRange::new(5);
|
||||
/// assert!(range.takes_values());
|
||||
///
|
||||
/// let range = ValuesRange::new(0);
|
||||
/// let range = ValueRange::new(0);
|
||||
/// assert!(!range.takes_values());
|
||||
/// ```
|
||||
pub fn takes_values(&self) -> bool {
|
||||
self.end_inclusive != 0
|
||||
}
|
||||
|
||||
pub(crate) fn is_unbounded(&self) -> bool {
|
||||
self.end_inclusive == usize::MAX
|
||||
}
|
||||
|
||||
pub(crate) fn is_fixed(&self) -> bool {
|
||||
self.start_inclusive == self.end_inclusive
|
||||
}
|
||||
|
@ -84,7 +100,7 @@ impl ValuesRange {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::ops::RangeBounds<usize> for ValuesRange {
|
||||
impl std::ops::RangeBounds<usize> for ValueRange {
|
||||
fn start_bound(&self) -> std::ops::Bound<&usize> {
|
||||
std::ops::Bound::Included(&self.start_inclusive)
|
||||
}
|
||||
|
@ -94,19 +110,19 @@ impl std::ops::RangeBounds<usize> for ValuesRange {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for ValuesRange {
|
||||
impl Default for ValueRange {
|
||||
fn default() -> Self {
|
||||
0.into()
|
||||
Self::SINGLE
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for ValuesRange {
|
||||
impl From<usize> for ValueRange {
|
||||
fn from(fixed: usize) -> Self {
|
||||
(fixed..=fixed).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::ops::Range<usize>> for ValuesRange {
|
||||
impl From<std::ops::Range<usize>> for ValueRange {
|
||||
fn from(range: std::ops::Range<usize>) -> Self {
|
||||
let start_inclusive = range.start;
|
||||
let end_inclusive = range.end.saturating_sub(1);
|
||||
|
@ -114,7 +130,7 @@ impl From<std::ops::Range<usize>> for ValuesRange {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<std::ops::RangeFull> for ValuesRange {
|
||||
impl From<std::ops::RangeFull> for ValueRange {
|
||||
fn from(_: std::ops::RangeFull) -> Self {
|
||||
let start_inclusive = 0;
|
||||
let end_inclusive = usize::MAX;
|
||||
|
@ -122,7 +138,7 @@ impl From<std::ops::RangeFull> for ValuesRange {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<std::ops::RangeFrom<usize>> for ValuesRange {
|
||||
impl From<std::ops::RangeFrom<usize>> for ValueRange {
|
||||
fn from(range: std::ops::RangeFrom<usize>) -> Self {
|
||||
let start_inclusive = range.start;
|
||||
let end_inclusive = usize::MAX;
|
||||
|
@ -130,7 +146,7 @@ impl From<std::ops::RangeFrom<usize>> for ValuesRange {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<std::ops::RangeTo<usize>> for ValuesRange {
|
||||
impl From<std::ops::RangeTo<usize>> for ValueRange {
|
||||
fn from(range: std::ops::RangeTo<usize>) -> Self {
|
||||
let start_inclusive = 0;
|
||||
let end_inclusive = range.end.saturating_sub(1);
|
||||
|
@ -138,7 +154,7 @@ impl From<std::ops::RangeTo<usize>> for ValuesRange {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<std::ops::RangeInclusive<usize>> for ValuesRange {
|
||||
impl From<std::ops::RangeInclusive<usize>> for ValueRange {
|
||||
fn from(range: std::ops::RangeInclusive<usize>) -> Self {
|
||||
let start_inclusive = *range.start();
|
||||
let end_inclusive = *range.end();
|
||||
|
@ -146,7 +162,7 @@ impl From<std::ops::RangeInclusive<usize>> for ValuesRange {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<std::ops::RangeToInclusive<usize>> for ValuesRange {
|
||||
impl From<std::ops::RangeToInclusive<usize>> for ValueRange {
|
||||
fn from(range: std::ops::RangeToInclusive<usize>) -> Self {
|
||||
let start_inclusive = 0;
|
||||
let end_inclusive = range.end;
|
||||
|
@ -154,7 +170,7 @@ impl From<std::ops::RangeToInclusive<usize>> for ValuesRange {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ValuesRange {
|
||||
impl std::fmt::Display for ValueRange {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
self.start_inclusive.fmt(f)?;
|
||||
if !self.is_fixed() {
|
||||
|
@ -165,7 +181,7 @@ impl std::fmt::Display for ValuesRange {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ValuesRange {
|
||||
impl std::fmt::Debug for ValueRange {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
|
@ -179,7 +195,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn from_fixed() {
|
||||
let range: ValuesRange = 5.into();
|
||||
let range: ValueRange = 5.into();
|
||||
assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
|
||||
assert_eq!(range.end_bound(), std::ops::Bound::Included(&5));
|
||||
assert!(range.is_fixed());
|
||||
|
@ -190,7 +206,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn from_fixed_empty() {
|
||||
let range: ValuesRange = 0.into();
|
||||
let range: ValueRange = 0.into();
|
||||
assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
|
||||
assert_eq!(range.end_bound(), std::ops::Bound::Included(&0));
|
||||
assert!(range.is_fixed());
|
||||
|
@ -201,7 +217,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn from_range() {
|
||||
let range: ValuesRange = (5..10).into();
|
||||
let range: ValueRange = (5..10).into();
|
||||
assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
|
||||
assert_eq!(range.end_bound(), std::ops::Bound::Included(&9));
|
||||
assert!(!range.is_fixed());
|
||||
|
@ -212,7 +228,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn from_range_inclusive() {
|
||||
let range: ValuesRange = (5..=10).into();
|
||||
let range: ValueRange = (5..=10).into();
|
||||
assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
|
||||
assert_eq!(range.end_bound(), std::ops::Bound::Included(&10));
|
||||
assert!(!range.is_fixed());
|
||||
|
@ -223,7 +239,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn from_range_full() {
|
||||
let range: ValuesRange = (..).into();
|
||||
let range: ValueRange = (..).into();
|
||||
assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
|
||||
assert_eq!(range.end_bound(), std::ops::Bound::Included(&usize::MAX));
|
||||
assert!(!range.is_fixed());
|
||||
|
@ -234,7 +250,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn from_range_from() {
|
||||
let range: ValuesRange = (5..).into();
|
||||
let range: ValueRange = (5..).into();
|
||||
assert_eq!(range.start_bound(), std::ops::Bound::Included(&5));
|
||||
assert_eq!(range.end_bound(), std::ops::Bound::Included(&usize::MAX));
|
||||
assert!(!range.is_fixed());
|
||||
|
@ -245,7 +261,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn from_range_to() {
|
||||
let range: ValuesRange = (..10).into();
|
||||
let range: ValueRange = (..10).into();
|
||||
assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
|
||||
assert_eq!(range.end_bound(), std::ops::Bound::Included(&9));
|
||||
assert!(!range.is_fixed());
|
||||
|
@ -256,7 +272,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn from_range_to_inclusive() {
|
||||
let range: ValuesRange = (..=10).into();
|
||||
let range: ValueRange = (..=10).into();
|
||||
assert_eq!(range.start_bound(), std::ops::Bound::Included(&0));
|
||||
assert_eq!(range.end_bound(), std::ops::Bound::Included(&10));
|
||||
assert!(!range.is_fixed());
|
||||
|
|
|
@ -207,9 +207,14 @@ macro_rules! arg_impl {
|
|||
if arg.get_id().is_empty() {
|
||||
arg = arg.id(long);
|
||||
}
|
||||
let action = match arg.get_id() {
|
||||
"help" => $crate::ArgAction::Help,
|
||||
"version" => $crate::ArgAction::Version,
|
||||
_ => $crate::ArgAction::SetTrue,
|
||||
};
|
||||
arg
|
||||
.long(long)
|
||||
.action($crate::ArgAction::SetTrue)
|
||||
.action(action)
|
||||
})
|
||||
$($tail)*
|
||||
}
|
||||
|
@ -231,9 +236,14 @@ macro_rules! arg_impl {
|
|||
if arg.get_id().is_empty() {
|
||||
arg = arg.id(long);
|
||||
}
|
||||
let action = match arg.get_id() {
|
||||
"help" => $crate::ArgAction::Help,
|
||||
"version" => $crate::ArgAction::Version,
|
||||
_ => $crate::ArgAction::SetTrue,
|
||||
};
|
||||
arg
|
||||
.long(long)
|
||||
.action($crate::ArgAction::SetTrue)
|
||||
.action(action)
|
||||
})
|
||||
$($tail)*
|
||||
}
|
||||
|
@ -251,9 +261,14 @@ macro_rules! arg_impl {
|
|||
debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values");
|
||||
debug_assert!(!matches!($arg.get_action(), $crate::ArgAction::Append), "Flags should precede `...`");
|
||||
|
||||
let action = match $arg.get_id() {
|
||||
"help" => $crate::ArgAction::Help,
|
||||
"version" => $crate::ArgAction::Version,
|
||||
_ => $crate::ArgAction::SetTrue,
|
||||
};
|
||||
$arg
|
||||
.short($crate::arg_impl! { @char $short })
|
||||
.action($crate::ArgAction::SetTrue)
|
||||
.action(action)
|
||||
})
|
||||
$($tail)*
|
||||
}
|
||||
|
@ -271,9 +286,14 @@ macro_rules! arg_impl {
|
|||
debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values");
|
||||
debug_assert!(!matches!($arg.get_action(), $crate::ArgAction::Append), "Flags should precede `...`");
|
||||
|
||||
let action = match $arg.get_id() {
|
||||
"help" => $crate::ArgAction::Help,
|
||||
"version" => $crate::ArgAction::Version,
|
||||
_ => $crate::ArgAction::SetTrue,
|
||||
};
|
||||
$arg
|
||||
.short($crate::arg_impl! { @char $short })
|
||||
.action($crate::ArgAction::SetTrue)
|
||||
.action(action)
|
||||
})
|
||||
$($tail)*
|
||||
}
|
||||
|
@ -413,7 +433,7 @@ macro_rules! arg_impl {
|
|||
$arg.action($crate::ArgAction::Append)
|
||||
}
|
||||
},
|
||||
$crate::ArgAction::SetTrue => {
|
||||
$crate::ArgAction::SetTrue | $crate::ArgAction::Help | $crate::ArgAction::Version => {
|
||||
$arg.action($crate::ArgAction::Count)
|
||||
}
|
||||
action => {
|
||||
|
|
|
@ -401,21 +401,25 @@ OPTIONS:
|
|||
Arg::new("all")
|
||||
.short('a')
|
||||
.long("all")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Also do versioning for private crates (will not be published)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("exact")
|
||||
.long("exact")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Specify inter dependency version numbers exactly with `=`"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("no_git_commit")
|
||||
.long("no-git-commit")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Do not commit version changes"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("no_git_push")
|
||||
.long("no-git-push")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Do not push generated commit and tags to git remote"),
|
||||
);
|
||||
utils::assert_output(cmd, "test --help", WRAPPED_HELP, false);
|
||||
|
@ -445,21 +449,25 @@ OPTIONS:
|
|||
Arg::new("all")
|
||||
.short('a')
|
||||
.long("all")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Also do versioning for private crates (will not be published)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("exact")
|
||||
.long("exact")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Specify inter dependency version numbers exactly with `=`"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("no_git_commit")
|
||||
.long("no-git-commit")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Do not commit version changes"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("no_git_push")
|
||||
.long("no-git-push")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("Do not push generated commit and tags to git remote"),
|
||||
);
|
||||
utils::assert_output(cmd, "test --help", UNWRAPPED_HELP, false);
|
||||
|
|
|
@ -172,6 +172,7 @@ fn hide_env_flag() {
|
|||
Arg::new("cafe")
|
||||
.short('c')
|
||||
.long("cafe")
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide_env(true)
|
||||
.env("ENVVAR")
|
||||
.help("A coffeehouse, coffee shop, or café."),
|
||||
|
@ -188,6 +189,7 @@ fn show_env_flag() {
|
|||
Arg::new("cafe")
|
||||
.short('c')
|
||||
.long("cafe")
|
||||
.action(ArgAction::SetTrue)
|
||||
.env("ENVVAR")
|
||||
.help("A coffeehouse, coffee shop, or café."),
|
||||
);
|
||||
|
@ -203,6 +205,7 @@ fn hide_env_vals_flag() {
|
|||
Arg::new("cafe")
|
||||
.short('c')
|
||||
.long("cafe")
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide_env_values(true)
|
||||
.env("ENVVAR")
|
||||
.help("A coffeehouse, coffee shop, or café."),
|
||||
|
@ -219,6 +222,7 @@ fn show_env_vals_flag() {
|
|||
Arg::new("cafe")
|
||||
.short('c')
|
||||
.long("cafe")
|
||||
.action(ArgAction::SetTrue)
|
||||
.env("ENVVAR")
|
||||
.help("A coffeehouse, coffee shop, or café."),
|
||||
);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
|
||||
// MIT/Apache 2.0 license.
|
||||
|
||||
use clap::CommandFactory;
|
||||
use clap::Parser;
|
||||
|
||||
#[test]
|
||||
|
@ -42,6 +43,43 @@ fn bool_type_is_flag() {
|
|||
assert!(Opt::try_parse_from(&["test", "-a", "foo"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // Not a good path for supporting this atm
|
||||
fn inferred_help() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
/// Foo
|
||||
#[clap(short, long)]
|
||||
help: bool,
|
||||
}
|
||||
|
||||
let mut cmd = Opt::command();
|
||||
cmd.build();
|
||||
let arg = cmd.get_arguments().find(|a| a.get_id() == "help").unwrap();
|
||||
assert_eq!(arg.get_help(), Some("Foo"), "Incorrect help");
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Help));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // Not a good path for supporting this atm
|
||||
fn inferred_version() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
/// Foo
|
||||
#[clap(short, long)]
|
||||
version: bool,
|
||||
}
|
||||
|
||||
let mut cmd = Opt::command();
|
||||
cmd.build();
|
||||
let arg = cmd
|
||||
.get_arguments()
|
||||
.find(|a| a.get_id() == "version")
|
||||
.unwrap();
|
||||
assert_eq!(arg.get_help(), Some("Foo"), "Incorrect help");
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Version));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
|
|
|
@ -112,6 +112,54 @@ mod arg {
|
|||
assert_eq!(arg.get_help(), Some("How to use it"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn short_help() {
|
||||
let arg = clap::arg!(help: -b);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Help));
|
||||
|
||||
let arg = clap::arg!(help: -b ...);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Count));
|
||||
|
||||
let arg = clap::arg!(help: -b <NUM>);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Set));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn long_help() {
|
||||
let arg = clap::arg!(-'?' - -help);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Help));
|
||||
|
||||
let arg = clap::arg!(-'?' --help ...);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Count));
|
||||
|
||||
let arg = clap::arg!(-'?' --help <NUM>);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Set));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn short_version() {
|
||||
let arg = clap::arg!(version: -b);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Version));
|
||||
|
||||
let arg = clap::arg!(version: -b ...);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Count));
|
||||
|
||||
let arg = clap::arg!(version: -b <NUM>);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Set));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn long_version() {
|
||||
let arg = clap::arg!(-'?' - -version);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Version));
|
||||
|
||||
let arg = clap::arg!(-'?' --version ...);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Count));
|
||||
|
||||
let arg = clap::arg!(-'?' --version <NUM>);
|
||||
assert!(matches!(arg.get_action(), clap::ArgAction::Set));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn short_with_value() {
|
||||
let arg = clap::arg!(foo: -b <NUM>);
|
||||
|
|
Loading…
Reference in a new issue