From 4a694f3592b702f86c7a6846b867b61d9feff5fe Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 6 Jun 2022 12:21:47 -0500 Subject: [PATCH 01/17] refactor(parser): Be explicit in checking present values --- src/parser/arg_matcher.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/parser/arg_matcher.rs b/src/parser/arg_matcher.rs index 690f504b8cc..b424863e4b7 100644 --- a/src/parser/arg_matcher.rs +++ b/src/parser/arg_matcher.rs @@ -70,11 +70,13 @@ impl ArgMatcher { // We have to check if the parent's global arg wasn't used but still exists // such as from a default value. // - // For example, `myprog subcommand --global-arg=value` where --global-arg defines + // For example, `myprog subcommand --global-arg=value` where `--global-arg` defines // a default value of `other` myprog would have an existing MatchedArg for - // --global-arg where the value is `other`, however the occurs will be 0. + // `--global-arg` where the value is `other` let to_update = if let Some(parent_ma) = vals_map.get(global_arg) { - if parent_ma.get_occurrences() > 0 && ma.get_occurrences() == 0 { + if parent_ma.check_explicit(ArgPredicate::IsPresent) + && !ma.check_explicit(ArgPredicate::IsPresent) + { parent_ma } else { ma From 1879826b218a341c49d39f356edd5a3ed6d7cede Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 6 Jun 2022 13:20:50 -0500 Subject: [PATCH 02/17] fix(parser): Deprecate StoreValue / IncOccurrences Actions Dropping these will help simplify a lot, including removing of occurrences. These come at the cost of the derive not yet supporting types that impl `From`. --- src/builder/action.rs | 62 ++++++++++++----------------------------- src/parser/parser.rs | 4 +++ tests/derive/options.rs | 48 +++++++++++++++++-------------- tests/derive/utf8.rs | 4 +-- 4 files changed, 51 insertions(+), 67 deletions(-) diff --git a/src/builder/action.rs b/src/builder/action.rs index 3e4404c7e09..4e435380161 100644 --- a/src/builder/action.rs +++ b/src/builder/action.rs @@ -71,51 +71,17 @@ pub enum ArgAction { /// ); /// ``` Append, - /// When encountered, store the associated value(s) in [`ArgMatches`][crate::ArgMatches] - /// - /// # Examples - /// - /// ```rust - /// # use clap::Command; - /// # use clap::Arg; - /// let cmd = Command::new("mycmd") - /// .arg( - /// Arg::new("flag") - /// .long("flag") - /// .action(clap::ArgAction::StoreValue) - /// ); - /// - /// let matches = cmd.try_get_matches_from(["mycmd", "--flag", "value"]).unwrap(); - /// assert!(matches.is_present("flag")); - /// assert_eq!(matches.occurrences_of("flag"), 1); - /// assert_eq!( - /// matches.get_many::("flag").unwrap_or_default().map(|v| v.as_str()).collect::>(), - /// vec!["value"] - /// ); - /// ``` + /// Deprecated, replaced with [`ArgAction::Set`] or [`ArgAction::Append`] + #[deprecated( + since = "3.2.0", + note = "Replaced with `ArgAction::Set` or `ArgAction::Append`" + )] StoreValue, - /// When encountered, increment [`ArgMatches::occurrences_of`][crate::ArgMatches::occurrences_of] - /// - /// No value is allowed - /// - /// # Examples - /// - /// ```rust - /// # use clap::Command; - /// # use clap::Arg; - /// let cmd = Command::new("mycmd") - /// .arg( - /// Arg::new("flag") - /// .long("flag") - /// .multiple_occurrences(true) - /// .action(clap::ArgAction::IncOccurrence) - /// ); - /// - /// let matches = cmd.try_get_matches_from(["mycmd", "--flag", "--flag"]).unwrap(); - /// assert!(matches.is_present("flag")); - /// assert_eq!(matches.occurrences_of("flag"), 2); - /// assert_eq!(matches.get_many::("flag").unwrap_or_default().count(), 0); - /// ``` + /// Deprecated, replaced with [`ArgAction::SetTrue`] or [`ArgAction::Count`] + #[deprecated( + since = "3.2.0", + note = "Replaced with `ArgAction::SetTrue` or `ArgAction::Count`" + )] IncOccurrence, /// When encountered, act as if `"true"` was encountered on the command-line /// @@ -284,7 +250,9 @@ impl ArgAction { match self { Self::Set => true, Self::Append => true, + #[allow(deprecated)] Self::StoreValue => true, + #[allow(deprecated)] Self::IncOccurrence => false, Self::SetTrue => false, Self::SetFalse => false, @@ -298,7 +266,9 @@ impl ArgAction { match self { Self::Set => None, Self::Append => None, + #[allow(deprecated)] Self::StoreValue => None, + #[allow(deprecated)] Self::IncOccurrence => None, Self::SetTrue => Some(std::ffi::OsStr::new("false")), Self::SetFalse => Some(std::ffi::OsStr::new("true")), @@ -312,7 +282,9 @@ impl ArgAction { match self { Self::Set => None, Self::Append => None, + #[allow(deprecated)] Self::StoreValue => None, + #[allow(deprecated)] Self::IncOccurrence => None, Self::SetTrue => Some(super::ValueParser::bool()), Self::SetFalse => Some(super::ValueParser::bool()), @@ -329,7 +301,9 @@ impl ArgAction { match self { Self::Set => None, Self::Append => None, + #[allow(deprecated)] Self::StoreValue => None, + #[allow(deprecated)] Self::IncOccurrence => None, Self::SetTrue => Some(AnyValueId::of::()), Self::SetFalse => Some(AnyValueId::of::()), diff --git a/src/parser/parser.rs b/src/parser/parser.rs index e6d9980081b..100efcb825e 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1176,6 +1176,7 @@ impl<'help, 'cmd> Parser<'help, 'cmd> { } Ok(ParseResult::ValuesDone) } + #[allow(deprecated)] ArgAction::StoreValue => { if ident == Some(Identifier::Index) && arg.is_multiple_values_set() @@ -1205,6 +1206,7 @@ impl<'help, 'cmd> Parser<'help, 'cmd> { } Ok(ParseResult::ValuesDone) } + #[allow(deprecated)] ArgAction::IncOccurrence => { debug_assert_eq!(raw_vals, Vec::::new()); if source == ValueSource::CommandLine { @@ -1362,7 +1364,9 @@ impl<'help, 'cmd> Parser<'help, 'cmd> { } } else { match arg.get_action() { + #[allow(deprecated)] ArgAction::StoreValue => unreachable!("{:?} is not a flag", arg.get_id()), + #[allow(deprecated)] ArgAction::IncOccurrence => { debug!("Parser::add_env: Found a flag with value `{:?}`", val); let predicate = str_to_bool(val.to_str_lossy()); diff --git a/tests/derive/options.rs b/tests/derive/options.rs index 8c774bc0214..e6e33f3191d 100644 --- a/tests/derive/options.rs +++ b/tests/derive/options.rs @@ -82,18 +82,20 @@ fn option_with_raw_default() { #[test] fn option_from_str() { - #[derive(Debug, PartialEq)] + #[derive(Clone, Debug, PartialEq)] struct A; - impl<'a> From<&'a str> for A { - fn from(_: &str) -> A { - A + impl std::str::FromStr for A { + type Err = std::convert::Infallible; + + fn from_str(_: &str) -> Result { + Ok(A) } } #[derive(Debug, Parser, PartialEq)] struct Opt { - #[clap(parse(from_str))] + #[clap(value_parser)] a: Option, } @@ -106,18 +108,20 @@ fn option_from_str() { #[test] fn vec_from_str() { - #[derive(Debug, PartialEq)] + #[derive(Clone, Debug, PartialEq)] struct A; - impl<'a> From<&'a str> for A { - fn from(_: &str) -> A { - A + impl std::str::FromStr for A { + type Err = std::convert::Infallible; + + fn from_str(_: &str) -> Result { + Ok(A) } } #[derive(Debug, Parser, PartialEq)] struct Opt { - #[clap(parse(from_str))] + #[clap(value_parser)] a: Vec, } @@ -133,18 +137,20 @@ fn vec_from_str() { #[test] fn option_vec_from_str() { - #[derive(Debug, PartialEq)] + #[derive(Clone, Debug, PartialEq)] struct A; - impl<'a> From<&'a str> for A { - fn from(_: &str) -> A { - A + impl std::str::FromStr for A { + type Err = std::convert::Infallible; + + fn from_str(_: &str) -> Result { + Ok(A) } } #[derive(Debug, Parser, PartialEq)] struct Opt { - #[clap(short, parse(from_str))] + #[clap(short, value_parser)] a: Option>, } @@ -214,13 +220,13 @@ fn required_with_option_type() { #[test] fn ignore_qualified_option_type() { - fn parser(s: &str) -> Option { - Some(s.to_string()) + fn parser(s: &str) -> Result, std::convert::Infallible> { + Ok(Some(s.to_string())) } #[derive(Parser, PartialEq, Debug)] struct Opt { - #[clap(parse(from_str = parser))] + #[clap(value_parser = parser)] arg: ::std::option::Option, } @@ -387,13 +393,13 @@ fn vec_type_with_multiple_values_only() { #[test] fn ignore_qualified_vec_type() { - fn parser(s: &str) -> Vec { - vec![s.to_string()] + fn parser(s: &str) -> Result, std::convert::Infallible> { + Ok(vec![s.to_string()]) } #[derive(Parser, PartialEq, Debug)] struct Opt { - #[clap(parse(from_str = parser))] + #[clap(value_parser = parser)] arg: ::std::vec::Vec, } diff --git a/tests/derive/utf8.rs b/tests/derive/utf8.rs index 3aa91ac91ec..6389ef67168 100644 --- a/tests/derive/utf8.rs +++ b/tests/derive/utf8.rs @@ -77,13 +77,13 @@ fn invalid_utf8_strict_option_long_equals() { #[derive(Parser, Debug, PartialEq, Eq)] struct PositionalOs { - #[clap(parse(from_os_str))] + #[clap(value_parser)] arg: OsString, } #[derive(Parser, Debug, PartialEq, Eq)] struct NamedOs { - #[clap(short, long, parse(from_os_str))] + #[clap(short, long, value_parser)] arg: OsString, } From 0ba63664fe147f1b8525197fd42661e24eac477b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 6 Jun 2022 14:02:01 -0500 Subject: [PATCH 03/17] feat(builder): Offer u64 ranges --- src/builder/action.rs | 2 +- src/builder/arg.rs | 2 +- src/builder/mod.rs | 1 + src/builder/value_parser.rs | 217 +++++++++++++++++++++++++++++++++++- 4 files changed, 219 insertions(+), 3 deletions(-) diff --git a/src/builder/action.rs b/src/builder/action.rs index 4e435380161..9cdb1d9da6c 100644 --- a/src/builder/action.rs +++ b/src/builder/action.rs @@ -288,7 +288,7 @@ impl ArgAction { Self::IncOccurrence => None, Self::SetTrue => Some(super::ValueParser::bool()), Self::SetFalse => Some(super::ValueParser::bool()), - Self::Count => Some(crate::value_parser!(u64)), + Self::Count => Some(crate::value_parser!(u64).into()), Self::Help => None, Self::Version => None, } diff --git a/src/builder/arg.rs b/src/builder/arg.rs index 048fe2af066..46dd47bcfb8 100644 --- a/src/builder/arg.rs +++ b/src/builder/arg.rs @@ -1041,7 +1041,7 @@ impl<'help> Arg<'help> { /// - [`value_parser!`][crate::value_parser!] for auto-selecting a value parser for a given type /// - [`BoolishValueParser`][crate::builder::BoolishValueParser], and [`FalseyValueParser`][crate::builder::FalseyValueParser] for alternative `bool` implementations /// - [`NonEmptyStringValueParser`][crate::builder::NonEmptyStringValueParser] for basic validation for strings - /// - [`RangedI64ValueParser`][crate::builder::RangedI64ValueParser] for numeric ranges + /// - [`RangedI64ValueParser`][crate::builder::RangedI64ValueParser] and [`RangedU64ValueParser`][crate::builder::RangedU64ValueParser] for numeric ranges /// - [`ArgEnumValueParser`][crate::builder::ArgEnumValueParser] and [`PossibleValuesParser`][crate::builder::PossibleValuesParser] for static enumerated values /// - or any other [`TypedValueParser`][crate::builder::TypedValueParser] implementation /// diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 1f18ad51b0e..885b46a1be1 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -44,6 +44,7 @@ pub use value_parser::OsStringValueParser; pub use value_parser::PathBufValueParser; pub use value_parser::PossibleValuesParser; pub use value_parser::RangedI64ValueParser; +pub use value_parser::RangedU64ValueParser; pub use value_parser::StringValueParser; pub use value_parser::TypedValueParser; pub use value_parser::ValueParser; diff --git a/src/builder/value_parser.rs b/src/builder/value_parser.rs index 30b3610d0aa..01cf58e975f 100644 --- a/src/builder/value_parser.rs +++ b/src/builder/value_parser.rs @@ -81,7 +81,7 @@ impl ValueParser { /// Pre-existing implementations include: /// - [`ArgEnumValueParser`] and [`PossibleValuesParser`] for static enumerated values /// - [`BoolishValueParser`] and [`FalseyValueParser`] for alternative `bool` implementations - /// - [`RangedI64ValueParser`] + /// - [`RangedI64ValueParser`] and [`RangedU64ValueParser`] /// - [`NonEmptyStringValueParser`] /// /// # Example @@ -302,6 +302,8 @@ impl From

for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -330,6 +332,8 @@ impl From> for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -358,6 +362,8 @@ impl From> for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -386,6 +392,8 @@ impl From> for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -414,6 +422,8 @@ impl From> for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -442,6 +452,8 @@ impl From> for ValueParser { /// /// See [`RangedI64ValueParser`] for more control over the output type. /// +/// See also [`RangedU64ValueParser`] +/// /// # Examples /// /// ```rust @@ -1235,6 +1247,201 @@ impl> Default for RangedI64ValueParser { } } +/// Parse number that fall within a range of values +/// +/// # Example +/// +/// Usage: +/// ```rust +/// let mut cmd = clap::Command::new("raw") +/// .arg( +/// clap::Arg::new("port") +/// .long("port") +/// .value_parser(clap::value_parser!(u64).range(3000..)) +/// .takes_value(true) +/// .required(true) +/// ); +/// +/// let m = cmd.try_get_matches_from_mut(["cmd", "--port", "3001"]).unwrap(); +/// let port: u64 = *m.get_one("port") +/// .expect("required"); +/// assert_eq!(port, 3001); +/// ``` +/// +/// Semantics: +/// ```rust +/// # use std::ffi::OsStr; +/// # use clap::builder::TypedValueParser; +/// # let cmd = clap::Command::new("test"); +/// # let arg = None; +/// let value_parser = clap::builder::RangedU64ValueParser::::new().range(0..200); +/// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("random")).is_err()); +/// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("")).is_err()); +/// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("-200")).is_err()); +/// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("300")).is_err()); +/// assert!(value_parser.parse_ref(&cmd, arg, OsStr::new("-1")).is_err()); +/// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("0")).unwrap(), 0); +/// assert_eq!(value_parser.parse_ref(&cmd, arg, OsStr::new("50")).unwrap(), 50); +/// ``` +#[derive(Copy, Clone, Debug)] +pub struct RangedU64ValueParser = u64> { + bounds: (std::ops::Bound, std::ops::Bound), + target: std::marker::PhantomData, +} + +impl> RangedU64ValueParser { + /// Select full range of `u64` + pub fn new() -> Self { + Self::from(..) + } + + /// Narrow the supported range + pub fn range>(mut self, range: B) -> Self { + // Consideration: when the user does `value_parser!(u8).range()` + // - Avoid programming mistakes by accidentally expanding the range + // - Make it convenient to limit the range like with `..10` + let start = match range.start_bound() { + l @ std::ops::Bound::Included(i) => { + debug_assert!( + self.bounds.contains(i), + "{} must be in {:?}", + i, + self.bounds + ); + l.cloned() + } + l @ std::ops::Bound::Excluded(i) => { + debug_assert!( + self.bounds.contains(&i.saturating_add(1)), + "{} must be in {:?}", + i, + self.bounds + ); + l.cloned() + } + std::ops::Bound::Unbounded => self.bounds.start_bound().cloned(), + }; + let end = match range.end_bound() { + l @ std::ops::Bound::Included(i) => { + debug_assert!( + self.bounds.contains(i), + "{} must be in {:?}", + i, + self.bounds + ); + l.cloned() + } + l @ std::ops::Bound::Excluded(i) => { + debug_assert!( + self.bounds.contains(&i.saturating_sub(1)), + "{} must be in {:?}", + i, + self.bounds + ); + l.cloned() + } + std::ops::Bound::Unbounded => self.bounds.end_bound().cloned(), + }; + self.bounds = (start, end); + self + } + + fn format_bounds(&self) -> String { + let mut result = match self.bounds.0 { + std::ops::Bound::Included(i) => i.to_string(), + std::ops::Bound::Excluded(i) => i.saturating_add(1).to_string(), + std::ops::Bound::Unbounded => u64::MIN.to_string(), + }; + result.push_str(".."); + match self.bounds.1 { + std::ops::Bound::Included(i) => { + result.push('='); + result.push_str(&i.to_string()); + } + std::ops::Bound::Excluded(i) => { + result.push_str(&i.to_string()); + } + std::ops::Bound::Unbounded => { + result.push_str(&u64::MAX.to_string()); + } + } + result + } +} + +impl> TypedValueParser for RangedU64ValueParser +where + >::Error: Send + Sync + 'static + std::error::Error + ToString, +{ + type Value = T; + + fn parse_ref( + &self, + cmd: &crate::Command, + arg: Option<&crate::Arg>, + raw_value: &std::ffi::OsStr, + ) -> Result { + let value = raw_value.to_str().ok_or_else(|| { + crate::Error::invalid_utf8( + cmd, + crate::output::Usage::new(cmd).create_usage_with_title(&[]), + ) + })?; + let value = value.parse::().map_err(|err| { + let arg = arg + .map(|a| a.to_string()) + .unwrap_or_else(|| "...".to_owned()); + crate::Error::value_validation( + arg, + raw_value.to_string_lossy().into_owned(), + err.into(), + ) + .with_cmd(cmd) + })?; + if !self.bounds.contains(&value) { + let arg = arg + .map(|a| a.to_string()) + .unwrap_or_else(|| "...".to_owned()); + return Err(crate::Error::value_validation( + arg, + raw_value.to_string_lossy().into_owned(), + format!("{} is not in {}", value, self.format_bounds()).into(), + ) + .with_cmd(cmd)); + } + + let value: Result = value.try_into(); + let value = value.map_err(|err| { + let arg = arg + .map(|a| a.to_string()) + .unwrap_or_else(|| "...".to_owned()); + crate::Error::value_validation( + arg, + raw_value.to_string_lossy().into_owned(), + err.into(), + ) + .with_cmd(cmd) + })?; + + Ok(value) + } +} + +impl, B: RangeBounds> From for RangedU64ValueParser { + fn from(range: B) -> Self { + Self { + bounds: (range.start_bound().cloned(), range.end_bound().cloned()), + target: Default::default(), + } + } +} + +impl> Default for RangedU64ValueParser { + fn default() -> Self { + Self::new() + } +} + /// Implementation for [`ValueParser::bool`] /// /// Useful for composing new [`TypedValueParser`]s @@ -1687,6 +1894,12 @@ impl ValueParserFactory for i64 { RangedI64ValueParser::new() } } +impl ValueParserFactory for u64 { + type Parser = RangedU64ValueParser; + fn value_parser() -> Self::Parser { + RangedU64ValueParser::new() + } +} #[doc(hidden)] #[derive(Debug)] @@ -1784,6 +1997,8 @@ pub mod via_prelude { /// assert_eq!(format!("{:?}", parser), "ValueParser::path_buf"); /// let parser = clap::value_parser!(u16).range(3000..); /// assert_eq!(format!("{:?}", parser), "RangedI64ValueParser { bounds: (Included(3000), Included(65535)), target: PhantomData }"); +/// let parser = clap::value_parser!(u64).range(3000..); +/// assert_eq!(format!("{:?}", parser), "RangedU64ValueParser { bounds: (Included(3000), Unbounded), target: PhantomData }"); /// /// // FromStr types /// let parser = clap::value_parser!(usize); From a979cf9bb8c3ddeec212d31ef45665c61e9c0081 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 6 Jun 2022 14:09:24 -0500 Subject: [PATCH 04/17] fix(parser): Deprecate max_occurrences --- src/builder/arg.rs | 54 +++------------------------ tests/builder/multiple_occurrences.rs | 3 ++ 2 files changed, 8 insertions(+), 49 deletions(-) diff --git a/src/builder/arg.rs b/src/builder/arg.rs index 46dd47bcfb8..0f4190c27be 100644 --- a/src/builder/arg.rs +++ b/src/builder/arg.rs @@ -831,57 +831,13 @@ impl<'help> Arg<'help> { } } - /// The *maximum* number of occurrences for this argument. - /// - /// For example, if you had a - /// `-v` flag and you wanted up to 3 levels of verbosity you would set `.max_occurrences(3)`, and - /// this argument would be satisfied if the user provided it once or twice or thrice. - /// - /// **NOTE:** This implicitly sets [`Arg::multiple_occurrences(true)`] if the value is greater than 1. - /// # Examples - /// - /// ```rust - /// # use clap::{Command, Arg}; - /// Arg::new("verbosity") - /// .short('v') - /// .max_occurrences(3); - /// ``` - /// - /// Supplying less than the maximum number of arguments is allowed - /// - /// ```rust - /// # use clap::{Command, Arg}; - /// let res = Command::new("prog") - /// .arg(Arg::new("verbosity") - /// .max_occurrences(3) - /// .short('v')) - /// .try_get_matches_from(vec![ - /// "prog", "-vvv" - /// ]); - /// - /// assert!(res.is_ok()); - /// let m = res.unwrap(); - /// assert_eq!(m.occurrences_of("verbosity"), 3); - /// ``` - /// - /// Supplying more than the maximum number of arguments is an error - /// - /// ```rust - /// # use clap::{Command, Arg, ErrorKind}; - /// let res = Command::new("prog") - /// .arg(Arg::new("verbosity") - /// .max_occurrences(2) - /// .short('v')) - /// .try_get_matches_from(vec![ - /// "prog", "-vvv" - /// ]); - /// - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind(), ErrorKind::TooManyOccurrences); - /// ``` - /// [`Arg::multiple_occurrences(true)`]: Arg::multiple_occurrences() + /// Deprecated, for flags this is replaced with `action(ArgAction::Count).value_parser(value_parser!(u64).range(..max))` #[inline] #[must_use] + #[deprecated( + since = "3.2.0", + note = "For flags, replaced with `action(ArgAction::Count).value_parser(value_parser!(u64).range(..max))`" + )] pub fn max_occurrences(mut self, qty: usize) -> Self { self.max_occurs = Some(qty); if qty > 1 { diff --git a/tests/builder/multiple_occurrences.rs b/tests/builder/multiple_occurrences.rs index e3f087a96b0..d04149191a9 100644 --- a/tests/builder/multiple_occurrences.rs +++ b/tests/builder/multiple_occurrences.rs @@ -162,6 +162,7 @@ fn multiple_occurrences_of_after_env() { } #[test] +#[allow(deprecated)] fn max_occurrences_implies_multiple_occurrences() { let cmd = Command::new("prog").arg( Arg::new("verbose") @@ -189,6 +190,7 @@ fn max_occurrences_implies_multiple_occurrences() { } #[test] +#[allow(deprecated)] fn max_occurrences_try_inputs() { let cmd = Command::new("prog").arg( Arg::new("verbose") @@ -226,6 +228,7 @@ fn max_occurrences_try_inputs() { } #[test] +#[allow(deprecated)] fn max_occurrences_positional() { let cmd = Command::new("prog").arg(Arg::new("verbose").max_occurrences(3)); let m = cmd.clone().try_get_matches_from(vec!["prog", "v"]); From 1428785677cb109437af57f90ac4b10bb3f89f0b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 6 Jun 2022 14:55:43 -0500 Subject: [PATCH 05/17] fix(parser): Deprecate args_override_self This shouldn't be needed anymore now that this is effectively the new behavior for the non-deprecated actions. This was briefly talked about in https://github.com/clap-rs/clap/discussions/2627 but I wasn't familiar enough with the implementation to know how safe it is. Now, maintainrs and users can be more confident because they are explicitly opting into it. See also #3795 --- examples/tutorial_builder/02_app_settings.rs | 7 ++-- examples/tutorial_derive/02_app_settings.rs | 1 - src/builder/command.rs | 13 ++----- tests/builder/app_settings.rs | 13 +++++++ tests/builder/grouped_values.rs | 36 ++++++++++++++++++-- 5 files changed, 52 insertions(+), 18 deletions(-) diff --git a/examples/tutorial_builder/02_app_settings.rs b/examples/tutorial_builder/02_app_settings.rs index d969f537edf..7bbedd3ebbc 100644 --- a/examples/tutorial_builder/02_app_settings.rs +++ b/examples/tutorial_builder/02_app_settings.rs @@ -1,14 +1,13 @@ // Note: this requires the `cargo` feature -use clap::{arg, command, AppSettings}; +use clap::{arg, command, AppSettings, ArgAction}; fn main() { let matches = command!() - .args_override_self(true) .global_setting(AppSettings::DeriveDisplayOrder) .allow_negative_numbers(true) - .arg(arg!(--two )) - .arg(arg!(--one )) + .arg(arg!(--two ).action(ArgAction::Set)) + .arg(arg!(--one ).action(ArgAction::Set)) .get_matches(); println!( diff --git a/examples/tutorial_derive/02_app_settings.rs b/examples/tutorial_derive/02_app_settings.rs index 87965cd36bb..6a06929bbb7 100644 --- a/examples/tutorial_derive/02_app_settings.rs +++ b/examples/tutorial_derive/02_app_settings.rs @@ -2,7 +2,6 @@ use clap::{AppSettings, Parser}; #[derive(Parser)] #[clap(author, version, about, long_about = None)] -#[clap(args_override_self = true)] #[clap(allow_negative_numbers = true)] #[clap(global_setting(AppSettings::DeriveDisplayOrder))] struct Cli { diff --git a/src/builder/command.rs b/src/builder/command.rs index 10823c705df..26acde48182 100644 --- a/src/builder/command.rs +++ b/src/builder/command.rs @@ -910,17 +910,8 @@ impl<'help> App<'help> { } } - /// Specifies that all arguments override themselves. - /// - /// This is the equivalent to saying the `foo` arg using [`Arg::overrides_with("foo")`] for all - /// defined arguments. - /// - /// **NOTE:** This will not be applied when [`Arg::multiple_occurrences(true)`]. - /// - /// **NOTE:** This choice is propagated to all child subcommands. - /// - /// [`Arg::overrides_with("foo")`]: crate::Arg::overrides_with() - #[inline] + /// Deprecated, replaced with [`ArgAction::Set`][super::ArgAction::Set] + #[deprecated(since = "3.2.0", note = "Replaced with `Arg::action(ArgAction::Set)`")] pub fn args_override_self(self, yes: bool) -> Self { if yes { self.global_setting(AppSettings::AllArgsOverrideSelf) diff --git a/tests/builder/app_settings.rs b/tests/builder/app_settings.rs index 405532dc0ea..65aea9c1d69 100644 --- a/tests/builder/app_settings.rs +++ b/tests/builder/app_settings.rs @@ -1064,6 +1064,7 @@ fn built_in_subcommand_escaped() { #[test] fn aaos_flags() { + #![allow(deprecated)] // flags let res = Command::new("posix") .args_override_self(true) @@ -1077,6 +1078,7 @@ fn aaos_flags() { #[test] fn aaos_flags_mult() { + #![allow(deprecated)] // flags with multiple let res = Command::new("posix") .args_override_self(true) @@ -1090,6 +1092,7 @@ fn aaos_flags_mult() { #[test] fn aaos_opts() { + #![allow(deprecated)] // opts let res = Command::new("posix") .args_override_self(true) @@ -1107,6 +1110,7 @@ fn aaos_opts() { #[test] fn aaos_opts_w_other_overrides() { + #![allow(deprecated)] // opts with other overrides let res = Command::new("posix") .args_override_self(true) @@ -1130,6 +1134,7 @@ fn aaos_opts_w_other_overrides() { #[test] fn aaos_opts_w_other_overrides_rev() { + #![allow(deprecated)] // opts with other overrides, rev let res = Command::new("posix") .args_override_self(true) @@ -1152,6 +1157,7 @@ fn aaos_opts_w_other_overrides_rev() { #[test] fn aaos_opts_w_other_overrides_2() { + #![allow(deprecated)] // opts with other overrides let res = Command::new("posix") .args_override_self(true) @@ -1175,6 +1181,7 @@ fn aaos_opts_w_other_overrides_2() { #[test] fn aaos_opts_w_other_overrides_rev_2() { + #![allow(deprecated)] // opts with other overrides, rev let res = Command::new("posix") .args_override_self(true) @@ -1197,6 +1204,7 @@ fn aaos_opts_w_other_overrides_rev_2() { #[test] fn aaos_opts_w_override_as_conflict_1() { + #![allow(deprecated)] // opts with other overrides, rev let res = Command::new("posix") .args_override_self(true) @@ -1216,6 +1224,7 @@ fn aaos_opts_w_override_as_conflict_1() { #[test] fn aaos_opts_w_override_as_conflict_2() { + #![allow(deprecated)] // opts with other overrides, rev let res = Command::new("posix") .args_override_self(true) @@ -1238,6 +1247,7 @@ fn aaos_opts_w_override_as_conflict_2() { #[test] fn aaos_opts_mult() { + #![allow(deprecated)] // opts with multiple let res = Command::new("posix") .args_override_self(true) @@ -1264,6 +1274,7 @@ fn aaos_opts_mult() { #[test] fn aaos_opts_mult_req_delims() { + #![allow(deprecated)] // opts with multiple and require delims let res = Command::new("posix") .args_override_self(true) @@ -1293,6 +1304,7 @@ fn aaos_opts_mult_req_delims() { #[test] fn aaos_pos_mult() { + #![allow(deprecated)] // opts with multiple let res = Command::new("posix") .args_override_self(true) @@ -1313,6 +1325,7 @@ fn aaos_pos_mult() { #[test] fn aaos_option_use_delim_false() { + #![allow(deprecated)] let m = Command::new("posix") .args_override_self(true) .arg(arg!(--opt "some option").use_value_delimiter(false)) diff --git a/tests/builder/grouped_values.rs b/tests/builder/grouped_values.rs index acb750923c6..9bf2de0d64a 100644 --- a/tests/builder/grouped_values.rs +++ b/tests/builder/grouped_values.rs @@ -1,6 +1,6 @@ #![cfg(feature = "unstable-grouped")] -use clap::{Arg, Command}; +use clap::{Arg, ArgAction, Command}; #[test] fn grouped_value_works() { @@ -251,7 +251,8 @@ fn issue_1374() { } #[test] -fn issue_2171() { +fn issue_2171_deprecated() { + #![allow(deprecated)] let schema = Command::new("ripgrep#1701 reproducer") .args_override_self(true) .arg(Arg::new("pretty").short('p').long("pretty")) @@ -271,3 +272,34 @@ fn issue_2171() { let _ = schema.clone().try_get_matches_from(argv).unwrap(); } } + +#[test] +fn issue_2171() { + let schema = Command::new("ripgrep#1701 reproducer") + .arg( + Arg::new("pretty") + .short('p') + .long("pretty") + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new("search_zip") + .short('z') + .long("search-zip") + .action(ArgAction::SetTrue), + ); + + let test_args = &[ + vec!["reproducer", "-pz", "-p"], + vec!["reproducer", "-pzp"], + vec!["reproducer", "-zpp"], + vec!["reproducer", "-pp", "-z"], + vec!["reproducer", "-p", "-p", "-z"], + vec!["reproducer", "-p", "-pz"], + vec!["reproducer", "-ppz"], + ]; + + for argv in test_args { + let _ = schema.clone().try_get_matches_from(argv).unwrap(); + } +} From f0cc8b8d25078d8bea96eeab802275d7cb7c011b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 7 Jun 2022 13:20:16 -0500 Subject: [PATCH 06/17] test: Improve failure output --- tests/builder/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/builder/tests.rs b/tests/builder/tests.rs index f817c85af3a..0e6ac25e7c8 100644 --- a/tests/builder/tests.rs +++ b/tests/builder/tests.rs @@ -242,7 +242,7 @@ pub fn check_complex_output(args: &str, out: &str) { } let res = str::from_utf8(&w).unwrap(); - assert_eq!(res, out); + snapbox::assert_eq(out, res); } #[test] From 86a162d1bb5e531e48faf6f84de0bf99cd479382 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 6 Jun 2022 12:35:00 -0500 Subject: [PATCH 07/17] fix(parser): Deprecate occurrences_of This mostly exist for - Knowing of the value came from the command-line but we now have `ArgMatches::source` - Counting the number of flags but we now have `ArgAction::Count` --- clap_complete/src/dynamic.rs | 18 +- examples/multicall-busybox.rs | 2 +- examples/tutorial_builder/01_quick.rs | 16 +- examples/tutorial_builder/03_01_flag_count.rs | 13 +- src/parser/arg_matcher.rs | 3 + src/parser/matches/arg_matches.rs | 51 +--- src/parser/matches/matched_arg.rs | 3 + src/parser/parser.rs | 1 + src/parser/validator.rs | 1 + tests/builder/action.rs | 105 ++++++-- tests/builder/default_missing_vals.rs | 55 +++- tests/builder/delimiters.rs | 35 ++- tests/builder/env.rs | 70 ++++-- tests/builder/flags.rs | 10 +- tests/builder/grouped_values.rs | 20 +- tests/builder/multiple_occurrences.rs | 135 ++++++++-- tests/builder/multiple_values.rs | 237 ++++++++++++++---- tests/builder/opts.rs | 5 +- tests/builder/posix_compatible.rs | 35 ++- tests/builder/propagate_globals.rs | 15 +- tests/builder/tests.rs | 54 ++-- tests/builder/utils.rs | 8 +- 22 files changed, 654 insertions(+), 238 deletions(-) diff --git a/clap_complete/src/dynamic.rs b/clap_complete/src/dynamic.rs index 9112ac5ee8e..8902ab204ba 100644 --- a/clap_complete/src/dynamic.rs +++ b/clap_complete/src/dynamic.rs @@ -22,7 +22,7 @@ pub mod bash { #[derive(Clone, Debug)] pub struct CompleteArgs { /// Path to write completion-registration to - #[clap(long, required = true, parse(from_os_str))] + #[clap(long, required = true, value_parser)] register: Option, #[clap( @@ -30,34 +30,36 @@ pub mod bash { required = true, value_name = "COMP_CWORD", hide_short_help = true, - group = "complete" + group = "complete", + value_parser )] index: Option, - #[clap(long, hide_short_help = true, group = "complete")] + #[clap(long, hide_short_help = true, group = "complete", value_parser)] ifs: Option, #[clap( long = "type", required = true, - arg_enum, hide_short_help = true, - group = "complete" + group = "complete", + value_parser )] comp_type: Option, - #[clap(long, hide_short_help = true, group = "complete")] + #[clap(long, hide_short_help = true, group = "complete", action)] space: bool, #[clap( long, conflicts_with = "space", hide_short_help = true, - group = "complete" + group = "complete", + action )] no_space: bool, - #[clap(raw = true, hide_short_help = true, group = "complete")] + #[clap(raw = true, hide_short_help = true, group = "complete", value_parser)] comp_words: Vec, } diff --git a/examples/multicall-busybox.rs b/examples/multicall-busybox.rs index ec0d7600bf5..ec6a58025d6 100644 --- a/examples/multicall-busybox.rs +++ b/examples/multicall-busybox.rs @@ -35,7 +35,7 @@ fn main() { let matches = cmd.get_matches(); let mut subcommand = matches.subcommand(); if let Some(("busybox", cmd)) = subcommand { - if cmd.occurrences_of("install") > 0 { + if cmd.is_present("install") { unimplemented!("Make hardlinks to the executable here"); } subcommand = cmd.subcommand(); diff --git a/examples/tutorial_builder/01_quick.rs b/examples/tutorial_builder/01_quick.rs index 417f738026e..d30b433f4e4 100644 --- a/examples/tutorial_builder/01_quick.rs +++ b/examples/tutorial_builder/01_quick.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; -use clap::{arg, command, value_parser, Command}; +use clap::{arg, command, value_parser, ArgAction, Command}; fn main() { let matches = command!() @@ -15,9 +15,12 @@ fn main() { .required(false) .value_parser(value_parser!(PathBuf)), ) - .arg(arg!( - -d --debug ... "Turn debugging information on" - )) + .arg( + arg!( + -d --debug "Turn debugging information on" + ) + .action(ArgAction::Count), + ) .subcommand( Command::new("test") .about("does testing things") @@ -36,7 +39,10 @@ fn main() { // You can see how many times a particular flag or argument occurred // Note, only flags can have multiple occurrences - match matches.occurrences_of("debug") { + match matches + .get_one::("debug") + .expect("Count's are defaulted") + { 0 => println!("Debug mode is off"), 1 => println!("Debug mode is kind of on"), 2 => println!("Debug mode is on"), diff --git a/examples/tutorial_builder/03_01_flag_count.rs b/examples/tutorial_builder/03_01_flag_count.rs index c5532c07a44..387ab7ac59c 100644 --- a/examples/tutorial_builder/03_01_flag_count.rs +++ b/examples/tutorial_builder/03_01_flag_count.rs @@ -1,9 +1,16 @@ // Note: this requires the `cargo` feature -use clap::{arg, command}; +use clap::{arg, command, ArgAction}; fn main() { - let matches = command!().arg(arg!(-v --verbose ...)).get_matches(); + let matches = command!() + .arg(arg!(-v - -verbose).action(ArgAction::Count)) + .get_matches(); - println!("verbose: {:?}", matches.occurrences_of("verbose")); + println!( + "verbose: {:?}", + matches + .get_one::("verbose") + .expect("Count always defaulted") + ); } diff --git a/src/parser/arg_matcher.rs b/src/parser/arg_matcher.rs index b424863e4b7..2c94484a0af 100644 --- a/src/parser/arg_matcher.rs +++ b/src/parser/arg_matcher.rs @@ -171,6 +171,7 @@ impl ArgMatcher { let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg)); debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id())); ma.set_source(ValueSource::CommandLine); + #[allow(deprecated)] ma.inc_occurrences(); ma.new_val_group(); } @@ -180,6 +181,7 @@ impl ArgMatcher { let ma = self.entry(id).or_insert(MatchedArg::new_group()); debug_assert_eq!(ma.type_id(), None); ma.set_source(ValueSource::CommandLine); + #[allow(deprecated)] ma.inc_occurrences(); ma.new_val_group(); } @@ -197,6 +199,7 @@ impl ArgMatcher { ) ); ma.set_source(ValueSource::CommandLine); + #[allow(deprecated)] ma.inc_occurrences(); ma.new_val_group(); } diff --git a/src/parser/matches/arg_matches.rs b/src/parser/matches/arg_matches.rs index 6b27174f78b..b0b1bd6aa31 100644 --- a/src/parser/matches/arg_matches.rs +++ b/src/parser/matches/arg_matches.rs @@ -561,51 +561,14 @@ impl ArgMatches { value.and_then(MatchedArg::source) } - /// The number of times an argument was used at runtime. - /// - /// If an argument isn't present it will return `0`. - /// - /// **NOTE:** This returns the number of times the argument was used, *not* the number of - /// values. For example, `-o val1 val2 val3 -o val4` would return `2` (2 occurrences, but 4 - /// values). See [Arg::multiple_occurrences][crate::Arg::multiple_occurrences]. - /// - /// # Panics - /// - /// If `id` is is not a valid argument or group name. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{Command, Arg}; - /// let m = Command::new("myprog") - /// .arg(Arg::new("debug") - /// .short('d') - /// .multiple_occurrences(true)) - /// .get_matches_from(vec![ - /// "myprog", "-d", "-d", "-d" - /// ]); - /// - /// assert_eq!(m.occurrences_of("debug"), 3); - /// ``` - /// - /// This next example shows that counts actual uses of the argument, not just `-`'s - /// - /// ```rust - /// # use clap::{Command, Arg}; - /// let m = Command::new("myprog") - /// .arg(Arg::new("debug") - /// .short('d') - /// .multiple_occurrences(true)) - /// .arg(Arg::new("flag") - /// .short('f')) - /// .get_matches_from(vec![ - /// "myprog", "-ddfd" - /// ]); - /// - /// assert_eq!(m.occurrences_of("debug"), 3); - /// assert_eq!(m.occurrences_of("flag"), 1); - /// ``` + /// Deprecated, replaced with [`ArgMatches::get_many`]`.len()` or + /// [`ArgAction::Count`][crate::ArgAction]. + #[deprecated( + since = "3.2.0", + note = "Replaced with either `ArgMatches::get_many(...).len()` or `ArgAction::Count`" + )] pub fn occurrences_of(&self, id: T) -> u64 { + #![allow(deprecated)] self.get_arg(&Id::from(id)) .map_or(0, |a| a.get_occurrences()) } diff --git a/src/parser/matches/matched_arg.rs b/src/parser/matches/matched_arg.rs index 558e3098ca8..9de738f8830 100644 --- a/src/parser/matches/matched_arg.rs +++ b/src/parser/matches/matched_arg.rs @@ -67,14 +67,17 @@ impl MatchedArg { } } + #[deprecated(since = "3.2.0")] pub(crate) fn inc_occurrences(&mut self) { self.occurs += 1; } + #[deprecated(since = "3.2.0")] pub(crate) fn set_occurrences(&mut self, occurs: u64) { self.occurs = occurs } + #[deprecated(since = "3.2.0")] pub(crate) fn get_occurrences(&self) -> u64 { self.occurs } diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 100efcb825e..bdda730e888 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1197,6 +1197,7 @@ impl<'help, 'cmd> Parser<'help, 'cmd> { if ident == Some(Identifier::Index) && arg.is_multiple_values_set() { // HACK: Maintain existing occurrence behavior let matched = matcher.get_mut(&arg.id).unwrap(); + #[allow(deprecated)] matched.set_occurrences(matched.num_vals() as u64); } if cfg!(debug_assertions) && matcher.needs_more_vals(arg) { diff --git a/src/parser/validator.rs b/src/parser/validator.rs index a7456f917df..f075d1a71e3 100644 --- a/src/parser/validator.rs +++ b/src/parser/validator.rs @@ -330,6 +330,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> { } fn validate_arg_num_occurs(&self, a: &Arg, ma: &MatchedArg) -> ClapResult<()> { + #![allow(deprecated)] debug!( "Validator::validate_arg_num_occurs: {:?}={}", a.name, diff --git a/tests/builder/action.rs b/tests/builder/action.rs index 5c08590f06a..e6d426d4cb6 100644 --- a/tests/builder/action.rs +++ b/tests/builder/action.rs @@ -11,7 +11,10 @@ fn set() { let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(matches.get_one::("mammal"), None); assert_eq!(matches.is_present("mammal"), false); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), None); let matches = cmd @@ -20,7 +23,10 @@ fn set() { .unwrap(); assert_eq!(matches.get_one::("mammal").unwrap(), "dog"); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(2)); let matches = cmd @@ -29,7 +35,10 @@ fn set() { .unwrap(); assert_eq!(matches.get_one::("mammal").unwrap(), "cat"); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(4)); } @@ -40,7 +49,10 @@ fn append() { let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(matches.get_one::("mammal"), None); assert_eq!(matches.is_present("mammal"), false); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), None); let matches = cmd @@ -49,7 +61,10 @@ fn append() { .unwrap(); assert_eq!(matches.get_one::("mammal").unwrap(), "dog"); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!( matches.indices_of("mammal").unwrap().collect::>(), vec![2] @@ -68,7 +83,10 @@ fn append() { vec!["dog", "cat"] ); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!( matches.indices_of("mammal").unwrap().collect::>(), vec![2, 4] @@ -83,7 +101,10 @@ fn set_true() { let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), false); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -92,7 +113,10 @@ fn set_true() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), true); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -101,7 +125,10 @@ fn set_true() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), true); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(2)); } @@ -120,13 +147,19 @@ fn set_true_with_explicit_default_value() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), true); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), false); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); } @@ -225,7 +258,10 @@ fn set_false() { let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), true); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -234,7 +270,10 @@ fn set_false() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), false); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -243,7 +282,10 @@ fn set_false() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), false); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(2)); } @@ -262,13 +304,19 @@ fn set_false_with_explicit_default_value() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), false); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), true); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); } @@ -333,7 +381,10 @@ fn count() { let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), 0); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -342,7 +393,10 @@ fn count() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), 1); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd @@ -351,7 +405,10 @@ fn count() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), 2); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(2)); } @@ -370,13 +427,19 @@ fn count_with_explicit_default_value() { .unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), 1); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); assert_eq!(*matches.get_one::("mammal").unwrap(), 10); assert_eq!(matches.is_present("mammal"), true); - assert_eq!(matches.occurrences_of("mammal"), 0); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } assert_eq!(matches.index_of("mammal"), Some(1)); } diff --git a/tests/builder/default_missing_vals.rs b/tests/builder/default_missing_vals.rs index 1d2913e4d12..ecab4b82e36 100644 --- a/tests/builder/default_missing_vals.rs +++ b/tests/builder/default_missing_vals.rs @@ -19,7 +19,10 @@ fn opt_missing() { m.get_one::("color").map(|v| v.as_str()).unwrap(), "auto" ); - assert_eq!(m.occurrences_of("color"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("color"), 0); + } assert_eq!( m.value_source("color").unwrap(), clap::ValueSource::DefaultValue @@ -46,7 +49,10 @@ fn opt_present_with_missing_value() { m.get_one::("color").map(|v| v.as_str()).unwrap(), "always" ); - assert_eq!(m.occurrences_of("color"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("color"), 1); + } assert_eq!( m.value_source("color").unwrap(), clap::ValueSource::CommandLine @@ -73,7 +79,10 @@ fn opt_present_with_value() { m.get_one::("color").map(|v| v.as_str()).unwrap(), "never" ); - assert_eq!(m.occurrences_of("color"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("color"), 1); + } assert_eq!( m.value_source("color").unwrap(), clap::ValueSource::CommandLine @@ -99,7 +108,10 @@ fn opt_present_with_empty_value() { m.get_one::("color").map(|v| v.as_str()).unwrap(), "" ); - assert_eq!(m.occurrences_of("color"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("color"), 1); + } assert_eq!( m.value_source("color").unwrap(), clap::ValueSource::CommandLine @@ -164,7 +176,10 @@ fn default_missing_value_flag_value() { m.get_one::("flag").map(|v| v.as_str()), Some("false") ); - assert_eq!(m.occurrences_of("flag"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 0); + } assert_eq!( m.value_source("flag").unwrap(), clap::ValueSource::DefaultValue @@ -179,7 +194,10 @@ fn default_missing_value_flag_value() { m.get_one::("flag").map(|v| v.as_str()), Some("true") ); - assert_eq!(m.occurrences_of("flag"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } assert_eq!( m.value_source("flag").unwrap(), clap::ValueSource::CommandLine @@ -194,7 +212,10 @@ fn default_missing_value_flag_value() { m.get_one::("flag").map(|v| v.as_str()), Some("true") ); - assert_eq!(m.occurrences_of("flag"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } assert_eq!( m.value_source("flag").unwrap(), clap::ValueSource::CommandLine @@ -206,7 +227,10 @@ fn default_missing_value_flag_value() { m.get_one::("flag").map(|v| v.as_str()), Some("false") ); - assert_eq!(m.occurrences_of("flag"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } assert_eq!( m.value_source("flag").unwrap(), clap::ValueSource::CommandLine @@ -233,7 +257,10 @@ fn delimited_missing_value() { .collect::>(), vec!["one", "two"] ); - assert_eq!(m.occurrences_of("flag"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 0); + } let m = cmd.try_get_matches_from(["test", "--flag"]).unwrap(); assert_eq!( @@ -243,7 +270,10 @@ fn delimited_missing_value() { .collect::>(), vec!["three", "four"] ); - assert_eq!(m.occurrences_of("flag"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } } #[cfg(debug_assertions)] @@ -295,7 +325,10 @@ fn valid_index() { m.get_one::("color").map(|v| v.as_str()).unwrap(), "always" ); - assert_eq!(m.occurrences_of("color"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("color"), 1); + } assert_eq!( m.value_source("color").unwrap(), clap::ValueSource::CommandLine diff --git a/tests/builder/delimiters.rs b/tests/builder/delimiters.rs index 81f5dac8d87..fbbc1b6622c 100644 --- a/tests/builder/delimiters.rs +++ b/tests/builder/delimiters.rs @@ -10,7 +10,10 @@ fn opt_default_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -27,7 +30,10 @@ fn opt_eq_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -44,7 +50,10 @@ fn opt_s_eq_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -61,7 +70,10 @@ fn opt_s_default_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -78,7 +90,10 @@ fn opt_s_no_space_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -100,7 +115,10 @@ fn opt_s_no_space_mult_no_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -123,7 +141,10 @@ fn opt_eq_mult_def_delim() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() diff --git a/tests/builder/env.rs b/tests/builder/env.rs index 28129df32dc..9b4538cd5e4 100644 --- a/tests/builder/env.rs +++ b/tests/builder/env.rs @@ -16,7 +16,10 @@ fn env() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" @@ -37,7 +40,10 @@ fn env_bool_literal() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("present")); - assert_eq!(m.occurrences_of("present"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("present"), 0); + } assert_eq!(m.get_one::("present").map(|v| v.as_str()), None); assert!(!m.is_present("negated")); assert!(!m.is_present("absent")); @@ -58,7 +64,10 @@ fn env_os() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" @@ -82,7 +91,10 @@ fn no_env() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(!m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!(m.get_one::("arg").map(|v| v.as_str()), None); } @@ -99,7 +111,10 @@ fn no_env_no_takes_value() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(!m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!(m.get_one::("arg").map(|v| v.as_str()), None); } @@ -119,7 +134,10 @@ fn with_default() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" @@ -141,7 +159,10 @@ fn opt_user_override() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 1); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "opt" @@ -171,7 +192,10 @@ fn positionals() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" @@ -193,7 +217,10 @@ fn positionals_user_override() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 1); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "opt" @@ -225,7 +252,10 @@ fn multiple_one() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_many::("arg") .unwrap() @@ -252,7 +282,10 @@ fn multiple_three() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_many::("arg") .unwrap() @@ -278,7 +311,10 @@ fn multiple_no_delimiter() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_many::("arg") .unwrap() @@ -304,7 +340,10 @@ fn possible_value() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" @@ -349,7 +388,10 @@ fn value_parser() { assert!(r.is_ok(), "{}", r.unwrap_err()); let m = r.unwrap(); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("arg"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("arg"), 0); + } assert_eq!( m.get_one::("arg").map(|v| v.as_str()).unwrap(), "env" diff --git a/tests/builder/flags.rs b/tests/builder/flags.rs index eb14a6e5ec2..fcd3a2e021e 100644 --- a/tests/builder/flags.rs +++ b/tests/builder/flags.rs @@ -56,7 +56,10 @@ fn lots_o_flags_sep() { assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); let m = r.unwrap(); assert!(m.is_present("o")); - assert_eq!(m.occurrences_of("o"), 297); // i.e. more than u8 + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("o"), 297); + } // i.e. more than u8 } #[test] @@ -74,7 +77,10 @@ fn lots_o_flags_combined() { assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); let m = r.unwrap(); assert!(m.is_present("o")); - assert_eq!(m.occurrences_of("o"), 297); // i.e. more than u8 + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("o"), 297); + } // i.e. more than u8 } #[test] diff --git a/tests/builder/grouped_values.rs b/tests/builder/grouped_values.rs index 9bf2de0d64a..4b5706c8fe0 100644 --- a/tests/builder/grouped_values.rs +++ b/tests/builder/grouped_values.rs @@ -191,10 +191,16 @@ fn grouped_interleaved_positional_values() { .unwrap(); let pos: Vec<_> = m.grouped_values_of("pos").unwrap().collect(); assert_eq!(pos, vec![vec!["1", "2", "3", "4"]]); - assert_eq!(m.occurrences_of("pos"), 4); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos"), 4); + } let flag: Vec<_> = m.grouped_values_of("flag").unwrap().collect(); assert_eq!(flag, vec![vec!["a"], vec!["b"]]); - assert_eq!(m.occurrences_of("flag"), 2); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 2); + } } #[test] @@ -214,10 +220,16 @@ fn grouped_interleaved_positional_occurrences() { .unwrap(); let pos: Vec<_> = m.grouped_values_of("pos").unwrap().collect(); assert_eq!(pos, vec![vec!["1"], vec!["2"], vec!["3"], vec!["4"]]); - assert_eq!(m.occurrences_of("pos"), 4); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos"), 4); + } let flag: Vec<_> = m.grouped_values_of("flag").unwrap().collect(); assert_eq!(flag, vec![vec!["a"], vec!["b"]]); - assert_eq!(m.occurrences_of("flag"), 2); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 2); + } } #[test] diff --git a/tests/builder/multiple_occurrences.rs b/tests/builder/multiple_occurrences.rs index d04149191a9..fac64d0a983 100644 --- a/tests/builder/multiple_occurrences.rs +++ b/tests/builder/multiple_occurrences.rs @@ -8,9 +8,15 @@ fn multiple_occurrences_of_flags_long() { .try_get_matches_from(vec!["", "--multflag", "--flag", "--multflag"]) .unwrap(); assert!(m.is_present("multflag")); - assert_eq!(m.occurrences_of("multflag"), 2); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("multflag"), 2); + } assert!(m.is_present("flag")); - assert_eq!(m.occurrences_of("flag"), 1) + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } } #[test] @@ -21,9 +27,15 @@ fn multiple_occurrences_of_flags_short() { .try_get_matches_from(vec!["", "-m", "-f", "-m"]) .unwrap(); assert!(m.is_present("multflag")); - assert_eq!(m.occurrences_of("multflag"), 2); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("multflag"), 2); + } assert!(m.is_present("flag")); - assert_eq!(m.occurrences_of("flag"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } } #[test] @@ -43,11 +55,20 @@ fn multiple_occurrences_of_flags_mixed() { ]) .unwrap(); assert!(m.is_present("multflag1")); - assert_eq!(m.occurrences_of("multflag1"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("multflag1"), 3); + } assert!(m.is_present("multflag2")); - assert_eq!(m.occurrences_of("multflag2"), 2); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("multflag2"), 2); + } assert!(m.is_present("flag")); - assert_eq!(m.occurrences_of("flag"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } } #[test] @@ -59,7 +80,10 @@ fn multiple_occurrences_of_positional() { .try_get_matches_from(&["test"]) .expect("zero occurrences work"); assert!(!m.is_present("multi")); - assert_eq!(m.occurrences_of("multi"), 0); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("multi"), 0); + } assert!(m.get_many::("multi").is_none()); let m = cmd @@ -67,7 +91,10 @@ fn multiple_occurrences_of_positional() { .try_get_matches_from(&["test", "one"]) .expect("single occurrence work"); assert!(m.is_present("multi")); - assert_eq!(m.occurrences_of("multi"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("multi"), 1); + } assert_eq!( m.get_many::("multi") .unwrap() @@ -81,7 +108,10 @@ fn multiple_occurrences_of_positional() { .try_get_matches_from(&["test", "one", "two", "three", "four"]) .expect("many occurrences work"); assert!(m.is_present("multi")); - assert_eq!(m.occurrences_of("multi"), 4); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("multi"), 4); + } assert_eq!( m.get_many::("multi") .unwrap() @@ -102,7 +132,10 @@ fn multiple_occurrences_of_flags_large_quantity() { .try_get_matches_from(args) .unwrap(); assert!(m.is_present("multflag")); - assert_eq!(m.occurrences_of("multflag"), 1024); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("multflag"), 1024); + } } #[cfg(feature = "env")] @@ -119,18 +152,30 @@ fn multiple_occurrences_of_before_env() { let m = cmd.clone().try_get_matches_from(vec![""]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 0); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 0); + } let m = cmd.clone().try_get_matches_from(vec!["", "-v"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 1); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 1); + } let m = cmd.clone().try_get_matches_from(vec!["", "-vv"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 2); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 2); + } let m = cmd.clone().try_get_matches_from(vec!["", "-vvv"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + } } #[cfg(feature = "env")] @@ -147,18 +192,30 @@ fn multiple_occurrences_of_after_env() { let m = cmd.clone().try_get_matches_from(vec![""]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 0); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 0); + } let m = cmd.clone().try_get_matches_from(vec!["", "-v"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 1); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 1); + } let m = cmd.clone().try_get_matches_from(vec!["", "-vv"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 2); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 2); + } let m = cmd.clone().try_get_matches_from(vec!["", "-vvv"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + } } #[test] @@ -173,7 +230,10 @@ fn max_occurrences_implies_multiple_occurrences() { let m = cmd.try_get_matches_from(vec!["prog", "-vvv"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + } // One max should not imply multiple occurrences let cmd = Command::new("prog").arg( @@ -200,15 +260,24 @@ fn max_occurrences_try_inputs() { ); let m = cmd.clone().try_get_matches_from(vec!["prog", "-v"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 1); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 1); + } let m = cmd.clone().try_get_matches_from(vec!["prog", "-vv"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 2); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 2); + } let m = cmd.clone().try_get_matches_from(vec!["prog", "-vvv"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + } let m = cmd.clone().try_get_matches_from(vec!["prog", "-vvvv"]); assert!(m.is_err()); @@ -218,7 +287,10 @@ fn max_occurrences_try_inputs() { .clone() .try_get_matches_from(vec!["prog", "-v", "-v", "-v"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + } let m = cmd .clone() @@ -233,17 +305,26 @@ fn max_occurrences_positional() { let cmd = Command::new("prog").arg(Arg::new("verbose").max_occurrences(3)); let m = cmd.clone().try_get_matches_from(vec!["prog", "v"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 1); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 1); + } let m = cmd.clone().try_get_matches_from(vec!["prog", "v", "v"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 2); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 2); + } let m = cmd .clone() .try_get_matches_from(vec!["prog", "v", "v", "v"]); assert!(m.is_ok(), "{}", m.unwrap_err()); - assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + #[allow(deprecated)] + { + assert_eq!(m.unwrap().occurrences_of("verbose"), 3); + } let m = cmd .clone() diff --git a/tests/builder/multiple_values.rs b/tests/builder/multiple_values.rs index 6f33b65f229..84d2cd412da 100644 --- a/tests/builder/multiple_values.rs +++ b/tests/builder/multiple_values.rs @@ -19,7 +19,10 @@ fn option_long() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 3); + } assert_eq!( m.get_many::("option") .unwrap() @@ -46,7 +49,10 @@ fn option_short() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 3); + } assert_eq!( m.get_many::("option") .unwrap() @@ -76,7 +82,10 @@ fn option_mixed() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 4); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 4); + } assert_eq!( m.get_many::("option") .unwrap() @@ -102,7 +111,10 @@ fn option_exact_exact() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 3); + } assert_eq!( m.get_many::("option") .unwrap() @@ -127,7 +139,10 @@ fn option_exact_exact_not_mult() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -155,7 +170,10 @@ fn option_exact_exact_mult() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 2); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 2); + } assert_eq!( m.get_many::("option") .unwrap() @@ -215,7 +233,10 @@ fn option_min_exact() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 3); + } assert_eq!( m.get_many::("option") .unwrap() @@ -261,7 +282,10 @@ fn option_short_min_more_mult_occurs() { assert!(m.is_present("option")); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("option"), 4); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 4); + } assert_eq!( m.get_many::("option") .unwrap() @@ -289,7 +313,10 @@ fn option_short_min_more_single_occur() { assert!(m.is_present("option")); assert!(m.is_present("arg")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -316,7 +343,10 @@ fn option_max_exact() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 3); + } assert_eq!( m.get_many::("option") .unwrap() @@ -342,7 +372,10 @@ fn option_max_less() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 2); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 2); + } assert_eq!( m.get_many::("option") .unwrap() @@ -385,7 +418,10 @@ fn positional() { let m = m.unwrap(); assert!(m.is_present("pos")); - assert_eq!(m.occurrences_of("pos"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos"), 3); + } assert_eq!( m.get_many::("pos") .unwrap() @@ -409,7 +445,10 @@ fn positional_exact_exact() { let m = m.unwrap(); assert!(m.is_present("pos")); - assert_eq!(m.occurrences_of("pos"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos"), 3); + } assert_eq!( m.get_many::("pos") .unwrap() @@ -457,7 +496,10 @@ fn positional_min_exact() { let m = m.unwrap(); assert!(m.is_present("pos")); - assert_eq!(m.occurrences_of("pos"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos"), 3); + } assert_eq!( m.get_many::("pos") .unwrap() @@ -487,7 +529,10 @@ fn positional_min_more() { let m = m.unwrap(); assert!(m.is_present("pos")); - assert_eq!(m.occurrences_of("pos"), 4); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos"), 4); + } assert_eq!( m.get_many::("pos") .unwrap() @@ -507,7 +552,10 @@ fn positional_max_exact() { let m = m.unwrap(); assert!(m.is_present("pos")); - assert_eq!(m.occurrences_of("pos"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos"), 3); + } assert_eq!( m.get_many::("pos") .unwrap() @@ -527,7 +575,10 @@ fn positional_max_less() { let m = m.unwrap(); assert!(m.is_present("pos")); - assert_eq!(m.occurrences_of("pos"), 2); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos"), 2); + } assert_eq!( m.get_many::("pos") .unwrap() @@ -562,7 +613,10 @@ fn sep_long_equals() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -587,7 +641,10 @@ fn sep_long_space() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -612,7 +669,10 @@ fn sep_short_equals() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -637,7 +697,10 @@ fn sep_short_space() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -662,7 +725,10 @@ fn sep_short_no_space() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -686,7 +752,10 @@ fn sep_positional() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -711,7 +780,10 @@ fn different_sep() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -735,7 +807,10 @@ fn different_sep_positional() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -761,7 +836,10 @@ fn no_sep() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -783,7 +861,10 @@ fn no_sep_positional() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_one::("option").map(|v| v.as_str()).unwrap(), "val1,val2,val3" @@ -812,7 +893,10 @@ fn req_delimiter_long() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -851,7 +935,10 @@ fn req_delimiter_long_with_equal() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -890,7 +977,10 @@ fn req_delimiter_short_with_space() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -929,7 +1019,10 @@ fn req_delimiter_short_with_no_space() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -968,7 +1061,10 @@ fn req_delimiter_short_with_equal() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 1); + } assert_eq!( m.get_many::("option") .unwrap() @@ -1036,7 +1132,10 @@ fn req_delimiter_complex() { let m = m.unwrap(); assert!(m.is_present("option")); - assert_eq!(m.occurrences_of("option"), 10); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("option"), 10); + } assert_eq!( m.get_many::("option") .unwrap() @@ -1139,9 +1238,15 @@ fn low_index_positional() { let m = m.unwrap(); assert!(m.is_present("files")); - assert_eq!(m.occurrences_of("files"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("files"), 3); + } assert!(m.is_present("target")); - assert_eq!(m.occurrences_of("target"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("target"), 1); + } assert_eq!( m.get_many::("files") .unwrap() @@ -1176,9 +1281,15 @@ fn low_index_positional_in_subcmd() { let sm = m.subcommand_matches("test").unwrap(); assert!(sm.is_present("files")); - assert_eq!(sm.occurrences_of("files"), 3); + #[allow(deprecated)] + { + assert_eq!(sm.occurrences_of("files"), 3); + } assert!(sm.is_present("target")); - assert_eq!(sm.occurrences_of("target"), 1); + #[allow(deprecated)] + { + assert_eq!(sm.occurrences_of("target"), 1); + } assert_eq!( sm.get_many::("files") .unwrap() @@ -1212,9 +1323,15 @@ fn low_index_positional_with_option() { let m = m.unwrap(); assert!(m.is_present("files")); - assert_eq!(m.occurrences_of("files"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("files"), 3); + } assert!(m.is_present("target")); - assert_eq!(m.occurrences_of("target"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("target"), 1); + } assert_eq!( m.get_many::("files") .unwrap() @@ -1250,9 +1367,15 @@ fn low_index_positional_with_flag() { let m = m.unwrap(); assert!(m.is_present("files")); - assert_eq!(m.occurrences_of("files"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("files"), 3); + } assert!(m.is_present("target")); - assert_eq!(m.occurrences_of("target"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("target"), 1); + } assert_eq!( m.get_many::("files") .unwrap() @@ -1284,7 +1407,10 @@ fn multiple_value_terminator_option() { let m = m.unwrap(); assert!(m.is_present("other")); - assert_eq!(m.occurrences_of("other"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("other"), 1); + } assert!(m.is_present("files")); assert_eq!( m.get_many::("files") @@ -1498,7 +1624,10 @@ fn values_per_occurrence_named() { .collect::>(), ["val1", "val2"] ); - assert_eq!(m.occurrences_of("pos"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos"), 1); + } let m = a.try_get_matches_from_mut(vec![ "myprog", "--pos", "val1", "val2", "--pos", "val3", "val4", @@ -1514,7 +1643,10 @@ fn values_per_occurrence_named() { .collect::>(), ["val1", "val2", "val3", "val4"] ); - assert_eq!(m.occurrences_of("pos"), 2); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos"), 2); + } } #[test] @@ -1537,7 +1669,10 @@ fn values_per_occurrence_positional() { .collect::>(), ["val1", "val2"] ); - assert_eq!(m.occurrences_of("pos"), 2); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos"), 2); + } let m = a.try_get_matches_from_mut(vec!["myprog", "val1", "val2", "val3", "val4"]); let m = match m { @@ -1551,7 +1686,7 @@ fn values_per_occurrence_positional() { .collect::>(), ["val1", "val2", "val3", "val4"] ); - //assert_eq!(m.occurrences_of("pos"), 2); // Fails, we don't recognize this as two occurrences + //#[allow(deprecated)]{assert_eq!(m.occurrences_of("pos"), 2);} // Fails, we don't recognize this as two occurrences } // Theoretically we could support this but we aren't tracking occurrence boundaries for positionals @@ -1566,7 +1701,10 @@ fn positional_parser_advances() { assert!(m.is_ok(), "{}", m.unwrap_err()); let m = m.unwrap(); - assert_eq!(m.occurrences_of("pos1"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos1"), 1); + } assert_eq!( m.get_many::("pos1") .unwrap() @@ -1575,7 +1713,10 @@ fn positional_parser_advances() { ["val1", "val2"] ); - assert_eq!(m.occurrences_of("pos2"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("pos2"), 1); + } assert_eq!( m.get_many::("pos2") .unwrap() diff --git a/tests/builder/opts.rs b/tests/builder/opts.rs index 53fd6fa6448..858892f0c6f 100644 --- a/tests/builder/opts.rs +++ b/tests/builder/opts.rs @@ -520,7 +520,10 @@ fn issue_1047_min_zero_vals_default_val() { ) .try_get_matches_from(vec!["foo", "-d"]) .unwrap(); - assert_eq!(m.occurrences_of("del"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("del"), 1); + } assert_eq!( m.get_one::("del").map(|v| v.as_str()), Some("default") diff --git a/tests/builder/posix_compatible.rs b/tests/builder/posix_compatible.rs index 7cdd10ec08a..db1c8f8940c 100644 --- a/tests/builder/posix_compatible.rs +++ b/tests/builder/posix_compatible.rs @@ -12,7 +12,10 @@ fn flag_overrides_itself() { assert!(res.is_ok(), "{}", res.unwrap_err()); let m = res.unwrap(); assert!(m.is_present("flag")); - assert_eq!(m.occurrences_of("flag"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 1); + } } #[test] @@ -23,7 +26,10 @@ fn mult_flag_overrides_itself() { assert!(res.is_ok(), "{}", res.unwrap_err()); let m = res.unwrap(); assert!(m.is_present("flag")); - assert_eq!(m.occurrences_of("flag"), 4); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("flag"), 4); + } } #[test] @@ -38,7 +44,10 @@ fn option_overrides_itself() { assert!(res.is_ok(), "{}", res.unwrap_err()); let m = res.unwrap(); assert!(m.is_present("opt")); - assert_eq!(m.occurrences_of("opt"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("opt"), 1); + } assert_eq!( m.get_one::("opt").map(|v| v.as_str()), Some("other") @@ -61,7 +70,10 @@ fn mult_option_require_delim_overrides_itself() { assert!(res.is_ok(), "{}", res.unwrap_err()); let m = res.unwrap(); assert!(m.is_present("opt")); - assert_eq!(m.occurrences_of("opt"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("opt"), 3); + } assert_eq!( m.get_many::("opt") .unwrap() @@ -93,7 +105,10 @@ fn mult_option_overrides_itself() { assert!(res.is_ok(), "{}", res.unwrap_err()); let m = res.unwrap(); assert!(m.is_present("opt")); - assert_eq!(m.occurrences_of("opt"), 2); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("opt"), 2); + } assert_eq!( m.get_many::("opt") .unwrap() @@ -114,7 +129,10 @@ fn option_use_delim_false_override_itself() { .try_get_matches_from(vec!["", "--opt=some,other", "--opt=one,two"]) .unwrap(); assert!(m.is_present("opt")); - assert_eq!(m.occurrences_of("opt"), 1); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("opt"), 1); + } assert_eq!( m.get_many::("opt") .unwrap() @@ -133,7 +151,10 @@ fn pos_mult_overrides_itself() { assert!(res.is_ok(), "{}", res.unwrap_err()); let m = res.unwrap(); assert!(m.is_present("val")); - assert_eq!(m.occurrences_of("val"), 3); + #[allow(deprecated)] + { + assert_eq!(m.occurrences_of("val"), 3); + } assert_eq!( m.get_many::("val") .unwrap() diff --git a/tests/builder/propagate_globals.rs b/tests/builder/propagate_globals.rs index 68ec2a1d62c..b47f022a9dd 100644 --- a/tests/builder/propagate_globals.rs +++ b/tests/builder/propagate_globals.rs @@ -55,17 +55,26 @@ fn outer_can_access_arg>>(m: &ArgMatches, val: T) - } fn top_can_access_flag(m: &ArgMatches, present: bool, occurrences: u64) -> bool { - (m.is_present("GLOBAL_FLAG") == present) && (m.occurrences_of("GLOBAL_FLAG") == occurrences) + #[allow(deprecated)] + { + (m.is_present("GLOBAL_FLAG") == present) && (m.occurrences_of("GLOBAL_FLAG") == occurrences) + } } fn inner_can_access_flag(m: &ArgMatches, present: bool, occurrences: u64) -> bool { let m = get_inner_matches(m); - (m.is_present("GLOBAL_FLAG") == present) && (m.occurrences_of("GLOBAL_FLAG") == occurrences) + #[allow(deprecated)] + { + (m.is_present("GLOBAL_FLAG") == present) && (m.occurrences_of("GLOBAL_FLAG") == occurrences) + } } fn outer_can_access_flag(m: &ArgMatches, present: bool, occurrences: u64) -> bool { let m = get_outer_matches(m); - (m.is_present("GLOBAL_FLAG") == present) && (m.occurrences_of("GLOBAL_FLAG") == occurrences) + #[allow(deprecated)] + { + (m.is_present("GLOBAL_FLAG") == present) && (m.occurrences_of("GLOBAL_FLAG") == occurrences) + } } #[test] diff --git a/tests/builder/tests.rs b/tests/builder/tests.rs index 0e6ac25e7c8..d8b6d69a81d 100644 --- a/tests/builder/tests.rs +++ b/tests/builder/tests.rs @@ -40,7 +40,7 @@ scpositional present with value: value "; static O2P: &str = "flag NOT present -option present 2 times with value: some +option present with value: some An option: some An option: other positional present with value: value @@ -49,7 +49,7 @@ option2 maybe present with value of: Nothing positional2 maybe present with value of: Nothing option3 NOT present positional3 NOT present -option present 2 times with value: some +option present with value: some An option: some An option: other positional present with value: value @@ -57,7 +57,7 @@ subcmd NOT present "; static F2OP: &str = "flag present 2 times -option present 1 times with value: some +option present with value: some An option: some positional present with value: value flag2 NOT present @@ -65,14 +65,14 @@ option2 maybe present with value of: Nothing positional2 maybe present with value of: Nothing option3 NOT present positional3 NOT present -option present 1 times with value: some +option present with value: some An option: some positional present with value: value subcmd NOT present "; static FOP: &str = "flag present 1 times -option present 1 times with value: some +option present with value: some An option: some positional present with value: value flag2 NOT present @@ -80,7 +80,7 @@ option2 maybe present with value of: Nothing positional2 maybe present with value of: Nothing option3 NOT present positional3 NOT present -option present 1 times with value: some +option present with value: some An option: some positional present with value: value subcmd NOT present @@ -91,21 +91,18 @@ pub fn check_complex_output(args: &str, out: &str) { let matches = utils::complex_app() .try_get_matches_from(args.split(' ').collect::>()) .unwrap(); - if matches.is_present("flag") { - writeln!(w, "flag present {} times", matches.occurrences_of("flag")).unwrap(); - } else { - writeln!(w, "flag NOT present").unwrap(); + match matches.get_one::("flag").unwrap() { + 0 => { + writeln!(w, "flag NOT present").unwrap(); + } + n => { + writeln!(w, "flag present {} times", n).unwrap(); + } } if matches.is_present("option") { if let Some(v) = matches.get_one::("option").map(|v| v.as_str()) { - writeln!( - w, - "option present {} times with value: {}", - matches.occurrences_of("option"), - v - ) - .unwrap(); + writeln!(w, "option present with value: {}", v).unwrap(); } if let Some(ov) = matches.get_many::("option") { for o in ov { @@ -186,13 +183,7 @@ pub fn check_complex_output(args: &str, out: &str) { if matches.is_present("option") { if let Some(v) = matches.get_one::("option").map(|v| v.as_str()) { - writeln!( - w, - "option present {} times with value: {}", - matches.occurrences_of("option"), - v - ) - .unwrap(); + writeln!(w, "option present with value: {}", v).unwrap(); } if let Some(ov) = matches.get_many::("option") { for o in ov { @@ -211,10 +202,13 @@ pub fn check_complex_output(args: &str, out: &str) { if let Some("subcmd") = matches.subcommand_name() { writeln!(w, "subcmd present").unwrap(); if let Some(matches) = matches.subcommand_matches("subcmd") { - if matches.is_present("flag") { - writeln!(w, "flag present {} times", matches.occurrences_of("flag")).unwrap(); - } else { - writeln!(w, "flag NOT present").unwrap(); + match matches.get_one::("flag").unwrap() { + 0 => { + writeln!(w, "flag NOT present").unwrap(); + } + n => { + writeln!(w, "flag present {} times", n).unwrap(); + } } if matches.is_present("option") { @@ -267,7 +261,7 @@ fn flag_x2_opt() { check_complex_output( "clap-test value -f -f -o some", "flag present 2 times -option present 1 times with value: some +option present with value: some An option: some positional present with value: value flag2 NOT present @@ -275,7 +269,7 @@ option2 maybe present with value of: Nothing positional2 maybe present with value of: Nothing option3 NOT present positional3 NOT present -option present 1 times with value: some +option present with value: some An option: some positional present with value: value subcmd NOT present diff --git a/tests/builder/utils.rs b/tests/builder/utils.rs index 16e738179cf..c3254d2c18e 100644 --- a/tests/builder/utils.rs +++ b/tests/builder/utils.rs @@ -5,7 +5,7 @@ use std::str; use regex::Regex; -use clap::{arg, Arg, ArgGroup, Command}; +use clap::{arg, Arg, ArgAction, ArgGroup, Command}; #[track_caller] pub fn assert_eq(expected: S, actual: S2) @@ -55,7 +55,11 @@ pub fn complex_app() -> Command<'static> { .multiple_occurrences(true), ) .arg(arg!([positional] "tests positionals")) - .arg(arg!(-f --flag ... "tests flags").global(true)) + .arg( + arg!(-f --flag "tests flags") + .action(ArgAction::Count) + .global(true), + ) .args(&[ arg!(flag2: -F "tests flags with exclusions") .conflicts_with("flag") From d88ca1373007e7f2597457e34572604d4bf1ef4c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 7 Jun 2022 14:03:42 -0500 Subject: [PATCH 08/17] docs: Reflect the dropping of occurrences_of --- src/builder/arg.rs | 33 +++++++++++-------------------- src/parser/matches/arg_matches.rs | 10 +++------- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/builder/arg.rs b/src/builder/arg.rs index 0f4190c27be..d9375c057e9 100644 --- a/src/builder/arg.rs +++ b/src/builder/arg.rs @@ -970,12 +970,12 @@ impl<'help> Arg<'help> { /// .arg( /// Arg::new("flag") /// .long("flag") - /// .action(clap::ArgAction::StoreValue) + /// .action(clap::ArgAction::Set) /// ); /// /// let matches = cmd.try_get_matches_from(["mycmd", "--flag", "value"]).unwrap(); /// assert!(matches.is_present("flag")); - /// assert_eq!(matches.occurrences_of("flag"), 1); + /// assert_eq!(matches.occurrences_of("flag"), 0); /// assert_eq!( /// matches.get_many::("flag").unwrap_or_default().map(|v| v.as_str()).collect::>(), /// vec!["value"] @@ -1114,7 +1114,6 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(m.is_present("file")); - /// assert_eq!(m.occurrences_of("file"), 1); // notice only one occurrence /// let files: Vec<_> = m.values_of("file").unwrap().collect(); /// assert_eq!(files, ["file1", "file2", "file3"]); /// ``` @@ -1854,7 +1853,6 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(delims.is_present("option")); - /// assert_eq!(delims.occurrences_of("option"), 1); /// assert_eq!(delims.values_of("option").unwrap().collect::>(), ["val1", "val2", "val3"]); /// ``` /// The next example shows the difference when turning delimiters off. This is the default @@ -1871,7 +1869,6 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(nodelims.is_present("option")); - /// assert_eq!(nodelims.occurrences_of("option"), 1); /// assert_eq!(nodelims.value_of("option").unwrap(), "val1,val2,val3"); /// ``` /// [`Arg::value_delimiter`]: Arg::value_delimiter() @@ -2116,8 +2113,7 @@ impl<'help> Arg<'help> { /// /// **NOTE:** If the user *does not* use this argument at runtime [`ArgMatches::is_present`] will /// still return `true`. If you wish to determine whether the argument was used at runtime or - /// not, consider [`ArgMatches::occurrences_of`] which will return `0` if the argument was *not* - /// used at runtime. + /// not, consider [`ArgMatches::value_source`][crate::ArgMatches::value_source]. /// /// **NOTE:** This setting is perfectly compatible with [`Arg::default_value_if`] but slightly /// different. `Arg::default_value` *only* takes effect when the user has not provided this arg @@ -2134,7 +2130,7 @@ impl<'help> Arg<'help> { /// First we use the default value without providing any value at runtime. /// /// ```rust - /// # use clap::{Command, Arg}; + /// # use clap::{Command, Arg, ValueSource}; /// let m = Command::new("prog") /// .arg(Arg::new("opt") /// .long("myopt") @@ -2145,13 +2141,13 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.value_of("opt"), Some("myval")); /// assert!(m.is_present("opt")); - /// assert_eq!(m.occurrences_of("opt"), 0); + /// assert_eq!(m.value_source("opt"), Some(ValueSource::DefaultValue)); /// ``` /// /// Next we provide a value at runtime to override the default. /// /// ```rust - /// # use clap::{Command, Arg}; + /// # use clap::{Command, Arg, ValueSource}; /// let m = Command::new("prog") /// .arg(Arg::new("opt") /// .long("myopt") @@ -2162,7 +2158,7 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.value_of("opt"), Some("non_default")); /// assert!(m.is_present("opt")); - /// assert_eq!(m.occurrences_of("opt"), 1); + /// assert_eq!(m.value_source("opt"), Some(ValueSource::CommandLine)); /// ``` /// [`ArgMatches::occurrences_of`]: crate::ArgMatches::occurrences_of() /// [`ArgMatches::value_of`]: crate::ArgMatches::value_of() @@ -2228,7 +2224,7 @@ impl<'help> Arg<'help> { /// Here is an implementation of the common POSIX style `--color` argument. /// /// ```rust - /// # use clap::{Command, Arg}; + /// # use clap::{Command, Arg, ValueSource}; /// /// macro_rules! cmd { /// () => {{ @@ -2256,7 +2252,7 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.value_of("color"), Some("auto")); /// assert!(m.is_present("color")); - /// assert_eq!(m.occurrences_of("color"), 0); + /// assert_eq!(m.value_source("color"), Some(ValueSource::DefaultValue)); /// /// // next, we'll provide a runtime value to override the default (as usually done). /// @@ -2266,7 +2262,7 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.value_of("color"), Some("never")); /// assert!(m.is_present("color")); - /// assert_eq!(m.occurrences_of("color"), 1); + /// assert_eq!(m.value_source("color"), Some(ValueSource::CommandLine)); /// /// // finally, we will use the shortcut and only provide the argument without a value. /// @@ -2276,9 +2272,8 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.value_of("color"), Some("always")); /// assert!(m.is_present("color")); - /// assert_eq!(m.occurrences_of("color"), 1); + /// assert_eq!(m.value_source("color"), Some(ValueSource::CommandLine)); /// ``` - /// [`ArgMatches::occurrences_of`]: ArgMatches::occurrences_of() /// [`ArgMatches::value_of`]: ArgMatches::value_of() /// [`Arg::takes_value(true)`]: Arg::takes_value() /// [`ArgMatches::is_present`]: ArgMatches::is_present() @@ -2459,7 +2454,6 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.values_of("flag").unwrap().collect::>(), vec!["env1", "env2"]); /// ``` - /// [`ArgMatches::occurrences_of`]: ArgMatches::occurrences_of() /// [`ArgMatches::value_of`]: crate::ArgMatches::value_of() /// [`ArgMatches::is_present`]: ArgMatches::is_present() /// [`Arg::takes_value(true)`]: Arg::takes_value() @@ -4139,7 +4133,6 @@ impl<'help> Arg<'help> { /// .arg(arg!(--flag "some flag").overrides_with("flag")) /// .get_matches_from(vec!["posix", "--flag", "--flag"]); /// assert!(m.is_present("flag")); - /// assert_eq!(m.occurrences_of("flag"), 1); /// ``` /// /// Making an arg [`Arg::multiple_occurrences`] and override itself @@ -4152,7 +4145,6 @@ impl<'help> Arg<'help> { /// .arg(arg!(--flag ... "some flag").overrides_with("flag")) /// .get_matches_from(vec!["", "--flag", "--flag", "--flag", "--flag"]); /// assert!(m.is_present("flag")); - /// assert_eq!(m.occurrences_of("flag"), 4); /// ``` /// /// Now notice with options (which *do not* set @@ -4165,7 +4157,6 @@ impl<'help> Arg<'help> { /// .arg(arg!(--opt "some option").overrides_with("opt")) /// .get_matches_from(vec!["", "--opt=some", "--opt=other"]); /// assert!(m.is_present("opt")); - /// assert_eq!(m.occurrences_of("opt"), 1); /// assert_eq!(m.value_of("opt"), Some("other")); /// ``` /// @@ -4183,7 +4174,6 @@ impl<'help> Arg<'help> { /// ) /// .get_matches_from(vec!["", "--opt", "1", "2", "--opt", "3", "4", "5"]); /// assert!(m.is_present("opt")); - /// assert_eq!(m.occurrences_of("opt"), 1); /// assert_eq!(m.values_of("opt").unwrap().collect::>(), &["3", "4", "5"]); /// ``` /// @@ -4198,7 +4188,6 @@ impl<'help> Arg<'help> { /// .overrides_with("opt")) /// .get_matches_from(vec!["", "--opt", "first", "over", "--opt", "other", "val"]); /// assert!(m.is_present("opt")); - /// assert_eq!(m.occurrences_of("opt"), 2); /// assert_eq!(m.values_of("opt").unwrap().collect::>(), &["first", "over", "other", "val"]); /// ``` #[must_use] diff --git a/src/parser/matches/arg_matches.rs b/src/parser/matches/arg_matches.rs index b0b1bd6aa31..0e27fce66b9 100644 --- a/src/parser/matches/arg_matches.rs +++ b/src/parser/matches/arg_matches.rs @@ -92,7 +92,7 @@ impl ArgMatches { /// Returns `None` if the option wasn't present. /// /// *NOTE:* This will always return `Some(value)` if [`default_value`] has been set. - /// [`occurrences_of`] can be used to check if a value is present at runtime. + /// [`ArgMatches::value_source`] can be used to check if a value is present at runtime. /// /// # Panic /// @@ -118,7 +118,6 @@ impl ArgMatches { /// [option]: crate::Arg::takes_value() /// [positional]: crate::Arg::index() /// [`default_value`]: crate::Arg::default_value() - /// [`occurrences_of`]: crate::ArgMatches::occurrences_of() #[track_caller] pub fn get_one(&self, name: &str) -> Option<&T> { let id = Id::from(name); @@ -223,7 +222,7 @@ impl ArgMatches { /// Returns `None` if the option wasn't present. /// /// *NOTE:* This will always return `Some(value)` if [`default_value`] has been set. - /// [`occurrences_of`] can be used to check if a value is present at runtime. + /// [`ArgMatches::value_source`] can be used to check if a value is present at runtime. /// /// # Panic /// @@ -248,7 +247,6 @@ impl ArgMatches { /// [option]: crate::Arg::takes_value() /// [positional]: crate::Arg::index() /// [`default_value`]: crate::Arg::default_value() - /// [`occurrences_of`]: crate::ArgMatches::occurrences_of() #[track_caller] pub fn remove_one(&mut self, name: &str) -> Option { let id = Id::from(name); @@ -500,7 +498,7 @@ impl ArgMatches { /// Check if an argument was present at runtime. /// /// *NOTE:* This will always return `true` if [`default_value`] has been set. - /// [`occurrences_of`] can be used to check if a value is present at runtime. + /// [`ArgMatches::value_source`] can be used to check if a value is present at runtime. /// /// # Panics /// @@ -521,7 +519,6 @@ impl ArgMatches { /// ``` /// /// [`default_value`]: crate::Arg::default_value() - /// [`occurrences_of`]: ArgMatches::occurrences_of() pub fn is_present(&self, id: T) -> bool { let id = Id::from(id); @@ -552,7 +549,6 @@ impl ArgMatches { /// ``` /// /// [`default_value`]: crate::Arg::default_value() - /// [`occurrences_of`]: ArgMatches::occurrences_of() pub fn value_source(&self, id: T) -> Option { let id = Id::from(id); From 7980c5ceb80677308fc2af094d2c55d32c856e88 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 7 Jun 2022 14:34:50 -0500 Subject: [PATCH 09/17] fix(parser): Force multiple occurrences with new Actions This is needed for deprecate `multiple_occurrences` --- src/builder/arg.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/builder/arg.rs b/src/builder/arg.rs index d9375c057e9..eb8c8f93730 100644 --- a/src/builder/arg.rs +++ b/src/builder/arg.rs @@ -4847,6 +4847,21 @@ impl<'help> Arg<'help> { } else { self.settings.unset(ArgSettings::TakesValue); } + match action { + ArgAction::StoreValue + | ArgAction::IncOccurrence + | ArgAction::Help + | ArgAction::Version => {} + ArgAction::Set + | ArgAction::Append + | ArgAction::SetTrue + | ArgAction::SetFalse + | ArgAction::Count => { + if !self.is_positional() { + self.settings.set(ArgSettings::MultipleOccurrences); + } + } + } } if self.value_parser.is_none() { From efc1520223b26358302c8b8e98898989d01e7a68 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 7 Jun 2022 15:05:11 -0500 Subject: [PATCH 10/17] perf(derive): Cache positional status --- clap_derive/src/attrs.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/clap_derive/src/attrs.rs b/clap_derive/src/attrs.rs index 5eb1245073a..ed5c581a182 100644 --- a/clap_derive/src/attrs.rs +++ b/clap_derive/src/attrs.rs @@ -50,6 +50,7 @@ pub struct Attrs { next_help_heading: Option, help_heading: Option, is_enum: bool, + is_positional: bool, kind: Sp, } @@ -436,6 +437,7 @@ impl Attrs { next_help_heading: None, help_heading: None, is_enum: false, + is_positional: true, kind: Sp::new(Kind::Arg(Sp::new(Ty::Other, default_span)), default_span), } } @@ -448,6 +450,9 @@ impl Attrs { } else if name == "action" { self.action = Some(Action::Explicit(Method::new(name, quote!(#arg)))); } else { + if name == "short" || name == "long" { + self.is_positional = false; + } self.methods.push(Method::new(name, quote!(#arg))); } } @@ -810,6 +815,10 @@ impl Attrs { self.is_enum } + pub fn is_positional(&self) -> bool { + self.is_positional + } + pub fn ignore_case(&self) -> TokenStream { let method = self.find_method("ignore_case"); @@ -828,12 +837,6 @@ impl Attrs { self.env_casing.clone() } - pub fn is_positional(&self) -> bool { - self.methods - .iter() - .all(|m| m.name != "long" && m.name != "short") - } - pub fn has_explicit_methods(&self) -> bool { self.methods .iter() From 19d8ca807f953a47338693e7429c723d9d68a421 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 7 Jun 2022 15:38:14 -0500 Subject: [PATCH 11/17] fix(derive): Transition off of multiple_occurrences For programs opting into the clap v4 behavior (with `action` or `value_parser` attributes), we'll no longer generate a `multiple_occurrences(true)` call in preparation for deprecating `multiple_occurrences`. See #3772. --- clap_derive/src/derives/args.rs | 82 ++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs index 47565edb177..345a68e73ff 100644 --- a/clap_derive/src/derives/args.rs +++ b/clap_derive/src/derives/args.rs @@ -304,25 +304,73 @@ pub fn gen_augment( #action }, - Ty::OptionVec => quote_spanned! { ty.span()=> - .takes_value(true) - .value_name(#value_name) - .multiple_occurrences(true) - #possible_values - #validator - #value_parser - #action - }, + Ty::OptionVec => { + if attrs.ignore_parser() { + if attrs.is_positional() { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + .multiple_values(true) // action won't be sufficient for getting multiple + #possible_values + #validator + #value_parser + #action + } + } else { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + #possible_values + #validator + #value_parser + #action + } + } + } else { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + .multiple_occurrences(true) + #possible_values + #validator + #value_parser + #action + } + } + } Ty::Vec => { - quote_spanned! { ty.span()=> - .takes_value(true) - .value_name(#value_name) - .multiple_occurrences(true) - #possible_values - #validator - #value_parser - #action + if attrs.ignore_parser() { + if attrs.is_positional() { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + .multiple_values(true) // action won't be sufficient for getting multiple + #possible_values + #validator + #value_parser + #action + } + } else { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + #possible_values + #validator + #value_parser + #action + } + } + } else { + quote_spanned! { ty.span()=> + .takes_value(true) + .value_name(#value_name) + .multiple_occurrences(true) + #possible_values + #validator + #value_parser + #action + } } } From 4a9c4dee64e275dae0653a8d5d95ebd2afd1d534 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 7 Jun 2022 16:21:12 -0500 Subject: [PATCH 12/17] refactor(test): Make it easier to fork tests --- tests/builder/app_settings.rs | 2 +- tests/builder/arg_aliases.rs | 2 +- tests/builder/arg_aliases_short.rs | 2 +- tests/builder/conflicts.rs | 2 +- tests/builder/default_vals.rs | 2 +- tests/builder/derive_order.rs | 2 +- tests/builder/display_order.rs | 2 +- tests/builder/empty_values.rs | 2 +- tests/builder/error.rs | 2 +- tests/builder/flag_subcommands.rs | 2 +- tests/builder/flags.rs | 2 +- tests/builder/groups.rs | 2 +- tests/builder/help.rs | 2 +- tests/builder/help_env.rs | 2 +- tests/builder/hidden_args.rs | 2 +- tests/builder/opts.rs | 2 +- tests/builder/possible_values.rs | 2 +- tests/builder/require.rs | 2 +- tests/builder/subcommands.rs | 2 +- tests/builder/template_help.rs | 2 +- tests/builder/tests.rs | 2 +- tests/builder/version.rs | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/builder/app_settings.rs b/tests/builder/app_settings.rs index 65aea9c1d69..34f8f6efc27 100644 --- a/tests/builder/app_settings.rs +++ b/tests/builder/app_settings.rs @@ -1,6 +1,6 @@ use std::ffi::OsString; -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, AppSettings, Arg, Command}; diff --git a/tests/builder/arg_aliases.rs b/tests/builder/arg_aliases.rs index 71ccb18f89a..bfe2762326b 100644 --- a/tests/builder/arg_aliases.rs +++ b/tests/builder/arg_aliases.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, Arg, Command}; diff --git a/tests/builder/arg_aliases_short.rs b/tests/builder/arg_aliases_short.rs index 6d70910a0d0..d0a80a0d1d1 100644 --- a/tests/builder/arg_aliases_short.rs +++ b/tests/builder/arg_aliases_short.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, Arg, Command}; diff --git a/tests/builder/conflicts.rs b/tests/builder/conflicts.rs index 345afa50674..2fce0170388 100644 --- a/tests/builder/conflicts.rs +++ b/tests/builder/conflicts.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command}; diff --git a/tests/builder/default_vals.rs b/tests/builder/default_vals.rs index b6de1e52e1c..dd05d94673b 100644 --- a/tests/builder/default_vals.rs +++ b/tests/builder/default_vals.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, Command}; #[test] diff --git a/tests/builder/derive_order.rs b/tests/builder/derive_order.rs index a257b77cc76..5277806480f 100644 --- a/tests/builder/derive_order.rs +++ b/tests/builder/derive_order.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use std::str; diff --git a/tests/builder/display_order.rs b/tests/builder/display_order.rs index 68b4c3d3634..139682d80e3 100644 --- a/tests/builder/display_order.rs +++ b/tests/builder/display_order.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::Command; diff --git a/tests/builder/empty_values.rs b/tests/builder/empty_values.rs index 34102be29a1..9313435c604 100644 --- a/tests/builder/empty_values.rs +++ b/tests/builder/empty_values.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{error::ErrorKind, Arg, Command}; diff --git a/tests/builder/error.rs b/tests/builder/error.rs index 317d7251b71..61f8dcf2385 100644 --- a/tests/builder/error.rs +++ b/tests/builder/error.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, value_parser, Arg, Command, Error}; diff --git a/tests/builder/flag_subcommands.rs b/tests/builder/flag_subcommands.rs index 6c43414846b..bb1201d44a6 100644 --- a/tests/builder/flag_subcommands.rs +++ b/tests/builder/flag_subcommands.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, Command}; diff --git a/tests/builder/flags.rs b/tests/builder/flags.rs index fcd3a2e021e..0cd99f4c62b 100644 --- a/tests/builder/flags.rs +++ b/tests/builder/flags.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, Arg, Command}; const USE_FLAG_AS_ARGUMENT: &str = diff --git a/tests/builder/groups.rs b/tests/builder/groups.rs index 2247b989728..f13b299eb4e 100644 --- a/tests/builder/groups.rs +++ b/tests/builder/groups.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command}; diff --git a/tests/builder/help.rs b/tests/builder/help.rs index 737ef25c8ae..05e57cb074f 100644 --- a/tests/builder/help.rs +++ b/tests/builder/help.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command, PossibleValue}; diff --git a/tests/builder/help_env.rs b/tests/builder/help_env.rs index 7aa5d8154b7..5b8c2e8aa2e 100644 --- a/tests/builder/help_env.rs +++ b/tests/builder/help_env.rs @@ -4,7 +4,7 @@ use std::env; use clap::{Arg, Command}; -use crate::utils; +use super::utils; static HIDE_ENV: &str = "ctest 0.1 diff --git a/tests/builder/hidden_args.rs b/tests/builder/hidden_args.rs index d2421d7ca47..8af6a788ac7 100644 --- a/tests/builder/hidden_args.rs +++ b/tests/builder/hidden_args.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, Arg, Command}; diff --git a/tests/builder/opts.rs b/tests/builder/opts.rs index 858892f0c6f..67cfdbfc19f 100644 --- a/tests/builder/opts.rs +++ b/tests/builder/opts.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, ArgMatches, Command}; diff --git a/tests/builder/possible_values.rs b/tests/builder/possible_values.rs index 0d3bf86cd8e..be04a98257d 100644 --- a/tests/builder/possible_values.rs +++ b/tests/builder/possible_values.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{error::ErrorKind, Arg, Command, PossibleValue}; diff --git a/tests/builder/require.rs b/tests/builder/require.rs index d3157ce0d21..c70224c3c1c 100644 --- a/tests/builder/require.rs +++ b/tests/builder/require.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command}; diff --git a/tests/builder/subcommands.rs b/tests/builder/subcommands.rs index 82f8725548d..3f904f59202 100644 --- a/tests/builder/subcommands.rs +++ b/tests/builder/subcommands.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, error::ErrorKind, Arg, Command}; diff --git a/tests/builder/template_help.rs b/tests/builder/template_help.rs index 08940567535..cf3642a670d 100644 --- a/tests/builder/template_help.rs +++ b/tests/builder/template_help.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use clap::{arg, Command}; diff --git a/tests/builder/tests.rs b/tests/builder/tests.rs index d8b6d69a81d..87911dc2cdd 100644 --- a/tests/builder/tests.rs +++ b/tests/builder/tests.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use std::io::Write; use std::str; diff --git a/tests/builder/version.rs b/tests/builder/version.rs index 1170380785d..f80d3d77deb 100644 --- a/tests/builder/version.rs +++ b/tests/builder/version.rs @@ -1,4 +1,4 @@ -use crate::utils; +use super::utils; use std::str; From 55a705c447819562dbdb0568b070eae8c2580c8d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 7 Jun 2022 16:21:37 -0500 Subject: [PATCH 13/17] test: Ensure we don't break compatibility --- tests/builder/legacy/action.rs | 505 +++ tests/builder/legacy/app_from_crate.rs | 28 + tests/builder/legacy/app_settings.rs | 1261 ++++++++ tests/builder/legacy/arg_aliases.rs | 196 ++ tests/builder/legacy/arg_aliases_short.rs | 193 ++ .../builder/legacy/arg_matcher_assertions.rs | 41 + tests/builder/legacy/arg_settings.rs | 44 + tests/builder/legacy/borrowed.rs | 17 + tests/builder/legacy/cargo.rs | 80 + tests/builder/legacy/command.rs | 27 + tests/builder/legacy/conflicts.rs | 509 +++ tests/builder/legacy/default_missing_vals.rs | 187 ++ tests/builder/legacy/default_vals.rs | 731 +++++ tests/builder/legacy/delimiters.rs | 113 + tests/builder/legacy/derive_order.rs | 293 ++ tests/builder/legacy/display_order.rs | 26 + tests/builder/legacy/double_require.rs | 88 + tests/builder/legacy/empty_values.rs | 128 + tests/builder/legacy/env.rs | 352 ++ tests/builder/legacy/error.rs | 90 + tests/builder/legacy/flag_subcommands.rs | 613 ++++ tests/builder/legacy/flags.rs | 198 ++ tests/builder/legacy/global_args.rs | 114 + tests/builder/legacy/grouped_values.rs | 221 ++ tests/builder/legacy/groups.rs | 325 ++ tests/builder/legacy/help.rs | 2867 +++++++++++++++++ tests/builder/legacy/help_env.rs | 227 ++ tests/builder/legacy/hidden_args.rs | 290 ++ tests/builder/legacy/ignore_errors.rs | 117 + tests/builder/legacy/indices.rs | 202 ++ tests/builder/legacy/mod.rs | 50 + tests/builder/legacy/multiple_occurrences.rs | 241 ++ tests/builder/legacy/multiple_values.rs | 1367 ++++++++ tests/builder/legacy/opts.rs | 616 ++++ tests/builder/legacy/positionals.rs | 310 ++ tests/builder/legacy/posix_compatible.rs | 401 +++ tests/builder/legacy/possible_values.rs | 393 +++ tests/builder/legacy/propagate_globals.rs | 144 + tests/builder/legacy/regex.rs | 36 + tests/builder/legacy/require.rs | 1374 ++++++++ tests/builder/legacy/subcommands.rs | 696 ++++ tests/builder/legacy/template_help.rs | 149 + tests/builder/legacy/tests.rs | 426 +++ tests/builder/legacy/unicode.rs | 18 + tests/builder/legacy/unique_args.rs | 32 + tests/builder/legacy/utf16.rs | 169 + tests/builder/legacy/utf8.rs | 480 +++ tests/builder/legacy/utils.rs | 107 + tests/builder/legacy/validators.rs | 66 + tests/builder/legacy/version.rs | 235 ++ tests/builder/main.rs | 1 + 51 files changed, 17394 insertions(+) create mode 100644 tests/builder/legacy/action.rs create mode 100644 tests/builder/legacy/app_from_crate.rs create mode 100644 tests/builder/legacy/app_settings.rs create mode 100644 tests/builder/legacy/arg_aliases.rs create mode 100644 tests/builder/legacy/arg_aliases_short.rs create mode 100644 tests/builder/legacy/arg_matcher_assertions.rs create mode 100644 tests/builder/legacy/arg_settings.rs create mode 100644 tests/builder/legacy/borrowed.rs create mode 100644 tests/builder/legacy/cargo.rs create mode 100644 tests/builder/legacy/command.rs create mode 100644 tests/builder/legacy/conflicts.rs create mode 100644 tests/builder/legacy/default_missing_vals.rs create mode 100644 tests/builder/legacy/default_vals.rs create mode 100644 tests/builder/legacy/delimiters.rs create mode 100644 tests/builder/legacy/derive_order.rs create mode 100644 tests/builder/legacy/display_order.rs create mode 100644 tests/builder/legacy/double_require.rs create mode 100644 tests/builder/legacy/empty_values.rs create mode 100644 tests/builder/legacy/env.rs create mode 100644 tests/builder/legacy/error.rs create mode 100644 tests/builder/legacy/flag_subcommands.rs create mode 100644 tests/builder/legacy/flags.rs create mode 100644 tests/builder/legacy/global_args.rs create mode 100644 tests/builder/legacy/grouped_values.rs create mode 100644 tests/builder/legacy/groups.rs create mode 100644 tests/builder/legacy/help.rs create mode 100644 tests/builder/legacy/help_env.rs create mode 100644 tests/builder/legacy/hidden_args.rs create mode 100644 tests/builder/legacy/ignore_errors.rs create mode 100644 tests/builder/legacy/indices.rs create mode 100644 tests/builder/legacy/mod.rs create mode 100644 tests/builder/legacy/multiple_occurrences.rs create mode 100644 tests/builder/legacy/multiple_values.rs create mode 100644 tests/builder/legacy/opts.rs create mode 100644 tests/builder/legacy/positionals.rs create mode 100644 tests/builder/legacy/posix_compatible.rs create mode 100644 tests/builder/legacy/possible_values.rs create mode 100644 tests/builder/legacy/propagate_globals.rs create mode 100644 tests/builder/legacy/regex.rs create mode 100644 tests/builder/legacy/require.rs create mode 100644 tests/builder/legacy/subcommands.rs create mode 100644 tests/builder/legacy/template_help.rs create mode 100644 tests/builder/legacy/tests.rs create mode 100644 tests/builder/legacy/unicode.rs create mode 100644 tests/builder/legacy/unique_args.rs create mode 100644 tests/builder/legacy/utf16.rs create mode 100644 tests/builder/legacy/utf8.rs create mode 100644 tests/builder/legacy/utils.rs create mode 100644 tests/builder/legacy/validators.rs create mode 100644 tests/builder/legacy/version.rs diff --git a/tests/builder/legacy/action.rs b/tests/builder/legacy/action.rs new file mode 100644 index 00000000000..e6d426d4cb6 --- /dev/null +++ b/tests/builder/legacy/action.rs @@ -0,0 +1,505 @@ +#![allow(clippy::bool_assert_comparison)] + +use clap::Arg; +use clap::ArgAction; +use clap::Command; + +#[test] +fn set() { + let cmd = Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::Set)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(matches.get_one::("mammal"), None); + assert_eq!(matches.is_present("mammal"), false); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), None); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "dog"]) + .unwrap(); + assert_eq!(matches.get_one::("mammal").unwrap(), "dog"); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(2)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "dog", "--mammal", "cat"]) + .unwrap(); + assert_eq!(matches.get_one::("mammal").unwrap(), "cat"); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(4)); +} + +#[test] +fn append() { + let cmd = Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::Append)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(matches.get_one::("mammal"), None); + assert_eq!(matches.is_present("mammal"), false); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), None); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "dog"]) + .unwrap(); + assert_eq!(matches.get_one::("mammal").unwrap(), "dog"); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!( + matches.indices_of("mammal").unwrap().collect::>(), + vec![2] + ); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "dog", "--mammal", "cat"]) + .unwrap(); + assert_eq!( + matches + .get_many::("mammal") + .unwrap() + .map(|s| s.as_str()) + .collect::>(), + vec!["dog", "cat"] + ); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!( + matches.indices_of("mammal").unwrap().collect::>(), + vec![2, 4] + ); +} + +#[test] +fn set_true() { + let cmd = + Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::SetTrue)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(2)); +} + +#[test] +fn set_true_with_explicit_default_value() { + let cmd = Command::new("test").arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetTrue) + .default_value("false"), + ); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); +} + +#[test] +fn set_true_with_default_value_if_present() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetTrue) + .default_value_if("dog", None, Some("true")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); +} + +#[test] +fn set_true_with_default_value_if_value() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetTrue) + .default_value_if("dog", Some("true"), Some("true")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); +} + +#[test] +fn set_true_with_required_if_eq() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetTrue) + .required_if_eq("dog", "true"), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::SetTrue)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + + cmd.clone() + .try_get_matches_from(["test", "--dog"]) + .unwrap_err(); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--dog", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); +} + +#[test] +fn set_false() { + let cmd = Command::new("test").arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetFalse), + ); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(2)); +} + +#[test] +fn set_false_with_explicit_default_value() { + let cmd = Command::new("test").arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetFalse) + .default_value("true"), + ); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); +} + +#[test] +fn set_false_with_default_value_if_present() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetFalse) + .default_value_if("dog", None, Some("false")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::SetFalse)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); +} + +#[test] +fn set_false_with_default_value_if_value() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::SetFalse) + .default_value_if("dog", Some("false"), Some("false")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::SetFalse)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), true); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), false); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), true); + assert_eq!(*matches.get_one::("mammal").unwrap(), false); +} + +#[test] +fn count() { + let cmd = Command::new("test").arg(Arg::new("mammal").long("mammal").action(ArgAction::Count)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), 0); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), 1); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), 2); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(2)); +} + +#[test] +fn count_with_explicit_default_value() { + let cmd = Command::new("test").arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::Count) + .default_value("10"), + ); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), 1); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("mammal").unwrap(), 10); + assert_eq!(matches.is_present("mammal"), true); + #[allow(deprecated)] + { + assert_eq!(matches.occurrences_of("mammal"), 0); + } + assert_eq!(matches.index_of("mammal"), Some(1)); +} + +#[test] +fn count_with_default_value_if_present() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::Count) + .default_value_if("dog", None, Some("10")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::Count)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 0); + assert_eq!(*matches.get_one::("mammal").unwrap(), 0); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 1); + assert_eq!(*matches.get_one::("mammal").unwrap(), 10); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 0); + assert_eq!(*matches.get_one::("mammal").unwrap(), 1); +} + +#[test] +fn count_with_default_value_if_value() { + let cmd = Command::new("test") + .arg( + Arg::new("mammal") + .long("mammal") + .action(ArgAction::Count) + .default_value_if("dog", Some("2"), Some("10")), + ) + .arg(Arg::new("dog").long("dog").action(ArgAction::Count)); + + let matches = cmd.clone().try_get_matches_from(["test"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 0); + assert_eq!(*matches.get_one::("mammal").unwrap(), 0); + + let matches = cmd.clone().try_get_matches_from(["test", "--dog"]).unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 1); + assert_eq!(*matches.get_one::("mammal").unwrap(), 0); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--dog", "--dog"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 2); + assert_eq!(*matches.get_one::("mammal").unwrap(), 10); + + let matches = cmd + .clone() + .try_get_matches_from(["test", "--mammal"]) + .unwrap(); + assert_eq!(*matches.get_one::("dog").unwrap(), 0); + assert_eq!(*matches.get_one::("mammal").unwrap(), 1); +} diff --git a/tests/builder/legacy/app_from_crate.rs b/tests/builder/legacy/app_from_crate.rs new file mode 100644 index 00000000000..7559e697ce1 --- /dev/null +++ b/tests/builder/legacy/app_from_crate.rs @@ -0,0 +1,28 @@ +#![cfg(feature = "cargo")] +#![allow(deprecated)] + +use clap::{app_from_crate, error::ErrorKind}; + +static EVERYTHING: &str = "clap {{version}} +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + clap + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +#[test] +fn app_from_crate() { + let res = app_from_crate!().try_get_matches_from(vec!["clap", "--help"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayHelp); + assert_eq!( + err.to_string(), + EVERYTHING.replace("{{version}}", env!("CARGO_PKG_VERSION")) + ); +} diff --git a/tests/builder/legacy/app_settings.rs b/tests/builder/legacy/app_settings.rs new file mode 100644 index 00000000000..3759d4cb15e --- /dev/null +++ b/tests/builder/legacy/app_settings.rs @@ -0,0 +1,1261 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, AppSettings, Arg, Command}; + +static ALLOW_EXT_SC: &str = "clap-test v1.4.8 + +USAGE: + clap-test [SUBCOMMAND] + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +static DONT_COLLAPSE_ARGS: &str = "clap-test v1.4.8 + +USAGE: + clap-test [arg1] [arg2] [arg3] + +ARGS: + some + some + some + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +static REQUIRE_EQUALS: &str = "clap-test v1.4.8 + +USAGE: + clap-test --opt= + +OPTIONS: + -h, --help Print help information + -o, --opt= some + -V, --version Print version information +"; + +static SKIP_POS_VALS: &str = "test 1.3 +Kevin K. +tests stuff + +USAGE: + test [OPTIONS] [arg1] + +ARGS: + some pos arg + +OPTIONS: + -h, --help Print help information + -o, --opt some option + -V, --version Print version information +"; + +static ARG_REQUIRED_ELSE_HELP: &str = "test 1.0 + +USAGE: + test [OPTIONS] + +OPTIONS: + -h, --help Print help information + -i, --info Provides more info + -V, --version Print version information +"; + +static SUBCOMMAND_REQUIRED_ELSE_HELP: &str = "test 1.0 + +USAGE: + test + +OPTIONS: + -h, --help Print help information + -V, --version Print version information + +SUBCOMMANDS: + help Print this message or the help of the given subcommand(s) + info +"; + +#[test] +fn setting() { + #![allow(deprecated)] + let m = Command::new("setting").args_override_self(true); + assert!(m.is_set(AppSettings::AllArgsOverrideSelf)); +} + +#[test] +fn global_setting() { + #![allow(deprecated)] + let m = Command::new("global_setting").args_override_self(true); + assert!(m.is_set(AppSettings::AllArgsOverrideSelf)); +} + +#[test] +fn unset_setting() { + #![allow(deprecated)] + let m = Command::new("unset_setting").args_override_self(true); + assert!(m.is_set(AppSettings::AllArgsOverrideSelf)); + + let m = m.args_override_self(false); + assert!(!m.is_set(AppSettings::AllArgsOverrideSelf), "{:#?}", m); +} + +#[test] +fn unset_global_setting() { + #![allow(deprecated)] + let m = Command::new("unset_global_setting").args_override_self(true); + assert!(m.is_set(AppSettings::AllArgsOverrideSelf)); + + let m = m.args_override_self(false); + assert!(!m.is_set(AppSettings::AllArgsOverrideSelf), "{:#?}", m); +} + +#[test] +fn setting_bitor() { + #![allow(deprecated)] + let m = Command::new("setting_bitor").setting( + AppSettings::InferSubcommands | AppSettings::Hidden | AppSettings::DisableHelpSubcommand, + ); + + assert!(m.is_set(AppSettings::InferSubcommands)); + assert!(m.is_set(AppSettings::Hidden)); + assert!(m.is_set(AppSettings::DisableHelpSubcommand)); +} + +#[test] +fn unset_setting_bitor() { + #![allow(deprecated)] + let m = Command::new("unset_setting_bitor") + .setting(AppSettings::InferSubcommands) + .setting(AppSettings::Hidden) + .setting(AppSettings::DisableHelpSubcommand); + + assert!(m.is_set(AppSettings::InferSubcommands)); + assert!(m.is_set(AppSettings::Hidden)); + assert!(m.is_set(AppSettings::DisableHelpSubcommand)); + + let m = m.unset_setting( + AppSettings::InferSubcommands | AppSettings::Hidden | AppSettings::DisableHelpSubcommand, + ); + assert!(!m.is_set(AppSettings::InferSubcommands), "{:#?}", m); + assert!(!m.is_set(AppSettings::Hidden), "{:#?}", m); + assert!(!m.is_set(AppSettings::DisableHelpSubcommand), "{:#?}", m); +} + +#[test] +fn sub_command_negate_required() { + Command::new("sub_command_negate") + .subcommand_negates_reqs(true) + .arg(Arg::new("test").required(true).index(1)) + .subcommand(Command::new("sub1")) + .try_get_matches_from(vec!["myprog", "sub1"]) + .unwrap(); +} + +#[test] +fn sub_command_negate_required_2() { + let result = Command::new("sub_command_negate") + .subcommand_negates_reqs(true) + .arg(Arg::new("test").required(true).index(1)) + .subcommand(Command::new("sub1")) + .try_get_matches_from(vec![""]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); +} + +#[test] +fn sub_command_required() { + let result = Command::new("sc_required") + .subcommand_required(true) + .subcommand(Command::new("sub1")) + .try_get_matches_from(vec![""]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::MissingSubcommand); +} + +#[test] +fn arg_required_else_help() { + let result = Command::new("arg_required") + .arg_required_else_help(true) + .arg(Arg::new("test").index(1)) + .try_get_matches_from(vec![""]); + + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!( + err.kind(), + ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + ); +} + +#[test] +fn arg_required_else_help_over_req_arg() { + let result = Command::new("arg_required") + .arg_required_else_help(true) + .arg(Arg::new("test").index(1).required(true)) + .try_get_matches_from(vec![""]); + + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!( + err.kind(), + ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + ); +} + +#[test] +fn arg_required_else_help_over_req_subcommand() { + let result = Command::new("sub_required") + .arg_required_else_help(true) + .subcommand_required(true) + .subcommand(Command::new("sub1")) + .try_get_matches_from(vec![""]); + + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!( + err.kind(), + ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + ); +} + +#[test] +fn arg_required_else_help_with_default() { + let result = Command::new("arg_required") + .arg_required_else_help(true) + .arg(arg!(--input ).required(false).default_value("-")) + .try_get_matches_from(vec![""]); + + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!( + err.kind(), + ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + ); +} + +#[test] +fn arg_required_else_help_error_message() { + let cmd = Command::new("test") + .arg_required_else_help(true) + .version("1.0") + .arg( + Arg::new("info") + .help("Provides more info") + .short('i') + .long("info"), + ); + utils::assert_output( + cmd, + "test", + ARG_REQUIRED_ELSE_HELP, + true, // Unlike normal displaying of help, we should provide a fatal exit code + ); +} + +#[test] +fn subcommand_required_else_help() { + #![allow(deprecated)] + let result = Command::new("test") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand(Command::new("info")) + .try_get_matches_from(&[""]); + + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!( + err.kind(), + ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + ); +} + +#[test] +fn subcommand_required_else_help_error_message() { + #![allow(deprecated)] + let cmd = Command::new("test") + .setting(AppSettings::SubcommandRequiredElseHelp) + .version("1.0") + .subcommand(Command::new("info").arg(Arg::new("filename"))); + utils::assert_output( + cmd, + "test", + SUBCOMMAND_REQUIRED_ELSE_HELP, + true, // Unlike normal displaying of help, we should provide a fatal exit code + ); +} + +#[cfg(not(feature = "suggestions"))] +#[test] +fn infer_subcommands_fail_no_args() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "te"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::UnrecognizedSubcommand); +} + +#[cfg(feature = "suggestions")] +#[test] +fn infer_subcommands_fail_no_args() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "te"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand); +} + +#[test] +fn infer_subcommands_fail_with_args() { + let m = Command::new("prog") + .infer_subcommands(true) + .arg(Arg::new("some")) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "t"]); + assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); + assert_eq!(m.unwrap().value_of("some"), Some("t")); +} + +#[test] +fn infer_subcommands_fail_with_args2() { + let m = Command::new("prog") + .infer_subcommands(true) + .arg(Arg::new("some")) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "te"]); + assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); + assert_eq!(m.unwrap().value_of("some"), Some("te")); +} + +#[test] +fn infer_subcommands_pass() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .try_get_matches_from(vec!["prog", "te"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +#[test] +fn infer_subcommands_pass_close() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "tes"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +#[test] +fn infer_subcommands_pass_exact_match() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("testa")) + .subcommand(Command::new("testb")) + .try_get_matches_from(vec!["prog", "test"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +#[cfg(feature = "suggestions")] +#[test] +fn infer_subcommands_fail_suggestions() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "temps"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand); +} + +#[cfg(not(feature = "suggestions"))] +#[test] +fn infer_subcommands_fail_suggestions() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test")) + .subcommand(Command::new("temp")) + .try_get_matches_from(vec!["prog", "temps"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::UnrecognizedSubcommand); +} + +#[test] +fn no_bin_name() { + let result = Command::new("arg_required") + .no_binary_name(true) + .arg(Arg::new("test").required(true).index(1)) + .try_get_matches_from(vec!["testing"]); + assert!(result.is_ok(), "{}", result.unwrap_err()); + let matches = result.unwrap(); + assert_eq!(matches.value_of("test").unwrap(), "testing"); +} + +#[test] +fn skip_possible_values() { + let cmd = Command::new("test") + .author("Kevin K.") + .about("tests stuff") + .version("1.3") + .hide_possible_values(true) + .args(&[ + arg!(-o --opt "some option") + .required(false) + .possible_values(["one", "two"]), + arg!([arg1] "some pos arg").possible_values(["three", "four"]), + ]); + + utils::assert_output(cmd, "test --help", SKIP_POS_VALS, false); +} + +#[test] +fn stop_delim_values_only_pos_follows() { + let r = Command::new("onlypos") + .dont_delimit_trailing_values(true) + .args(&[ + arg!(f: -f "some opt").required(false), + arg!([arg] ... "some arg"), + ]) + .try_get_matches_from(vec!["", "--", "-f", "-g,x"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert!(!m.is_present("f")); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + &["-f", "-g,x"] + ); +} + +#[test] +fn dont_delim_values_trailingvararg() { + let m = Command::new("positional") + .trailing_var_arg(true) + .dont_delimit_trailing_values(true) + .arg(arg!([opt] ... "some pos")) + .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]) + .unwrap(); + assert!(m.is_present("opt")); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["test", "--foo", "-Wl,-bar"] + ); +} + +#[test] +fn delim_values_only_pos_follows() { + let r = Command::new("onlypos") + .args(&[arg!(f: -f [flag] "some opt"), arg!([arg] ... "some arg")]) + .try_get_matches_from(vec!["", "--", "-f", "-g,x"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert!(!m.is_present("f")); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + &["-f", "-g,x"] + ); +} + +#[test] +fn delim_values_trailingvararg() { + let m = Command::new("positional") + .trailing_var_arg(true) + .arg(arg!([opt] ... "some pos")) + .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]) + .unwrap(); + assert!(m.is_present("opt")); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["test", "--foo", "-Wl,-bar"] + ); +} + +#[test] +fn delim_values_only_pos_follows_with_delim() { + let r = Command::new("onlypos") + .args(&[ + arg!(f: -f [flag] "some opt"), + arg!([arg] ... "some arg").use_value_delimiter(true), + ]) + .try_get_matches_from(vec!["", "--", "-f", "-g,x"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert!(!m.is_present("f")); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + &["-f", "-g", "x"] + ); +} + +#[test] +fn delim_values_trailingvararg_with_delim() { + let m = Command::new("positional") + .trailing_var_arg(true) + .arg(arg!([opt] ... "some pos").use_value_delimiter(true)) + .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]) + .unwrap(); + assert!(m.is_present("opt")); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["test", "--foo", "-Wl", "-bar"] + ); +} + +#[test] +fn leading_hyphen_short() { + let res = Command::new("leadhy") + .allow_hyphen_values(true) + .arg(Arg::new("some")) + .arg(Arg::new("other").short('o')) + .try_get_matches_from(vec!["", "-bar", "-o"]); + assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert!(m.is_present("some")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("some").unwrap(), "-bar"); +} + +#[test] +fn leading_hyphen_long() { + let res = Command::new("leadhy") + .allow_hyphen_values(true) + .arg(Arg::new("some")) + .arg(Arg::new("other").short('o')) + .try_get_matches_from(vec!["", "--bar", "-o"]); + assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert!(m.is_present("some")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("some").unwrap(), "--bar"); +} + +#[test] +fn leading_hyphen_opt() { + let res = Command::new("leadhy") + .allow_hyphen_values(true) + .arg(Arg::new("some").takes_value(true).long("opt")) + .arg(Arg::new("other").short('o')) + .try_get_matches_from(vec!["", "--opt", "--bar", "-o"]); + assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert!(m.is_present("some")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("some").unwrap(), "--bar"); +} + +#[test] +fn allow_negative_numbers() { + let res = Command::new("negnum") + .allow_negative_numbers(true) + .arg(Arg::new("panum")) + .arg(Arg::new("onum").short('o').takes_value(true)) + .try_get_matches_from(vec!["negnum", "-20", "-o", "-1.2"]); + assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert_eq!(m.value_of("panum").unwrap(), "-20"); + assert_eq!(m.value_of("onum").unwrap(), "-1.2"); +} + +#[test] +fn allow_negative_numbers_fail() { + let res = Command::new("negnum") + .allow_negative_numbers(true) + .arg(Arg::new("panum")) + .arg(Arg::new("onum").short('o').takes_value(true)) + .try_get_matches_from(vec!["negnum", "--foo", "-o", "-1.2"]); + assert!(res.is_err()); + assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument) +} + +#[test] +fn leading_double_hyphen_trailingvararg() { + let m = Command::new("positional") + .trailing_var_arg(true) + .allow_hyphen_values(true) + .arg(arg!([opt] ... "some pos")) + .try_get_matches_from(vec!["", "--foo", "-Wl", "bar"]) + .unwrap(); + assert!(m.is_present("opt")); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["--foo", "-Wl", "bar"] + ); +} + +#[test] +fn disable_help_subcommand() { + let result = Command::new("disablehelp") + .disable_help_subcommand(true) + .subcommand(Command::new("sub1")) + .try_get_matches_from(vec!["", "help"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::UnknownArgument); +} + +#[test] +fn dont_collapse_args() { + let cmd = Command::new("clap-test") + .version("v1.4.8") + .dont_collapse_args_in_usage(true) + .args(&[ + Arg::new("arg1").help("some"), + Arg::new("arg2").help("some"), + Arg::new("arg3").help("some"), + ]); + utils::assert_output(cmd, "clap-test --help", DONT_COLLAPSE_ARGS, false); +} + +#[test] +fn require_eq() { + let cmd = Command::new("clap-test").version("v1.4.8").arg( + Arg::new("opt") + .long("opt") + .short('o') + .required(true) + .require_equals(true) + .value_name("FILE") + .help("some"), + ); + utils::assert_output(cmd, "clap-test --help", REQUIRE_EQUALS, false); +} + +#[test] +fn args_negate_subcommands_one_level() { + let res = Command::new("disablehelp") + .args_conflicts_with_subcommands(true) + .subcommand_negates_reqs(true) + .arg(arg!( "some arg")) + .arg(arg!( "some arg")) + .subcommand( + Command::new("sub1").subcommand(Command::new("sub2").subcommand(Command::new("sub3"))), + ) + .try_get_matches_from(vec!["", "pickles", "sub1"]); + assert!(res.is_ok(), "error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert_eq!(m.value_of("arg2"), Some("sub1")); +} + +#[test] +fn args_negate_subcommands_two_levels() { + let res = Command::new("disablehelp") + .args_conflicts_with_subcommands(true) + .subcommand_negates_reqs(true) + .arg(arg!( "some arg")) + .arg(arg!( "some arg")) + .subcommand( + Command::new("sub1") + .args_conflicts_with_subcommands(true) + .subcommand_negates_reqs(true) + .arg(arg!( "some")) + .arg(arg!( "some")) + .subcommand(Command::new("sub2").subcommand(Command::new("sub3"))), + ) + .try_get_matches_from(vec!["", "sub1", "arg", "sub2"]); + assert!(res.is_ok(), "error: {:?}", res.unwrap_err().kind()); + let m = res.unwrap(); + assert_eq!( + m.subcommand_matches("sub1").unwrap().value_of("arg2"), + Some("sub2") + ); +} + +#[test] +fn propagate_vals_down() { + let m = Command::new("myprog") + .arg(arg!([cmd] "command to run").global(true)) + .subcommand(Command::new("foo")) + .try_get_matches_from(vec!["myprog", "set", "foo"]); + assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); + let m = m.unwrap(); + assert_eq!(m.value_of("cmd"), Some("set")); + let sub_m = m.subcommand_matches("foo").unwrap(); + assert_eq!(sub_m.value_of("cmd"), Some("set")); +} + +#[test] +fn allow_missing_positional() { + let m = Command::new("test") + .allow_missing_positional(true) + .arg(arg!([src] "some file").default_value("src")) + .arg(arg!( "some file")) + .try_get_matches_from(vec!["test", "file"]); + assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); + let m = m.unwrap(); + assert_eq!(m.value_of("src"), Some("src")); + assert_eq!(m.value_of("dest"), Some("file")); +} + +#[test] +fn allow_missing_positional_no_default() { + let m = Command::new("test") + .allow_missing_positional(true) + .arg(arg!([src] "some file")) + .arg(arg!( "some file")) + .try_get_matches_from(vec!["test", "file"]); + assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); + let m = m.unwrap(); + assert_eq!(m.value_of("src"), None); + assert_eq!(m.value_of("dest"), Some("file")); +} + +#[test] +fn missing_positional_no_hyphen() { + let r = Command::new("bench") + .allow_missing_positional(true) + .arg(arg!([BENCH] "some bench")) + .arg(arg!([ARGS] ... "some args")) + .try_get_matches_from(vec!["bench", "foo", "arg1", "arg2", "arg3"]); + assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); + + let m = r.unwrap(); + + let expected_bench = Some("foo"); + let expected_args = vec!["arg1", "arg2", "arg3"]; + + assert_eq!(m.value_of("BENCH"), expected_bench); + assert_eq!( + m.values_of("ARGS").unwrap().collect::>(), + &*expected_args + ); +} + +#[test] +fn missing_positional_hyphen() { + let r = Command::new("bench") + .allow_missing_positional(true) + .arg(arg!([BENCH] "some bench")) + .arg(arg!([ARGS] ... "some args")) + .try_get_matches_from(vec!["bench", "--", "arg1", "arg2", "arg3"]); + assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); + + let m = r.unwrap(); + + let expected_bench = None; + let expected_args = vec!["arg1", "arg2", "arg3"]; + + assert_eq!(m.value_of("BENCH"), expected_bench); + assert_eq!( + m.values_of("ARGS").unwrap().collect::>(), + &*expected_args + ); +} + +#[test] +fn missing_positional_hyphen_far_back() { + let r = Command::new("bench") + .allow_missing_positional(true) + .arg(arg!([BENCH1] "some bench")) + .arg(arg!([BENCH2] "some bench")) + .arg(arg!([BENCH3] "some bench")) + .arg(arg!([ARGS] ... "some args")) + .try_get_matches_from(vec!["bench", "foo", "--", "arg1", "arg2", "arg3"]); + assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); + + let m = r.unwrap(); + + let expected_bench1 = Some("foo"); + let expected_bench2 = None; + let expected_bench3 = None; + let expected_args = vec!["arg1", "arg2", "arg3"]; + + assert_eq!(m.value_of("BENCH1"), expected_bench1); + assert_eq!(m.value_of("BENCH2"), expected_bench2); + assert_eq!(m.value_of("BENCH3"), expected_bench3); + assert_eq!( + m.values_of("ARGS").unwrap().collect::>(), + &*expected_args + ); +} + +#[test] +fn missing_positional_hyphen_req_error() { + let r = Command::new("bench") + .allow_missing_positional(true) + .arg(arg!([BENCH1] "some bench")) + .arg(arg!( "some bench")) + .arg(arg!([ARGS] ... "some args")) + .try_get_matches_from(vec!["bench", "foo", "--", "arg1", "arg2", "arg3"]); + assert!(r.is_err()); + assert_eq!(r.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); +} + +#[test] +fn issue_1066_allow_leading_hyphen_and_unknown_args() { + let res = Command::new("prog") + .allow_hyphen_values(true) + .arg(arg!(--"some-argument")) + .try_get_matches_from(vec!["prog", "hello"]); + + assert!(res.is_err()); + assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); +} + +#[test] +fn issue_1066_allow_leading_hyphen_and_unknown_args_no_vals() { + let res = Command::new("prog") + .allow_hyphen_values(true) + .arg(arg!(--"some-argument")) + .try_get_matches_from(vec!["prog", "--hello"]); + + assert!(res.is_err()); + assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); +} + +#[test] +fn issue_1066_allow_leading_hyphen_and_unknown_args_option() { + let res = Command::new("prog") + .allow_hyphen_values(true) + .arg(arg!(--"some-argument" )) + .try_get_matches_from(vec!["prog", "-hello"]); + + assert!(res.is_err()); + assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); +} + +#[test] +fn issue_1437_allow_hyphen_values_for_positional_arg() { + let m = Command::new("tmp") + .arg( + Arg::new("pat") + .allow_hyphen_values(true) + .required(true) + .takes_value(true), + ) + .try_get_matches_from(["tmp", "-file"]) + .unwrap(); + assert_eq!(m.value_of("pat"), Some("-file")); +} + +#[test] +fn issue_1093_allow_ext_sc() { + let cmd = Command::new("clap-test") + .version("v1.4.8") + .allow_external_subcommands(true); + utils::assert_output(cmd, "clap-test --help", ALLOW_EXT_SC, false); +} + +#[test] +#[cfg(not(feature = "unstable-v4"))] +fn allow_ext_sc_empty_args() { + let res = Command::new("clap-test") + .version("v1.4.8") + .allow_external_subcommands(true) + .allow_invalid_utf8_for_external_subcommands(true) + .try_get_matches_from(vec!["clap-test", "external-cmd"]); + + assert!(res.is_ok(), "{}", res.unwrap_err()); + + match res.unwrap().subcommand() { + Some((name, args)) => { + assert_eq!(name, "external-cmd"); + assert_eq!(args.values_of_lossy(""), None); + } + _ => unreachable!(), + } +} + +#[test] +#[cfg(feature = "unstable-v4")] +fn allow_ext_sc_empty_args() { + let res = Command::new("clap-test") + .version("v1.4.8") + .allow_external_subcommands(true) + .allow_invalid_utf8_for_external_subcommands(true) + .try_get_matches_from(vec!["clap-test", "external-cmd"]); + + assert!(res.is_ok(), "{}", res.unwrap_err()); + + match res.unwrap().subcommand() { + Some((name, args)) => { + assert_eq!(name, "external-cmd"); + assert_eq!(args.values_of_lossy(""), Some(vec![])); + } + _ => unreachable!(), + } +} + +#[test] +fn allow_ext_sc_when_sc_required() { + let res = Command::new("clap-test") + .version("v1.4.8") + .allow_external_subcommands(true) + .allow_invalid_utf8_for_external_subcommands(true) + .subcommand_required(true) + .try_get_matches_from(vec!["clap-test", "external-cmd", "foo"]); + + assert!(res.is_ok(), "{}", res.unwrap_err()); + + match res.unwrap().subcommand() { + Some((name, args)) => { + assert_eq!(name, "external-cmd"); + assert_eq!(args.values_of_lossy(""), Some(vec!["foo".to_string()])); + } + _ => unreachable!(), + } +} + +#[test] +fn external_subcommand_looks_like_built_in() { + let res = Command::new("cargo") + .version("1.26.0") + .allow_external_subcommands(true) + .allow_invalid_utf8_for_external_subcommands(true) + .subcommand(Command::new("install")) + .try_get_matches_from(vec!["cargo", "install-update", "foo"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + match m.subcommand() { + Some((name, args)) => { + assert_eq!(name, "install-update"); + assert_eq!(args.values_of_lossy(""), Some(vec!["foo".to_string()])); + } + _ => panic!("external_subcommand didn't work"), + } +} + +#[test] +fn built_in_subcommand_escaped() { + let res = Command::new("cargo") + .version("1.26.0") + .allow_external_subcommands(true) + .allow_invalid_utf8_for_external_subcommands(true) + .subcommand(Command::new("install")) + .try_get_matches_from(vec!["cargo", "--", "install", "foo"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + match m.subcommand() { + Some((name, args)) => { + assert_eq!(name, "install"); + assert_eq!(args.values_of_lossy(""), Some(vec!["foo".to_string()])); + } + _ => panic!("external_subcommand didn't work"), + } +} + +#[test] +fn aaos_flags() { + // flags + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--flag "some flag")) + .try_get_matches_from(vec!["", "--flag", "--flag"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("flag")); + assert_eq!(m.occurrences_of("flag"), 1); +} + +#[test] +fn aaos_flags_mult() { + // flags with multiple + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--flag ... "some flag")) + .try_get_matches_from(vec!["", "--flag", "--flag", "--flag", "--flag"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("flag")); + assert_eq!(m.occurrences_of("flag"), 4); +} + +#[test] +fn aaos_opts() { + // opts + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--opt "some option")) + .try_get_matches_from(vec!["", "--opt=some", "--opt=other"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.occurrences_of("opt"), 1); + assert_eq!(m.value_of("opt"), Some("other")); +} + +#[test] +fn aaos_opts_w_other_overrides() { + // opts with other overrides + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--opt "some option").required(false)) + .arg( + arg!(--other "some other option") + .required(false) + .overrides_with("opt"), + ) + .try_get_matches_from(vec!["", "--opt=some", "--other=test", "--opt=other"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert!(!m.is_present("other")); + assert_eq!(m.occurrences_of("opt"), 1); + assert_eq!(m.value_of("opt"), Some("other")); +} + +#[test] +fn aaos_opts_w_other_overrides_rev() { + // opts with other overrides, rev + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--opt "some option").required(true)) + .arg( + arg!(--other "some other option") + .required(true) + .overrides_with("opt"), + ) + .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--other=val"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(!m.is_present("opt")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("other"), Some("val")); +} + +#[test] +fn aaos_opts_w_other_overrides_2() { + // opts with other overrides + let res = Command::new("posix") + .args_override_self(true) + .arg( + arg!(--opt "some option") + .required(false) + .overrides_with("other"), + ) + .arg(arg!(--other "some other option").required(false)) + .try_get_matches_from(vec!["", "--opt=some", "--other=test", "--opt=other"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert!(!m.is_present("other")); + assert_eq!(m.occurrences_of("opt"), 1); + assert_eq!(m.value_of("opt"), Some("other")); +} + +#[test] +fn aaos_opts_w_other_overrides_rev_2() { + // opts with other overrides, rev + let res = Command::new("posix") + .args_override_self(true) + .arg( + arg!(--opt "some option") + .required(true) + .overrides_with("other"), + ) + .arg(arg!(--other "some other option").required(true)) + .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--other=val"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(!m.is_present("opt")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("other"), Some("val")); +} + +#[test] +fn aaos_opts_w_override_as_conflict_1() { + // opts with other overrides, rev + let res = Command::new("posix") + .args_override_self(true) + .arg( + arg!(--opt "some option") + .required(true) + .overrides_with("other"), + ) + .arg(arg!(--other "some other option").required(true)) + .try_get_matches_from(vec!["", "--opt=some"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert!(!m.is_present("other")); + assert_eq!(m.value_of("opt"), Some("some")); +} + +#[test] +fn aaos_opts_w_override_as_conflict_2() { + // opts with other overrides, rev + let res = Command::new("posix") + .args_override_self(true) + .arg( + arg!(--opt "some option") + .required(true) + .overrides_with("other"), + ) + .arg(arg!(--other "some other option").required(true)) + .try_get_matches_from(vec!["", "--other=some"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(!m.is_present("opt")); + assert!(m.is_present("other")); + assert_eq!(m.value_of("other"), Some("some")); +} + +#[test] +fn aaos_opts_mult() { + // opts with multiple + let res = Command::new("posix") + .args_override_self(true) + .arg( + arg!(--opt ... "some option") + .number_of_values(1) + .takes_value(true) + .use_value_delimiter(true) + .require_value_delimiter(true), + ) + .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--opt=one,two"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.occurrences_of("opt"), 3); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["some", "other", "one", "two"] + ); +} + +#[test] +fn aaos_opts_mult_req_delims() { + // opts with multiple and require delims + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!(--opt ... "some option").multiple_values(true)) + .try_get_matches_from(vec![ + "", + "--opt", + "first", + "overrides", + "--opt", + "some", + "other", + "val", + ]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.occurrences_of("opt"), 2); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["first", "overrides", "some", "other", "val"] + ); +} + +#[test] +fn aaos_pos_mult() { + // opts with multiple + let res = Command::new("posix") + .args_override_self(true) + .arg(arg!([val] ... "some pos")) + .try_get_matches_from(vec!["", "some", "other", "value"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert!(m.is_present("val")); + assert_eq!(m.occurrences_of("val"), 3); + assert_eq!( + m.values_of("val").unwrap().collect::>(), + &["some", "other", "value"] + ); +} + +#[test] +fn aaos_option_use_delim_false() { + let m = Command::new("posix") + .args_override_self(true) + .arg(arg!(--opt "some option").use_value_delimiter(false)) + .try_get_matches_from(vec!["", "--opt=some,other", "--opt=one,two"]) + .unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.occurrences_of("opt"), 1); + assert_eq!( + m.values_of("opt").unwrap().collect::>(), + &["one,two"] + ); +} + +#[test] +fn no_auto_help() { + let cmd = Command::new("myprog") + .setting(AppSettings::NoAutoHelp) + .subcommand(Command::new("foo")); + + let result = cmd.clone().try_get_matches_from("myprog --help".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("help")); + + let result = cmd.clone().try_get_matches_from("myprog -h".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("help")); + + let result = cmd.clone().try_get_matches_from("myprog help".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert_eq!(result.unwrap().subcommand_name(), Some("help")); +} + +#[test] +fn no_auto_version() { + let cmd = Command::new("myprog") + .version("3.0") + .setting(AppSettings::NoAutoVersion); + + let result = cmd + .clone() + .try_get_matches_from("myprog --version".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("version")); + + let result = cmd.clone().try_get_matches_from("myprog -V".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("version")); +} + +#[test] +fn no_auto_version_mut_arg() { + let cmd = Command::new("myprog") + .version("3.0") + .mut_arg("version", |v| v.help("custom help")) + .setting(AppSettings::NoAutoVersion); + + let result = cmd + .clone() + .try_get_matches_from("myprog --version".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("version")); + + let result = cmd.clone().try_get_matches_from("myprog -V".split(' ')); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + assert!(result.unwrap().is_present("version")); +} + +#[test] +#[cfg(feature = "color")] +fn color_is_global() { + let mut cmd = Command::new("myprog") + .color(clap::ColorChoice::Never) + .subcommand(Command::new("foo")); + cmd.build(); + assert_eq!(cmd.get_color(), clap::ColorChoice::Never); + + let sub = cmd.get_subcommands().collect::>()[0]; + assert_eq!(sub.get_color(), clap::ColorChoice::Never); +} diff --git a/tests/builder/legacy/arg_aliases.rs b/tests/builder/legacy/arg_aliases.rs new file mode 100644 index 00000000000..09b5000c681 --- /dev/null +++ b/tests/builder/legacy/arg_aliases.rs @@ -0,0 +1,196 @@ +use super::utils; + +use clap::{arg, Arg, Command}; + +static SC_VISIBLE_ALIAS_HELP: &str = "ct-test 1.2 +Some help + +USAGE: + ct test [OPTIONS] + +OPTIONS: + -f, --flag [aliases: v_flg, flag2, flg3] + -h, --help Print help information + -o, --opt [aliases: visible] + -V, --version Print version information +"; + +static SC_INVISIBLE_ALIAS_HELP: &str = "ct-test 1.2 +Some help + +USAGE: + ct test [OPTIONS] + +OPTIONS: + -f, --flag + -h, --help Print help information + -o, --opt + -V, --version Print version information +"; + +#[test] +fn single_alias_of_option() { + let a = Command::new("single_alias") + .arg( + Arg::new("alias") + .long("alias") + .takes_value(true) + .help("single alias") + .alias("new-opt"), + ) + .try_get_matches_from(vec!["", "--new-opt", "cool"]); + assert!(a.is_ok(), "{}", a.unwrap_err()); + let a = a.unwrap(); + assert!(a.is_present("alias")); + assert_eq!(a.value_of("alias").unwrap(), "cool"); +} + +#[test] +fn multiple_aliases_of_option() { + let a = Command::new("multiple_aliases").arg( + Arg::new("aliases") + .long("aliases") + .takes_value(true) + .help("multiple aliases") + .aliases(&["alias1", "alias2", "alias3"]), + ); + let long = a + .clone() + .try_get_matches_from(vec!["", "--aliases", "value"]); + assert!(long.is_ok(), "{}", long.unwrap_err()); + let long = long.unwrap(); + + let als1 = a + .clone() + .try_get_matches_from(vec!["", "--alias1", "value"]); + assert!(als1.is_ok(), "{}", als1.unwrap_err()); + let als1 = als1.unwrap(); + + let als2 = a + .clone() + .try_get_matches_from(vec!["", "--alias2", "value"]); + assert!(als2.is_ok(), "{}", als2.unwrap_err()); + let als2 = als2.unwrap(); + + let als3 = a + .clone() + .try_get_matches_from(vec!["", "--alias3", "value"]); + assert!(als3.is_ok(), "{}", als3.unwrap_err()); + let als3 = als3.unwrap(); + + assert!(long.is_present("aliases")); + assert!(als1.is_present("aliases")); + assert!(als2.is_present("aliases")); + assert!(als3.is_present("aliases")); + assert_eq!(long.value_of("aliases").unwrap(), "value"); + assert_eq!(als1.value_of("aliases").unwrap(), "value"); + assert_eq!(als2.value_of("aliases").unwrap(), "value"); + assert_eq!(als3.value_of("aliases").unwrap(), "value"); +} + +#[test] +fn single_alias_of_flag() { + let a = Command::new("test") + .arg(Arg::new("flag").long("flag").alias("alias")) + .try_get_matches_from(vec!["", "--alias"]); + assert!(a.is_ok(), "{}", a.unwrap_err()); + let a = a.unwrap(); + assert!(a.is_present("flag")); +} + +#[test] +fn multiple_aliases_of_flag() { + let a = Command::new("test").arg(Arg::new("flag").long("flag").aliases(&[ + "invisible", + "set", + "of", + "cool", + "aliases", + ])); + + let flag = a.clone().try_get_matches_from(vec!["", "--flag"]); + assert!(flag.is_ok(), "{}", flag.unwrap_err()); + let flag = flag.unwrap(); + + let inv = a.clone().try_get_matches_from(vec!["", "--invisible"]); + assert!(inv.is_ok(), "{}", inv.unwrap_err()); + let inv = inv.unwrap(); + + let cool = a.clone().try_get_matches_from(vec!["", "--cool"]); + assert!(cool.is_ok(), "{}", cool.unwrap_err()); + let cool = cool.unwrap(); + + let als = a.clone().try_get_matches_from(vec!["", "--aliases"]); + assert!(als.is_ok(), "{}", als.unwrap_err()); + let als = als.unwrap(); + + assert!(flag.is_present("flag")); + assert!(inv.is_present("flag")); + assert!(cool.is_present("flag")); + assert!(als.is_present("flag")); +} + +#[test] +fn alias_on_a_subcommand_option() { + let m = Command::new("test") + .subcommand( + Command::new("some").arg( + Arg::new("test") + .short('t') + .long("test") + .takes_value(true) + .alias("opt") + .help("testing testing"), + ), + ) + .arg(Arg::new("other").long("other").aliases(&["o1", "o2", "o3"])) + .try_get_matches_from(vec!["test", "some", "--opt", "awesome"]) + .unwrap(); + + assert!(m.subcommand_matches("some").is_some()); + let sub_m = m.subcommand_matches("some").unwrap(); + assert!(sub_m.is_present("test")); + assert_eq!(sub_m.value_of("test").unwrap(), "awesome"); +} + +#[test] +fn invisible_arg_aliases_help_output() { + let cmd = Command::new("ct").author("Salim Afiune").subcommand( + Command::new("test") + .about("Some help") + .version("1.2") + .arg( + Arg::new("opt") + .long("opt") + .short('o') + .takes_value(true) + .aliases(&["invisible", "als1", "more"]), + ) + .arg(arg!(-f - -flag).aliases(&["unseeable", "flg1", "anyway"])), + ); + utils::assert_output(cmd, "ct test --help", SC_INVISIBLE_ALIAS_HELP, false); +} + +#[test] +fn visible_arg_aliases_help_output() { + let cmd = Command::new("ct").author("Salim Afiune").subcommand( + Command::new("test") + .about("Some help") + .version("1.2") + .arg( + Arg::new("opt") + .long("opt") + .short('o') + .takes_value(true) + .alias("invisible") + .visible_alias("visible"), + ) + .arg( + Arg::new("flg") + .long("flag") + .short('f') + .visible_aliases(&["v_flg", "flag2", "flg3"]), + ), + ); + utils::assert_output(cmd, "ct test --help", SC_VISIBLE_ALIAS_HELP, false); +} diff --git a/tests/builder/legacy/arg_aliases_short.rs b/tests/builder/legacy/arg_aliases_short.rs new file mode 100644 index 00000000000..f5f712847cc --- /dev/null +++ b/tests/builder/legacy/arg_aliases_short.rs @@ -0,0 +1,193 @@ +use super::utils; + +use clap::{arg, Arg, Command}; + +static SC_VISIBLE_ALIAS_HELP: &str = "ct-test 1.2 +Some help + +USAGE: + ct test [OPTIONS] + +OPTIONS: + -f, --flag [aliases: flag1] [short aliases: a, b, 🦆] + -h, --help Print help information + -o, --opt [short aliases: v] + -V, --version Print version information +"; + +static SC_INVISIBLE_ALIAS_HELP: &str = "ct-test 1.2 +Some help + +USAGE: + ct test [OPTIONS] + +OPTIONS: + -f, --flag + -h, --help Print help information + -o, --opt + -V, --version Print version information +"; + +#[test] +fn single_short_alias_of_option() { + let a = Command::new("single_alias") + .arg( + Arg::new("alias") + .long("alias") + .takes_value(true) + .help("single short alias") + .short_alias('a'), + ) + .try_get_matches_from(vec!["", "-a", "cool"]); + assert!(a.is_ok(), "{}", a.unwrap_err()); + let a = a.unwrap(); + assert!(a.is_present("alias")); + assert_eq!(a.value_of("alias").unwrap(), "cool"); +} + +#[test] +fn multiple_short_aliases_of_option() { + let a = Command::new("multiple_aliases").arg( + Arg::new("aliases") + .long("aliases") + .takes_value(true) + .help("multiple aliases") + .short_aliases(&['1', '2', '3']), + ); + let long = a + .clone() + .try_get_matches_from(vec!["", "--aliases", "value"]); + assert!(long.is_ok(), "{}", long.unwrap_err()); + let long = long.unwrap(); + + let als1 = a.clone().try_get_matches_from(vec!["", "-1", "value"]); + assert!(als1.is_ok(), "{}", als1.unwrap_err()); + let als1 = als1.unwrap(); + + let als2 = a.clone().try_get_matches_from(vec!["", "-2", "value"]); + assert!(als2.is_ok(), "{}", als2.unwrap_err()); + let als2 = als2.unwrap(); + + let als3 = a.clone().try_get_matches_from(vec!["", "-3", "value"]); + assert!(als3.is_ok(), "{}", als3.unwrap_err()); + let als3 = als3.unwrap(); + + assert!(long.is_present("aliases")); + assert!(als1.is_present("aliases")); + assert!(als2.is_present("aliases")); + assert!(als3.is_present("aliases")); + assert_eq!(long.value_of("aliases").unwrap(), "value"); + assert_eq!(als1.value_of("aliases").unwrap(), "value"); + assert_eq!(als2.value_of("aliases").unwrap(), "value"); + assert_eq!(als3.value_of("aliases").unwrap(), "value"); +} + +#[test] +fn single_short_alias_of_flag() { + let a = Command::new("test") + .arg(Arg::new("flag").long("flag").short_alias('f')) + .try_get_matches_from(vec!["", "-f"]); + assert!(a.is_ok(), "{}", a.unwrap_err()); + let a = a.unwrap(); + assert!(a.is_present("flag")); +} + +#[test] +fn multiple_short_aliases_of_flag() { + let a = Command::new("test").arg( + Arg::new("flag") + .long("flag") + .short_aliases(&['a', 'b', 'c', 'd', 'e']), + ); + + let flag = a.clone().try_get_matches_from(vec!["", "--flag"]); + assert!(flag.is_ok(), "{}", flag.unwrap_err()); + let flag = flag.unwrap(); + + let als1 = a.clone().try_get_matches_from(vec!["", "-a"]); + assert!(als1.is_ok(), "{}", als1.unwrap_err()); + let als1 = als1.unwrap(); + + let als2 = a.clone().try_get_matches_from(vec!["", "-b"]); + assert!(als2.is_ok(), "{}", als2.unwrap_err()); + let als2 = als2.unwrap(); + + let als3 = a.clone().try_get_matches_from(vec!["", "-c"]); + assert!(als3.is_ok(), "{}", als3.unwrap_err()); + let als3 = als3.unwrap(); + + assert!(flag.is_present("flag")); + assert!(als1.is_present("flag")); + assert!(als2.is_present("flag")); + assert!(als3.is_present("flag")); +} + +#[test] +fn short_alias_on_a_subcommand_option() { + let m = Command::new("test") + .subcommand( + Command::new("some").arg( + Arg::new("test") + .short('t') + .long("test") + .takes_value(true) + .short_alias('o') + .help("testing testing"), + ), + ) + .arg( + Arg::new("other") + .long("other") + .short_aliases(&['1', '2', '3']), + ) + .try_get_matches_from(vec!["test", "some", "-o", "awesome"]) + .unwrap(); + + assert!(m.subcommand_matches("some").is_some()); + let sub_m = m.subcommand_matches("some").unwrap(); + assert!(sub_m.is_present("test")); + assert_eq!(sub_m.value_of("test").unwrap(), "awesome"); +} + +#[test] +fn invisible_short_arg_aliases_help_output() { + let cmd = Command::new("ct").author("Salim Afiune").subcommand( + Command::new("test") + .about("Some help") + .version("1.2") + .arg( + Arg::new("opt") + .long("opt") + .short('o') + .takes_value(true) + .short_aliases(&['a', 'b', 'c']), + ) + .arg(arg!(-f - -flag).short_aliases(&['x', 'y', 'z'])), + ); + utils::assert_output(cmd, "ct test --help", SC_INVISIBLE_ALIAS_HELP, false); +} + +#[test] +fn visible_short_arg_aliases_help_output() { + let cmd = Command::new("ct").author("Salim Afiune").subcommand( + Command::new("test") + .about("Some help") + .version("1.2") + .arg( + Arg::new("opt") + .long("opt") + .short('o') + .takes_value(true) + .short_alias('i') + .visible_short_alias('v'), + ) + .arg( + Arg::new("flg") + .long("flag") + .short('f') + .visible_alias("flag1") + .visible_short_aliases(&['a', 'b', '🦆']), + ), + ); + utils::assert_output(cmd, "ct test --help", SC_VISIBLE_ALIAS_HELP, false); +} diff --git a/tests/builder/legacy/arg_matcher_assertions.rs b/tests/builder/legacy/arg_matcher_assertions.rs new file mode 100644 index 00000000000..e2c2378e8f0 --- /dev/null +++ b/tests/builder/legacy/arg_matcher_assertions.rs @@ -0,0 +1,41 @@ +#[cfg(debug_assertions)] +use clap::{Arg, Command}; + +#[test] +#[cfg(debug_assertions)] +#[should_panic = "`f` is not a name of an argument or a group."] +fn arg_matches_if_present_wrong_arg() { + let m = Command::new("test") + .arg(Arg::new("flag").short('f')) + .try_get_matches_from(&["test", "-f"]) + .unwrap(); + + assert!(m.is_present("flag")); + m.is_present("f"); +} + +#[test] +#[cfg(debug_assertions)] +#[should_panic = "`o` is not a name of an argument or a group."] +fn arg_matches_value_of_wrong_arg() { + let m = Command::new("test") + .arg(Arg::new("opt").short('o').takes_value(true)) + .try_get_matches_from(&["test", "-o", "val"]) + .unwrap(); + + assert_eq!(m.value_of("opt"), Some("val")); + m.value_of("o"); +} + +#[test] +#[cfg(debug_assertions)] +#[should_panic = "`seed` is not a name of a subcommand."] +fn arg_matches_subcommand_matches_wrong_sub() { + let m = Command::new("test") + .subcommand(Command::new("speed")) + .try_get_matches_from(&["test", "speed"]) + .unwrap(); + + assert!(m.subcommand_matches("speed").is_some()); + m.subcommand_matches("seed"); +} diff --git a/tests/builder/legacy/arg_settings.rs b/tests/builder/legacy/arg_settings.rs new file mode 100644 index 00000000000..d4e900ab92c --- /dev/null +++ b/tests/builder/legacy/arg_settings.rs @@ -0,0 +1,44 @@ +#![allow(deprecated)] +use clap::{Arg, ArgSettings}; + +#[test] +fn setting() { + let m = Arg::new("setting").setting(ArgSettings::Required); + assert!(m.is_required_set()); +} + +#[test] +fn unset_setting() { + let m = Arg::new("unset_setting").setting(ArgSettings::Required); + assert!(m.is_required_set()); + + let m = m.unset_setting(ArgSettings::Required); + assert!(!m.is_required_set(), "{:#?}", m); +} + +#[test] +fn setting_bitor() { + let m = Arg::new("setting_bitor") + .setting(ArgSettings::Required | ArgSettings::Hidden | ArgSettings::Last); + + assert!(m.is_required_set()); + assert!(m.is_hide_set()); + assert!(m.is_last_set()); +} + +#[test] +fn unset_setting_bitor() { + let m = Arg::new("unset_setting_bitor") + .setting(ArgSettings::Required) + .setting(ArgSettings::Hidden) + .setting(ArgSettings::Last); + + assert!(m.is_required_set()); + assert!(m.is_hide_set()); + assert!(m.is_last_set()); + + let m = m.unset_setting(ArgSettings::Required | ArgSettings::Hidden | ArgSettings::Last); + assert!(!m.is_required_set(), "{:#?}", m); + assert!(!m.is_hide_set(), "{:#?}", m); + assert!(!m.is_last_set(), "{:#?}", m); +} diff --git a/tests/builder/legacy/borrowed.rs b/tests/builder/legacy/borrowed.rs new file mode 100644 index 00000000000..4b4cd39a2d2 --- /dev/null +++ b/tests/builder/legacy/borrowed.rs @@ -0,0 +1,17 @@ +use clap::{Arg, Command}; + +#[test] +fn borrowed_args() { + let arg = Arg::new("some").short('s').long("some").help("other help"); + let arg2 = Arg::new("some2") + .short('S') + .long("some-thing") + .help("other help"); + let result = Command::new("sub_command_negate") + .arg(Arg::new("test").index(1)) + .arg(&arg) + .arg(&arg2) + .subcommand(Command::new("sub1").arg(&arg)) + .try_get_matches_from(vec!["prog"]); + assert!(result.is_ok(), "{}", result.unwrap_err()); +} diff --git a/tests/builder/legacy/cargo.rs b/tests/builder/legacy/cargo.rs new file mode 100644 index 00000000000..d2eb6822fa6 --- /dev/null +++ b/tests/builder/legacy/cargo.rs @@ -0,0 +1,80 @@ +#![cfg(feature = "cargo")] + +use clap::{ + crate_authors, crate_description, crate_name, crate_version, error::ErrorKind, Command, +}; + +static DESCRIPTION_ONLY: &str = "prog 1 +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + prog + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +static AUTHORS_ONLY: &str = "prog 1 + + +USAGE: + prog + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +#[test] +fn crate_version() { + let res = Command::new("prog") + .version(crate_version!()) + .try_get_matches_from(vec!["prog", "--version"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayVersion); + assert_eq!( + err.to_string(), + format!("prog {}\n", env!("CARGO_PKG_VERSION")) + ); +} + +#[test] +fn crate_description() { + let res = Command::new("prog") + .version("1") + .about(crate_description!()) + .try_get_matches_from(vec!["prog", "--help"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayHelp); + assert_eq!(err.to_string(), DESCRIPTION_ONLY); +} + +#[test] +fn crate_authors() { + let res = Command::new("prog") + .version("1") + .author(crate_authors!()) + .try_get_matches_from(vec!["prog", "--help"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayHelp); + assert_eq!(err.to_string(), AUTHORS_ONLY); +} + +#[test] +fn crate_name() { + let res = Command::new(crate_name!()) + .version("3.0") + .try_get_matches_from(vec!["clap", "--version"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayVersion); + assert_eq!(err.to_string(), "clap 3.0\n"); +} diff --git a/tests/builder/legacy/command.rs b/tests/builder/legacy/command.rs new file mode 100644 index 00000000000..9f31d7a9d6a --- /dev/null +++ b/tests/builder/legacy/command.rs @@ -0,0 +1,27 @@ +#![cfg(feature = "cargo")] + +use clap::{command, error::ErrorKind}; + +static EVERYTHING: &str = "clap {{version}} +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + clap + +OPTIONS: + -h, --help Print help information + -V, --version Print version information +"; + +#[test] +fn command() { + let res = command!().try_get_matches_from(vec!["clap", "--help"]); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayHelp); + assert_eq!( + err.to_string(), + EVERYTHING.replace("{{version}}", env!("CARGO_PKG_VERSION")) + ); +} diff --git a/tests/builder/legacy/conflicts.rs b/tests/builder/legacy/conflicts.rs new file mode 100644 index 00000000000..8a728e89429 --- /dev/null +++ b/tests/builder/legacy/conflicts.rs @@ -0,0 +1,509 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command}; + +static CONFLICT_ERR: &str = "error: The argument '--flag' cannot be used with '-F' + +USAGE: + clap-test --flag --long-option-2 + +For more information try --help +"; + +static CONFLICT_ERR_REV: &str = "error: The argument '-F' cannot be used with '--flag' + +USAGE: + clap-test -F --long-option-2 + +For more information try --help +"; + +static CONFLICT_ERR_THREE: &str = "error: The argument '--one' cannot be used with: + --two + --three + +USAGE: + three_conflicting_arguments --one + +For more information try --help +"; + +#[test] +fn flag_conflict() { + let result = Command::new("flag_conflict") + .arg(arg!(-f --flag "some flag").conflicts_with("other")) + .arg(arg!(-o --other "some flag")) + .try_get_matches_from(vec!["myprog", "-f", "-o"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn flag_conflict_2() { + let result = Command::new("flag_conflict") + .arg(arg!(-f --flag "some flag").conflicts_with("other")) + .arg(arg!(-o --other "some flag")) + .try_get_matches_from(vec!["myprog", "-o", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn flag_conflict_with_all() { + let result = Command::new("flag_conflict") + .arg(arg!(-f --flag "some flag").conflicts_with_all(&["other"])) + .arg(arg!(-o --other "some flag")) + .try_get_matches_from(vec!["myprog", "-o", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn flag_conflict_with_everything() { + let result = Command::new("flag_conflict") + .arg(arg!(-f --flag "some flag").exclusive(true)) + .arg(arg!(-o --other "some flag")) + .try_get_matches_from(vec!["myprog", "-o", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn arg_conflicts_with_group() { + let mut cmd = Command::new("group_conflict") + .arg(arg!(-f --flag "some flag").conflicts_with("gr")) + .group(ArgGroup::new("gr").arg("some").arg("other")) + .arg(arg!(--some "some arg")) + .arg(arg!(--other "other arg")); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f", "--some"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--flag"]); + if let Err(err) = result { + panic!("{}", err); + } +} + +#[test] +fn arg_conflicts_with_group_with_multiple_sources() { + let mut cmd = clap::Command::new("group_conflict") + .arg(clap::arg!(-f --flag "some flag").conflicts_with("gr")) + .group(clap::ArgGroup::new("gr").multiple(true)) + .arg( + clap::arg!(--some "some arg") + .required(false) + .group("gr"), + ) + .arg( + clap::arg!(--other "other arg") + .required(false) + .default_value("1000") + .group("gr"), + ); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some", "usb1"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some", "usb1", "--other", "40"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f", "--some", "usb1"]); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn group_conflicts_with_arg() { + let mut cmd = Command::new("group_conflict") + .arg(arg!(-f --flag "some flag")) + .group( + ArgGroup::new("gr") + .arg("some") + .arg("other") + .conflicts_with("flag"), + ) + .arg(arg!(--some "some arg")) + .arg(arg!(--other "other arg")); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f", "--some"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--flag"]); + if let Err(err) = result { + panic!("{}", err); + } +} + +#[test] +fn arg_conflicts_with_required_group() { + let mut cmd = Command::new("group_conflict") + .arg(arg!(-f --flag "some flag").conflicts_with("gr")) + .group(ArgGroup::new("gr").required(true).arg("some").arg("other")) + .arg(arg!(--some "some arg")) + .arg(arg!(--other "other arg")); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f", "--some"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other"]); + if let Err(err) = result { + panic!("{}", err); + } +} + +#[test] +fn required_group_conflicts_with_arg() { + let mut cmd = Command::new("group_conflict") + .arg(arg!(-f --flag "some flag")) + .group( + ArgGroup::new("gr") + .required(true) + .arg("some") + .arg("other") + .conflicts_with("flag"), + ) + .arg(arg!(--some "some arg")) + .arg(arg!(--other "other arg")); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other", "-f"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "-f", "--some"]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--some"]); + if let Err(err) = result { + panic!("{}", err); + } + + let result = cmd.try_get_matches_from_mut(vec!["myprog", "--other"]); + if let Err(err) = result { + panic!("{}", err); + } +} + +#[test] +fn conflict_output() { + utils::assert_output( + utils::complex_app(), + "clap-test val1 fa --flag --long-option-2 val2 -F", + CONFLICT_ERR, + true, + ); +} + +#[test] +fn conflict_output_rev() { + utils::assert_output( + utils::complex_app(), + "clap-test val1 fa -F --long-option-2 val2 --flag", + CONFLICT_ERR_REV, + true, + ); +} + +#[test] +fn conflict_output_with_required() { + utils::assert_output( + utils::complex_app(), + "clap-test val1 --flag --long-option-2 val2 -F", + CONFLICT_ERR, + true, + ); +} + +#[test] +fn conflict_output_rev_with_required() { + utils::assert_output( + utils::complex_app(), + "clap-test val1 -F --long-option-2 val2 --flag", + CONFLICT_ERR_REV, + true, + ); +} + +#[test] +fn conflict_output_three_conflicting() { + let cmd = Command::new("three_conflicting_arguments") + .arg( + Arg::new("one") + .long("one") + .conflicts_with_all(&["two", "three"]), + ) + .arg( + Arg::new("two") + .long("two") + .conflicts_with_all(&["one", "three"]), + ) + .arg( + Arg::new("three") + .long("three") + .conflicts_with_all(&["one", "two"]), + ); + utils::assert_output( + cmd, + "three_conflicting_arguments --one --two --three", + CONFLICT_ERR_THREE, + true, + ); +} + +#[test] +fn two_conflicting_arguments() { + let a = Command::new("two_conflicting_arguments") + .arg( + Arg::new("develop") + .long("develop") + .conflicts_with("production"), + ) + .arg( + Arg::new("production") + .long("production") + .conflicts_with("develop"), + ) + .try_get_matches_from(vec!["", "--develop", "--production"]); + + assert!(a.is_err()); + let a = a.unwrap_err(); + assert!( + a.to_string() + .contains("The argument \'--develop\' cannot be used with \'--production\'"), + "{}", + a + ); +} + +#[test] +fn three_conflicting_arguments() { + let a = Command::new("three_conflicting_arguments") + .arg( + Arg::new("one") + .long("one") + .conflicts_with_all(&["two", "three"]), + ) + .arg( + Arg::new("two") + .long("two") + .conflicts_with_all(&["one", "three"]), + ) + .arg( + Arg::new("three") + .long("three") + .conflicts_with_all(&["one", "two"]), + ) + .try_get_matches_from(vec!["", "--one", "--two", "--three"]); + + assert!(a.is_err()); + let a = a.unwrap_err(); + assert!( + a.to_string() + .contains("The argument \'--one\' cannot be used with:"), + "{}", + a + ); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument 'config' cannot conflict with itself"] +fn self_conflicting_arg() { + let _ = Command::new("prog") + .arg(Arg::new("config").long("config").conflicts_with("config")) + .try_get_matches_from(vec!["", "--config"]); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument or group 'extra' specified in 'conflicts_with*' for 'config' does not exist"] +fn conflicts_with_invalid_arg() { + let _ = Command::new("prog") + .arg(Arg::new("config").long("config").conflicts_with("extra")) + .try_get_matches_from(vec!["", "--config"]); +} + +#[test] +fn conflict_with_unused_default() { + let result = Command::new("conflict") + .arg( + arg!(-o --opt "some opt") + .required(false) + .default_value("default"), + ) + .arg(arg!(-f --flag "some flag").conflicts_with("opt")) + .try_get_matches_from(vec!["myprog", "-f"]); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + let m = result.unwrap(); + + assert_eq!(m.value_of("opt"), Some("default")); + assert!(m.is_present("flag")); +} + +#[test] +fn conflicts_with_alongside_default() { + let result = Command::new("conflict") + .arg( + arg!(-o --opt "some opt") + .default_value("default") + .required(false) + .conflicts_with("flag"), + ) + .arg(arg!(-f --flag "some flag")) + .try_get_matches_from(vec!["myprog", "-f"]); + + assert!( + result.is_ok(), + "conflicts_with should ignore default_value: {:?}", + result.unwrap_err() + ); + let m = result.unwrap(); + + assert_eq!(m.value_of("opt"), Some("default")); + assert!(m.is_present("flag")); +} + +#[test] +fn group_in_conflicts_with() { + let result = Command::new("conflict") + .arg( + Arg::new("opt") + .long("opt") + .default_value("default") + .group("one"), + ) + .arg(Arg::new("flag").long("flag").conflicts_with("one")) + .try_get_matches_from(vec!["myprog", "--flag"]); + + assert!( + result.is_ok(), + "conflicts_with on an arg group should ignore default_value: {:?}", + result.unwrap_err() + ); + let m = result.unwrap(); + + assert_eq!(m.value_of("opt"), Some("default")); + assert!(m.is_present("flag")); +} + +#[test] +fn group_conflicts_with_default_value() { + let result = Command::new("conflict") + .arg( + Arg::new("opt") + .long("opt") + .default_value("default") + .group("one"), + ) + .arg(Arg::new("flag").long("flag").group("one")) + .try_get_matches_from(vec!["myprog", "--flag"]); + + assert!( + result.is_ok(), + "arg group count should ignore default_value: {:?}", + result.unwrap_err() + ); + let m = result.unwrap(); + + assert_eq!(m.value_of("opt"), Some("default")); + assert!(m.is_present("flag")); +} + +#[test] +fn group_conflicts_with_default_arg() { + let result = Command::new("conflict") + .arg(Arg::new("opt").long("opt").default_value("default")) + .arg(Arg::new("flag").long("flag").group("one")) + .group(ArgGroup::new("one").conflicts_with("opt")) + .try_get_matches_from(vec!["myprog", "--flag"]); + + assert!( + result.is_ok(), + "arg group conflicts_with should ignore default_value: {:?}", + result.unwrap_err() + ); + let m = result.unwrap(); + + assert_eq!(m.value_of("opt"), Some("default")); + assert!(m.is_present("flag")); +} + +#[test] +fn exclusive_with_required() { + let cmd = Command::new("bug") + .arg(Arg::new("test").long("test").exclusive(true)) + .arg(Arg::new("input").takes_value(true).required(true)); + + cmd.clone() + .try_get_matches_from(["bug", "--test", "required"]) + .unwrap_err(); + + cmd.clone() + .try_get_matches_from(["bug", "required"]) + .unwrap(); + + cmd.clone().try_get_matches_from(["bug", "--test"]).unwrap(); +} diff --git a/tests/builder/legacy/default_missing_vals.rs b/tests/builder/legacy/default_missing_vals.rs new file mode 100644 index 00000000000..52ed95226fa --- /dev/null +++ b/tests/builder/legacy/default_missing_vals.rs @@ -0,0 +1,187 @@ +use clap::{arg, Arg, ArgMatches, Command}; + +#[test] +fn opt_missing() { + let r = Command::new("df") + .arg( + Arg::new("color") + .long("color") + .default_value("auto") + .min_values(0) + .require_equals(true) + .default_missing_value("always"), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("color")); + assert_eq!(m.value_of("color").unwrap(), "auto"); + assert_eq!(m.occurrences_of("color"), 0); +} + +#[test] +fn opt_present_with_missing_value() { + let r = Command::new("df") + .arg( + Arg::new("color") + .long("color") + .default_value("auto") + .min_values(0) + .require_equals(true) + .default_missing_value("always"), + ) + .try_get_matches_from(vec!["", "--color"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("color")); + assert_eq!(m.value_of("color").unwrap(), "always"); + assert_eq!(m.occurrences_of("color"), 1); +} + +#[test] +fn opt_present_with_value() { + let r = Command::new("df") + .arg( + Arg::new("color") + .long("color") + .default_value("auto") + .min_values(0) + .require_equals(true) + .default_missing_value("always"), + ) + .try_get_matches_from(vec!["", "--color=never"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("color")); + assert_eq!(m.value_of("color").unwrap(), "never"); + assert_eq!(m.occurrences_of("color"), 1); +} + +#[test] +fn opt_present_with_empty_value() { + let r = Command::new("df") + .arg( + Arg::new("color") + .long("color") + .default_value("auto") + .require_equals(true) + .default_missing_value("always"), + ) + .try_get_matches_from(vec!["", "--color="]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("color")); + assert_eq!(m.value_of("color").unwrap(), ""); + assert_eq!(m.occurrences_of("color"), 1); +} + +//## `default_value`/`default_missing_value` non-interaction checks + +#[test] +fn opt_default() { + // assert no change to usual argument handling when adding default_missing_value() + let r = Command::new("cmd") + .arg( + arg!(o: -o [opt] "some opt") + .default_value("default") + .default_missing_value("default_missing"), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.value_of("o").unwrap(), "default"); +} + +#[test] +fn opt_default_user_override() { + // assert no change to usual argument handling when adding default_missing_value() + let r = Command::new("cmd") + .arg( + arg!(o: -o [opt] "some opt") + .default_value("default") + .default_missing_value("default_missing"), + ) + .try_get_matches_from(vec!["", "-o=value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.value_of("o").unwrap(), "value"); +} + +#[test] +#[allow(clippy::bool_assert_comparison)] +fn default_missing_value_flag_value() { + let cmd = Command::new("test").arg( + Arg::new("flag") + .long("flag") + .takes_value(true) + .default_missing_value("true"), + ); + + fn flag_value(m: ArgMatches) -> bool { + match m.value_of("flag") { + None => false, + Some(x) => x.parse().expect("non boolean value"), + } + } + + assert_eq!( + flag_value(cmd.clone().try_get_matches_from(&["test"]).unwrap()), + false + ); + assert_eq!( + flag_value( + cmd.clone() + .try_get_matches_from(&["test", "--flag"]) + .unwrap() + ), + true + ); + assert_eq!( + flag_value( + cmd.clone() + .try_get_matches_from(&["test", "--flag=true"]) + .unwrap() + ), + true + ); + assert_eq!( + flag_value( + cmd.clone() + .try_get_matches_from(&["test", "--flag=false"]) + .unwrap() + ), + false + ); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument `arg`'s default_missing_value=value doesn't match possible values"] +fn default_missing_values_are_possible_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .possible_values(["one", "two"]) + .default_missing_value("value"), + ) + .try_get_matches(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument `arg`'s default_missing_value=value failed validation: invalid digit found in string"] +fn default_missing_values_are_valid() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .validator(|val| val.parse::().map_err(|e| e.to_string())) + .default_missing_value("value"), + ) + .try_get_matches(); +} diff --git a/tests/builder/legacy/default_vals.rs b/tests/builder/legacy/default_vals.rs new file mode 100644 index 00000000000..0bba658eac4 --- /dev/null +++ b/tests/builder/legacy/default_vals.rs @@ -0,0 +1,731 @@ +use super::utils; +use clap::{arg, error::ErrorKind, Arg, Command}; + +#[test] +fn opts() { + let r = Command::new("df") + .arg( + arg!(o: -o "some opt") + .required(false) + .default_value("default"), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.value_of("o").unwrap(), "default"); +} + +#[test] +fn opt_without_value_fail() { + let r = Command::new("df") + .arg( + arg!(o: -o "some opt") + .required(false) + .default_value("default") + .forbid_empty_values(true), + ) + .try_get_matches_from(vec!["", "-o"]); + assert!(r.is_err()); + let err = r.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::EmptyValue); + assert!(err + .to_string() + .contains("The argument '-o ' requires a value but none was supplied")); +} + +#[test] +fn opt_user_override() { + let r = Command::new("df") + .arg( + arg!(--opt "some arg") + .required(false) + .default_value("default"), + ) + .try_get_matches_from(vec!["", "--opt", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.value_of("opt").unwrap(), "value"); +} + +#[test] +fn positionals() { + let r = Command::new("df") + .arg(arg!([arg] "some opt").default_value("default")) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +#[test] +fn positional_user_override() { + let r = Command::new("df") + .arg(arg!([arg] "some arg").default_value("default")) + .try_get_matches_from(vec!["", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "value"); +} + +// OsStr Default Values + +#[test] +fn osstr_opts() { + use std::ffi::OsStr; + let expected = OsStr::new("default"); + + let r = Command::new("df") + .arg( + arg!(o: -o "some opt") + .required(false) + .default_value_os(expected), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.value_of("o").unwrap(), expected); +} + +#[test] +fn osstr_opt_user_override() { + use std::ffi::OsStr; + let default = OsStr::new("default"); + + let r = Command::new("df") + .arg( + arg!(--opt "some arg") + .required(false) + .default_value_os(default), + ) + .try_get_matches_from(vec!["", "--opt", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("opt")); + assert_eq!(m.value_of("opt").unwrap(), "value"); +} + +#[test] +fn osstr_positionals() { + use std::ffi::OsStr; + let expected = OsStr::new("default"); + + let r = Command::new("df") + .arg(arg!([arg] "some opt").default_value_os(expected)) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), expected); +} + +#[test] +fn osstr_positional_user_override() { + use std::ffi::OsStr; + let default = OsStr::new("default"); + + let r = Command::new("df") + .arg(arg!([arg] "some arg").default_value_os(default)) + .try_get_matches_from(vec!["", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "value"); +} + +// --- Default if arg is present + +#[test] +fn default_if_arg_present_no_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(true)) + .arg(arg!([arg] "some arg").default_value_if("opt", None, Some("default"))) + .try_get_matches_from(vec!["", "--opt", "some"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +#[test] +fn default_if_arg_present_no_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!([arg] "some arg").default_value_if("opt", None, Some("default"))) + .try_get_matches_from(vec!["", "--opt", "some", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn default_if_arg_present_no_arg_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", None, Some("default")), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "first"); +} + +#[test] +fn default_if_arg_present_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", None, Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "some"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +#[test] +fn default_if_arg_present_with_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", None, Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "some", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn default_if_arg_present_no_arg_with_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", None, Some("default")), + ) + .try_get_matches_from(vec!["", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +// Conditional Default Values + +#[test] +fn default_if_arg_present_with_value_no_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!([arg] "some arg").default_value_if("opt", Some("value"), Some("default"))) + .try_get_matches_from(vec!["", "--opt", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +#[test] +fn default_if_arg_present_with_value_no_default_fail() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!([arg] "some arg").default_value_if("opt", Some("value"), Some("default"))) + .try_get_matches_from(vec!["", "--opt", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); + assert!(m.value_of("arg").is_none()); +} + +#[test] +fn default_if_arg_present_with_value_no_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!([arg] "some arg").default_value_if("opt", Some("some"), Some("default"))) + .try_get_matches_from(vec!["", "--opt", "some", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn default_if_arg_present_with_value_no_arg_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "first"); +} + +#[test] +fn default_if_arg_present_with_value_no_arg_with_default_fail() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "first"); +} + +#[test] +fn default_if_arg_present_with_value_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "some"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +#[test] +fn default_if_arg_present_with_value_with_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "some", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn default_if_arg_present_no_arg_with_value_with_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec!["", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn default_if_arg_present_no_arg_with_value_with_default_user_override_fail() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_if("opt", Some("some"), Some("default")), + ) + .try_get_matches_from(vec!["", "--opt", "value", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +// Unsetting the default + +#[test] +fn no_default_if_arg_present_with_value_no_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!([arg] "some arg").default_value_if("opt", Some("value"), None)) + .try_get_matches_from(vec!["", "--opt", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); +} + +#[test] +fn no_default_if_arg_present_with_value_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("default") + .default_value_if("opt", Some("value"), None), + ) + .try_get_matches_from(vec!["", "--opt", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); + assert!(m.value_of("arg").is_none()); +} + +#[test] +fn no_default_if_arg_present_with_value_with_default_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("default") + .default_value_if("opt", Some("value"), None), + ) + .try_get_matches_from(vec!["", "--opt", "value", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "other"); +} + +#[test] +fn no_default_if_arg_present_no_arg_with_value_with_default() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg( + arg!([arg] "some arg") + .default_value("default") + .default_value_if("opt", Some("value"), None), + ) + .try_get_matches_from(vec!["", "--opt", "other"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +// Multiple conditions + +#[test] +fn default_ifs_arg_present() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!(--flag "some arg")) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_ifs(&[ + ("opt", Some("some"), Some("default")), + ("flag", None, Some("flg")), + ]), + ) + .try_get_matches_from(vec!["", "--flag"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "flg"); +} + +#[test] +fn no_default_ifs_arg_present() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!(--flag "some arg")) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_ifs(&[("opt", Some("some"), Some("default")), ("flag", None, None)]), + ) + .try_get_matches_from(vec!["", "--flag"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); + assert!(m.value_of("arg").is_none()); +} + +#[test] +fn default_ifs_arg_present_user_override() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!(--flag "some arg")) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_ifs(&[ + ("opt", Some("some"), Some("default")), + ("flag", None, Some("flg")), + ]), + ) + .try_get_matches_from(vec!["", "--flag", "value"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "value"); +} + +#[test] +fn default_ifs_arg_present_order() { + let r = Command::new("df") + .arg(arg!(--opt "some arg").required(false)) + .arg(arg!(--flag "some arg")) + .arg( + arg!([arg] "some arg") + .default_value("first") + .default_value_ifs(&[ + ("opt", Some("some"), Some("default")), + ("flag", None, Some("flg")), + ]), + ) + .try_get_matches_from(vec!["", "--opt=some", "--flag"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.value_of("arg").unwrap(), "default"); +} + +// Interaction with requires + +#[test] +fn conditional_reqs_pass() { + let m = Command::new("Test cmd") + .arg( + Arg::new("target") + .takes_value(true) + .default_value("file") + .long("target"), + ) + .arg( + Arg::new("input") + .takes_value(true) + .required(true) + .long("input"), + ) + .arg( + Arg::new("output") + .takes_value(true) + .required_if_eq("target", "file") + .long("output"), + ) + .try_get_matches_from(vec!["test", "--input", "some", "--output", "other"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + assert_eq!(m.value_of("output"), Some("other")); + assert_eq!(m.value_of("input"), Some("some")); +} + +#[test] +fn multiple_defaults() { + let r = Command::new("diff") + .arg( + Arg::new("files") + .long("files") + .number_of_values(2) + .allow_invalid_utf8(true) + .default_values(&["old", "new"]), + ) + .try_get_matches_from(vec![""]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("files")); + assert_eq!(m.values_of_lossy("files").unwrap(), vec!["old", "new"]); +} + +#[test] +fn multiple_defaults_override() { + let r = Command::new("diff") + .arg( + Arg::new("files") + .long("files") + .number_of_values(2) + .allow_invalid_utf8(true) + .default_values(&["old", "new"]), + ) + .try_get_matches_from(vec!["", "--files", "other", "mine"]); + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("files")); + assert_eq!(m.values_of_lossy("files").unwrap(), vec!["other", "mine"]); +} + +#[test] +fn default_vals_donnot_show_in_smart_usage() { + let cmd = Command::new("bug") + .arg( + Arg::new("foo") + .long("config") + .takes_value(true) + .default_value("bar"), + ) + .arg(Arg::new("input").required(true)); + + utils::assert_output( + cmd, + "bug", + "error: The following required arguments were not provided: + + +USAGE: + bug [OPTIONS] + +For more information try --help +", + true, + ); +} + +#[test] +fn issue_1050_num_vals_and_defaults() { + let res = Command::new("hello") + .arg( + Arg::new("exit-code") + .long("exit-code") + .takes_value(true) + .number_of_values(1) + .default_value("0"), + ) + .try_get_matches_from(vec!["hello", "--exit-code=1"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let m = res.unwrap(); + assert_eq!(m.value_of("exit-code"), Some("1")); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument group 'group' is required but all of it's arguments have a default value."] +fn required_groups_with_default_values() { + use clap::{Arg, ArgGroup, Command}; + + let _ = Command::new("test") + .arg(Arg::new("arg").default_value("value")) + .group(ArgGroup::new("group").args(&["arg"]).required(true)) + .try_get_matches(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument 'arg' is required and can't have a default value"] +fn required_args_with_default_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg(Arg::new("arg").required(true).default_value("value")) + .try_get_matches(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument `arg`'s default_value=value doesn't match possible values"] +fn default_values_are_possible_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .possible_values(["one", "two"]) + .default_value("value"), + ) + .try_get_matches(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument `arg`'s default_value=one failed validation: invalid digit found in string"] +fn invalid_default_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .validator(|val| val.parse::().map_err(|e| e.to_string())) + .default_value("one"), + ) + .try_get_matches(); +} + +#[test] +fn valid_delimited_default_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .validator(|val| val.parse::().map_err(|e| e.to_string())) + .use_value_delimiter(true) + .require_value_delimiter(true) + .default_value("1,2,3"), + ) + .try_get_matches(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument `arg`'s default_value=one failed validation: invalid digit found in string"] +fn invalid_delimited_default_values() { + use clap::{Arg, Command}; + + let _ = Command::new("test") + .arg( + Arg::new("arg") + .validator(|val| val.parse::().map_err(|e| e.to_string())) + .use_value_delimiter(true) + .require_value_delimiter(true) + .default_value("one,two"), + ) + .try_get_matches(); +} + +#[test] +fn with_value_delimiter() { + let cmd = Command::new("multiple_values").arg( + Arg::new("option") + .long("option") + .help("multiple options") + .value_delimiter(';') + .default_value("first;second"), + ); + + let matches = cmd.try_get_matches_from(vec![""]).unwrap(); + + assert_eq!( + matches.values_of("option").unwrap().collect::>(), + ["first", "second"] + ); +} + +#[test] +fn missing_with_value_delimiter() { + let cmd = Command::new("program").arg( + Arg::new("option") + .long("option") + .value_delimiter(';') + .default_missing_values(&["value1;value2;value3", "value4;value5"]), + ); + + let matches = cmd + .try_get_matches_from(vec!["program", "--option"]) + .unwrap(); + + assert_eq!( + matches.values_of("option").unwrap().collect::>(), + ["value1", "value2", "value3", "value4", "value5"] + ); +} diff --git a/tests/builder/legacy/delimiters.rs b/tests/builder/legacy/delimiters.rs new file mode 100644 index 00000000000..2ead7a3d333 --- /dev/null +++ b/tests/builder/legacy/delimiters.rs @@ -0,0 +1,113 @@ +use clap::{Arg, Command}; + +#[test] +fn opt_default_no_delim() { + let m = Command::new("no_delim") + .arg(Arg::new("option").long("option").takes_value(true)) + .try_get_matches_from(vec!["", "--option", "val1,val2,val3"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_eq_no_delim() { + let m = Command::new("no_delim") + .arg(Arg::new("option").long("option").takes_value(true)) + .try_get_matches_from(vec!["", "--option=val1,val2,val3"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_s_eq_no_delim() { + let m = Command::new("no_delim") + .arg(Arg::new("option").short('o').takes_value(true)) + .try_get_matches_from(vec!["", "-o=val1,val2,val3"]); + + assert!(m.is_ok(), "{:?}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_s_default_no_delim() { + let m = Command::new("no_delim") + .arg(Arg::new("option").short('o').takes_value(true)) + .try_get_matches_from(vec!["", "-o", "val1,val2,val3"]); + + assert!(m.is_ok(), "{:?}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_s_no_space_no_delim() { + let m = Command::new("no_delim") + .arg(Arg::new("option").short('o').takes_value(true)) + .try_get_matches_from(vec!["", "-o", "val1,val2,val3"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_s_no_space_mult_no_delim() { + let m = Command::new("no_delim") + .arg( + Arg::new("option") + .short('o') + .takes_value(true) + .multiple_values(true), + ) + .try_get_matches_from(vec!["", "-o", "val1,val2,val3"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!(m.value_of("option").unwrap(), "val1,val2,val3"); +} + +#[test] +fn opt_eq_mult_def_delim() { + let m = Command::new("no_delim") + .arg( + Arg::new("option") + .long("opt") + .takes_value(true) + .multiple_values(true) + .use_value_delimiter(true), + ) + .try_get_matches_from(vec!["", "--opt=val1,val2,val3"]); + + assert!(m.is_ok(), "{}", m.unwrap_err()); + let m = m.unwrap(); + + assert!(m.is_present("option")); + assert_eq!(m.occurrences_of("option"), 1); + assert_eq!( + m.values_of("option").unwrap().collect::>(), + &["val1", "val2", "val3"] + ); +} diff --git a/tests/builder/legacy/derive_order.rs b/tests/builder/legacy/derive_order.rs new file mode 100644 index 00000000000..5277806480f --- /dev/null +++ b/tests/builder/legacy/derive_order.rs @@ -0,0 +1,293 @@ +use super::utils; + +use std::str; + +use clap::{AppSettings, Arg, Command}; + +static NO_DERIVE_ORDER: &str = "test 1.2 + +USAGE: + test [OPTIONS] + +OPTIONS: + --flag_a second flag + --flag_b first flag + -h, --help Print help information + --option_a second option + --option_b first option + -V, --version Print version information +"; + +static UNIFIED_HELP_AND_DERIVE: &str = "test 1.2 + +USAGE: + test [OPTIONS] + +OPTIONS: + --flag_b first flag + --option_b first option + --flag_a second flag + --option_a second option + -h, --help Print help information + -V, --version Print version information +"; + +static UNIFIED_DERIVE_SC_PROP: &str = "test-sub 1.2 + +USAGE: + test sub [OPTIONS] + +OPTIONS: + --flag_b first flag + --option_b first option + --flag_a second flag + --option_a second option + -h, --help Print help information + -V, --version Print version information +"; + +static UNIFIED_DERIVE_SC_PROP_EXPLICIT_ORDER: &str = "test-sub 1.2 + +USAGE: + test sub [OPTIONS] + +OPTIONS: + --flag_a second flag + --flag_b first flag + --option_b first option + --option_a second option + -h, --help Print help information + -V, --version Print version information +"; + +static PREFER_USER_HELP_DERIVE_ORDER: &str = "test 1.2 + +USAGE: + test [OPTIONS] + +OPTIONS: + -h, --help Print help message + --flag_b first flag + --flag_a second flag + -V, --version Print version information +"; + +static PREFER_USER_HELP_SUBCMD_DERIVE_ORDER: &str = "test-sub 1.2 + +USAGE: + test sub [OPTIONS] + +OPTIONS: + -h, --help Print help message + --flag_b first flag + --flag_a second flag + -V, --version Print version information +"; + +#[test] +fn no_derive_order() { + let cmd = Command::new("test").version("1.2").args(&[ + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("first option"), + Arg::new("flag_a").long("flag_a").help("second flag"), + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("second option"), + ]); + + utils::assert_output(cmd, "test --help", NO_DERIVE_ORDER, false); +} + +#[test] +fn derive_order() { + let cmd = Command::new("test") + .setting(AppSettings::DeriveDisplayOrder) + .version("1.2") + .args(&[ + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("first option"), + Arg::new("flag_a").long("flag_a").help("second flag"), + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("second option"), + ]); + + utils::assert_output(cmd, "test --help", UNIFIED_HELP_AND_DERIVE, false); +} + +#[test] +fn derive_order_next_order() { + static HELP: &str = "test 1.2 + +USAGE: + test [OPTIONS] + +OPTIONS: + --flag_b first flag + --option_b first option + -h, --help Print help information + -V, --version Print version information + --flag_a second flag + --option_a second option +"; + + let cmd = Command::new("test") + .setting(AppSettings::DeriveDisplayOrder) + .version("1.2") + .next_display_order(10000) + .arg(Arg::new("flag_a").long("flag_a").help("second flag")) + .arg( + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("second option"), + ) + .next_display_order(10) + .arg(Arg::new("flag_b").long("flag_b").help("first flag")) + .arg( + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("first option"), + ); + + utils::assert_output(cmd, "test --help", HELP, false); +} + +#[test] +fn derive_order_no_next_order() { + static HELP: &str = "test 1.2 + +USAGE: + test [OPTIONS] + +OPTIONS: + --flag_a first flag + --flag_b second flag + -h, --help Print help information + --option_a first option + --option_b second option + -V, --version Print version information +"; + + let cmd = Command::new("test") + .setting(AppSettings::DeriveDisplayOrder) + .version("1.2") + .next_display_order(None) + .arg(Arg::new("flag_a").long("flag_a").help("first flag")) + .arg( + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("first option"), + ) + .arg(Arg::new("flag_b").long("flag_b").help("second flag")) + .arg( + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("second option"), + ); + + utils::assert_output(cmd, "test --help", HELP, false); +} + +#[test] +fn derive_order_subcommand_propagate() { + let cmd = Command::new("test") + .global_setting(AppSettings::DeriveDisplayOrder) + .subcommand( + Command::new("sub").version("1.2").args(&[ + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("first option"), + Arg::new("flag_a").long("flag_a").help("second flag"), + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("second option"), + ]), + ); + + utils::assert_output(cmd, "test sub --help", UNIFIED_DERIVE_SC_PROP, false); +} + +#[test] +fn derive_order_subcommand_propagate_with_explicit_display_order() { + let cmd = Command::new("test") + .global_setting(AppSettings::DeriveDisplayOrder) + .subcommand( + Command::new("sub").version("1.2").args(&[ + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("option_b") + .long("option_b") + .takes_value(true) + .help("first option"), + Arg::new("flag_a") + .long("flag_a") + .help("second flag") + .display_order(0), + Arg::new("option_a") + .long("option_a") + .takes_value(true) + .help("second option"), + ]), + ); + + utils::assert_output( + cmd, + "test sub --help", + UNIFIED_DERIVE_SC_PROP_EXPLICIT_ORDER, + false, + ); +} + +#[test] +fn prefer_user_help_with_derive_order() { + let cmd = Command::new("test") + .setting(AppSettings::DeriveDisplayOrder) + .version("1.2") + .args(&[ + Arg::new("help") + .long("help") + .short('h') + .help("Print help message"), + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("flag_a").long("flag_a").help("second flag"), + ]); + + utils::assert_output(cmd, "test --help", PREFER_USER_HELP_DERIVE_ORDER, false); +} + +#[test] +fn prefer_user_help_in_subcommand_with_derive_order() { + let cmd = Command::new("test") + .global_setting(AppSettings::DeriveDisplayOrder) + .subcommand( + Command::new("sub").version("1.2").args(&[ + Arg::new("help") + .long("help") + .short('h') + .help("Print help message"), + Arg::new("flag_b").long("flag_b").help("first flag"), + Arg::new("flag_a").long("flag_a").help("second flag"), + ]), + ); + + utils::assert_output( + cmd, + "test sub --help", + PREFER_USER_HELP_SUBCMD_DERIVE_ORDER, + false, + ); +} diff --git a/tests/builder/legacy/display_order.rs b/tests/builder/legacy/display_order.rs new file mode 100644 index 00000000000..139682d80e3 --- /dev/null +++ b/tests/builder/legacy/display_order.rs @@ -0,0 +1,26 @@ +use super::utils; + +use clap::Command; + +#[test] +fn very_large_display_order() { + let cmd = Command::new("test").subcommand(Command::new("sub").display_order(usize::MAX)); + + utils::assert_output( + cmd, + "test --help", + "test + +USAGE: + test [SUBCOMMAND] + +OPTIONS: + -h, --help Print help information + +SUBCOMMANDS: + help Print this message or the help of the given subcommand(s) + sub +", + false, + ); +} diff --git a/tests/builder/legacy/double_require.rs b/tests/builder/legacy/double_require.rs new file mode 100644 index 00000000000..13470cff6c1 --- /dev/null +++ b/tests/builder/legacy/double_require.rs @@ -0,0 +1,88 @@ +use clap::{error::ErrorKind, Arg, Command}; + +static HELP: &str = "prog + +USAGE: + prog [OPTIONS] + +OPTIONS: + -a + -b + -c + -h, --help Print help information +"; + +static ONLY_B_ERROR: &str = "error: The following required arguments were not provided: + -c + +USAGE: + prog -b -c + +For more information try --help +"; + +static ONLY_C_ERROR: &str = "error: The following required arguments were not provided: + -b + +USAGE: + prog -c -b + +For more information try --help +"; + +fn cmd() -> Command<'static> { + Command::new("prog") + .arg( + Arg::new("a") + .short('a') + .required_unless_present_any(&["b", "c"]) + .conflicts_with_all(&["b", "c"]), + ) + .arg( + Arg::new("b") + .short('b') + .required_unless_present("a") + .requires("c"), + ) + .arg( + Arg::new("c") + .short('c') + .required_unless_present("a") + .requires("b"), + ) +} + +#[test] +fn valid_cases() { + let res = cmd().try_get_matches_from(vec!["", "-a"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let res = cmd().clone().try_get_matches_from(vec!["", "-b", "-c"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + let res = cmd().try_get_matches_from(vec!["", "-c", "-b"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); +} + +#[test] +fn help_text() { + let res = cmd().try_get_matches_from(vec!["prog", "--help"]); + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::DisplayHelp); + println!("{}", err); + assert_eq!(err.to_string(), HELP); +} + +#[test] +fn no_duplicate_error() { + let res = cmd().try_get_matches_from(vec!["", "-b"]); + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); + assert_eq!(err.to_string(), ONLY_B_ERROR); + + let res = cmd().try_get_matches_from(vec!["", "-c"]); + assert!(res.is_err()); + let err = res.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); + assert_eq!(err.to_string(), ONLY_C_ERROR); +} diff --git a/tests/builder/legacy/empty_values.rs b/tests/builder/legacy/empty_values.rs new file mode 100644 index 00000000000..46def9899b4 --- /dev/null +++ b/tests/builder/legacy/empty_values.rs @@ -0,0 +1,128 @@ +use super::utils; + +use clap::{error::ErrorKind, Arg, Command}; + +#[test] +fn empty_values() { + let m = Command::new("config") + .arg(Arg::new("config").long("config").takes_value(true)) + .try_get_matches_from(&["config", "--config", ""]) + .unwrap(); + assert_eq!(m.value_of("config"), Some("")); +} + +#[test] +fn empty_values_with_equals() { + let m = Command::new("config") + .arg(Arg::new("config").long("config").takes_value(true)) + .try_get_matches_from(&["config", "--config="]) + .unwrap(); + assert_eq!(m.value_of("config"), Some("")); + + let m = Command::new("config") + .arg(Arg::new("config").short('c').takes_value(true)) + .try_get_matches_from(&["config", "-c="]) + .unwrap(); + assert_eq!(m.value_of("config"), Some("")) +} + +#[test] +fn no_empty_values() { + let m = Command::new("config") + .arg( + Arg::new("config") + .long("config") + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "--config", ""]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue); + + let m = Command::new("config") + .arg( + Arg::new("config") + .short('c') + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "-c", ""]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue) +} + +#[test] +fn no_empty_values_with_equals() { + let m = Command::new("config") + .arg( + Arg::new("config") + .long("config") + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "--config="]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue); + + let m = Command::new("config") + .arg( + Arg::new("config") + .short('c') + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "-c="]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue); +} + +#[test] +fn no_empty_values_without_equals() { + let m = Command::new("config") + .arg( + Arg::new("config") + .long("config") + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "--config"]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue); + + let m = Command::new("config") + .arg( + Arg::new("config") + .short('c') + .takes_value(true) + .forbid_empty_values(true), + ) + .try_get_matches_from(&["config", "-c"]); + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::EmptyValue) +} + +#[test] +fn no_empty_values_without_equals_but_requires_equals() { + let cmd = Command::new("config").arg( + Arg::new("config") + .long("config") + .takes_value(true) + .forbid_empty_values(true) + .require_equals(true), + ); + let m = cmd.clone().try_get_matches_from(&["config", "--config"]); + // Should error on no equals rather than empty value. + assert!(m.is_err()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::NoEquals); + + static NO_EUQALS_ERROR: &str = + "error: Equal sign is needed when assigning values to '--config='. + +USAGE: + config [OPTIONS] + +For more information try --help +"; + + utils::assert_output(cmd, "config --config", NO_EUQALS_ERROR, true); +} diff --git a/tests/builder/legacy/env.rs b/tests/builder/legacy/env.rs new file mode 100644 index 00000000000..e5416eef470 --- /dev/null +++ b/tests/builder/legacy/env.rs @@ -0,0 +1,352 @@ +#![cfg(feature = "env")] + +use std::env; +use std::ffi::OsStr; + +use clap::{arg, Arg, Command}; + +#[test] +fn env() { + env::set_var("CLP_TEST_ENV", "env"); + + let r = Command::new("df") + .arg(arg!([arg] "some opt").env("CLP_TEST_ENV").takes_value(true)) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn env_bool_literal() { + env::set_var("CLP_TEST_FLAG_TRUE", "On"); + env::set_var("CLP_TEST_FLAG_FALSE", "nO"); + + let r = Command::new("df") + .arg(Arg::new("present").short('p').env("CLP_TEST_FLAG_TRUE")) + .arg(Arg::new("negated").short('n').env("CLP_TEST_FLAG_FALSE")) + .arg(Arg::new("absent").short('a').env("CLP_TEST_FLAG_ABSENT")) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("present")); + assert_eq!(m.occurrences_of("present"), 0); + assert_eq!(m.value_of("present"), None); + assert!(!m.is_present("negated")); + assert!(!m.is_present("absent")); +} + +#[test] +fn env_os() { + env::set_var("CLP_TEST_ENV_OS", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env_os(OsStr::new("CLP_TEST_ENV_OS")) + .takes_value(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn no_env() { + // All the other tests use the presence of the Environment variable... + // we need another variable just in case one of the others is running at the same time... + env::remove_var("CLP_TEST_ENV_NONE"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_NONE") + .takes_value(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg"), None); +} + +#[test] +fn no_env_no_takes_value() { + // All the other tests use the presence of the Environment variable... + // we need another variable just in case one of the others is running at the same time... + env::remove_var("CLP_TEST_ENV_NONE"); + + let r = Command::new("df") + .arg(arg!([arg] "some opt").env("CLP_TEST_ENV_NONE")) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(!m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg"), None); +} + +#[test] +fn with_default() { + env::set_var("CLP_TEST_ENV_WD", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_WD") + .takes_value(true) + .default_value("default"), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn opt_user_override() { + env::set_var("CLP_TEST_ENV_OR", "env"); + + let r = Command::new("df") + .arg( + arg!(--arg [FILE] "some arg") + .env("CLP_TEST_ENV_OR") + .takes_value(true), + ) + .try_get_matches_from(vec!["", "--arg", "opt"]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 1); + assert_eq!(m.value_of("arg").unwrap(), "opt"); + + // see https://github.com/clap-rs/clap/issues/1835 + let values: Vec<_> = m.values_of("arg").unwrap().collect(); + assert_eq!(values, vec!["opt"]); +} + +#[test] +fn positionals() { + env::set_var("CLP_TEST_ENV_P", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_P") + .takes_value(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn positionals_user_override() { + env::set_var("CLP_TEST_ENV_POR", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_POR") + .takes_value(true), + ) + .try_get_matches_from(vec!["", "opt"]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 1); + assert_eq!(m.value_of("arg").unwrap(), "opt"); + + // see https://github.com/clap-rs/clap/issues/1835 + let values: Vec<_> = m.values_of("arg").unwrap().collect(); + assert_eq!(values, vec!["opt"]); +} + +#[test] +fn multiple_one() { + env::set_var("CLP_TEST_ENV_MO", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_MO") + .takes_value(true) + .use_value_delimiter(true) + .multiple_values(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.values_of("arg").unwrap().collect::>(), vec!["env"]); +} + +#[test] +fn multiple_three() { + env::set_var("CLP_TEST_ENV_MULTI1", "env1,env2,env3"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_MULTI1") + .takes_value(true) + .use_value_delimiter(true) + .multiple_values(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + vec!["env1", "env2", "env3"] + ); +} + +#[test] +fn multiple_no_delimiter() { + env::set_var("CLP_TEST_ENV_MULTI2", "env1 env2 env3"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_MULTI2") + .takes_value(true) + .multiple_values(true), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!( + m.values_of("arg").unwrap().collect::>(), + vec!["env1 env2 env3"] + ); +} + +#[test] +fn possible_value() { + env::set_var("CLP_TEST_ENV_PV", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_PV") + .takes_value(true) + .possible_value("env"), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn not_possible_value() { + env::set_var("CLP_TEST_ENV_NPV", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_NPV") + .takes_value(true) + .possible_value("never"), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_err()); +} + +#[test] +fn validator() { + env::set_var("CLP_TEST_ENV_VDOR", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_VDOR") + .takes_value(true) + .validator(|s| { + if s == "env" { + Ok(()) + } else { + Err("not equal".to_string()) + } + }), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_ok(), "{}", r.unwrap_err()); + let m = r.unwrap(); + assert!(m.is_present("arg")); + assert_eq!(m.occurrences_of("arg"), 0); + assert_eq!(m.value_of("arg").unwrap(), "env"); +} + +#[test] +fn validator_output() { + env::set_var("CLP_TEST_ENV_VO", "42"); + + let m = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_VO") + .takes_value(true) + .validator(|s| s.parse::()), + ) + .try_get_matches_from(vec![""]) + .unwrap(); + + assert_eq!(m.value_of("arg").unwrap().parse(), Ok(42)); +} + +#[test] +fn validator_invalid() { + env::set_var("CLP_TEST_ENV_IV", "env"); + + let r = Command::new("df") + .arg( + arg!([arg] "some opt") + .env("CLP_TEST_ENV_IV") + .takes_value(true) + .validator(|s| { + if s != "env" { + Ok(()) + } else { + Err("is equal".to_string()) + } + }), + ) + .try_get_matches_from(vec![""]); + + assert!(r.is_err()); +} diff --git a/tests/builder/legacy/error.rs b/tests/builder/legacy/error.rs new file mode 100644 index 00000000000..249024bd048 --- /dev/null +++ b/tests/builder/legacy/error.rs @@ -0,0 +1,90 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, Arg, Command, Error}; + +fn assert_error(err: Error, expected_kind: ErrorKind, expected_output: &str, stderr: bool) { + let actual_output = err.to_string(); + assert_eq!( + stderr, + err.use_stderr(), + "Should Use STDERR failed. Should be {} but is {}", + stderr, + err.use_stderr() + ); + assert_eq!(expected_kind, err.kind()); + utils::assert_eq(expected_output, actual_output) +} + +#[test] +fn app_error() { + static MESSAGE: &str = "error: Failed for mysterious reasons + +USAGE: + test [OPTIONS] --all + +For more information try --help +"; + let cmd = Command::new("test") + .arg( + Arg::new("all") + .short('a') + .long("all") + .required(true) + .help("Also do versioning for private crates (will not be published)"), + ) + .arg( + Arg::new("exact") + .long("exact") + .help("Specify inter dependency version numbers exactly with `=`"), + ) + .arg( + Arg::new("no_git_commit") + .long("no-git-commit") + .help("Do not commit version changes"), + ) + .arg( + Arg::new("no_git_push") + .long("no-git-push") + .help("Do not push generated commit and tags to git remote"), + ); + let mut cmd = cmd; + let expected_kind = ErrorKind::InvalidValue; + let err = cmd.error(expected_kind, "Failed for mysterious reasons"); + assert_error(err, expected_kind, MESSAGE, true); +} + +#[test] +fn value_validation_has_newline() { + let m = Command::new("test") + .arg(arg!().help("Network port to use")) + .try_get_matches_from(["test", "foo"]) + .unwrap(); + + let res = m.value_of_t::("PORT"); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert!( + err.to_string().ends_with('\n'), + "Errors should have a trailing newline, got {:?}", + err.to_string() + ); +} + +#[test] +fn argument_not_found_auto_has_newline() { + let m = Command::new("test") + .arg(arg!([PORT]).help("Network port to use")) + .try_get_matches_from(["test"]) + .unwrap(); + + let res = m.value_of_t::("PORT"); + + assert!(res.is_err()); + let err = res.unwrap_err(); + assert!( + err.to_string().ends_with('\n'), + "Errors should have a trailing newline, got {:?}", + err.to_string() + ); +} diff --git a/tests/builder/legacy/flag_subcommands.rs b/tests/builder/legacy/flag_subcommands.rs new file mode 100644 index 00000000000..bb1201d44a6 --- /dev/null +++ b/tests/builder/legacy/flag_subcommands.rs @@ -0,0 +1,613 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, Arg, Command}; + +#[test] +fn flag_subcommand_normal() { + let matches = Command::new("test") + .subcommand( + Command::new("some").short_flag('S').long_flag("some").arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ), + ) + .try_get_matches_from(vec!["myprog", "some", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_normal_with_alias() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .long_flag("S") + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .alias("result"), + ) + .try_get_matches_from(vec!["myprog", "result", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_short() { + let matches = Command::new("test") + .subcommand( + Command::new("some").short_flag('S').arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ), + ) + .try_get_matches_from(vec!["myprog", "-S", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_short_with_args() { + let matches = Command::new("test") + .subcommand( + Command::new("some").short_flag('S').arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ), + ) + .try_get_matches_from(vec!["myprog", "-St"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_short_with_alias() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .short_flag_alias('M') + .short_flag_alias('B'), + ) + .try_get_matches_from(vec!["myprog", "-Bt"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_short_with_alias_same_as_short_flag() { + let matches = Command::new("test") + .subcommand(Command::new("some").short_flag('S').short_flag_alias('S')) + .try_get_matches_from(vec!["myprog", "-S"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); +} + +#[test] +fn flag_subcommand_long_with_alias_same_as_long_flag() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .long_flag("sync") + .long_flag_alias("sync"), + ) + .try_get_matches_from(vec!["myprog", "--sync"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); +} + +#[test] +fn flag_subcommand_short_with_aliases_vis_and_hidden() { + let cmd = Command::new("test").subcommand( + Command::new("some") + .short_flag('S') + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .visible_short_flag_aliases(&['M', 'B']) + .short_flag_alias('C'), + ); + let app1 = cmd.clone(); + let matches1 = app1.try_get_matches_from(vec!["test", "-M"]).unwrap(); + assert_eq!(matches1.subcommand_name().unwrap(), "some"); + + let app2 = cmd.clone(); + let matches2 = app2.try_get_matches_from(vec!["test", "-C"]).unwrap(); + assert_eq!(matches2.subcommand_name().unwrap(), "some"); + + let app3 = cmd.clone(); + let matches3 = app3.try_get_matches_from(vec!["test", "-B"]).unwrap(); + assert_eq!(matches3.subcommand_name().unwrap(), "some"); +} + +#[test] +fn flag_subcommand_short_with_aliases() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .short_flag_aliases(&['M', 'B']), + ) + .try_get_matches_from(vec!["myprog", "-Bt"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +#[should_panic] +fn flag_subcommand_short_with_alias_hyphen() { + let _ = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .short_flag_alias('-'), + ) + .try_get_matches_from(vec!["myprog", "-Bt"]) + .unwrap(); +} + +#[test] +#[should_panic] +fn flag_subcommand_short_with_aliases_hyphen() { + let _ = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .short_flag_aliases(&['-', '-', '-']), + ) + .try_get_matches_from(vec!["myprog", "-Bt"]) + .unwrap(); +} + +#[test] +fn flag_subcommand_short_after_long_arg() { + let m = Command::new("pacman") + .subcommand( + Command::new("sync") + .short_flag('S') + .arg(Arg::new("clean").short('c')), + ) + .arg(Arg::new("arg").long("arg").takes_value(true)) + .try_get_matches_from(vec!["pacman", "--arg", "foo", "-Sc"]) + .unwrap(); + let subm = m.subcommand_matches("sync"); + assert!(subm.is_some()); + let subm = subm.unwrap(); + assert!(subm.is_present("clean")); +} + +#[test] +fn flag_subcommand_long() { + let matches = Command::new("test") + .subcommand( + Command::new("some").long_flag("some").arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ), + ) + .try_get_matches_from(vec!["myprog", "--some", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_long_with_alias() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .long_flag("some") + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .long_flag_alias("result"), + ) + .try_get_matches_from(vec!["myprog", "--result", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_long_with_aliases() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .long_flag("some") + .arg( + Arg::new("test") + .short('t') + .long("test") + .help("testing testing"), + ) + .long_flag_aliases(&["result", "someall"]), + ) + .try_get_matches_from(vec!["myprog", "--result", "--test"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("test")); +} + +#[test] +fn flag_subcommand_multiple() { + let matches = Command::new("test") + .subcommand( + Command::new("some") + .short_flag('S') + .long_flag("some") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-p --print "print something")) + .subcommand( + Command::new("result") + .short_flag('R') + .long_flag("result") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-p --print "print something")), + ), + ) + .try_get_matches_from(vec!["myprog", "-SfpRfp"]) + .unwrap(); + assert_eq!(matches.subcommand_name().unwrap(), "some"); + let sub_matches = matches.subcommand_matches("some").unwrap(); + assert!(sub_matches.is_present("flag")); + assert!(sub_matches.is_present("print")); + assert_eq!(sub_matches.subcommand_name().unwrap(), "result"); + let result_matches = sub_matches.subcommand_matches("result").unwrap(); + assert!(result_matches.is_present("flag")); + assert!(result_matches.is_present("print")); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'-f\' short flag for the \'test\' argument conflicts with the short flag for \'some\' subcommand"] +fn flag_subcommand_short_conflict_with_arg() { + let _ = Command::new("test") + .subcommand(Command::new("some").short_flag('f').long_flag("some")) + .arg(Arg::new("test").short('f')) + .try_get_matches_from(vec!["myprog", "-f"]) + .unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'-f\' short flag is specified for both \'some\' and \'result\' subcommands"] +fn flag_subcommand_short_conflict_with_alias() { + let _ = Command::new("test") + .subcommand(Command::new("some").short_flag('f').long_flag("some")) + .subcommand(Command::new("result").short_flag('t').short_flag_alias('f')) + .try_get_matches_from(vec!["myprog", "-f"]) + .unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'--flag\' long flag is specified for both \'some\' and \'result\' subcommands"] +fn flag_subcommand_long_conflict_with_alias() { + let _ = Command::new("test") + .subcommand(Command::new("some").long_flag("flag")) + .subcommand( + Command::new("result") + .long_flag("test") + .long_flag_alias("flag"), + ) + .try_get_matches_from(vec!["myprog", "--flag"]) + .unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'-f\' short flag for the \'test\' argument conflicts with the short flag for \'some\' subcommand"] +fn flag_subcommand_short_conflict_with_arg_alias() { + let _ = Command::new("test") + .subcommand(Command::new("some").short_flag('f').long_flag("some")) + .arg(Arg::new("test").short('t').short_alias('f')) + .try_get_matches_from(vec!["myprog", "-f"]) + .unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'--some\' long flag for the \'test\' argument conflicts with the short flag for \'some\' subcommand"] +fn flag_subcommand_long_conflict_with_arg_alias() { + let _ = Command::new("test") + .subcommand(Command::new("some").short_flag('f').long_flag("some")) + .arg(Arg::new("test").long("test").alias("some")) + .try_get_matches_from(vec!["myprog", "--some"]) + .unwrap(); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "the \'--flag\' long flag for the \'flag\' argument conflicts with the short flag for \'some\' subcommand"] +fn flag_subcommand_long_conflict_with_arg() { + let _ = Command::new("test") + .subcommand(Command::new("some").short_flag('a').long_flag("flag")) + .arg(Arg::new("flag").long("flag")) + .try_get_matches_from(vec!["myprog", "--flag"]) + .unwrap(); +} + +#[test] +fn flag_subcommand_conflict_with_help() { + let _ = Command::new("test") + .subcommand(Command::new("help").short_flag('h').long_flag("help")) + .try_get_matches_from(vec!["myprog", "--help"]) + .unwrap(); +} + +#[test] +fn flag_subcommand_conflict_with_version() { + let _ = Command::new("test") + .subcommand(Command::new("ver").short_flag('V').long_flag("version")) + .try_get_matches_from(vec!["myprog", "--version"]) + .unwrap(); +} + +#[test] +fn flag_subcommand_long_infer_pass() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test").long_flag("test")) + .try_get_matches_from(vec!["prog", "--te"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +#[cfg(not(feature = "suggestions"))] +#[test] +fn flag_subcommand_long_infer_fail() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test").long_flag("test")) + .subcommand(Command::new("temp").long_flag("temp")) + .try_get_matches_from(vec!["prog", "--te"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument); +} + +#[cfg(feature = "suggestions")] +#[test] +fn flag_subcommand_long_infer_fail() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test").long_flag("test")) + .subcommand(Command::new("temp").long_flag("temp")) + .try_get_matches_from(vec!["prog", "--te"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument); +} + +#[test] +fn flag_subcommand_long_infer_pass_close() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test").long_flag("test")) + .subcommand(Command::new("temp").long_flag("temp")) + .try_get_matches_from(vec!["prog", "--tes"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +#[test] +fn flag_subcommand_long_infer_exact_match() { + let m = Command::new("prog") + .infer_subcommands(true) + .subcommand(Command::new("test").long_flag("test")) + .subcommand(Command::new("testa").long_flag("testa")) + .subcommand(Command::new("testb").long_flag("testb")) + .try_get_matches_from(vec!["prog", "--test"]) + .unwrap(); + assert_eq!(m.subcommand_name(), Some("test")); +} + +static FLAG_SUBCOMMAND_HELP: &str = "pacman-query +Query the package database. + +USAGE: + pacman {query|--query|-Q} [OPTIONS] + +OPTIONS: + -h, --help Print help information + -i, --info ... view package information + -s, --search ... search locally installed packages for matching strings +"; + +#[test] +fn flag_subcommand_long_short_normal_usage_string() { + let cmd = Command::new("pacman") + .about("package manager utility") + .version("5.2.1") + .subcommand_required(true) + .author("Pacman Development Team") + // Query subcommand + // + // Only a few of its arguments are implemented below. + .subcommand( + Command::new("query") + .short_flag('Q') + .long_flag("query") + .about("Query the package database.") + .arg( + Arg::new("search") + .short('s') + .long("search") + .help("search locally installed packages for matching strings") + .conflicts_with("info") + .takes_value(true) + .multiple_values(true), + ) + .arg( + Arg::new("info") + .long("info") + .short('i') + .conflicts_with("search") + .help("view package information") + .takes_value(true) + .multiple_values(true), + ), + ); + utils::assert_output(cmd, "pacman -Qh", FLAG_SUBCOMMAND_HELP, false); +} + +static FLAG_SUBCOMMAND_NO_SHORT_HELP: &str = "pacman-query +Query the package database. + +USAGE: + pacman {query|--query} [OPTIONS] + +OPTIONS: + -h, --help Print help information + -i, --info ... view package information + -s, --search ... search locally installed packages for matching strings +"; + +#[test] +fn flag_subcommand_long_normal_usage_string() { + let cmd = Command::new("pacman") + .about("package manager utility") + .version("5.2.1") + .subcommand_required(true) + .author("Pacman Development Team") + // Query subcommand + // + // Only a few of its arguments are implemented below. + .subcommand( + Command::new("query") + .long_flag("query") + .about("Query the package database.") + .arg( + Arg::new("search") + .short('s') + .long("search") + .help("search locally installed packages for matching strings") + .conflicts_with("info") + .takes_value(true) + .multiple_values(true), + ) + .arg( + Arg::new("info") + .long("info") + .short('i') + .conflicts_with("search") + .help("view package information") + .takes_value(true) + .multiple_values(true), + ), + ); + utils::assert_output( + cmd, + "pacman query --help", + FLAG_SUBCOMMAND_NO_SHORT_HELP, + false, + ); +} + +static FLAG_SUBCOMMAND_NO_LONG_HELP: &str = "pacman-query +Query the package database. + +USAGE: + pacman {query|-Q} [OPTIONS] + +OPTIONS: + -h, --help Print help information + -i, --info ... view package information + -s, --search ... search locally installed packages for matching strings +"; + +#[test] +fn flag_subcommand_short_normal_usage_string() { + let cmd = Command::new("pacman") + .about("package manager utility") + .version("5.2.1") + .subcommand_required(true) + .author("Pacman Development Team") + // Query subcommand + // + // Only a few of its arguments are implemented below. + .subcommand( + Command::new("query") + .short_flag('Q') + .about("Query the package database.") + .arg( + Arg::new("search") + .short('s') + .long("search") + .help("search locally installed packages for matching strings") + .conflicts_with("info") + .takes_value(true) + .multiple_values(true), + ) + .arg( + Arg::new("info") + .long("info") + .short('i') + .conflicts_with("search") + .help("view package information") + .takes_value(true) + .multiple_values(true), + ), + ); + utils::assert_output( + cmd, + "pacman query --help", + FLAG_SUBCOMMAND_NO_LONG_HELP, + false, + ); +} diff --git a/tests/builder/legacy/flags.rs b/tests/builder/legacy/flags.rs new file mode 100644 index 00000000000..f397296c51c --- /dev/null +++ b/tests/builder/legacy/flags.rs @@ -0,0 +1,198 @@ +use super::utils; +use clap::{arg, Arg, Command}; + +const USE_FLAG_AS_ARGUMENT: &str = + "error: Found argument '--another-flag' which wasn't expected, or isn't valid in this context + +\tIf you tried to supply `--another-flag` as a value rather than a flag, use `-- --another-flag` + +USAGE: + mycat [OPTIONS] [filename] + +For more information try --help +"; + +#[test] +fn flag_using_short() { + let m = Command::new("flag") + .args(&[ + arg!(-f --flag "some flag"), + arg!(-c --color "some other flag"), + ]) + .try_get_matches_from(vec!["", "-f", "-c"]) + .unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); +} + +#[test] +fn lots_o_flags_sep() { + let r = Command::new("opts") + .arg(arg!(o: -o ... "some flag")) + .try_get_matches_from(vec![ + "", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", "-o", + "-o", "-o", "-o", + ]); + assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.occurrences_of("o"), 297); // i.e. more than u8 +} + +#[test] +fn lots_o_flags_combined() { + let r = Command::new("opts") + .arg(arg!(o: -o ... "some flag")) + .try_get_matches_from(vec![ + "", + "-oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", + "-oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", + "-oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", + "-oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo", + "-ooooooooooooooooooooooooooooooooooooooooo", + ]); + assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.occurrences_of("o"), 297); // i.e. more than u8 +} + +#[test] +fn flag_using_long() { + let m = Command::new("flag") + .args(&[arg!(--flag "some flag"), arg!(--color "some other flag")]) + .try_get_matches_from(vec!["", "--flag", "--color"]) + .unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); +} + +#[test] +fn flag_using_long_with_literals() { + use clap::error::ErrorKind; + + let m = Command::new("flag") + .arg(Arg::new("rainbow").long("rainbow")) + .try_get_matches_from(vec!["", "--rainbow=false"]); + assert!(m.is_err(), "{:#?}", m.unwrap()); + assert_eq!(m.unwrap_err().kind(), ErrorKind::TooManyValues); +} + +#[test] +fn flag_using_mixed() { + let m = Command::new("flag") + .args(&[ + arg!(-f --flag "some flag"), + arg!(-c --color "some other flag"), + ]) + .try_get_matches_from(vec!["", "-f", "--color"]) + .unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); + + let m = Command::new("flag") + .args(&[ + arg!(-f --flag "some flag"), + arg!(-c --color "some other flag"), + ]) + .try_get_matches_from(vec!["", "--flag", "-c"]) + .unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); +} + +#[test] +fn multiple_flags_in_single() { + let m = Command::new("multe_flags") + .args(&[ + arg!(-f --flag "some flag"), + arg!(-c --color "some other flag"), + arg!(-d --debug "another other flag"), + ]) + .try_get_matches_from(vec!["", "-fcd"]) + .unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); + assert!(m.is_present("debug")); +} + +#[test] +fn issue_1284_argument_in_flag_style() { + let cmd = Command::new("mycat") + .arg(Arg::new("filename")) + .arg(Arg::new("a-flag").long("a-flag")); + + let m = cmd + .clone() + .try_get_matches_from(vec!["", "--", "--another-flag"]) + .unwrap(); + assert_eq!(m.value_of("filename"), Some("--another-flag")); + + let m = cmd + .clone() + .try_get_matches_from(vec!["", "--a-flag"]) + .unwrap(); + assert!(m.is_present("a-flag")); + + let m = cmd + .clone() + .try_get_matches_from(vec!["", "--", "--a-flag"]) + .unwrap(); + assert_eq!(m.value_of("filename"), Some("--a-flag")); + + utils::assert_output(cmd, "mycat --another-flag", USE_FLAG_AS_ARGUMENT, true); +} + +#[test] +fn issue_2308_multiple_dashes() { + static MULTIPLE_DASHES: &str = + "error: Found argument '-----' which wasn't expected, or isn't valid in this context + + If you tried to supply `-----` as a value rather than a flag, use `-- -----` + +USAGE: + test + +For more information try --help +"; + let cmd = Command::new("test").arg(Arg::new("arg").takes_value(true).required(true)); + + utils::assert_output(cmd, "test -----", MULTIPLE_DASHES, true); +} + +#[test] +#[cfg(not(feature = "unstable-v4"))] +fn leading_dash_stripped() { + let cmd = Command::new("mycat").arg(Arg::new("filename").long("--filename")); + let matches = cmd.try_get_matches_from(["mycat", "--filename"]).unwrap(); + assert!(matches.is_present("filename")); +} + +#[test] +#[cfg(feature = "unstable-v4")] +#[cfg(debug_assertions)] +#[should_panic = "Argument filename: long \"--filename\" must not start with a `-`, that will be handled by the parser"] +fn leading_dash_stripped() { + let cmd = Command::new("mycat").arg(Arg::new("filename").long("--filename")); + cmd.debug_assert(); +} diff --git a/tests/builder/legacy/global_args.rs b/tests/builder/legacy/global_args.rs new file mode 100644 index 00000000000..d8024089664 --- /dev/null +++ b/tests/builder/legacy/global_args.rs @@ -0,0 +1,114 @@ +use clap::{arg, Arg, Command}; + +#[test] +fn issue_1076() { + let mut cmd = Command::new("myprog") + .arg( + Arg::new("GLOBAL_ARG") + .long("global-arg") + .help("Specifies something needed by the subcommands") + .global(true) + .takes_value(true) + .default_value("default_value"), + ) + .arg( + Arg::new("GLOBAL_FLAG") + .long("global-flag") + .help("Specifies something needed by the subcommands") + .global(true) + .takes_value(true), + ) + .subcommand(Command::new("outer").subcommand(Command::new("inner"))); + let _ = cmd.try_get_matches_from_mut(vec!["myprog"]); + let _ = cmd.try_get_matches_from_mut(vec!["myprog"]); + let _ = cmd.try_get_matches_from_mut(vec!["myprog"]); +} + +#[test] +fn propagate_global_arg_in_subcommand_to_subsubcommand_1385() { + let m1 = Command::new("foo") + .subcommand( + Command::new("sub1") + .arg(Arg::new("arg1").long("arg1").takes_value(true).global(true)) + .subcommand(Command::new("sub1a")), + ) + .try_get_matches_from(&["foo", "sub1", "--arg1", "v1", "sub1a"]) + .unwrap(); + assert_eq!( + "v1", + m1.subcommand_matches("sub1") + .unwrap() + .subcommand_matches("sub1a") + .unwrap() + .value_of("arg1") + .unwrap() + ); +} + +#[test] +fn propagate_global_arg_to_subcommand_in_subsubcommand_2053() { + let m = Command::new("opts") + .arg(arg!(--"global-flag").global(true)) + .arg(arg!(--"global-str" ).required(false).global(true)) + .subcommand( + Command::new("test") + .arg(arg!(--"sub-flag").global(true)) + .arg(arg!(--"sub-str" ).required(false).global(true)) + .subcommand(Command::new("test")), + ) + .try_get_matches_from(&[ + "cmd", + "test", + "test", + "--global-flag", + "--global-str", + "hello", + "--sub-flag", + "--sub-str", + "world", + ]) + .unwrap(); + assert_eq!( + Some("world"), + m.subcommand_matches("test").unwrap().value_of("sub-str") + ); +} + +#[test] +fn global_arg_available_in_subcommand() { + let m = Command::new("opt") + .args(&[ + Arg::new("global").global(true).long("global"), + Arg::new("not").global(false).long("not"), + ]) + .subcommand(Command::new("ping")) + .try_get_matches_from(&["opt", "ping", "--global"]) + .unwrap(); + + assert!(m.is_present("global")); + assert!(m.subcommand_matches("ping").unwrap().is_present("global")); +} + +#[test] +fn deeply_nested_discovery() { + let cmd = Command::new("a") + .arg(arg!(--"long-a").global(true)) + .subcommand( + Command::new("b") + .arg(arg!(--"long-b").global(true)) + .subcommand( + Command::new("c") + .arg(arg!(--"long-c").global(true)) + .subcommand(Command::new("d")), + ), + ); + + let m = cmd + .try_get_matches_from(["a", "b", "c", "d", "--long-a", "--long-b", "--long-c"]) + .unwrap(); + assert!(m.is_present("long-a")); + let m = m.subcommand_matches("b").unwrap(); + assert!(m.is_present("long-b")); + let m = m.subcommand_matches("c").unwrap(); + assert!(m.is_present("long-c")); +} diff --git a/tests/builder/legacy/grouped_values.rs b/tests/builder/legacy/grouped_values.rs new file mode 100644 index 00000000000..622e9c93e87 --- /dev/null +++ b/tests/builder/legacy/grouped_values.rs @@ -0,0 +1,221 @@ +#![cfg(feature = "unstable-grouped")] + +use clap::{Arg, Command}; + +#[test] +fn grouped_value_works() { + let m = Command::new("cli") + .arg( + Arg::new("option") + .long("option") + .takes_value(true) + .multiple_values(true) + .multiple_occurrences(true), + ) + .try_get_matches_from(&[ + "cli", + "--option", + "fr_FR:mon option 1", + "en_US:my option 1", + "--option", + "fr_FR:mon option 2", + "en_US:my option 2", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("option").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![ + vec!["fr_FR:mon option 1", "en_US:my option 1",], + vec!["fr_FR:mon option 2", "en_US:my option 2",], + ] + ); +} + +#[test] +fn issue_1026() { + let m = Command::new("cli") + .arg(Arg::new("server").short('s').takes_value(true)) + .arg(Arg::new("user").short('u').takes_value(true)) + .arg( + Arg::new("target") + .long("target") + .takes_value(true) + .multiple_values(true) + .multiple_occurrences(true), + ) + .try_get_matches_from(&[ + "backup", "-s", "server", "-u", "user", "--target", "target1", "file1", "file2", + "file3", "--target", "target2", "file4", "file5", "file6", "file7", "--target", + "target3", "file8", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("target").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![ + vec!["target1", "file1", "file2", "file3"], + vec!["target2", "file4", "file5", "file6", "file7",], + vec!["target3", "file8"] + ] + ); +} + +#[test] +fn grouped_value_long_flag_delimiter() { + let m = Command::new("myapp") + .arg( + Arg::new("option") + .long("option") + .takes_value(true) + .use_value_delimiter(true) + .multiple_values(true) + .multiple_occurrences(true), + ) + .try_get_matches_from(vec![ + "myapp", + "--option=hmm", + "--option=val1,val2,val3", + "--option", + "alice,bob", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("option").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![ + vec!["hmm"], + vec!["val1", "val2", "val3"], + vec!["alice", "bob"] + ] + ); +} + +#[test] +fn grouped_value_short_flag_delimiter() { + let m = Command::new("myapp") + .arg( + Arg::new("option") + .short('o') + .takes_value(true) + .use_value_delimiter(true) + .multiple_values(true) + .multiple_occurrences(true), + ) + .try_get_matches_from(vec!["myapp", "-o=foo", "-o=val1,val2,val3", "-o=bar"]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("option").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![vec!["foo"], vec!["val1", "val2", "val3"], vec!["bar"]] + ); +} + +#[test] +fn grouped_value_positional_arg() { + let m = Command::new("multiple_values") + .arg( + Arg::new("pos") + .help("multiple positionals") + .takes_value(true) + .multiple_values(true), + ) + .try_get_matches_from(vec![ + "myprog", "val1", "val2", "val3", "val4", "val5", "val6", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("pos").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![vec!["val1", "val2", "val3", "val4", "val5", "val6"]] + ); +} + +#[test] +fn grouped_value_multiple_positional_arg() { + let m = Command::new("multiple_values") + .arg(Arg::new("pos1").help("multiple positionals")) + .arg( + Arg::new("pos2") + .help("multiple positionals") + .takes_value(true) + .multiple_values(true), + ) + .try_get_matches_from(vec![ + "myprog", "val1", "val2", "val3", "val4", "val5", "val6", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("pos2").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![vec!["val2", "val3", "val4", "val5", "val6"]] + ); +} + +#[test] +fn grouped_value_multiple_positional_arg_last_multiple() { + let m = Command::new("multiple_values") + .arg(Arg::new("pos1").help("multiple positionals")) + .arg( + Arg::new("pos2") + .help("multiple positionals") + .takes_value(true) + .multiple_values(true) + .last(true), + ) + .try_get_matches_from(vec![ + "myprog", "val1", "--", "val2", "val3", "val4", "val5", "val6", + ]) + .unwrap(); + let grouped_vals: Vec<_> = m.grouped_values_of("pos2").unwrap().collect(); + assert_eq!( + grouped_vals, + vec![vec!["val2", "val3", "val4", "val5", "val6"]] + ); +} + +#[test] +fn issue_1374() { + let cmd = Command::new("MyApp").arg( + Arg::new("input") + .takes_value(true) + .long("input") + .overrides_with("input") + .min_values(0) + .multiple_occurrences(true), + ); + let matches = cmd + .clone() + .try_get_matches_from(&["MyApp", "--input", "a", "b", "c", "--input", "d"]) + .unwrap(); + let vs = matches.values_of("input").unwrap(); + assert_eq!(vs.collect::>(), vec!["a", "b", "c", "d"]); + let matches = cmd + .clone() + .try_get_matches_from(&["MyApp", "--input", "a", "b", "--input", "c", "d"]) + .unwrap(); + let vs = matches.values_of("input").unwrap(); + assert_eq!(vs.collect::>(), vec!["a", "b", "c", "d"]); +} + +#[test] +fn issue_2171() { + let schema = Command::new("ripgrep#1701 reproducer") + .args_override_self(true) + .arg(Arg::new("pretty").short('p').long("pretty")) + .arg(Arg::new("search_zip").short('z').long("search-zip")); + + let test_args = &[ + vec!["reproducer", "-pz", "-p"], + vec!["reproducer", "-pzp"], + vec!["reproducer", "-zpp"], + vec!["reproducer", "-pp", "-z"], + vec!["reproducer", "-p", "-p", "-z"], + vec!["reproducer", "-p", "-pz"], + vec!["reproducer", "-ppz"], + ]; + + for argv in test_args { + let _ = schema.clone().try_get_matches_from(argv).unwrap(); + } +} diff --git a/tests/builder/legacy/groups.rs b/tests/builder/legacy/groups.rs new file mode 100644 index 00000000000..67a6fea0615 --- /dev/null +++ b/tests/builder/legacy/groups.rs @@ -0,0 +1,325 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command}; + +static REQ_GROUP_USAGE: &str = "error: The following required arguments were not provided: + + +USAGE: + clap-test + +For more information try --help +"; + +static REQ_GROUP_CONFLICT_USAGE: &str = + "error: The argument '--delete' cannot be used with '' + +USAGE: + clap-test + +For more information try --help +"; + +static REQ_GROUP_CONFLICT_ONLY_OPTIONS: &str = + "error: The argument '--delete' cannot be used with '--all' + +USAGE: + clap-test <--all|--delete> + +For more information try --help +"; + +#[test] +fn required_group_missing_arg() { + let result = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!( -c --color "some other flag")) + .group(ArgGroup::new("req").args(&["flag", "color"]).required(true)) + .try_get_matches_from(vec![""]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument group 'req' contains non-existent argument"] +fn non_existing_arg() { + let _ = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some other flag")) + .group(ArgGroup::new("req").args(&["flg", "color"]).required(true)) + .try_get_matches_from(vec![""]); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument group name must be unique\n\n\t'req' is already in use"] +fn unique_group_name() { + let _ = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some other flag")) + .group(ArgGroup::new("req").args(&["flag"]).required(true)) + .group(ArgGroup::new("req").args(&["color"]).required(true)) + .try_get_matches_from(vec![""]); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument group name '' must not conflict with argument name"] +fn groups_new_of_arg_name() { + let _ = Command::new("group") + .arg(Arg::new("a").long("a").group("a")) + .try_get_matches_from(vec!["", "--a"]); +} + +#[cfg(debug_assertions)] +#[test] +#[should_panic = "Argument group name 'a' must not conflict with argument name"] +fn arg_group_new_of_arg_name() { + let _ = Command::new("group") + .arg(Arg::new("a").long("a").group("a")) + .group(ArgGroup::new("a")) + .try_get_matches_from(vec!["", "--a"]); +} + +#[test] +fn group_single_value() { + let res = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color [color] "some option")) + .group(ArgGroup::new("grp").args(&["flag", "color"])) + .try_get_matches_from(vec!["", "-c", "blue"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + let m = res.unwrap(); + assert!(m.is_present("grp")); + assert_eq!(m.value_of("grp").unwrap(), "blue"); +} + +#[test] +fn group_single_flag() { + let res = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color [color] "some option")) + .group(ArgGroup::new("grp").args(&["flag", "color"])) + .try_get_matches_from(vec!["", "-f"]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + let m = res.unwrap(); + assert!(m.is_present("grp")); + assert!(m.value_of("grp").is_none()); +} + +#[test] +fn group_empty() { + let res = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color [color] "some option")) + .group(ArgGroup::new("grp").args(&["flag", "color"])) + .try_get_matches_from(vec![""]); + assert!(res.is_ok(), "{}", res.unwrap_err()); + + let m = res.unwrap(); + assert!(!m.is_present("grp")); + assert!(m.value_of("grp").is_none()); +} + +#[test] +fn group_reqired_flags_empty() { + let result = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some option")) + .group(ArgGroup::new("grp").required(true).args(&["flag", "color"])) + .try_get_matches_from(vec![""]); + assert!(result.is_err()); + let err = result.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); +} + +#[test] +fn group_multi_value_single_arg() { + let res = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some option").multiple_values(true)) + .group(ArgGroup::new("grp").args(&["flag", "color"])) + .try_get_matches_from(vec!["", "-c", "blue", "red", "green"]); + assert!(res.is_ok(), "{:?}", res.unwrap_err().kind()); + + let m = res.unwrap(); + assert!(m.is_present("grp")); + assert_eq!( + &*m.values_of("grp").unwrap().collect::>(), + &["blue", "red", "green"] + ); +} + +#[test] +fn empty_group() { + let r = Command::new("empty_group") + .arg(arg!(-f --flag "some flag")) + .group(ArgGroup::new("vers").required(true)) + .try_get_matches_from(vec!["empty_prog"]); + assert!(r.is_err()); + let err = r.err().unwrap(); + assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); +} + +#[test] +fn req_group_usage_string() { + let cmd = Command::new("req_group") + .arg(arg!([base] "Base commit")) + .arg(arg!( + -d --delete "Remove the base commit information" + )) + .group( + ArgGroup::new("base_or_delete") + .args(&["base", "delete"]) + .required(true), + ); + + utils::assert_output(cmd, "clap-test", REQ_GROUP_USAGE, true); +} + +#[test] +fn req_group_with_conflict_usage_string() { + let cmd = Command::new("req_group") + .arg(arg!([base] "Base commit").conflicts_with("delete")) + .arg(arg!( + -d --delete "Remove the base commit information" + )) + .group( + ArgGroup::new("base_or_delete") + .args(&["base", "delete"]) + .required(true), + ); + + utils::assert_output( + cmd, + "clap-test --delete base", + REQ_GROUP_CONFLICT_USAGE, + true, + ); +} + +#[test] +fn req_group_with_conflict_usage_string_only_options() { + let cmd = Command::new("req_group") + .arg(arg!(-a --all "All").conflicts_with("delete")) + .arg(arg!( + -d --delete "Remove the base commit information" + )) + .group( + ArgGroup::new("all_or_delete") + .args(&["all", "delete"]) + .required(true), + ); + utils::assert_output( + cmd, + "clap-test --delete --all", + REQ_GROUP_CONFLICT_ONLY_OPTIONS, + true, + ); +} + +#[test] +fn required_group_multiple_args() { + let result = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some other flag")) + .group( + ArgGroup::new("req") + .args(&["flag", "color"]) + .required(true) + .multiple(true), + ) + .try_get_matches_from(vec!["group", "-f", "-c"]); + assert!(result.is_ok(), "{}", result.unwrap_err()); + let m = result.unwrap(); + assert!(m.is_present("flag")); + assert!(m.is_present("color")); +} + +#[test] +fn group_multiple_args_error() { + let result = Command::new("group") + .arg(arg!(-f --flag "some flag")) + .arg(arg!(-c --color "some other flag")) + .group(ArgGroup::new("req").args(&["flag", "color"])) + .try_get_matches_from(vec!["group", "-f", "-c"]); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::ArgumentConflict); +} + +#[test] +fn group_overrides_required() { + let command = Command::new("group") + .arg(arg!(--foo )) + .arg(arg!(--bar )) + .group(ArgGroup::new("group").args(&["foo", "bar"]).required(true)); + let result = command.try_get_matches_from(vec!["group", "--foo", "value"]); + assert!(result.is_ok(), "{}", result.unwrap_err()); + let m = result.unwrap(); + assert!(m.is_present("foo")); + assert!(!m.is_present("bar")); +} + +#[test] +fn group_usage_use_val_name() { + static GROUP_USAGE_USE_VAL_NAME: &str = "prog + +USAGE: + prog + +ARGS: + + +OPTIONS: + -h, --help Print help information +"; + let cmd = Command::new("prog") + .arg(Arg::new("a").value_name("A")) + .group(ArgGroup::new("group").arg("a").required(true)); + utils::assert_output(cmd, "prog --help", GROUP_USAGE_USE_VAL_NAME, false); +} + +#[test] +fn group_acts_like_arg() { + let result = Command::new("prog") + .arg(Arg::new("debug").long("debug").group("mode")) + .arg(Arg::new("verbose").long("verbose").group("mode")) + .try_get_matches_from(vec!["prog", "--debug"]); + + assert!(result.is_ok(), "{}", result.unwrap_err()); + let m = result.unwrap(); + assert!(m.is_present("mode")); +} + +/* This is used to be fixed in a hack, we need to find a better way to fix it. +#[test] +fn issue_1794() { + let cmd = clap::Command::new("hello") + .bin_name("deno") + .arg(Arg::new("option1").long("option1").takes_value(false)) + .arg(Arg::new("pos1").takes_value(true)) + .arg(Arg::new("pos2").takes_value(true)) + .group( + ArgGroup::new("arg1") + .args(&["pos1", "option1"]) + .required(true), + ); + + let m = cmd.clone().try_get_matches_from(&["cmd", "pos1", "pos2"]).unwrap(); + assert_eq!(m.value_of("pos1"), Some("pos1")); + assert_eq!(m.value_of("pos2"), Some("pos2")); + assert!(!m.is_present("option1")); + + let m = cmd + .clone() + .try_get_matches_from(&["cmd", "--option1", "positional"]).unwrap(); + assert_eq!(m.value_of("pos1"), None); + assert_eq!(m.value_of("pos2"), Some("positional")); + assert!(m.is_present("option1")); +} +*/ diff --git a/tests/builder/legacy/help.rs b/tests/builder/legacy/help.rs new file mode 100644 index 00000000000..6701ed712fe --- /dev/null +++ b/tests/builder/legacy/help.rs @@ -0,0 +1,2867 @@ +use super::utils; + +use clap::{arg, error::ErrorKind, Arg, ArgGroup, Command, PossibleValue}; + +static REQUIRE_DELIM_HELP: &str = "test 1.3 +Kevin K. +tests stuff + +USAGE: + test --fake : + +OPTIONS: + -f, --fake : some help + -h, --help Print help information + -V, --version Print version information +"; + +static HELP: &str = "clap-test v1.4.8 +Kevin K. +tests clap library + +USAGE: + clap-test [OPTIONS] [ARGS] [SUBCOMMAND] + +ARGS: + tests positionals + tests positionals with exclusions + ... tests specific values [possible values: vi, emacs] + +OPTIONS: + -f, --flag tests flags + -F tests flags with exclusions + -h, --help Print help information + --long-option-2 tests long options with exclusions + --maxvals3 ... Tests 3 max vals + --minvals2 ... Tests 2 min vals + --multvals Tests multiple values, not mult occs + --multvalsmo Tests multiple values, and mult occs + -o, --option ... tests options + -O, --option3 specific vals [possible values: fast, slow] + --optvaleq[=] Tests optional value, require = sign + --optvalnoeq [] Tests optional value + -V, --version Print version information + +SUBCOMMANDS: + help Print this message or the help of the given subcommand(s) + subcmd tests subcommands +"; + +static SC_NEGATES_REQS: &str = "prog 1.0 + +USAGE: + prog --opt [PATH] + prog [PATH] + +ARGS: + help + +OPTIONS: + -h, --help Print help information + -o, --opt tests options + -V, --version Print version information + +SUBCOMMANDS: + help Print this message or the help of the given subcommand(s) + test +"; + +static ARGS_NEGATE_SC: &str = "prog 1.0 + +USAGE: + prog [OPTIONS] [PATH] + prog + +ARGS: + help + +OPTIONS: + -f, --flag testing flags + -h, --help Print help information + -o, --opt tests options + -V, --version Print version information + +SUBCOMMANDS: + help Print this message or the help of the given subcommand(s) + test +"; + +static AFTER_HELP: &str = "some text that comes before the help + +clap-test v1.4.8 +tests clap library + +USAGE: + clap-test + +OPTIONS: + -h, --help Print help information + -V, --version Print version information + +some text that comes after the help +"; + +static AFTER_LONG_HELP: &str = "some longer text that comes before the help + +clap-test v1.4.8 +tests clap library + +USAGE: + clap-test + +OPTIONS: + -h, --help + Print help information + + -V, --version + Print version information + +some longer text that comes after the help +"; + +static HIDDEN_ARGS: &str = "prog 1.0 + +USAGE: + prog [OPTIONS] + +OPTIONS: + -f, --flag testing flags + -h, --help Print help information + -o, --opt tests options + -V, --version Print version information +"; + +static SC_HELP: &str = "clap-test-subcmd 0.1 +Kevin K. +tests subcommands + +USAGE: + clap-test subcmd [OPTIONS] [--] [scpositional] + +ARGS: + tests positionals + +OPTIONS: + -f, --flag tests flags + -h, --help Print help information + -o, --option ... tests options + -s, --subcmdarg tests other args + -V, --version Print version information +"; + +static ISSUE_1046_HIDDEN_SCS: &str = "prog 1.0 + +USAGE: + prog [OPTIONS] [PATH] + +ARGS: + some + +OPTIONS: + -f, --flag testing flags + -h, --help Print help information + -o, --opt tests options + -V, --version Print version information +"; + +// Using number_of_values(1) with multiple_values(true) misaligns help message +static ISSUE_760: &str = "ctest 0.1 + +USAGE: + ctest [OPTIONS] + +OPTIONS: + -h, --help Print help information + -o, --option