fix!: Remove unstable-v4 feature gate

This commit is contained in:
Ed Page 2022-07-22 09:19:14 -05:00
parent 982d4ca361
commit 01a3ea425f
94 changed files with 271 additions and 6072 deletions

View file

@ -7,8 +7,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## 4.0.0 - Upcoming
_gated behind `unstable-v4`_
### Breaking Changes
- `Error::EmptyValue` replaced with `Error::InvalidValue`

View file

@ -79,8 +79,8 @@ unicode = ["textwrap/unicode-width", "unicase"] # Support for unicode character
# In-work features
unstable-replace = []
unstable-grouped = []
# note: this will always enable clap_derive, change this to `clap_derive?/unstable-v4` when MSRV is bigger than 1.60
unstable-v4 = ["clap_derive/unstable-v4", "deprecated"]
# note: this will always enable clap_derive, change this to `clap_derive?/unstable-v5` when MSRV is bigger than 1.60
unstable-v5 = ["clap_derive/unstable-v5", "deprecated"]
[lib]
bench = false

View file

@ -17,7 +17,7 @@ _FEATURES_minimal = --no-default-features --features "std"
_FEATURES_default =
_FEATURES_wasm = --features "deprecated derive cargo env unicode unstable-replace unstable-grouped"
_FEATURES_full = --features "deprecated derive cargo env unicode unstable-replace unstable-grouped wrap_help"
_FEATURES_next = ${_FEATURES_full} --features unstable-v4
_FEATURES_next = ${_FEATURES_full} --features unstable-v5
_FEATURES_debug = ${_FEATURES_full} --features debug
_FEATURES_release = ${_FEATURES_full} --release

View file

@ -49,6 +49,6 @@ proc-macro-error = "1"
[features]
default = []
debug = []
unstable-v4 = ["deprecated"]
unstable-v5 = ["deprecated"]
deprecated = []
raw-deprecated = ["deprecated"]

View file

@ -750,16 +750,10 @@ impl Attrs {
quote!( #(#next_help_heading)* #(#help_heading)* )
}
#[cfg(feature = "unstable-v4")]
pub fn id(&self) -> TokenStream {
self.name.clone().raw()
}
#[cfg(not(feature = "unstable-v4"))]
pub fn id(&self) -> TokenStream {
self.cased_name()
}
pub fn cased_name(&self) -> TokenStream {
self.name.clone().translate(*self.casing)
}
@ -780,7 +774,7 @@ impl Attrs {
let inner_type = inner_type(field_type);
let span = action.span();
default_value_parser(inner_type, span)
} else if !self.ignore_parser() || cfg!(not(feature = "unstable-v4")) {
} else if !self.ignore_parser() {
self.parser(field_type).value_parser()
} else {
let inner_type = inner_type(field_type);
@ -802,7 +796,7 @@ impl Attrs {
if let Some(value_parser) = self.value_parser.as_ref() {
let span = value_parser.span();
default_action(field_type, span)
} else if !self.ignore_parser() || cfg!(not(feature = "unstable-v4")) {
} else if !self.ignore_parser() {
self.parser(field_type).action()
} else {
let span = self
@ -815,16 +809,10 @@ impl Attrs {
})
}
#[cfg(feature = "unstable-v4")]
pub fn ignore_parser(&self) -> bool {
self.parser.is_none()
}
#[cfg(not(feature = "unstable-v4"))]
pub fn ignore_parser(&self) -> bool {
self.value_parser.is_some() || self.action.is_some()
}
pub fn explicit_parser(&self) -> bool {
self.parser.is_some()
}
@ -1200,7 +1188,6 @@ pub enum Name {
}
impl Name {
#[cfg(feature = "unstable-v4")]
pub fn raw(self) -> TokenStream {
match self {
Name::Assigned(tokens) => tokens,

View file

@ -484,17 +484,9 @@ fn gen_from_arg_matches(
Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
};
if cfg!(feature = "unstable-v4") {
quote! {
if #sub_name == #subcommand_name_var && !#sub_arg_matches_var.contains_id("") {
return ::std::result::Result::Ok(#name :: #variant_name #constructor_block)
}
}
} else {
quote! {
if #sub_name == #subcommand_name_var {
return ::std::result::Result::Ok(#name :: #variant_name #constructor_block)
}
quote! {
if #sub_name == #subcommand_name_var && !#sub_arg_matches_var.contains_id("") {
return ::std::result::Result::Ok(#name :: #variant_name #constructor_block)
}
}
});
@ -528,7 +520,7 @@ fn gen_from_arg_matches(
.chain(
#sub_arg_matches_var
.remove_many::<#str_ty>("")
.into_iter().flatten() // `""` isn't present, bug in `unstable-v4`
.unwrap()
.map(#str_ty::from)
)
.collect::<::std::vec::Vec<_>>()

View file

@ -40,7 +40,7 @@ SUBCOMMANDS:
stash
$ git-derive help add
git-derive[EXE]-add
git-add
adds things
USAGE:
@ -58,7 +58,7 @@ A basic argument:
```console
$ git-derive add
? failed
git-derive[EXE]-add
git-add
adds things
USAGE:
@ -78,7 +78,7 @@ Adding ["Cargo.toml", "Cargo.lock"]
Default subcommand:
```console
$ git-derive stash -h
git-derive[EXE]-stash
git-stash
USAGE:
git-derive[EXE] stash [OPTIONS]
@ -95,7 +95,7 @@ SUBCOMMANDS:
push
$ git-derive stash push -h
git-derive[EXE]-stash-push
git-stash-push
USAGE:
git-derive[EXE] stash push [OPTIONS]
@ -105,7 +105,7 @@ OPTIONS:
-m, --message <MESSAGE>
$ git-derive stash pop -h
git-derive[EXE]-stash-pop
git-stash-pop
USAGE:
git-derive[EXE] stash pop [STASH]

View file

@ -38,7 +38,7 @@ SUBCOMMANDS:
stash
$ git help add
git[EXE]-add
git-add
adds things
USAGE:
@ -56,7 +56,7 @@ A basic argument:
```console
$ git add
? failed
git[EXE]-add
git-add
adds things
USAGE:
@ -76,7 +76,7 @@ Adding ["Cargo.toml", "Cargo.lock"]
Default subcommand:
```console
$ git stash -h
git[EXE]-stash
git-stash
USAGE:
git[EXE] stash [OPTIONS]
@ -93,7 +93,7 @@ SUBCOMMANDS:
push
$ git stash push -h
git[EXE]-stash-push
git-stash-push
USAGE:
git[EXE] stash push [OPTIONS]
@ -103,7 +103,7 @@ OPTIONS:
-m, --message <MESSAGE>
$ git stash pop -h
git[EXE]-stash-pop
git-stash-pop
USAGE:
git[EXE] stash pop [STASH]

View file

@ -52,7 +52,7 @@ SUBCOMMANDS:
sync -S --sync Synchronize packages.
$ pacman -S -h
pacman[EXE]-sync
pacman-sync
Synchronize packages.
USAGE:

View file

@ -15,7 +15,7 @@ SUBCOMMANDS:
help Print this message or the help of the given subcommand(s)
$ 03_04_subcommands help add
03_04_subcommands[EXE]-add [..]
clap-add 3.2.14
Adds files to myapp
USAGE:

View file

@ -15,7 +15,7 @@ SUBCOMMANDS:
help Print this message or the help of the given subcommand(s)
$ 03_04_subcommands help add
03_04_subcommands[EXE]-add [..]
clap-add 3.2.14
Adds files to myapp
USAGE:
@ -59,6 +59,6 @@ $ 03_04_subcommands --version
clap [..]
$ 03_04_subcommands add --version
03_04_subcommands[EXE]-add [..]
03_04_subcommands-add [..]
```

View file

@ -23,4 +23,4 @@
//!
//! * **unstable-replace**: Enable [`Command::replace`](https://github.com/clap-rs/clap/issues/2836)
//! * **unstable-grouped**: Enable [`ArgMatches::grouped_values_of`](https://github.com/clap-rs/clap/issues/2924)
//! * **unstable-v4**: Preview features which will be stable on the v4.0 release
//! * **unstable-v5**: Preview features which will be stable on the v5.0 release

View file

@ -186,14 +186,7 @@ impl<'help> Arg<'help> {
#[inline]
#[must_use]
pub fn long(mut self, l: &'help str) -> Self {
#[cfg(feature = "unstable-v4")]
{
self.long = Some(l);
}
#[cfg(not(feature = "unstable-v4"))]
{
self.long = Some(l.trim_start_matches(|c| c == '-'));
}
self.long = Some(l);
self
}

View file

@ -2273,14 +2273,7 @@ impl<'help> Command<'help> {
/// [`Arg::long`]: Arg::long()
#[must_use]
pub fn long_flag(mut self, long: &'help str) -> Self {
#[cfg(feature = "unstable-v4")]
{
self.long_flag = Some(long);
}
#[cfg(not(feature = "unstable-v4"))]
{
self.long_flag = Some(long.trim_start_matches(|c| c == '-'));
}
self.long_flag = Some(long);
self
}
@ -4674,10 +4667,9 @@ impl<'help> Command<'help> {
v.long_help.is_some()
|| v.is_hide_long_help_set()
|| v.is_hide_short_help_set()
|| cfg!(feature = "unstable-v4")
&& v.get_possible_values()
.iter()
.any(PossibleValue::should_show_help)
|| v.get_possible_values()
.iter()
.any(PossibleValue::should_show_help)
};
// Subcommands aren't checked because we prefer short help for them, deferring to

View file

@ -27,9 +27,6 @@ pub(crate) fn assert_app(cmd: &Command) {
.get_arguments()
.filter(|x| {
let action_set = matches!(x.get_action(), ArgAction::Version);
#[cfg(not(feature = "unstable-v4"))]
let provider_set = matches!(x.provider, ArgProvider::GeneratedMutated);
#[cfg(feature = "unstable-v4")]
let provider_set = matches!(
x.provider,
ArgProvider::User | ArgProvider::GeneratedMutated
@ -54,10 +51,7 @@ pub(crate) fn assert_app(cmd: &Command) {
}
if let Some(l) = sc.get_long_flag().as_ref() {
#[cfg(feature = "unstable-v4")]
{
assert!(!l.starts_with('-'), "Command {}: long_flag {:?} must not start with a `-`, that will be handled by the parser", sc.get_name(), l);
}
assert!(!l.starts_with('-'), "Command {}: long_flag {:?} must not start with a `-`, that will be handled by the parser", sc.get_name(), l);
long_flags.push(Flag::Command(format!("--{}", l), sc.get_name()));
}
@ -85,10 +79,7 @@ pub(crate) fn assert_app(cmd: &Command) {
}
if let Some(l) = arg.long.as_ref() {
#[cfg(feature = "unstable-v4")]
{
assert!(!l.starts_with('-'), "Argument {}: long {:?} must not start with a `-`, that will be handled by the parser", arg.name, l);
}
assert!(!l.starts_with('-'), "Argument {}: long {:?} must not start with a `-`, that will be handled by the parser", arg.name, l);
long_flags.push(Flag::Arg(format!("--{}", l), &*arg.name));
}
@ -161,14 +152,11 @@ pub(crate) fn assert_app(cmd: &Command) {
}
for req in &arg.r_ifs {
#[cfg(feature = "unstable-v4")]
{
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_if_eq*`",
arg.name
);
}
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_if_eq*`",
arg.name
);
assert!(
cmd.id_exists(&req.0),
"Command {}: Argument or group '{:?}' specified in 'required_if_eq*' for '{}' does not exist",
@ -179,14 +167,11 @@ pub(crate) fn assert_app(cmd: &Command) {
}
for req in &arg.r_ifs_all {
#[cfg(feature = "unstable-v4")]
{
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_if_eq_all`",
arg.name
);
}
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_if_eq_all`",
arg.name
);
assert!(
cmd.id_exists(&req.0),
"Command {}: Argument or group '{:?}' specified in 'required_if_eq_all' for '{}' does not exist",
@ -197,14 +182,11 @@ pub(crate) fn assert_app(cmd: &Command) {
}
for req in &arg.r_unless {
#[cfg(feature = "unstable-v4")]
{
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_unless*`",
arg.name
);
}
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_unless*`",
arg.name
);
assert!(
cmd.id_exists(req),
"Command {}: Argument or group '{:?}' specified in 'required_unless*' for '{}' does not exist",
@ -215,14 +197,11 @@ pub(crate) fn assert_app(cmd: &Command) {
}
for req in &arg.r_unless_all {
#[cfg(feature = "unstable-v4")]
{
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_unless*`",
arg.name
);
}
assert!(
!arg.is_required_set(),
"Argument {}: `required` conflicts with `required_unless*`",
arg.name
);
assert!(
cmd.id_exists(req),
"Command {}: Argument or group '{:?}' specified in 'required_unless*' for '{}' does not exist",
@ -680,16 +659,13 @@ fn assert_arg(arg: &Arg) {
);
}
#[cfg(feature = "unstable-v4")]
{
let num_vals = arg.get_num_vals().unwrap_or(usize::MAX);
let num_val_names = arg.get_value_names().unwrap_or(&[]).len();
if num_vals < num_val_names {
panic!(
"Argument {}: Too many value names ({}) compared to number_of_values ({})",
arg.name, num_val_names, num_vals
);
}
let num_vals = arg.get_num_vals().unwrap_or(usize::MAX);
let num_val_names = arg.get_value_names().unwrap_or(&[]).len();
if num_vals < num_val_names {
panic!(
"Argument {}: Too many value names ({}) compared to number_of_values ({})",
arg.name, num_val_names, num_vals
);
}
assert_arg_flags(arg);

View file

@ -153,7 +153,6 @@ impl<'help> PossibleValue<'help> {
/// Get the help specified for this argument, if any and the argument
/// value is not hidden
#[inline]
#[cfg(feature = "unstable-v4")]
pub(crate) fn get_visible_help(&self) -> Option<&'help str> {
if !self.hide {
self.help

View file

@ -28,7 +28,6 @@ pub(crate) struct Help<'help, 'cmd, 'writer> {
// Public Functions
impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> {
#[cfg(feature = "unstable-v4")]
const DEFAULT_TEMPLATE: &'static str = "\
{before-help}{name} {version}\n\
{author-with-newline}{about-with-newline}\n\
@ -36,27 +35,12 @@ impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> {
\n\
{all-args}{after-help}\
";
#[cfg(not(feature = "unstable-v4"))]
const DEFAULT_TEMPLATE: &'static str = "\
{before-help}{bin} {version}\n\
{author-with-newline}{about-with-newline}\n\
{usage-heading}\n {usage}\n\
\n\
{all-args}{after-help}\
";
#[cfg(feature = "unstable-v4")]
const DEFAULT_NO_ARGS_TEMPLATE: &'static str = "\
{before-help}{name} {version}\n\
{author-with-newline}{about-with-newline}\n\
{usage-heading}\n {usage}{after-help}\
";
#[cfg(not(feature = "unstable-v4"))]
const DEFAULT_NO_ARGS_TEMPLATE: &'static str = "\
{before-help}{bin} {version}\n\
{author-with-newline}{about-with-newline}\n\
{usage-heading}\n {usage}{after-help}\
";
/// Create a new `Help` instance.
pub(crate) fn new(
@ -477,7 +461,6 @@ impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> {
self.none(part)?;
}
#[cfg(feature = "unstable-v4")]
if let Some(arg) = arg {
const DASH_SPACE: usize = "- ".len();
const COLON_SPACE: usize = ": ".len();
@ -663,9 +646,7 @@ impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> {
let possible_vals = a.get_possible_values();
if !(a.is_hide_possible_values_set()
|| possible_vals.is_empty()
|| cfg!(feature = "unstable-v4")
&& self.use_long
&& possible_vals.iter().any(PossibleValue::should_show_help))
|| self.use_long && possible_vals.iter().any(PossibleValue::should_show_help))
{
debug!("Help::spec_vals: Found possible vals...{:?}", possible_vals);

View file

@ -31,16 +31,6 @@ impl ArgMatcher {
},
#[cfg(debug_assertions)]
valid_subcommands: _cmd.get_subcommands().map(|sc| sc.get_id()).collect(),
// HACK: Allow an external subcommand's ArgMatches be a stand-in for any ArgMatches
// since users can't detect it and avoid the asserts.
//
// See clap-rs/clap#3263
#[cfg(debug_assertions)]
#[cfg(not(feature = "unstable-v4"))]
disable_asserts: _cmd.is_allow_external_subcommands_set(),
#[cfg(debug_assertions)]
#[cfg(feature = "unstable-v4")]
disable_asserts: false,
..Default::default()
},
pending: None,

View file

@ -67,8 +67,6 @@ pub struct ArgMatches {
pub(crate) valid_args: Vec<Id>,
#[cfg(debug_assertions)]
pub(crate) valid_subcommands: Vec<Id>,
#[cfg(debug_assertions)]
pub(crate) disable_asserts: bool,
pub(crate) args: IndexMap<Id, MatchedArg>,
pub(crate) subcommand: Option<Box<SubCommand>>,
}
@ -685,7 +683,7 @@ impl ArgMatches {
#[cfg(debug_assertions)]
{
let id = Id::from(_id);
self.disable_asserts || id == Id::empty_hash() || self.valid_args.contains(&id)
id == Id::empty_hash() || self.valid_args.contains(&id)
}
#[cfg(not(debug_assertions))]
{
@ -890,7 +888,7 @@ impl ArgMatches {
#[cfg(debug_assertions)]
{
let id = Id::from(_id);
self.disable_asserts || id == Id::empty_hash() || self.valid_subcommands.contains(&id)
id == Id::empty_hash() || self.valid_subcommands.contains(&id)
}
#[cfg(not(debug_assertions))]
{
@ -1066,7 +1064,7 @@ impl ArgMatches {
fn verify_arg(&self, _arg: &Id) -> Result<(), MatchesError> {
#[cfg(debug_assertions)]
{
if self.disable_asserts || *_arg == Id::empty_hash() || self.valid_args.contains(_arg) {
if *_arg == Id::empty_hash() || self.valid_args.contains(_arg) {
} else if self.valid_subcommands.contains(_arg) {
debug!(
"Subcommand `{:?}` used where an argument or group name was expected.",
@ -1091,7 +1089,7 @@ impl ArgMatches {
fn get_arg(&self, arg: &Id) -> Option<&MatchedArg> {
#[cfg(debug_assertions)]
{
if self.disable_asserts || *arg == Id::empty_hash() || self.valid_args.contains(arg) {
if *arg == Id::empty_hash() || self.valid_args.contains(arg) {
} else if self.valid_subcommands.contains(arg) {
panic!(
"Subcommand `{:?}` used where an argument or group name was expected.",
@ -1115,10 +1113,7 @@ impl ArgMatches {
fn get_subcommand(&self, id: &Id) -> Option<&SubCommand> {
#[cfg(debug_assertions)]
{
if self.disable_asserts
|| *id == Id::empty_hash()
|| self.valid_subcommands.contains(id)
{
if *id == Id::empty_hash() || self.valid_subcommands.contains(id) {
} else if self.valid_args.contains(id) {
panic!(
"Argument or group `{:?}` used where a subcommand name was expected.",

View file

@ -431,9 +431,7 @@ impl<'help, 'cmd> Parser<'help, 'cmd> {
// Collect the external subcommand args
let mut sc_m = ArgMatcher::new(self.cmd);
if cfg!(feature = "unstable-v4") || !raw_args.is_end(&args_cursor) {
sc_m.start_occurrence_of_external(self.cmd);
}
sc_m.start_occurrence_of_external(self.cmd);
for raw_val in raw_args.remaining(&mut args_cursor) {
let val = external_parser.parse_ref(self.cmd, None, raw_val)?;

View file

@ -890,27 +890,6 @@ fn issue_1093_allow_ext_sc() {
}
#[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!(args.get_many::<OsString>("").is_none());
}
_ => unreachable!(),
}
}
#[test]
#[cfg(feature = "unstable-v4")]
fn allow_ext_sc_empty_args() {
let res = Command::new("clap-test")
.version("v1.4.8")

View file

@ -190,21 +190,6 @@ For more information try --help
}
#[test]
#[cfg(not(feature = "unstable-v4"))]
fn leading_dash_stripped() {
let cmd = Command::new("mycat").arg(
Arg::new("filename")
.long("--filename")
.action(ArgAction::SetTrue),
);
let matches = cmd.try_get_matches_from(["mycat", "--filename"]).unwrap();
assert!(*matches
.get_one::<bool>("filename")
.expect("defaulted by clap"));
}
#[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() {

View file

@ -942,7 +942,6 @@ OPTIONS:
#[test]
#[cfg(all(feature = "wrap_help"))]
fn possible_value_wrapped_help() {
#[cfg(feature = "unstable-v4")]
static WRAPPED_HELP: &str = "test
USAGE:
@ -971,18 +970,6 @@ OPTIONS:
- name: Short enough help message with no wrapping
- second: short help
";
#[cfg(not(feature = "unstable-v4"))]
static WRAPPED_HELP: &str = r#"test
USAGE:
test [OPTIONS]
OPTIONS:
-h, --help Print help information
--possible-values <possible_values> [possible values: short_name, second]
--possible-values-with-new-line <possible_values_with_new_line> [possible values: "long enough name to trigger new line", second]
--possible-values-without-new-line <possible_values_without_new_line> [possible values: name, second]
"#;
let cmd = Command::new("test")
.term_width(67)
.arg(
@ -1100,7 +1087,6 @@ fn hide_single_possible_val() {
#[test]
fn possible_vals_with_help() {
#[cfg(feature = "unstable-v4")]
static POS_VALS_HELP: &str = "ctest 0.1
USAGE:
@ -1122,18 +1108,6 @@ OPTIONS:
-V, --version
Print version information
";
#[cfg(not(feature = "unstable-v4"))]
static POS_VALS_HELP: &str = "ctest 0.1
USAGE:
ctest [OPTIONS]
OPTIONS:
-c, --cafe <FILE> A coffeehouse, coffee shop, or café.
-h, --help Print help information
-p, --pos <VAL> Some vals [possible values: fast, slow]
-V, --version Print version information
";
let app = Command::new("ctest")
.version("0.1")
@ -2603,22 +2577,6 @@ OPTIONS:
}
#[test]
#[cfg(not(feature = "unstable-v4"))]
fn too_many_value_names_panics() {
Command::new("test")
.arg(
Arg::new("foo")
.long("foo")
.required(true)
.takes_value(true)
.number_of_values(1)
.value_names(&["one", "two"]),
)
.debug_assert()
}
#[test]
#[cfg(feature = "unstable-v4")]
#[should_panic = "Argument foo: Too many value names (2) compared to number_of_values (1)"]
fn too_many_value_names_panics() {
Command::new("test")

View file

@ -19,7 +19,6 @@ use clap::Parser;
fn required_argument() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser)]
arg: i32,
}
assert_eq!(
@ -34,7 +33,7 @@ fn required_argument() {
fn argument_with_default() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser, default_value = "42")]
#[clap(default_value = "42")]
arg: i32,
}
assert_eq!(
@ -49,7 +48,6 @@ fn argument_with_default() {
fn auto_value_name() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser)]
my_special_arg: i32,
}
@ -69,7 +67,7 @@ fn auto_value_name() {
fn explicit_value_name() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser, value_name = "BROWNIE_POINTS")]
#[clap(value_name = "BROWNIE_POINTS")]
my_special_arg: i32,
}
@ -90,7 +88,6 @@ fn explicit_value_name() {
fn option_type_is_optional() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser)]
arg: Option<i32>,
}
assert_eq!(
@ -105,7 +102,6 @@ fn option_type_is_optional() {
fn vec_type_is_multiple_values() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser)]
arg: Vec<i32>,
}
assert_eq!(

View file

@ -18,7 +18,7 @@ use clap::Parser;
fn basic() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short = 'a', long = "arg", value_parser)]
#[clap(short = 'a', long = "arg")]
arg: i32,
}
assert_eq!(
@ -31,7 +31,7 @@ fn basic() {
fn update_basic() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short = 'a', long = "arg", value_parser)]
#[clap(short = 'a', long = "arg")]
single_value: i32,
}

View file

@ -16,7 +16,6 @@ enum Sub {
#[derive(Args, PartialEq, Debug)]
struct Ext {
#[clap(value_parser)]
arg: u32,
}

View file

@ -21,19 +21,19 @@ use std::path::PathBuf;
#[derive(Parser, PartialEq, Debug)]
struct PathOpt {
#[clap(short, long, value_parser)]
#[clap(short, long)]
path: PathBuf,
#[clap(short, default_value = "../", value_parser)]
#[clap(short, default_value = "../")]
default_path: PathBuf,
#[clap(short, value_parser, multiple_occurrences(true))]
#[clap(short, multiple_occurrences(true))]
vector_path: Vec<PathBuf>,
#[clap(short, value_parser)]
#[clap(short)]
option_path_1: Option<PathBuf>,
#[clap(short = 'q', value_parser)]
#[clap(short = 'q')]
option_path_2: Option<PathBuf>,
}
@ -134,10 +134,10 @@ struct DefaultedOpt {
#[clap(short, parse(from_str))]
bytes: Bytes,
#[clap(short, value_parser)]
#[clap(short)]
integer: u64,
#[clap(short, value_parser)]
#[clap(short)]
path: PathBuf,
}

View file

@ -1,5 +1,3 @@
use std::path::PathBuf;
use clap::{CommandFactory, Parser};
use crate::utils;
@ -8,7 +6,7 @@ use crate::utils;
fn default_value() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser, default_value = "3")]
#[clap(default_value = "3")]
arg: i32,
}
assert_eq!(Opt { arg: 3 }, Opt::try_parse_from(&["test"]).unwrap());
@ -22,7 +20,7 @@ fn default_value() {
fn default_value_t() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser, default_value_t = 3)]
#[clap(default_value_t = 3)]
arg: i32,
}
assert_eq!(Opt { arg: 3 }, Opt::try_parse_from(&["test"]).unwrap());
@ -36,7 +34,7 @@ fn default_value_t() {
fn auto_default_value_t() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser, default_value_t)]
#[clap(default_value_t)]
arg: i32,
}
assert_eq!(Opt { arg: 0 }, Opt::try_parse_from(&["test"]).unwrap());
@ -46,37 +44,13 @@ fn auto_default_value_t() {
assert!(help.contains("[default: 0]"));
}
#[test]
fn default_value_os_t() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser, default_value_os_t = PathBuf::from("abc.def"))]
arg: PathBuf,
}
assert_eq!(
Opt {
arg: PathBuf::from("abc.def")
},
Opt::try_parse_from(&["test"]).unwrap()
);
assert_eq!(
Opt {
arg: PathBuf::from("ghi")
},
Opt::try_parse_from(&["test", "ghi"]).unwrap()
);
let help = utils::get_long_help::<Opt>();
assert!(help.contains("[default: abc.def]"));
}
#[test]
fn detect_os_variant() {
#![allow(unused_parens)] // needed for `as_ref` call
#[derive(clap::Parser)]
pub struct Options {
#[clap(value_parser, default_value_os = ("123".as_ref()))]
#[clap(default_value_os = ("123".as_ref()))]
x: String,
}
Options::command().debug_assert();

View file

@ -23,7 +23,7 @@ fn doc_comments() {
struct LoremIpsum {
/// Fooify a bar
/// and a baz
#[clap(short, long, action)]
#[clap(short, long)]
foo: bool,
}
@ -39,12 +39,7 @@ fn help_is_better_than_comments() {
#[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
struct LoremIpsum {
/// Fooify a bar
#[clap(
short,
long,
help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES",
action
)]
#[clap(short, long, help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES")]
foo: bool,
}
@ -76,11 +71,11 @@ fn field_long_doc_comment_both_help_long_help() {
/// Dot is removed from multiline comments.
///
/// Long help
#[clap(long, action)]
#[clap(long)]
foo: bool,
/// Dot is removed from one short comment.
#[clap(long, action)]
#[clap(long)]
bar: bool,
}
@ -111,7 +106,7 @@ fn top_long_doc_comment_both_help_long_help() {
///
/// Or something else
Foo {
#[clap(value_parser, help = "foo")]
#[clap(help = "foo")]
bars: String,
},
}
@ -146,7 +141,7 @@ fn verbatim_doc_comment() {
#[derive(Parser, Debug)]
#[clap(verbatim_doc_comment)]
struct SeeFigure1 {
#[clap(long, action)]
#[clap(long)]
foo: bool,
}
@ -176,10 +171,10 @@ fn verbatim_doc_comment_field() {
#[derive(Parser, Debug)]
struct Command {
/// This help ends in a period.
#[clap(long, verbatim_doc_comment, action)]
#[clap(long, verbatim_doc_comment)]
foo: bool,
/// This help does not end in a period.
#[clap(long, action)]
#[clap(long)]
bar: bool,
}
@ -196,7 +191,7 @@ fn multiline_separates_default() {
/// Multiline
///
/// Doc comment
#[clap(long, default_value = "x", value_parser)]
#[clap(long, default_value = "x")]
x: String,
}
@ -234,10 +229,7 @@ fn doc_comment_about_handles_both_abouts() {
/// Sub doc comment body
#[derive(Parser, PartialEq, Eq, Debug)]
pub enum Sub {
Compress {
#[clap(value_parser)]
output: String,
},
Compress { output: String },
}
let cmd = Opts::command();

View file

@ -6,7 +6,7 @@ use clap::Parser;
fn explicit_short_long_no_rename() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short = '.', long = ".foo", value_parser)]
#[clap(short = '.', long = ".foo")]
foo: String,
}
@ -27,7 +27,7 @@ fn explicit_short_long_no_rename() {
fn explicit_name_no_rename() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(name = ".options", value_parser)]
#[clap(name = ".options")]
foo: String,
}

View file

@ -20,7 +20,7 @@ use clap::Parser;
fn bool_type_is_flag() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, action)]
#[clap(short, long)]
alice: bool,
}
@ -113,7 +113,7 @@ fn non_bool_type_flag() {
fn mixed_type_flags() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, action)]
#[clap(short, long)]
alice: bool,
#[clap(short, long, action = clap::ArgAction::Count)]
bob: u8,
@ -181,7 +181,6 @@ fn ignore_qualified_bool_type() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(action)]
arg: inner::bool,
}

View file

@ -20,7 +20,6 @@ use clap::{Args, Parser, Subcommand};
fn flatten() {
#[derive(Args, PartialEq, Debug)]
struct Common {
#[clap(value_parser)]
arg: i32,
}
@ -45,7 +44,6 @@ fn flatten() {
fn flatten_twice() {
#[derive(Args, PartialEq, Debug)]
struct Common {
#[clap(value_parser)]
arg: i32,
}
@ -64,13 +62,12 @@ fn flatten_twice() {
fn flatten_in_subcommand() {
#[derive(Args, PartialEq, Debug)]
struct Common {
#[clap(value_parser)]
arg: i32,
}
#[derive(Args, PartialEq, Debug)]
struct Add {
#[clap(short, action)]
#[clap(short)]
interactive: bool,
#[clap(flatten)]
common: Common,
@ -79,7 +76,7 @@ fn flatten_in_subcommand() {
#[derive(Parser, PartialEq, Debug)]
enum Opt {
Fetch {
#[clap(short, action)]
#[clap(short)]
all: bool,
#[clap(flatten)]
common: Common,
@ -108,7 +105,6 @@ fn flatten_in_subcommand() {
fn update_args_with_flatten() {
#[derive(Args, PartialEq, Debug)]
struct Common {
#[clap(value_parser)]
arg: i32,
}
@ -138,15 +134,13 @@ enum BaseCli {
#[derive(Args, PartialEq, Debug)]
struct Command1 {
#[clap(value_parser)]
arg1: i32,
#[clap(value_parser)]
arg2: i32,
}
#[derive(Args, PartialEq, Debug)]
struct Command2 {
#[clap(value_parser)]
arg2: i32,
}
@ -199,7 +193,6 @@ fn flatten_with_doc_comment() {
#[derive(Args, PartialEq, Debug)]
struct Common {
/// This is an arg. Arg means "argument". Command line argument.
#[clap(value_parser)]
arg: i32,
}
@ -227,7 +220,7 @@ fn docstrings_ordering_with_multiple_clap() {
/// This is the docstring for Flattened
#[derive(Args)]
struct Flattened {
#[clap(long, action)]
#[clap(long)]
foo: bool,
}
@ -248,7 +241,7 @@ fn docstrings_ordering_with_multiple_clap_partial() {
/// This is the docstring for Flattened
#[derive(Args)]
struct Flattened {
#[clap(long, action)]
#[clap(long)]
foo: bool,
}

View file

@ -4,7 +4,6 @@ use clap::{Args, Parser};
fn generic_struct_flatten() {
#[derive(Args, PartialEq, Debug)]
struct Inner {
#[clap(value_parser)]
pub answer: isize,
}
@ -26,7 +25,6 @@ fn generic_struct_flatten() {
fn generic_struct_flatten_w_where_clause() {
#[derive(Args, PartialEq, Debug)]
struct Inner {
#[clap(value_parser)]
pub answer: isize,
}
@ -51,7 +49,6 @@ fn generic_struct_flatten_w_where_clause() {
fn generic_enum() {
#[derive(Args, PartialEq, Debug)]
struct Inner {
#[clap(value_parser)]
pub answer: isize,
}
@ -71,7 +68,6 @@ fn generic_enum() {
fn generic_enum_w_where_clause() {
#[derive(Args, PartialEq, Debug)]
struct Inner {
#[clap(value_parser)]
pub answer: isize,
}
@ -100,7 +96,6 @@ fn generic_w_fromstr_trait_bound() {
T: FromStr + Send + Sync + Clone + 'static,
<T as FromStr>::Err: std::error::Error + Sync + Send + 'static,
{
#[clap(value_parser)]
answer: T,
}
@ -116,7 +111,6 @@ fn generic_wo_trait_bound() {
#[derive(Parser, PartialEq, Debug)]
struct Opt<T> {
#[clap(value_parser)]
answer: isize,
#[clap(skip)]
took: Option<T>,
@ -141,7 +135,6 @@ fn generic_where_clause_w_trailing_comma() {
T: FromStr + Send + Sync + Clone + 'static,
<T as FromStr>::Err: std::error::Error + Sync + Send + 'static,
{
#[clap(value_parser)]
pub answer: T,
}

View file

@ -4,36 +4,26 @@ use clap::{AppSettings, Args, CommandFactory, Parser, Subcommand};
fn arg_help_heading_applied() {
#[derive(Debug, Clone, Parser)]
struct CliOptions {
#[clap(long, value_parser)]
#[clap(long)]
#[clap(help_heading = Some("HEADING A"))]
should_be_in_section_a: u32,
#[clap(long, value_parser)]
#[clap(long)]
no_section: u32,
}
let cmd = CliOptions::command();
let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-section-a")
.unwrap()
};
let should_be_in_section_a = cmd
.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap();
assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));
let should_be_in_section_b = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "no_section")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "no-section")
.unwrap()
};
let should_be_in_section_b = cmd
.get_arguments()
.find(|a| a.get_id() == "no_section")
.unwrap();
assert_eq!(should_be_in_section_b.get_help_heading(), None);
}
@ -42,36 +32,26 @@ fn app_help_heading_applied() {
#[derive(Debug, Clone, Parser)]
#[clap(next_help_heading = "DEFAULT")]
struct CliOptions {
#[clap(long, value_parser)]
#[clap(long)]
#[clap(help_heading = Some("HEADING A"))]
should_be_in_section_a: u32,
#[clap(long, value_parser)]
#[clap(long)]
should_be_in_default_section: u32,
}
let cmd = CliOptions::command();
let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-section-a")
.unwrap()
};
let should_be_in_section_a = cmd
.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap();
assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));
let should_be_in_default_section = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_default_section")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-default-section")
.unwrap()
};
let should_be_in_default_section = cmd
.get_arguments()
.find(|a| a.get_id() == "should_be_in_default_section")
.unwrap();
assert_eq!(
should_be_in_default_section.get_help_heading(),
Some("DEFAULT")
@ -94,21 +74,21 @@ fn app_help_heading_flattened() {
#[clap(subcommand)]
sub_a: SubA,
#[clap(long, value_parser)]
#[clap(long)]
should_be_in_default_section: u32,
}
#[derive(Debug, Clone, Args)]
#[clap(next_help_heading = "HEADING A")]
struct OptionsA {
#[clap(long, value_parser)]
#[clap(long)]
should_be_in_section_a: u32,
}
#[derive(Debug, Clone, Args)]
#[clap(next_help_heading = "HEADING B")]
struct OptionsB {
#[clap(long, value_parser)]
#[clap(long)]
should_be_in_section_b: u32,
}
@ -121,7 +101,6 @@ fn app_help_heading_flattened() {
SubAOne,
#[clap(next_help_heading = "SUB A")]
SubATwo {
#[clap(value_parser)]
should_be_in_sub_a: u32,
},
}
@ -129,100 +108,58 @@ fn app_help_heading_flattened() {
#[derive(Debug, Clone, Subcommand)]
enum SubB {
#[clap(next_help_heading = "SUB B")]
SubBOne {
#[clap(value_parser)]
should_be_in_sub_b: u32,
},
SubBOne { should_be_in_sub_b: u32 },
}
#[derive(Debug, Clone, Subcommand)]
enum SubC {
#[clap(next_help_heading = "SUB C")]
SubCOne {
#[clap(value_parser)]
should_be_in_sub_c: u32,
},
SubCOne { should_be_in_sub_c: u32 },
}
let cmd = CliOptions::command();
let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-section-a")
.unwrap()
};
let should_be_in_section_a = cmd
.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap();
assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));
let should_be_in_section_b = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_b")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-section-b")
.unwrap()
};
let should_be_in_section_b = cmd
.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_b")
.unwrap();
assert_eq!(should_be_in_section_b.get_help_heading(), Some("HEADING B"));
let should_be_in_default_section = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_default_section")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-default-section")
.unwrap()
};
let should_be_in_default_section = cmd
.get_arguments()
.find(|a| a.get_id() == "should_be_in_default_section")
.unwrap();
assert_eq!(should_be_in_default_section.get_help_heading(), None);
let sub_a_two = cmd.find_subcommand("sub-a-two").unwrap();
let should_be_in_sub_a = if cfg!(feature = "unstable-v4") {
sub_a_two
.get_arguments()
.find(|a| a.get_id() == "should_be_in_sub_a")
.unwrap()
} else {
sub_a_two
.get_arguments()
.find(|a| a.get_id() == "should-be-in-sub-a")
.unwrap()
};
let should_be_in_sub_a = sub_a_two
.get_arguments()
.find(|a| a.get_id() == "should_be_in_sub_a")
.unwrap();
assert_eq!(should_be_in_sub_a.get_help_heading(), Some("SUB A"));
let sub_b_one = cmd.find_subcommand("sub-b-one").unwrap();
let should_be_in_sub_b = if cfg!(feature = "unstable-v4") {
sub_b_one
.get_arguments()
.find(|a| a.get_id() == "should_be_in_sub_b")
.unwrap()
} else {
sub_b_one
.get_arguments()
.find(|a| a.get_id() == "should-be-in-sub-b")
.unwrap()
};
let should_be_in_sub_b = sub_b_one
.get_arguments()
.find(|a| a.get_id() == "should_be_in_sub_b")
.unwrap();
assert_eq!(should_be_in_sub_b.get_help_heading(), Some("SUB B"));
let sub_c = cmd.find_subcommand("sub-c").unwrap();
let sub_c_one = sub_c.find_subcommand("sub-c-one").unwrap();
let should_be_in_sub_c = if cfg!(feature = "unstable-v4") {
sub_c_one
.get_arguments()
.find(|a| a.get_id() == "should_be_in_sub_c")
.unwrap()
} else {
sub_c_one
.get_arguments()
.find(|a| a.get_id() == "should-be-in-sub-c")
.unwrap()
};
let should_be_in_sub_c = sub_c_one
.get_arguments()
.find(|a| a.get_id() == "should_be_in_sub_c")
.unwrap();
assert_eq!(should_be_in_sub_c.get_help_heading(), Some("SUB C"));
}
@ -237,21 +174,16 @@ fn flatten_field_with_help_heading() {
#[derive(Debug, Clone, Args)]
struct OptionsA {
#[clap(long, value_parser)]
#[clap(long)]
should_be_in_section_a: u32,
}
let cmd = CliOptions::command();
let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-section-a")
.unwrap()
};
let should_be_in_section_a = cmd
.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap();
assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));
}
@ -264,7 +196,7 @@ fn derive_generated_error_has_full_context() {
#[derive(Debug, Parser)]
#[clap(subcommand_negates_reqs = true)]
struct Opts {
#[clap(long, value_parser)]
#[clap(long)]
req_str: String,
#[clap(subcommand)]
@ -286,8 +218,7 @@ fn derive_generated_error_has_full_context() {
result.unwrap()
);
if cfg!(feature = "unstable-v4") {
let expected = r#"error: The following required argument was not provided: req_str
let expected = r#"error: The following required argument was not provided: req_str
USAGE:
clap --req-str <REQ_STR>
@ -295,18 +226,7 @@ USAGE:
For more information try --help
"#;
assert_eq!(result.unwrap_err().to_string(), expected);
} else {
let expected = r#"error: The following required argument was not provided: req-str
USAGE:
clap --req-str <REQ_STR>
clap <SUBCOMMAND>
For more information try --help
"#;
assert_eq!(result.unwrap_err().to_string(), expected);
}
assert_eq!(result.unwrap_err().to_string(), expected);
}
#[test]
@ -339,10 +259,10 @@ OPTIONS:
#[clap(next_display_order = 10000)]
struct A {
/// second flag
#[clap(long, action)]
#[clap(long)]
flag_a: bool,
/// second option
#[clap(long, value_parser)]
#[clap(long)]
option_a: Option<String>,
}
@ -350,10 +270,10 @@ OPTIONS:
#[clap(next_display_order = 10)]
struct B {
/// first flag
#[clap(long, action)]
#[clap(long)]
flag_b: bool,
/// first option
#[clap(long, value_parser)]
#[clap(long)]
option_b: Option<String>,
}
@ -397,20 +317,20 @@ OPTIONS:
#[derive(Args, Debug)]
struct A {
/// second flag
#[clap(long, action)]
#[clap(long)]
flag_a: bool,
/// second option
#[clap(long, value_parser)]
#[clap(long)]
option_a: Option<String>,
}
#[derive(Args, Debug)]
struct B {
/// first flag
#[clap(long, action)]
#[clap(long)]
flag_b: bool,
/// first option
#[clap(long, value_parser)]
#[clap(long)]
option_b: Option<String>,
}
@ -453,20 +373,20 @@ OPTIONS:
#[derive(Args, Debug)]
struct A {
/// first flag
#[clap(long, action)]
#[clap(long)]
flag_a: bool,
/// first option
#[clap(long, value_parser)]
#[clap(long)]
option_a: Option<String>,
}
#[derive(Args, Debug)]
struct B {
/// second flag
#[clap(long, action)]
#[clap(long)]
flag_b: bool,
/// second option
#[clap(long, value_parser)]
#[clap(long)]
option_b: Option<String>,
}
@ -480,7 +400,6 @@ OPTIONS:
}
#[test]
#[cfg(feature = "unstable-v4")]
fn derive_possible_value_help() {
static HELP: &str = "clap
Application help
@ -505,7 +424,7 @@ OPTIONS:
#[derive(Parser, PartialEq, Debug)]
struct Args {
/// Argument help
#[clap(value_enum, value_parser)]
#[clap(value_enum)]
arg: ArgChoice,
}

View file

@ -9,9 +9,9 @@ fn issue_151_groups_within_subcommands() {
#[derive(Args, Debug)]
#[clap(group = ArgGroup::new("verb").required(true).multiple(true))]
struct Opt {
#[clap(long, group = "verb", value_parser)]
#[clap(long, group = "verb")]
foo: Option<String>,
#[clap(long, group = "verb", value_parser)]
#[clap(long, group = "verb")]
bar: Option<String>,
}
@ -89,7 +89,6 @@ fn issue_418() {
#[clap(visible_alias = "ret")]
Reticulate {
/// How many splines
#[clap(value_parser)]
num_splines: u8,
},
/// Frobnicate the rest
@ -122,9 +121,8 @@ fn issue_490() {
#[derive(Parser, Debug)]
struct Opt {
#[clap(value_parser)]
opt_vec: Vec<u16>,
#[clap(long, value_parser)]
#[clap(long)]
opt_opt_vec: Option<Vec<u16>>,
}

View file

@ -22,7 +22,7 @@ fn use_option() {
($name:ident: $ty:ty) => {
#[derive(Parser)]
struct Outer {
#[clap(short, long, value_parser)]
#[clap(short, long)]
#[allow(dead_code)]
$name: $ty,
}

View file

@ -1,7 +1,5 @@
#![cfg(feature = "derive")]
mod next;
mod app_name;
mod arguments;
mod author_version_about;

View file

@ -5,7 +5,7 @@ fn test_standalone_long_generates_kebab_case() {
#[derive(Parser, Debug, PartialEq)]
#[allow(non_snake_case)]
struct Opt {
#[clap(long, action)]
#[clap(long)]
FOO_OPTION: bool,
}
@ -19,7 +19,7 @@ fn test_standalone_long_generates_kebab_case() {
fn test_custom_long_overwrites_default_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(long = "foo", action)]
#[clap(long = "foo")]
foo_option: bool,
}
@ -33,7 +33,7 @@ fn test_custom_long_overwrites_default_name() {
fn test_standalone_long_uses_previous_defined_custom_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(name = "foo", long, action)]
#[clap(name = "foo", long)]
foo_option: bool,
}
@ -47,7 +47,7 @@ fn test_standalone_long_uses_previous_defined_custom_name() {
fn test_standalone_long_ignores_afterwards_defined_custom_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(long, name = "foo", action)]
#[clap(long, name = "foo")]
foo_option: bool,
}
@ -62,7 +62,7 @@ fn test_standalone_short_generates_kebab_case() {
#[derive(Parser, Debug, PartialEq)]
#[allow(non_snake_case)]
struct Opt {
#[clap(short, action)]
#[clap(short)]
FOO_OPTION: bool,
}
@ -76,7 +76,7 @@ fn test_standalone_short_generates_kebab_case() {
fn test_custom_short_overwrites_default_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(short = 'o', action)]
#[clap(short = 'o')]
foo_option: bool,
}
@ -90,7 +90,7 @@ fn test_custom_short_overwrites_default_name() {
fn test_standalone_short_uses_previous_defined_custom_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(name = "option", short, action)]
#[clap(name = "option", short)]
foo_option: bool,
}
@ -104,7 +104,7 @@ fn test_standalone_short_uses_previous_defined_custom_name() {
fn test_standalone_short_ignores_afterwards_defined_custom_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(short, name = "option", action)]
#[clap(short, name = "option")]
foo_option: bool,
}
@ -118,7 +118,7 @@ fn test_standalone_short_ignores_afterwards_defined_custom_name() {
fn test_standalone_long_uses_previous_defined_casing() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(rename_all = "screaming_snake", long, action)]
#[clap(rename_all = "screaming_snake", long)]
foo_option: bool,
}
@ -132,7 +132,7 @@ fn test_standalone_long_uses_previous_defined_casing() {
fn test_standalone_short_uses_previous_defined_casing() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(rename_all = "screaming_snake", short, action)]
#[clap(rename_all = "screaming_snake", short)]
foo_option: bool,
}
@ -147,7 +147,7 @@ fn test_standalone_long_works_with_verbatim_casing() {
#[derive(Parser, Debug, PartialEq)]
#[allow(non_snake_case)]
struct Opt {
#[clap(rename_all = "verbatim", long, action)]
#[clap(rename_all = "verbatim", long)]
_fOO_oPtiON: bool,
}
@ -161,7 +161,7 @@ fn test_standalone_long_works_with_verbatim_casing() {
fn test_standalone_short_works_with_verbatim_casing() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(rename_all = "verbatim", short, action)]
#[clap(rename_all = "verbatim", short)]
_foo: bool,
}
@ -176,7 +176,7 @@ fn test_rename_all_is_propagated_from_struct_to_fields() {
#[derive(Parser, Debug, PartialEq)]
#[clap(rename_all = "screaming_snake")]
struct Opt {
#[clap(long, action)]
#[clap(long)]
foo: bool,
}
@ -197,7 +197,7 @@ fn test_rename_all_is_not_propagated_from_struct_into_flattened() {
#[derive(Parser, Debug, PartialEq)]
struct Foo {
#[clap(long, action)]
#[clap(long)]
foo: bool,
}
@ -213,7 +213,7 @@ fn test_rename_all_is_not_propagated_from_struct_into_flattened() {
fn test_lower_is_renamed() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(rename_all = "lower", long, action)]
#[clap(rename_all = "lower", long)]
foo_option: bool,
}
@ -227,7 +227,7 @@ fn test_lower_is_renamed() {
fn test_upper_is_renamed() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(rename_all = "upper", long, action)]
#[clap(rename_all = "upper", long)]
foo_option: bool,
}
@ -241,10 +241,7 @@ fn test_upper_is_renamed() {
fn test_single_word_enum_variant_is_default_renamed_into_kebab_case() {
#[derive(Parser, Debug, PartialEq)]
enum Opt {
Command {
#[clap(value_parser)]
foo: u32,
},
Command { foo: u32 },
}
assert_eq!(
@ -257,10 +254,7 @@ fn test_single_word_enum_variant_is_default_renamed_into_kebab_case() {
fn test_multi_word_enum_variant_is_renamed() {
#[derive(Parser, Debug, PartialEq)]
enum Opt {
FirstCommand {
#[clap(value_parser)]
foo: u32,
},
FirstCommand { foo: u32 },
}
assert_eq!(
@ -281,7 +275,7 @@ fn test_rename_all_is_not_propagated_from_struct_into_subcommand() {
#[derive(Parser, Debug, PartialEq)]
enum Foo {
Command {
#[clap(long, action)]
#[clap(long)]
foo: bool,
},
}
@ -301,7 +295,7 @@ fn test_rename_all_is_propagated_from_enum_to_variants() {
enum Opt {
FirstVariant,
SecondVariant {
#[clap(long, value_parser)]
#[clap(long)]
foo: String,
},
}
@ -319,7 +313,7 @@ fn test_rename_all_is_propagated_from_enum_to_variant_fields() {
enum Opt {
FirstVariant,
SecondVariant {
#[clap(long, value_parser)]
#[clap(long)]
foo: String,
},
}
@ -339,11 +333,11 @@ fn test_rename_all_is_propagation_can_be_overridden() {
enum Opt {
#[clap(rename_all = "kebab_case")]
FirstVariant {
#[clap(long, action)]
#[clap(long)]
foo_option: bool,
},
SecondVariant {
#[clap(rename_all = "kebab_case", long, action)]
#[clap(rename_all = "kebab_case", long)]
foo_option: bool,
},
}

View file

@ -16,7 +16,7 @@ use clap::{Parser, Subcommand};
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, action)]
#[clap(short, long)]
force: bool,
#[clap(short, long, action = clap::ArgAction::Count)]
verbose: u8,
@ -32,7 +32,7 @@ enum Sub {
#[derive(Parser, PartialEq, Debug)]
struct Opt2 {
#[clap(short, long, action)]
#[clap(short, long)]
force: bool,
#[clap(short, long, action = clap::ArgAction::Count)]
verbose: u8,
@ -109,7 +109,7 @@ fn test_badinput() {
#[derive(Parser, PartialEq, Debug)]
struct Opt3 {
#[clap(short, long, action)]
#[clap(short, long)]
all: bool,
#[clap(subcommand)]
cmd: Sub2,
@ -118,7 +118,6 @@ struct Opt3 {
#[derive(Subcommand, PartialEq, Debug)]
enum Sub2 {
Foo {
#[clap(value_parser)]
file: String,
#[clap(subcommand)]
cmd: Sub3,
@ -159,16 +158,8 @@ enum SubSubCmdWithOption {
}
#[derive(Subcommand, PartialEq, Debug)]
enum Remote {
Add {
#[clap(value_parser)]
name: String,
#[clap(value_parser)]
url: String,
},
Remove {
#[clap(value_parser)]
name: String,
},
Add { name: String, url: String },
Remove { name: String },
}
#[derive(Subcommand, PartialEq, Debug)]

View file

@ -1,97 +0,0 @@
use clap::CommandFactory;
use clap::Parser;
#[test]
fn app_name_in_short_help_from_struct() {
#[derive(Parser)]
#[clap(name = "my-cmd")]
struct MyApp {}
let mut help = Vec::new();
MyApp::command().write_help(&mut help).unwrap();
let help = String::from_utf8(help).unwrap();
assert!(help.contains("my-cmd"));
}
#[test]
fn app_name_in_long_help_from_struct() {
#[derive(Parser)]
#[clap(name = "my-cmd")]
struct MyApp {}
let mut help = Vec::new();
MyApp::command().write_long_help(&mut help).unwrap();
let help = String::from_utf8(help).unwrap();
assert!(help.contains("my-cmd"));
}
#[test]
fn app_name_in_short_help_from_enum() {
#[derive(Parser)]
#[clap(name = "my-cmd")]
enum MyApp {}
let mut help = Vec::new();
MyApp::command().write_help(&mut help).unwrap();
let help = String::from_utf8(help).unwrap();
assert!(help.contains("my-cmd"));
}
#[test]
fn app_name_in_long_help_from_enum() {
#[derive(Parser)]
#[clap(name = "my-cmd")]
enum MyApp {}
let mut help = Vec::new();
MyApp::command().write_long_help(&mut help).unwrap();
let help = String::from_utf8(help).unwrap();
assert!(help.contains("my-cmd"));
}
#[test]
fn app_name_in_short_version_from_struct() {
#[derive(Parser)]
#[clap(name = "my-cmd")]
struct MyApp {}
let version = MyApp::command().render_version();
assert!(version.contains("my-cmd"));
}
#[test]
fn app_name_in_long_version_from_struct() {
#[derive(Parser)]
#[clap(name = "my-cmd")]
struct MyApp {}
let version = MyApp::command().render_long_version();
assert!(version.contains("my-cmd"));
}
#[test]
fn app_name_in_short_version_from_enum() {
#[derive(Parser)]
#[clap(name = "my-cmd")]
enum MyApp {}
let version = MyApp::command().render_version();
assert!(version.contains("my-cmd"));
}
#[test]
fn app_name_in_long_version_from_enum() {
#[derive(Parser)]
#[clap(name = "my-cmd")]
enum MyApp {}
let version = MyApp::command().render_long_version();
assert!(version.contains("my-cmd"));
}

View file

@ -1,120 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use clap::CommandFactory;
use clap::Parser;
#[test]
fn required_argument() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
arg: i32,
}
assert_eq!(
Opt { arg: 42 },
Opt::try_parse_from(&["test", "42"]).unwrap()
);
assert!(Opt::try_parse_from(&["test"]).is_err());
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
}
#[test]
fn argument_with_default() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(default_value = "42")]
arg: i32,
}
assert_eq!(
Opt { arg: 24 },
Opt::try_parse_from(&["test", "24"]).unwrap()
);
assert_eq!(Opt { arg: 42 }, Opt::try_parse_from(&["test"]).unwrap());
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
}
#[test]
fn auto_value_name() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
my_special_arg: i32,
}
let mut help = Vec::new();
Opt::command().write_help(&mut help).unwrap();
let help = String::from_utf8(help).unwrap();
assert!(help.contains("MY_SPECIAL_ARG"));
// Ensure the implicit `num_vals` is just 1
assert_eq!(
Opt { my_special_arg: 10 },
Opt::try_parse_from(&["test", "10"]).unwrap()
);
}
#[test]
fn explicit_value_name() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_name = "BROWNIE_POINTS")]
my_special_arg: i32,
}
let mut help = Vec::new();
Opt::command().write_help(&mut help).unwrap();
let help = String::from_utf8(help).unwrap();
assert!(help.contains("BROWNIE_POINTS"));
assert!(!help.contains("MY_SPECIAL_ARG"));
// Ensure the implicit `num_vals` is just 1
assert_eq!(
Opt { my_special_arg: 10 },
Opt::try_parse_from(&["test", "10"]).unwrap()
);
}
#[test]
fn option_type_is_optional() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
arg: Option<i32>,
}
assert_eq!(
Opt { arg: Some(42) },
Opt::try_parse_from(&["test", "42"]).unwrap()
);
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
}
#[test]
fn vec_type_is_multiple_values() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
arg: Vec<i32>,
}
assert_eq!(
Opt { arg: vec![24] },
Opt::try_parse_from(&["test", "24"]).unwrap()
);
assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { arg: vec![24, 42] },
Opt::try_parse_from(&["test", "24", "42"]).unwrap()
);
assert_eq!(
clap::ErrorKind::ValueValidation,
Opt::try_parse_from(&["test", "NOPE"]).err().unwrap().kind()
);
}

View file

@ -1,51 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use crate::utils;
use clap::Parser;
#[test]
fn no_author_version_about() {
#[derive(Parser, PartialEq, Debug)]
#[clap(name = "foo")]
struct Opt {}
let output = utils::get_long_help::<Opt>();
assert!(output.starts_with("foo \n\nUSAGE:"));
}
#[test]
fn use_env() {
#[derive(Parser, PartialEq, Debug)]
#[clap(author, about, version)]
struct Opt {}
let output = utils::get_long_help::<Opt>();
assert!(output.starts_with("clap"));
assert!(output
.contains("A simple to use, efficient, and full-featured Command Line Argument Parser"));
}
#[test]
fn explicit_version_not_str_lit() {
const VERSION: &str = "custom version";
#[derive(Parser)]
#[clap(version = VERSION)]
pub struct Opt {}
let output = utils::get_long_help::<Opt>();
assert!(output.contains("custom version"));
}

View file

@ -1,51 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use clap::Parser;
#[test]
fn basic() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short = 'a', long = "arg")]
arg: i32,
}
assert_eq!(
Opt { arg: 24 },
Opt::try_parse_from(&["test", "-a24"]).unwrap()
);
}
#[test]
fn update_basic() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short = 'a', long = "arg")]
single_value: i32,
}
let mut opt = Opt::try_parse_from(&["test", "-a0"]).unwrap();
opt.update_from(&["test", "-a42"]);
assert_eq!(Opt { single_value: 42 }, opt);
}
#[test]
fn unit_struct() {
#[derive(Parser, PartialEq, Debug)]
struct Opt;
assert_eq!(Opt {}, Opt::try_parse_from(&["test"]).unwrap());
}

View file

@ -1,48 +0,0 @@
use clap::{Args, Parser, Subcommand};
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(subcommand)]
sub: Box<Sub>,
}
#[derive(Subcommand, PartialEq, Debug)]
enum Sub {
Flame {
#[clap(flatten)]
arg: Box<Ext>,
},
}
#[derive(Args, PartialEq, Debug)]
struct Ext {
arg: u32,
}
#[test]
fn boxed_flatten_subcommand() {
assert_eq!(
Opt {
sub: Box::new(Sub::Flame {
arg: Box::new(Ext { arg: 1 })
})
},
Opt::try_parse_from(&["test", "flame", "1"]).unwrap()
);
}
#[test]
fn update_boxed_flatten_subcommand() {
let mut opt = Opt::try_parse_from(&["test", "flame", "1"]).unwrap();
opt.update_from(&["test", "flame", "42"]);
assert_eq!(
Opt {
sub: Box::new(Sub::Flame {
arg: Box::new(Ext { arg: 42 })
})
},
opt
);
}

View file

@ -1,205 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
#![allow(deprecated)]
use clap::Parser;
use std::num::ParseIntError;
use std::path::PathBuf;
#[derive(Parser, PartialEq, Debug)]
struct PathOpt {
#[clap(short, long)]
path: PathBuf,
#[clap(short, default_value = "../")]
default_path: PathBuf,
#[clap(short, multiple_occurrences(true))]
vector_path: Vec<PathBuf>,
#[clap(short)]
option_path_1: Option<PathBuf>,
#[clap(short = 'q')]
option_path_2: Option<PathBuf>,
}
#[test]
fn test_path_opt_simple() {
assert_eq!(
PathOpt {
path: PathBuf::from("/usr/bin"),
default_path: PathBuf::from("../"),
vector_path: vec![
PathBuf::from("/a/b/c"),
PathBuf::from("/d/e/f"),
PathBuf::from("/g/h/i"),
],
option_path_1: None,
option_path_2: Some(PathBuf::from("j.zip")),
},
PathOpt::try_parse_from(&[
"test", "-p", "/usr/bin", "-v", "/a/b/c", "-v", "/d/e/f", "-v", "/g/h/i", "-q",
"j.zip",
])
.unwrap()
);
}
fn parse_hex(input: &str) -> Result<u64, ParseIntError> {
u64::from_str_radix(input, 16)
}
#[derive(Parser, PartialEq, Debug)]
struct HexOpt {
#[clap(short, value_parser = parse_hex)]
number: u64,
}
#[test]
fn test_parse_hex() {
assert_eq!(
HexOpt { number: 5 },
HexOpt::try_parse_from(&["test", "-n", "5"]).unwrap()
);
assert_eq!(
HexOpt {
number: 0x00ab_cdef
},
HexOpt::try_parse_from(&["test", "-n", "abcdef"]).unwrap()
);
let err = HexOpt::try_parse_from(&["test", "-n", "gg"]).unwrap_err();
assert!(
err.to_string().contains("invalid digit found in string"),
"{}",
err
);
}
#[derive(Debug)]
struct ErrCode(u32);
impl std::error::Error for ErrCode {}
impl std::fmt::Display for ErrCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
fn custom_parser_2(_: &str) -> Result<&'static str, ErrCode> {
Ok("B")
}
#[derive(Parser, PartialEq, Debug)]
struct NoOpOpt {
#[clap(short, value_parser = custom_parser_2)]
b: &'static str,
}
#[test]
fn test_every_custom_parser() {
assert_eq!(
NoOpOpt { b: "B" },
NoOpOpt::try_parse_from(&["test", "-b=?"]).unwrap()
);
}
#[test]
fn update_every_custom_parser() {
let mut opt = NoOpOpt { b: "0" };
opt.try_update_from(&["test", "-b=?"]).unwrap();
assert_eq!(NoOpOpt { b: "B" }, opt);
}
// Note: can't use `Vec<u8>` directly, as clap would instead look for
// conversion function from `&str` to `u8`.
type Bytes = Vec<u8>;
#[derive(Parser, PartialEq, Debug)]
struct DefaultedOpt {
#[clap(short, parse(from_str))]
bytes: Bytes,
#[clap(short)]
integer: u64,
#[clap(short)]
path: PathBuf,
}
#[test]
fn test_parser_with_default_value() {
assert_eq!(
DefaultedOpt {
bytes: b"E\xc2\xb2=p\xc2\xb2c\xc2\xb2+m\xc2\xb2c\xe2\x81\xb4".to_vec(),
integer: 9000,
path: PathBuf::from("src/lib.rs"),
},
DefaultedOpt::try_parse_from(&[
"test",
"-b",
"E²=p²c²+m²c⁴",
"-i",
"9000",
"-p",
"src/lib.rs",
])
.unwrap()
);
}
#[derive(PartialEq, Debug)]
struct Foo(u8);
fn foo(value: u64) -> Foo {
Foo(value as u8)
}
#[derive(Parser, PartialEq, Debug)]
struct Occurrences {
#[clap(short, long, parse(from_occurrences))]
signed: i32,
#[clap(short, parse(from_occurrences))]
little_signed: i8,
#[clap(short, parse(from_occurrences))]
unsigned: usize,
#[clap(short = 'r', parse(from_occurrences))]
little_unsigned: u8,
#[clap(short, long, parse(from_occurrences = foo))]
custom: Foo,
}
#[test]
fn test_parser_occurrences() {
assert_eq!(
Occurrences {
signed: 3,
little_signed: 1,
unsigned: 0,
little_unsigned: 4,
custom: Foo(5),
},
Occurrences::try_parse_from(&[
"test", "-s", "--signed", "--signed", "-l", "-rrrr", "-cccc", "--custom",
])
.unwrap()
);
}

View file

@ -1,57 +0,0 @@
use clap::{CommandFactory, Parser};
use crate::utils;
#[test]
fn default_value() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(default_value = "3")]
arg: i32,
}
assert_eq!(Opt { arg: 3 }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(Opt { arg: 1 }, Opt::try_parse_from(&["test", "1"]).unwrap());
let help = utils::get_long_help::<Opt>();
assert!(help.contains("[default: 3]"));
}
#[test]
fn default_value_t() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(default_value_t = 3)]
arg: i32,
}
assert_eq!(Opt { arg: 3 }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(Opt { arg: 1 }, Opt::try_parse_from(&["test", "1"]).unwrap());
let help = utils::get_long_help::<Opt>();
assert!(help.contains("[default: 3]"));
}
#[test]
fn auto_default_value_t() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(default_value_t)]
arg: i32,
}
assert_eq!(Opt { arg: 0 }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(Opt { arg: 1 }, Opt::try_parse_from(&["test", "1"]).unwrap());
let help = utils::get_long_help::<Opt>();
assert!(help.contains("[default: 0]"));
}
#[test]
fn detect_os_variant() {
#![allow(unused_parens)] // needed for `as_ref` call
#[derive(clap::Parser)]
pub struct Options {
#[clap(default_value_os = ("123".as_ref()))]
x: String,
}
Options::command().debug_assert();
}

View file

@ -1,53 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
#![deny(warnings)]
use clap::Parser;
fn try_str(s: &str) -> Result<String, std::convert::Infallible> {
Ok(s.into())
}
#[test]
fn warning_never_struct() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(value_parser = try_str, default_value_t)]
s: String,
}
assert_eq!(
Opt {
s: "foo".to_string()
},
Opt::try_parse_from(&["test", "foo"]).unwrap()
);
}
#[test]
fn warning_never_enum() {
#[derive(Parser, Debug, PartialEq)]
enum Opt {
Foo {
#[clap(value_parser = try_str, default_value_t)]
s: String,
},
}
assert_eq!(
Opt::Foo {
s: "foo".to_string()
},
Opt::try_parse_from(&["test", "foo", "foo"]).unwrap()
);
}

View file

@ -1,240 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use crate::utils;
use clap::{CommandFactory, Parser, ValueEnum};
#[test]
fn doc_comments() {
/// Lorem ipsum
#[derive(Parser, PartialEq, Debug)]
struct LoremIpsum {
/// Fooify a bar
/// and a baz
#[clap(short, long)]
foo: bool,
}
let help = utils::get_long_help::<LoremIpsum>();
assert!(help.contains("Lorem ipsum"));
assert!(help.contains("Fooify a bar and a baz"));
}
#[test]
fn help_is_better_than_comments() {
/// Lorem ipsum
#[derive(Parser, PartialEq, Debug)]
#[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
struct LoremIpsum {
/// Fooify a bar
#[clap(short, long, help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES")]
foo: bool,
}
let help = utils::get_long_help::<LoremIpsum>();
assert!(help.contains("Dolor sit amet"));
assert!(!help.contains("Lorem ipsum"));
assert!(help.contains("DO NOT PASS A BAR"));
}
#[test]
fn empty_line_in_doc_comment_is_double_linefeed() {
/// Foo.
///
/// Bar
#[derive(Parser, PartialEq, Debug)]
#[clap(name = "lorem-ipsum")]
struct LoremIpsum {}
let help = utils::get_long_help::<LoremIpsum>();
assert!(help.starts_with("lorem-ipsum \nFoo.\n\nBar\n\nUSAGE:"));
}
#[test]
fn field_long_doc_comment_both_help_long_help() {
/// Lorem ipsumclap
#[derive(Parser, PartialEq, Debug)]
#[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
struct LoremIpsum {
/// Dot is removed from multiline comments.
///
/// Long help
#[clap(long)]
foo: bool,
/// Dot is removed from one short comment.
#[clap(long)]
bar: bool,
}
let short_help = utils::get_help::<LoremIpsum>();
let long_help = utils::get_long_help::<LoremIpsum>();
assert!(short_help.contains("Dot is removed from one short comment"));
assert!(!short_help.contains("Dot is removed from one short comment."));
assert!(short_help.contains("Dot is removed from multiline comments"));
assert!(!short_help.contains("Dot is removed from multiline comments."));
assert!(long_help.contains("Long help"));
assert!(!short_help.contains("Long help"));
}
#[test]
fn top_long_doc_comment_both_help_long_help() {
/// Lorem ipsumclap
#[derive(Parser, Debug)]
#[clap(name = "lorem-ipsum", about = "Dolor sit amet")]
struct LoremIpsum {
#[clap(subcommand)]
foo: SubCommand,
}
#[derive(Parser, Debug)]
pub enum SubCommand {
/// DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES
///
/// Or something else
Foo {
#[clap(help = "foo")]
bars: String,
},
}
let short_help = utils::get_help::<LoremIpsum>();
let long_help = utils::get_subcommand_long_help::<LoremIpsum>("foo");
assert!(!short_help.contains("Or something else"));
assert!(long_help.contains("DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES"));
assert!(long_help.contains("Or something else"));
}
#[test]
fn verbatim_doc_comment() {
/// DANCE!
///
/// ()
/// |
/// ( () )
/// ) ________ // )
/// () |\ \ //
/// ( \\__ \ ______\//
/// \__) | |
/// | | |
/// \ | |
/// \|_______|
/// // \\
/// (( ||
/// \\ ||
/// ( () ||
/// ( () ) )
#[derive(Parser, Debug)]
#[clap(verbatim_doc_comment)]
struct SeeFigure1 {
#[clap(long)]
foo: bool,
}
let help = utils::get_long_help::<SeeFigure1>();
let sample = r#"
()
|
( () )
) ________ // )
() |\ \ //
( \\__ \ ______\//
\__) | |
| | |
\ | |
\|_______|
// \\
(( ||
\\ ||
( () ||
( () ) )"#;
assert!(help.contains(sample))
}
#[test]
fn verbatim_doc_comment_field() {
#[derive(Parser, Debug)]
struct Command {
/// This help ends in a period.
#[clap(long, verbatim_doc_comment)]
foo: bool,
/// This help does not end in a period.
#[clap(long)]
bar: bool,
}
let help = utils::get_long_help::<Command>();
assert!(help.contains("This help ends in a period."));
assert!(help.contains("This help does not end in a period"));
}
#[test]
fn multiline_separates_default() {
#[derive(Parser, Debug)]
struct Command {
/// Multiline
///
/// Doc comment
#[clap(long, default_value = "x")]
x: String,
}
let help = utils::get_long_help::<Command>();
assert!(!help.contains("Doc comment [default"));
assert!(help.lines().any(|s| s.trim().starts_with("[default")));
// The short help should still have the default on the same line
let help = utils::get_help::<Command>();
assert!(help.contains("Multiline [default"));
}
#[test]
fn argenum_multiline_doc_comment() {
#[derive(ValueEnum, Clone)]
enum LoremIpsum {
/// Multiline
///
/// Doc comment
Bar,
}
}
#[test]
fn doc_comment_about_handles_both_abouts() {
/// Opts doc comment summary
#[derive(Parser, Debug)]
pub struct Opts {
#[clap(subcommand)]
pub cmd: Sub,
}
/// Sub doc comment summary
///
/// Sub doc comment body
#[derive(Parser, PartialEq, Eq, Debug)]
pub enum Sub {
Compress { output: String },
}
let cmd = Opts::command();
assert_eq!(cmd.get_about(), Some("Opts doc comment summary"));
// clap will fallback to `about` on `None`. The main care about is not providing a `Sub` doc
// comment.
assert_eq!(cmd.get_long_about(), None);
}

View file

@ -1,36 +0,0 @@
use crate::utils;
use clap::Parser;
#[test]
fn explicit_short_long_no_rename() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short = '.', long = ".foo")]
foo: String,
}
assert_eq!(
Opt { foo: "long".into() },
Opt::try_parse_from(&["test", "--.foo", "long"]).unwrap()
);
assert_eq!(
Opt {
foo: "short".into(),
},
Opt::try_parse_from(&["test", "-.", "short"]).unwrap()
);
}
#[test]
fn explicit_name_no_rename() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(name = ".options")]
foo: String,
}
let help = utils::get_long_help::<Opt>();
assert!(help.contains("<.options>"))
}

View file

@ -1,231 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
#![allow(deprecated)]
use clap::Parser;
#[test]
fn bool_type_is_flag() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long)]
alice: bool,
}
assert_eq!(
Opt { alice: false },
Opt::try_parse_from(&["test"]).unwrap()
);
assert_eq!(
Opt { alice: true },
Opt::try_parse_from(&["test", "-a"]).unwrap()
);
assert_eq!(
Opt { alice: true },
Opt::try_parse_from(&["test", "-a", "-a"]).unwrap()
);
assert_eq!(
Opt { alice: true },
Opt::try_parse_from(&["test", "--alice"]).unwrap()
);
assert!(Opt::try_parse_from(&["test", "-i"]).is_err());
assert!(Opt::try_parse_from(&["test", "-a", "foo"]).is_err());
}
#[test]
fn count() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, action = clap::ArgAction::Count)]
alice: u8,
#[clap(short, long, action = clap::ArgAction::Count)]
bob: u8,
}
assert_eq!(
Opt { alice: 0, bob: 0 },
Opt::try_parse_from(&["test"]).unwrap()
);
assert_eq!(
Opt { alice: 1, bob: 0 },
Opt::try_parse_from(&["test", "-a"]).unwrap()
);
assert_eq!(
Opt { alice: 2, bob: 0 },
Opt::try_parse_from(&["test", "-a", "-a"]).unwrap()
);
assert_eq!(
Opt { alice: 2, bob: 2 },
Opt::try_parse_from(&["test", "-a", "--alice", "-bb"]).unwrap()
);
assert_eq!(
Opt { alice: 3, bob: 1 },
Opt::try_parse_from(&["test", "-aaa", "--bob"]).unwrap()
);
assert!(Opt::try_parse_from(&["test", "-i"]).is_err());
assert!(Opt::try_parse_from(&["test", "-a", "foo"]).is_err());
}
#[test]
fn non_bool_type_flag() {
fn parse_from_flag(b: bool) -> std::sync::atomic::AtomicBool {
std::sync::atomic::AtomicBool::new(b)
}
#[derive(Parser, Debug)]
struct Opt {
#[clap(short, long, parse(from_flag = parse_from_flag))]
alice: std::sync::atomic::AtomicBool,
#[clap(short, long, parse(from_flag))]
bob: std::sync::atomic::AtomicBool,
}
let falsey = Opt::try_parse_from(&["test"]).unwrap();
assert!(!falsey.alice.load(std::sync::atomic::Ordering::Relaxed));
assert!(!falsey.bob.load(std::sync::atomic::Ordering::Relaxed));
let alice = Opt::try_parse_from(&["test", "-a"]).unwrap();
assert!(alice.alice.load(std::sync::atomic::Ordering::Relaxed));
assert!(!alice.bob.load(std::sync::atomic::Ordering::Relaxed));
let bob = Opt::try_parse_from(&["test", "-b"]).unwrap();
assert!(!bob.alice.load(std::sync::atomic::Ordering::Relaxed));
assert!(bob.bob.load(std::sync::atomic::Ordering::Relaxed));
let both = Opt::try_parse_from(&["test", "-b", "-a"]).unwrap();
assert!(both.alice.load(std::sync::atomic::Ordering::Relaxed));
assert!(both.bob.load(std::sync::atomic::Ordering::Relaxed));
}
#[test]
fn mixed_type_flags() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long)]
alice: bool,
#[clap(short, long, action = clap::ArgAction::Count)]
bob: u8,
}
assert_eq!(
Opt {
alice: false,
bob: 0
},
Opt::try_parse_from(&["test"]).unwrap()
);
assert_eq!(
Opt {
alice: true,
bob: 0
},
Opt::try_parse_from(&["test", "-a"]).unwrap()
);
assert_eq!(
Opt {
alice: true,
bob: 0
},
Opt::try_parse_from(&["test", "-a"]).unwrap()
);
assert_eq!(
Opt {
alice: false,
bob: 1
},
Opt::try_parse_from(&["test", "-b"]).unwrap()
);
assert_eq!(
Opt {
alice: true,
bob: 1
},
Opt::try_parse_from(&["test", "--alice", "--bob"]).unwrap()
);
assert_eq!(
Opt {
alice: true,
bob: 4
},
Opt::try_parse_from(&["test", "-bb", "-a", "-bb"]).unwrap()
);
}
#[test]
fn ignore_qualified_bool_type() {
mod inner {
#[allow(non_camel_case_types)]
#[derive(PartialEq, Debug, Clone)]
pub struct bool(pub String);
impl std::str::FromStr for self::bool {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(self::bool(s.into()))
}
}
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
arg: inner::bool,
}
assert_eq!(
Opt {
arg: inner::bool("success".into())
},
Opt::try_parse_from(&["test", "success"]).unwrap()
);
}
#[test]
fn override_implicit_action() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(long, action = clap::ArgAction::Set)]
arg: bool,
}
assert_eq!(
Opt { arg: false },
Opt::try_parse_from(&["test", "--arg", "false"]).unwrap()
);
assert_eq!(
Opt { arg: true },
Opt::try_parse_from(&["test", "--arg", "true"]).unwrap()
);
}
#[test]
fn override_implicit_from_flag_positional() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(action = clap::ArgAction::Set)]
arg: bool,
}
assert_eq!(
Opt { arg: false },
Opt::try_parse_from(&["test", "false"]).unwrap()
);
assert_eq!(
Opt { arg: true },
Opt::try_parse_from(&["test", "true"]).unwrap()
);
}

View file

@ -1,257 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use crate::utils;
use clap::{Args, Parser, Subcommand};
#[test]
fn flatten() {
#[derive(Args, PartialEq, Debug)]
struct Common {
arg: i32,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(flatten)]
common: Common,
}
assert_eq!(
Opt {
common: Common { arg: 42 }
},
Opt::try_parse_from(&["test", "42"]).unwrap()
);
assert!(Opt::try_parse_from(&["test"]).is_err());
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
}
#[cfg(debug_assertions)]
#[test]
#[should_panic]
fn flatten_twice() {
#[derive(Args, PartialEq, Debug)]
struct Common {
arg: i32,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(flatten)]
c1: Common,
// Defines "arg" twice, so this should not work.
#[clap(flatten)]
c2: Common,
}
Opt::try_parse_from(&["test", "42", "43"]).unwrap();
}
#[test]
fn flatten_in_subcommand() {
#[derive(Args, PartialEq, Debug)]
struct Common {
arg: i32,
}
#[derive(Args, PartialEq, Debug)]
struct Add {
#[clap(short)]
interactive: bool,
#[clap(flatten)]
common: Common,
}
#[derive(Parser, PartialEq, Debug)]
enum Opt {
Fetch {
#[clap(short)]
all: bool,
#[clap(flatten)]
common: Common,
},
Add(Add),
}
assert_eq!(
Opt::Fetch {
all: false,
common: Common { arg: 42 }
},
Opt::try_parse_from(&["test", "fetch", "42"]).unwrap()
);
assert_eq!(
Opt::Add(Add {
interactive: true,
common: Common { arg: 43 }
}),
Opt::try_parse_from(&["test", "add", "-i", "43"]).unwrap()
);
}
#[test]
fn update_args_with_flatten() {
#[derive(Args, PartialEq, Debug)]
struct Common {
arg: i32,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(flatten)]
common: Common,
}
let mut opt = Opt {
common: Common { arg: 42 },
};
opt.try_update_from(&["test"]).unwrap();
assert_eq!(Opt::try_parse_from(&["test", "42"]).unwrap(), opt);
let mut opt = Opt {
common: Common { arg: 42 },
};
opt.try_update_from(&["test", "52"]).unwrap();
assert_eq!(Opt::try_parse_from(&["test", "52"]).unwrap(), opt);
}
#[derive(Subcommand, PartialEq, Debug)]
enum BaseCli {
Command1(Command1),
}
#[derive(Args, PartialEq, Debug)]
struct Command1 {
arg1: i32,
arg2: i32,
}
#[derive(Args, PartialEq, Debug)]
struct Command2 {
arg2: i32,
}
#[derive(Parser, PartialEq, Debug)]
enum Opt {
#[clap(flatten)]
BaseCli(BaseCli),
Command2(Command2),
}
#[test]
fn merge_subcommands_with_flatten() {
assert_eq!(
Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 42, arg2: 44 })),
Opt::try_parse_from(&["test", "command1", "42", "44"]).unwrap()
);
assert_eq!(
Opt::Command2(Command2 { arg2: 43 }),
Opt::try_parse_from(&["test", "command2", "43"]).unwrap()
);
}
#[test]
fn update_subcommands_with_flatten() {
let mut opt = Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 12, arg2: 14 }));
opt.try_update_from(&["test", "command1", "42", "44"])
.unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "command1", "42", "44"]).unwrap(),
opt
);
let mut opt = Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 12, arg2: 14 }));
opt.try_update_from(&["test", "command1", "42"]).unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "command1", "42", "14"]).unwrap(),
opt
);
let mut opt = Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 12, arg2: 14 }));
opt.try_update_from(&["test", "command2", "43"]).unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "command2", "43"]).unwrap(),
opt
);
}
#[test]
fn flatten_with_doc_comment() {
#[derive(Args, PartialEq, Debug)]
struct Common {
/// This is an arg. Arg means "argument". Command line argument.
arg: i32,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
/// The very important comment that clippy had me put here.
/// It knows better.
#[clap(flatten)]
common: Common,
}
assert_eq!(
Opt {
common: Common { arg: 42 }
},
Opt::try_parse_from(&["test", "42"]).unwrap()
);
let help = utils::get_help::<Opt>();
assert!(help.contains("This is an arg."));
assert!(!help.contains("The very important"));
}
#[test]
fn docstrings_ordering_with_multiple_clap() {
/// This is the docstring for Flattened
#[derive(Args)]
struct Flattened {
#[clap(long)]
foo: bool,
}
/// This is the docstring for Command
#[derive(Parser)]
struct Command {
#[clap(flatten)]
flattened: Flattened,
}
let short_help = utils::get_help::<Command>();
assert!(short_help.contains("This is the docstring for Command"));
}
#[test]
fn docstrings_ordering_with_multiple_clap_partial() {
/// This is the docstring for Flattened
#[derive(Args)]
struct Flattened {
#[clap(long)]
foo: bool,
}
#[derive(Parser)]
struct Command {
#[clap(flatten)]
flattened: Flattened,
}
let short_help = utils::get_help::<Command>();
assert!(short_help.contains("This is the docstring for Flattened"));
}

View file

@ -1,145 +0,0 @@
use clap::{Args, Parser};
#[test]
fn generic_struct_flatten() {
#[derive(Args, PartialEq, Debug)]
struct Inner {
pub answer: isize,
}
#[derive(Parser, PartialEq, Debug)]
struct Outer<T: Args> {
#[clap(flatten)]
pub inner: T,
}
assert_eq!(
Outer {
inner: Inner { answer: 42 }
},
Outer::parse_from(&["--answer", "42"])
)
}
#[test]
fn generic_struct_flatten_w_where_clause() {
#[derive(Args, PartialEq, Debug)]
struct Inner {
pub answer: isize,
}
#[derive(Parser, PartialEq, Debug)]
struct Outer<T>
where
T: Args,
{
#[clap(flatten)]
pub inner: T,
}
assert_eq!(
Outer {
inner: Inner { answer: 42 }
},
Outer::parse_from(&["--answer", "42"])
)
}
#[test]
fn generic_enum() {
#[derive(Args, PartialEq, Debug)]
struct Inner {
pub answer: isize,
}
#[derive(Parser, PartialEq, Debug)]
enum GenericEnum<T: Args> {
Start(T),
Stop,
}
assert_eq!(
GenericEnum::Start(Inner { answer: 42 }),
GenericEnum::parse_from(&["test", "start", "42"])
)
}
#[test]
fn generic_enum_w_where_clause() {
#[derive(Args, PartialEq, Debug)]
struct Inner {
pub answer: isize,
}
#[derive(Parser, PartialEq, Debug)]
enum GenericEnum<T>
where
T: Args,
{
Start(T),
Stop,
}
assert_eq!(
GenericEnum::Start(Inner { answer: 42 }),
GenericEnum::parse_from(&["test", "start", "42"])
)
}
#[test]
fn generic_w_fromstr_trait_bound() {
use std::str::FromStr;
#[derive(Parser, PartialEq, Debug)]
struct Opt<T>
where
T: FromStr + Send + Sync + Clone + 'static,
<T as FromStr>::Err: std::error::Error + Sync + Send + 'static,
{
answer: T,
}
assert_eq!(
Opt::<isize> { answer: 42 },
Opt::<isize>::parse_from(&["--answer", "42"])
)
}
#[test]
fn generic_wo_trait_bound() {
use std::time::Duration;
#[derive(Parser, PartialEq, Debug)]
struct Opt<T> {
answer: isize,
#[clap(skip)]
took: Option<T>,
}
assert_eq!(
Opt::<Duration> {
answer: 42,
took: None
},
Opt::<Duration>::parse_from(&["--answer", "42"])
)
}
#[test]
fn generic_where_clause_w_trailing_comma() {
use std::str::FromStr;
#[derive(Parser, PartialEq, Debug)]
struct Opt<T>
where
T: FromStr + Send + Sync + Clone + 'static,
<T as FromStr>::Err: std::error::Error + Sync + Send + 'static,
{
pub answer: T,
}
assert_eq!(
Opt::<isize> { answer: 42 },
Opt::<isize>::parse_from(&["--answer", "42"])
)
}

View file

@ -1,520 +0,0 @@
use clap::{AppSettings, Args, CommandFactory, Parser, Subcommand};
#[test]
fn arg_help_heading_applied() {
#[derive(Debug, Clone, Parser)]
struct CliOptions {
#[clap(long)]
#[clap(help_heading = Some("HEADING A"))]
should_be_in_section_a: u32,
#[clap(long)]
no_section: u32,
}
let cmd = CliOptions::command();
let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-section-a")
.unwrap()
};
assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));
let should_be_in_section_b = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "no_section")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "no-section")
.unwrap()
};
assert_eq!(should_be_in_section_b.get_help_heading(), None);
}
#[test]
fn app_help_heading_applied() {
#[derive(Debug, Clone, Parser)]
#[clap(next_help_heading = "DEFAULT")]
struct CliOptions {
#[clap(long)]
#[clap(help_heading = Some("HEADING A"))]
should_be_in_section_a: u32,
#[clap(long)]
should_be_in_default_section: u32,
}
let cmd = CliOptions::command();
let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-section-a")
.unwrap()
};
assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));
let should_be_in_default_section = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_default_section")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-default-section")
.unwrap()
};
assert_eq!(
should_be_in_default_section.get_help_heading(),
Some("DEFAULT")
);
}
#[test]
fn app_help_heading_flattened() {
// Used to help track the cause in tests
#![allow(clippy::enum_variant_names)]
#[derive(Debug, Clone, Parser)]
struct CliOptions {
#[clap(flatten)]
options_a: OptionsA,
#[clap(flatten)]
options_b: OptionsB,
#[clap(subcommand)]
sub_a: SubA,
#[clap(long)]
should_be_in_default_section: u32,
}
#[derive(Debug, Clone, Args)]
#[clap(next_help_heading = "HEADING A")]
struct OptionsA {
#[clap(long)]
should_be_in_section_a: u32,
}
#[derive(Debug, Clone, Args)]
#[clap(next_help_heading = "HEADING B")]
struct OptionsB {
#[clap(long)]
should_be_in_section_b: u32,
}
#[derive(Debug, Clone, Subcommand)]
enum SubA {
#[clap(flatten)]
SubB(SubB),
#[clap(subcommand)]
SubC(SubC),
SubAOne,
#[clap(next_help_heading = "SUB A")]
SubATwo {
should_be_in_sub_a: u32,
},
}
#[derive(Debug, Clone, Subcommand)]
enum SubB {
#[clap(next_help_heading = "SUB B")]
SubBOne { should_be_in_sub_b: u32 },
}
#[derive(Debug, Clone, Subcommand)]
enum SubC {
#[clap(next_help_heading = "SUB C")]
SubCOne { should_be_in_sub_c: u32 },
}
let cmd = CliOptions::command();
let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-section-a")
.unwrap()
};
assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));
let should_be_in_section_b = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_b")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-section-b")
.unwrap()
};
assert_eq!(should_be_in_section_b.get_help_heading(), Some("HEADING B"));
let should_be_in_default_section = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_default_section")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-default-section")
.unwrap()
};
assert_eq!(should_be_in_default_section.get_help_heading(), None);
let sub_a_two = cmd.find_subcommand("sub-a-two").unwrap();
let should_be_in_sub_a = if cfg!(feature = "unstable-v4") {
sub_a_two
.get_arguments()
.find(|a| a.get_id() == "should_be_in_sub_a")
.unwrap()
} else {
sub_a_two
.get_arguments()
.find(|a| a.get_id() == "should-be-in-sub-a")
.unwrap()
};
assert_eq!(should_be_in_sub_a.get_help_heading(), Some("SUB A"));
let sub_b_one = cmd.find_subcommand("sub-b-one").unwrap();
let should_be_in_sub_b = if cfg!(feature = "unstable-v4") {
sub_b_one
.get_arguments()
.find(|a| a.get_id() == "should_be_in_sub_b")
.unwrap()
} else {
sub_b_one
.get_arguments()
.find(|a| a.get_id() == "should-be-in-sub-b")
.unwrap()
};
assert_eq!(should_be_in_sub_b.get_help_heading(), Some("SUB B"));
let sub_c = cmd.find_subcommand("sub-c").unwrap();
let sub_c_one = sub_c.find_subcommand("sub-c-one").unwrap();
let should_be_in_sub_c = if cfg!(feature = "unstable-v4") {
sub_c_one
.get_arguments()
.find(|a| a.get_id() == "should_be_in_sub_c")
.unwrap()
} else {
sub_c_one
.get_arguments()
.find(|a| a.get_id() == "should-be-in-sub-c")
.unwrap()
};
assert_eq!(should_be_in_sub_c.get_help_heading(), Some("SUB C"));
}
#[test]
fn flatten_field_with_help_heading() {
#[derive(Debug, Clone, Parser)]
struct CliOptions {
#[clap(flatten)]
#[clap(next_help_heading = "HEADING A")]
options_a: OptionsA,
}
#[derive(Debug, Clone, Args)]
struct OptionsA {
#[clap(long)]
should_be_in_section_a: u32,
}
let cmd = CliOptions::command();
let should_be_in_section_a = if cfg!(feature = "unstable-v4") {
cmd.get_arguments()
.find(|a| a.get_id() == "should_be_in_section_a")
.unwrap()
} else {
cmd.get_arguments()
.find(|a| a.get_id() == "should-be-in-section-a")
.unwrap()
};
assert_eq!(should_be_in_section_a.get_help_heading(), Some("HEADING A"));
}
// The challenge with this test is creating an error situation not caught by `clap`'s error checking
// but by the code that `clap_derive` generates.
//
// Ultimately, the easiest way to confirm is to put a debug statement in the desired error path.
#[test]
fn derive_generated_error_has_full_context() {
#[derive(Debug, Parser)]
#[clap(subcommand_negates_reqs = true)]
struct Opts {
#[clap(long)]
req_str: String,
#[clap(subcommand)]
cmd: Option<SubCommands>,
}
#[derive(Debug, Parser)]
enum SubCommands {
Sub {
#[clap(short, long, action = clap::ArgAction::Count)]
verbose: u8,
},
}
let result = Opts::try_parse_from(&["test", "sub"]);
assert!(
result.is_err(),
"`SubcommandsNegateReqs` with non-optional `req_str` should fail: {:?}",
result.unwrap()
);
if cfg!(feature = "unstable-v4") {
let expected = r#"error: The following required argument was not provided: req_str
USAGE:
clap --req-str <REQ_STR>
clap <SUBCOMMAND>
For more information try --help
"#;
assert_eq!(result.unwrap_err().to_string(), expected);
} else {
let expected = r#"error: The following required argument was not provided: req-str
USAGE:
clap --req-str <REQ_STR>
clap <SUBCOMMAND>
For more information try --help
"#;
assert_eq!(result.unwrap_err().to_string(), expected);
}
}
#[test]
fn derive_order_next_order() {
static HELP: &str = "test 1.2
USAGE:
test [OPTIONS]
OPTIONS:
--flag-b first flag
--option-b <OPTION_B> first option
-h, --help Print help information
-V, --version Print version information
--flag-a second flag
--option-a <OPTION_A> second option
";
#[derive(Parser, Debug)]
#[clap(name = "test", version = "1.2")]
#[clap(setting = AppSettings::DeriveDisplayOrder)]
struct Args {
#[clap(flatten)]
a: A,
#[clap(flatten)]
b: B,
}
#[derive(Args, Debug)]
#[clap(next_display_order = 10000)]
struct A {
/// second flag
#[clap(long)]
flag_a: bool,
/// second option
#[clap(long)]
option_a: Option<String>,
}
#[derive(Args, Debug)]
#[clap(next_display_order = 10)]
struct B {
/// first flag
#[clap(long)]
flag_b: bool,
/// first option
#[clap(long)]
option_b: Option<String>,
}
use clap::CommandFactory;
let mut cmd = Args::command();
let mut buffer: Vec<u8> = Default::default();
cmd.write_help(&mut buffer).unwrap();
let help = String::from_utf8(buffer).unwrap();
snapbox::assert_eq(HELP, help);
}
#[test]
fn derive_order_next_order_flatten() {
static HELP: &str = "test 1.2
USAGE:
test [OPTIONS]
OPTIONS:
--flag-b first flag
--option-b <OPTION_B> first option
-h, --help Print help information
-V, --version Print version information
--flag-a second flag
--option-a <OPTION_A> second option
";
#[derive(Parser, Debug)]
#[clap(setting = AppSettings::DeriveDisplayOrder)]
#[clap(name = "test", version = "1.2")]
struct Args {
#[clap(flatten)]
#[clap(next_display_order = 10000)]
a: A,
#[clap(flatten)]
#[clap(next_display_order = 10)]
b: B,
}
#[derive(Args, Debug)]
struct A {
/// second flag
#[clap(long)]
flag_a: bool,
/// second option
#[clap(long)]
option_a: Option<String>,
}
#[derive(Args, Debug)]
struct B {
/// first flag
#[clap(long)]
flag_b: bool,
/// first option
#[clap(long)]
option_b: Option<String>,
}
use clap::CommandFactory;
let mut cmd = Args::command();
let mut buffer: Vec<u8> = Default::default();
cmd.write_help(&mut buffer).unwrap();
let help = String::from_utf8(buffer).unwrap();
snapbox::assert_eq(HELP, help);
}
#[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 <OPTION_A> first option
--option-b <OPTION_B> second option
-V, --version Print version information
";
#[derive(Parser, Debug)]
#[clap(name = "test", version = "1.2")]
#[clap(setting = AppSettings::DeriveDisplayOrder)]
#[clap(next_display_order = None)]
struct Args {
#[clap(flatten)]
a: A,
#[clap(flatten)]
b: B,
}
#[derive(Args, Debug)]
struct A {
/// first flag
#[clap(long)]
flag_a: bool,
/// first option
#[clap(long)]
option_a: Option<String>,
}
#[derive(Args, Debug)]
struct B {
/// second flag
#[clap(long)]
flag_b: bool,
/// second option
#[clap(long)]
option_b: Option<String>,
}
use clap::CommandFactory;
let mut cmd = Args::command();
let mut buffer: Vec<u8> = Default::default();
cmd.write_help(&mut buffer).unwrap();
let help = String::from_utf8(buffer).unwrap();
snapbox::assert_eq(HELP, help);
}
#[test]
#[cfg(feature = "unstable-v4")]
fn derive_possible_value_help() {
static HELP: &str = "clap
Application help
USAGE:
clap <ARG>
ARGS:
<ARG>
Argument help
Possible values:
- foo: Foo help
- bar: Bar help
OPTIONS:
-h, --help
Print help information
";
/// Application help
#[derive(Parser, PartialEq, Debug)]
struct Args {
/// Argument help
#[clap(value_enum)]
arg: ArgChoice,
}
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
/// Foo help
Foo,
/// Bar help
Bar,
}
use clap::CommandFactory;
let mut cmd = Args::command();
let mut buffer: Vec<u8> = Default::default();
cmd.write_long_help(&mut buffer).unwrap();
let help = String::from_utf8(buffer).unwrap();
snapbox::assert_eq(HELP, help);
}

View file

@ -1,130 +0,0 @@
// https://github.com/TeXitoi/structopt/issues/{NUMBER}
use crate::utils;
use clap::{ArgGroup, Args, Parser, Subcommand};
#[test]
fn issue_151_groups_within_subcommands() {
#[derive(Args, Debug)]
#[clap(group = ArgGroup::new("verb").required(true).multiple(true))]
struct Opt {
#[clap(long, group = "verb")]
foo: Option<String>,
#[clap(long, group = "verb")]
bar: Option<String>,
}
#[derive(Debug, Parser)]
struct Cli {
#[clap(flatten)]
a: Opt,
}
assert!(Cli::try_parse_from(&["test"]).is_err());
assert!(Cli::try_parse_from(&["test", "--foo=v1"]).is_ok());
assert!(Cli::try_parse_from(&["test", "--bar=v2"]).is_ok());
assert!(Cli::try_parse_from(&["test", "--zebra=v3"]).is_err());
assert!(Cli::try_parse_from(&["test", "--foo=v1", "--bar=v2"]).is_ok());
}
#[test]
fn issue_289() {
#[derive(Parser)]
#[clap(infer_subcommands = true)]
enum Args {
SomeCommand {
#[clap(subcommand)]
sub: SubSubCommand,
},
AnotherCommand,
}
#[derive(Subcommand)]
#[clap(infer_subcommands = true)]
enum SubSubCommand {
TestCommand,
}
assert!(Args::try_parse_from(&["test", "some-command", "test-command"]).is_ok());
assert!(Args::try_parse_from(&["test", "some", "test-command"]).is_ok());
assert!(Args::try_parse_from(&["test", "some-command", "test"]).is_ok());
assert!(Args::try_parse_from(&["test", "some", "test"]).is_ok());
}
#[test]
fn issue_324() {
fn my_version() -> &'static str {
"MY_VERSION"
}
#[derive(Parser)]
#[clap(version = my_version())]
struct Opt {
#[clap(subcommand)]
_cmd: SubCommand,
}
#[derive(Subcommand)]
enum SubCommand {
Start,
}
let help = utils::get_long_help::<Opt>();
assert!(help.contains("MY_VERSION"));
}
#[test]
fn issue_418() {
#[derive(Debug, Parser)]
struct Opts {
#[clap(subcommand)]
/// The command to run
command: Command,
}
#[derive(Debug, Subcommand)]
enum Command {
/// Reticulate the splines
#[clap(visible_alias = "ret")]
Reticulate {
/// How many splines
num_splines: u8,
},
/// Frobnicate the rest
#[clap(visible_alias = "frob")]
Frobnicate,
}
let help = utils::get_long_help::<Opts>();
assert!(help.contains("Reticulate the splines [aliases: ret]"));
}
#[test]
fn issue_490() {
use clap::Parser;
use std::iter::FromIterator;
use std::str::FromStr;
struct U16ish;
impl FromStr for U16ish {
type Err = ();
fn from_str(_: &str) -> Result<Self, Self::Err> {
unimplemented!()
}
}
impl<'a> FromIterator<&'a U16ish> for Vec<u16> {
fn from_iter<T: IntoIterator<Item = &'a U16ish>>(_: T) -> Self {
unimplemented!()
}
}
#[derive(Parser, Debug)]
struct Opt {
opt_vec: Vec<u16>,
#[clap(long)]
opt_opt_vec: Option<Vec<u16>>,
}
// Assert that it compiles
}

View file

@ -1,53 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use clap::Parser;
// Tests that clap_derive properly detects an `Option` field
// that results from a macro expansion
#[test]
fn use_option() {
macro_rules! expand_ty {
($name:ident: $ty:ty) => {
#[derive(Parser)]
struct Outer {
#[clap(short, long)]
#[allow(dead_code)]
$name: $ty,
}
};
}
expand_ty!(my_field: Option<String>);
}
#[test]
fn issue_447() {
macro_rules! Command {
( $name:ident, [
#[$meta:meta] $var:ident($inner:ty)
] ) => {
#[derive(Debug, PartialEq, clap::Parser)]
enum $name {
#[$meta]
$var($inner),
}
};
}
Command! {GitCmd, [
#[clap(external_subcommand)]
Ext(Vec<String>)
]}
}

View file

@ -1,33 +0,0 @@
#![cfg(feature = "derive")]
#![cfg(feature = "unstable-v4")]
mod app_name;
mod arguments;
mod author_version_about;
mod basic;
mod boxed;
mod custom_string_parsers;
mod default_value;
mod deny_warnings;
mod doc_comments_help;
mod explicit_name_no_renaming;
mod flags;
mod flatten;
mod generic;
mod help;
mod issues;
mod macros;
mod naming;
mod nested_subcommands;
mod non_literal_attributes;
mod options;
mod privacy;
mod raw_bool_literal;
mod raw_idents;
mod rename_all_env;
mod skip;
mod subcommands;
mod type_alias_regressions;
mod utf8;
mod utils;
mod value_enum;

View file

@ -1,354 +0,0 @@
use clap::Parser;
#[test]
fn test_standalone_long_generates_kebab_case() {
#[derive(Parser, Debug, PartialEq)]
#[allow(non_snake_case)]
struct Opt {
#[clap(long)]
FOO_OPTION: bool,
}
assert_eq!(
Opt { FOO_OPTION: true },
Opt::try_parse_from(&["test", "--foo-option"]).unwrap()
);
}
#[test]
fn test_custom_long_overwrites_default_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(long = "foo")]
foo_option: bool,
}
assert_eq!(
Opt { foo_option: true },
Opt::try_parse_from(&["test", "--foo"]).unwrap()
);
}
#[test]
fn test_standalone_long_uses_previous_defined_custom_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(name = "foo", long)]
foo_option: bool,
}
assert_eq!(
Opt { foo_option: true },
Opt::try_parse_from(&["test", "--foo"]).unwrap()
);
}
#[test]
fn test_standalone_long_ignores_afterwards_defined_custom_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(long, name = "foo")]
foo_option: bool,
}
assert_eq!(
Opt { foo_option: true },
Opt::try_parse_from(&["test", "--foo-option"]).unwrap()
);
}
#[test]
fn test_standalone_short_generates_kebab_case() {
#[derive(Parser, Debug, PartialEq)]
#[allow(non_snake_case)]
struct Opt {
#[clap(short)]
FOO_OPTION: bool,
}
assert_eq!(
Opt { FOO_OPTION: true },
Opt::try_parse_from(&["test", "-f"]).unwrap()
);
}
#[test]
fn test_custom_short_overwrites_default_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(short = 'o')]
foo_option: bool,
}
assert_eq!(
Opt { foo_option: true },
Opt::try_parse_from(&["test", "-o"]).unwrap()
);
}
#[test]
fn test_standalone_short_uses_previous_defined_custom_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(name = "option", short)]
foo_option: bool,
}
assert_eq!(
Opt { foo_option: true },
Opt::try_parse_from(&["test", "-o"]).unwrap()
);
}
#[test]
fn test_standalone_short_ignores_afterwards_defined_custom_name() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(short, name = "option")]
foo_option: bool,
}
assert_eq!(
Opt { foo_option: true },
Opt::try_parse_from(&["test", "-f"]).unwrap()
);
}
#[test]
fn test_standalone_long_uses_previous_defined_casing() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(rename_all = "screaming_snake", long)]
foo_option: bool,
}
assert_eq!(
Opt { foo_option: true },
Opt::try_parse_from(&["test", "--FOO_OPTION"]).unwrap()
);
}
#[test]
fn test_standalone_short_uses_previous_defined_casing() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(rename_all = "screaming_snake", short)]
foo_option: bool,
}
assert_eq!(
Opt { foo_option: true },
Opt::try_parse_from(&["test", "-F"]).unwrap()
);
}
#[test]
fn test_standalone_long_works_with_verbatim_casing() {
#[derive(Parser, Debug, PartialEq)]
#[allow(non_snake_case)]
struct Opt {
#[clap(rename_all = "verbatim", long)]
_fOO_oPtiON: bool,
}
assert_eq!(
Opt { _fOO_oPtiON: true },
Opt::try_parse_from(&["test", "--_fOO_oPtiON"]).unwrap()
);
}
#[test]
fn test_standalone_short_works_with_verbatim_casing() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(rename_all = "verbatim", short)]
_foo: bool,
}
assert_eq!(
Opt { _foo: true },
Opt::try_parse_from(&["test", "-_"]).unwrap()
);
}
#[test]
fn test_rename_all_is_propagated_from_struct_to_fields() {
#[derive(Parser, Debug, PartialEq)]
#[clap(rename_all = "screaming_snake")]
struct Opt {
#[clap(long)]
foo: bool,
}
assert_eq!(
Opt { foo: true },
Opt::try_parse_from(&["test", "--FOO"]).unwrap()
);
}
#[test]
fn test_rename_all_is_not_propagated_from_struct_into_flattened() {
#[derive(Parser, Debug, PartialEq)]
#[clap(rename_all = "screaming_snake")]
struct Opt {
#[clap(flatten)]
foo: Foo,
}
#[derive(Parser, Debug, PartialEq)]
struct Foo {
#[clap(long)]
foo: bool,
}
assert_eq!(
Opt {
foo: Foo { foo: true }
},
Opt::try_parse_from(&["test", "--foo"]).unwrap()
);
}
#[test]
fn test_lower_is_renamed() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(rename_all = "lower", long)]
foo_option: bool,
}
assert_eq!(
Opt { foo_option: true },
Opt::try_parse_from(&["test", "--foooption"]).unwrap()
);
}
#[test]
fn test_upper_is_renamed() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(rename_all = "upper", long)]
foo_option: bool,
}
assert_eq!(
Opt { foo_option: true },
Opt::try_parse_from(&["test", "--FOOOPTION"]).unwrap()
);
}
#[test]
fn test_single_word_enum_variant_is_default_renamed_into_kebab_case() {
#[derive(Parser, Debug, PartialEq)]
enum Opt {
Command { foo: u32 },
}
assert_eq!(
Opt::Command { foo: 0 },
Opt::try_parse_from(&["test", "command", "0"]).unwrap()
);
}
#[test]
fn test_multi_word_enum_variant_is_renamed() {
#[derive(Parser, Debug, PartialEq)]
enum Opt {
FirstCommand { foo: u32 },
}
assert_eq!(
Opt::FirstCommand { foo: 0 },
Opt::try_parse_from(&["test", "first-command", "0"]).unwrap()
);
}
#[test]
fn test_rename_all_is_not_propagated_from_struct_into_subcommand() {
#[derive(Parser, Debug, PartialEq)]
#[clap(rename_all = "screaming_snake")]
struct Opt {
#[clap(subcommand)]
foo: Foo,
}
#[derive(Parser, Debug, PartialEq)]
enum Foo {
Command {
#[clap(long)]
foo: bool,
},
}
assert_eq!(
Opt {
foo: Foo::Command { foo: true }
},
Opt::try_parse_from(&["test", "command", "--foo"]).unwrap()
);
}
#[test]
fn test_rename_all_is_propagated_from_enum_to_variants() {
#[derive(Parser, Debug, PartialEq)]
#[clap(rename_all = "screaming_snake")]
enum Opt {
FirstVariant,
SecondVariant {
#[clap(long)]
foo: String,
},
}
assert_eq!(
Opt::FirstVariant,
Opt::try_parse_from(&["test", "FIRST_VARIANT"]).unwrap()
);
}
#[test]
fn test_rename_all_is_propagated_from_enum_to_variant_fields() {
#[derive(Parser, Debug, PartialEq)]
#[clap(rename_all = "screaming_snake")]
enum Opt {
FirstVariant,
SecondVariant {
#[clap(long)]
foo: String,
},
}
assert_eq!(
Opt::SecondVariant {
foo: "value".into()
},
Opt::try_parse_from(&["test", "SECOND_VARIANT", "--FOO", "value"]).unwrap()
);
}
#[test]
fn test_rename_all_is_propagation_can_be_overridden() {
#[derive(Parser, Debug, PartialEq)]
#[clap(rename_all = "screaming_snake")]
enum Opt {
#[clap(rename_all = "kebab_case")]
FirstVariant {
#[clap(long)]
foo_option: bool,
},
SecondVariant {
#[clap(rename_all = "kebab_case", long)]
foo_option: bool,
},
}
assert_eq!(
Opt::FirstVariant { foo_option: true },
Opt::try_parse_from(&["test", "first-variant", "--foo-option"]).unwrap()
);
assert_eq!(
Opt::SecondVariant { foo_option: true },
Opt::try_parse_from(&["test", "SECOND_VARIANT", "--foo-option"]).unwrap()
);
}

View file

@ -1,194 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use clap::{Parser, Subcommand};
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long)]
force: bool,
#[clap(short, long, action = clap::ArgAction::Count)]
verbose: u8,
#[clap(subcommand)]
cmd: Sub,
}
#[derive(Subcommand, PartialEq, Debug)]
enum Sub {
Fetch {},
Add {},
}
#[derive(Parser, PartialEq, Debug)]
struct Opt2 {
#[clap(short, long)]
force: bool,
#[clap(short, long, action = clap::ArgAction::Count)]
verbose: u8,
#[clap(subcommand)]
cmd: Option<Sub>,
}
#[test]
fn test_no_cmd() {
let result = Opt::try_parse_from(&["test"]);
assert!(result.is_err());
assert_eq!(
Opt2 {
force: false,
verbose: 0,
cmd: None
},
Opt2::try_parse_from(&["test"]).unwrap()
);
}
#[test]
fn test_fetch() {
assert_eq!(
Opt {
force: false,
verbose: 3,
cmd: Sub::Fetch {}
},
Opt::try_parse_from(&["test", "-vvv", "fetch"]).unwrap()
);
assert_eq!(
Opt {
force: true,
verbose: 0,
cmd: Sub::Fetch {}
},
Opt::try_parse_from(&["test", "--force", "fetch"]).unwrap()
);
}
#[test]
fn test_add() {
assert_eq!(
Opt {
force: false,
verbose: 0,
cmd: Sub::Add {}
},
Opt::try_parse_from(&["test", "add"]).unwrap()
);
assert_eq!(
Opt {
force: false,
verbose: 2,
cmd: Sub::Add {}
},
Opt::try_parse_from(&["test", "-vv", "add"]).unwrap()
);
}
#[test]
fn test_badinput() {
let result = Opt::try_parse_from(&["test", "badcmd"]);
assert!(result.is_err());
let result = Opt::try_parse_from(&["test", "add", "--verbose"]);
assert!(result.is_err());
let result = Opt::try_parse_from(&["test", "--badopt", "add"]);
assert!(result.is_err());
let result = Opt::try_parse_from(&["test", "add", "--badopt"]);
assert!(result.is_err());
}
#[derive(Parser, PartialEq, Debug)]
struct Opt3 {
#[clap(short, long)]
all: bool,
#[clap(subcommand)]
cmd: Sub2,
}
#[derive(Subcommand, PartialEq, Debug)]
enum Sub2 {
Foo {
file: String,
#[clap(subcommand)]
cmd: Sub3,
},
Bar {},
}
#[derive(Subcommand, PartialEq, Debug)]
enum Sub3 {
Baz {},
Quux {},
}
#[test]
fn test_subsubcommand() {
assert_eq!(
Opt3 {
all: true,
cmd: Sub2::Foo {
file: "lib.rs".to_string(),
cmd: Sub3::Quux {}
}
},
Opt3::try_parse_from(&["test", "--all", "foo", "lib.rs", "quux"]).unwrap()
);
}
#[derive(Parser, PartialEq, Debug)]
enum SubSubCmdWithOption {
Remote {
#[clap(subcommand)]
cmd: Option<Remote>,
},
Stash {
#[clap(subcommand)]
cmd: Stash,
},
}
#[derive(Subcommand, PartialEq, Debug)]
enum Remote {
Add { name: String, url: String },
Remove { name: String },
}
#[derive(Subcommand, PartialEq, Debug)]
enum Stash {
Save,
Pop,
}
#[test]
fn sub_sub_cmd_with_option() {
fn make(args: &[&str]) -> Option<SubSubCmdWithOption> {
SubSubCmdWithOption::try_parse_from(args).ok()
}
assert_eq!(
Some(SubSubCmdWithOption::Remote { cmd: None }),
make(&["", "remote"])
);
assert_eq!(
Some(SubSubCmdWithOption::Remote {
cmd: Some(Remote::Add {
name: "origin".into(),
url: "http".into()
})
}),
make(&["", "remote", "add", "origin", "http"])
);
assert_eq!(
Some(SubSubCmdWithOption::Stash { cmd: Stash::Save }),
make(&["", "stash", "save"])
);
assert_eq!(None, make(&["", "stash"]));
}

View file

@ -1,156 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use clap::{ErrorKind, Parser};
use std::num::ParseIntError;
pub const DISPLAY_ORDER: usize = 2;
// Check if the global settings compile
#[derive(Parser, Debug, PartialEq, Eq)]
#[clap(allow_hyphen_values = true)]
struct Opt {
#[clap(
long = "x",
display_order = DISPLAY_ORDER,
next_line_help = true,
default_value = "0",
require_equals = true,
)]
x: i32,
#[clap(short = 'l', long = "level", aliases = &["set-level", "lvl"])]
level: String,
#[clap(long("values"))]
values: Vec<i32>,
#[clap(name = "FILE", requires_if("FILE", "values"))]
files: Vec<String>,
}
#[test]
fn test_slice() {
assert_eq!(
Opt {
x: 0,
level: "1".to_string(),
files: Vec::new(),
values: vec![],
},
Opt::try_parse_from(&["test", "-l", "1"]).unwrap()
);
assert_eq!(
Opt {
x: 0,
level: "1".to_string(),
files: Vec::new(),
values: vec![],
},
Opt::try_parse_from(&["test", "--level", "1"]).unwrap()
);
assert_eq!(
Opt {
x: 0,
level: "1".to_string(),
files: Vec::new(),
values: vec![],
},
Opt::try_parse_from(&["test", "--set-level", "1"]).unwrap()
);
assert_eq!(
Opt {
x: 0,
level: "1".to_string(),
files: Vec::new(),
values: vec![],
},
Opt::try_parse_from(&["test", "--lvl", "1"]).unwrap()
);
}
#[test]
fn test_multi_args() {
assert_eq!(
Opt {
x: 0,
level: "1".to_string(),
files: vec!["file".to_string()],
values: vec![],
},
Opt::try_parse_from(&["test", "-l", "1", "file"]).unwrap()
);
assert_eq!(
Opt {
x: 0,
level: "1".to_string(),
files: vec!["FILE".to_string()],
values: vec![1],
},
Opt::try_parse_from(&["test", "-l", "1", "--values", "1", "--", "FILE"]).unwrap()
);
}
#[test]
fn test_multi_args_fail() {
let result = Opt::try_parse_from(&["test", "-l", "1", "--", "FILE"]);
assert!(result.is_err());
}
#[test]
fn test_bool() {
assert_eq!(
Opt {
x: 1,
level: "1".to_string(),
files: vec![],
values: vec![],
},
Opt::try_parse_from(&["test", "-l", "1", "--x=1"]).unwrap()
);
let result = Opt::try_parse_from(&["test", "-l", "1", "--x", "1"]);
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), ErrorKind::NoEquals);
}
fn parse_hex(input: &str) -> Result<u64, ParseIntError> {
u64::from_str_radix(input, 16)
}
#[derive(Parser, PartialEq, Debug)]
struct HexOpt {
#[clap(short, value_parser = parse_hex)]
number: u64,
}
#[test]
fn test_parse_hex_function_path() {
assert_eq!(
HexOpt { number: 5 },
HexOpt::try_parse_from(&["test", "-n", "5"]).unwrap()
);
assert_eq!(
HexOpt {
number: 0x00ab_cdef
},
HexOpt::try_parse_from(&["test", "-n", "abcdef"]).unwrap()
);
let err = HexOpt::try_parse_from(&["test", "-n", "gg"]).unwrap_err();
assert!(
err.to_string().contains("invalid digit found in string"),
"{}",
err
);
}

View file

@ -1,520 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
#![allow(clippy::option_option)]
use crate::utils;
use clap::{Parser, Subcommand};
#[test]
fn required_option() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long)]
arg: i32,
}
assert_eq!(
Opt { arg: 42 },
Opt::try_parse_from(&["test", "-a42"]).unwrap()
);
assert_eq!(
Opt { arg: 42 },
Opt::try_parse_from(&["test", "-a", "42"]).unwrap()
);
assert_eq!(
Opt { arg: 42 },
Opt::try_parse_from(&["test", "--arg", "42"]).unwrap()
);
assert_eq!(
Opt { arg: 42 },
Opt::try_parse_from(&["test", "--arg", "24", "--arg", "42"]).unwrap()
);
assert!(Opt::try_parse_from(&["test"]).is_err());
}
#[test]
fn option_with_default() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, default_value = "42")]
arg: i32,
}
assert_eq!(
Opt { arg: 24 },
Opt::try_parse_from(&["test", "-a24"]).unwrap()
);
assert_eq!(
Opt { arg: 42 },
Opt::try_parse_from(&["test", "-a", "24", "-a", "42"]).unwrap()
);
assert_eq!(Opt { arg: 42 }, Opt::try_parse_from(&["test"]).unwrap());
}
#[test]
fn option_with_raw_default() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, default_value = "42")]
arg: i32,
}
assert_eq!(
Opt { arg: 24 },
Opt::try_parse_from(&["test", "-a24"]).unwrap()
);
assert_eq!(
Opt { arg: 42 },
Opt::try_parse_from(&["test", "-a", "24", "-a", "42"]).unwrap()
);
assert_eq!(Opt { arg: 42 }, Opt::try_parse_from(&["test"]).unwrap());
}
#[test]
fn option_from_str() {
#[derive(Clone, Debug, PartialEq)]
struct A;
impl std::str::FromStr for A {
type Err = std::convert::Infallible;
fn from_str(_: &str) -> Result<A, Self::Err> {
Ok(A)
}
}
#[derive(Debug, Parser, PartialEq)]
struct Opt {
a: Option<A>,
}
assert_eq!(Opt { a: None }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { a: Some(A) },
Opt::try_parse_from(&["test", "foo"]).unwrap()
);
}
#[test]
fn vec_from_str() {
#[derive(Clone, Debug, PartialEq)]
struct A;
impl std::str::FromStr for A {
type Err = std::convert::Infallible;
fn from_str(_: &str) -> Result<A, Self::Err> {
Ok(A)
}
}
#[derive(Debug, Parser, PartialEq)]
struct Opt {
a: Vec<A>,
}
assert_eq!(
Opt { a: Vec::new() },
Opt::try_parse_from(&["test"]).unwrap()
);
assert_eq!(
Opt { a: vec![A] },
Opt::try_parse_from(&["test", "foo"]).unwrap()
);
}
#[test]
fn option_vec_from_str() {
#[derive(Clone, Debug, PartialEq)]
struct A;
impl std::str::FromStr for A {
type Err = std::convert::Infallible;
fn from_str(_: &str) -> Result<A, Self::Err> {
Ok(A)
}
}
#[derive(Debug, Parser, PartialEq)]
struct Opt {
#[clap(short)]
a: Option<Vec<A>>,
}
assert_eq!(Opt { a: None }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { a: Some(vec![A]) },
Opt::try_parse_from(&["test", "-a", "foo"]).unwrap()
);
}
#[test]
fn option_type_is_optional() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short)]
arg: Option<i32>,
}
assert_eq!(
Opt { arg: Some(42) },
Opt::try_parse_from(&["test", "-a42"]).unwrap()
);
assert_eq!(
Opt { arg: Some(42) },
Opt::try_parse_from(&["test", "-a", "24", "-a", "42"]).unwrap()
);
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
}
#[test]
fn required_with_option_type() {
#[derive(Debug, PartialEq, Eq, Parser)]
#[clap(subcommand_negates_reqs = true)]
struct Opt {
#[clap(required = true)]
req_str: Option<String>,
#[clap(subcommand)]
cmd: Option<SubCommands>,
}
#[derive(Debug, PartialEq, Eq, Subcommand)]
enum SubCommands {
ExSub {
#[clap(short, long, action = clap::ArgAction::Count)]
verbose: u8,
},
}
assert_eq!(
Opt {
req_str: Some(("arg").into()),
cmd: None,
},
Opt::try_parse_from(&["test", "arg"]).unwrap()
);
assert_eq!(
Opt {
req_str: None,
cmd: Some(SubCommands::ExSub { verbose: 1 }),
},
Opt::try_parse_from(&["test", "ex-sub", "-v"]).unwrap()
);
assert!(Opt::try_parse_from(&["test"]).is_err());
}
#[test]
fn ignore_qualified_option_type() {
fn parser(s: &str) -> Result<Option<String>, std::convert::Infallible> {
Ok(Some(s.to_string()))
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser = parser)]
arg: ::std::option::Option<String>,
}
assert_eq!(
Opt {
arg: Some("success".into())
},
Opt::try_parse_from(&["test", "success"]).unwrap()
);
}
#[test]
fn option_option_type_is_optional_value() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short)]
#[allow(clippy::option_option)]
arg: Option<Option<i32>>,
}
assert_eq!(
Opt {
arg: Some(Some(42))
},
Opt::try_parse_from(&["test", "-a42"]).unwrap()
);
assert_eq!(
Opt { arg: Some(None) },
Opt::try_parse_from(&["test", "-a"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(Some(42))
},
Opt::try_parse_from(&["test", "-a", "24", "-a", "42"]).unwrap()
);
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
}
#[test]
fn option_option_type_help() {
#[derive(Parser, Debug)]
struct Opt {
#[clap(long, value_name = "val")]
arg: Option<Option<i32>>,
}
let help = utils::get_help::<Opt>();
assert!(help.contains("--arg [<val>]"));
assert!(!help.contains("--arg [<val>]..."));
}
#[test]
fn two_option_option_types() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short)]
arg: Option<Option<i32>>,
#[clap(long)]
field: Option<Option<String>>,
}
assert_eq!(
Opt {
arg: Some(Some(42)),
field: Some(Some("f".into()))
},
Opt::try_parse_from(&["test", "-a42", "--field", "f"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(Some(42)),
field: Some(None)
},
Opt::try_parse_from(&["test", "-a42", "--field"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(None),
field: Some(None)
},
Opt::try_parse_from(&["test", "-a", "--field"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(None),
field: Some(Some("f".into()))
},
Opt::try_parse_from(&["test", "-a", "--field", "f"]).unwrap()
);
assert_eq!(
Opt {
arg: None,
field: Some(None)
},
Opt::try_parse_from(&["test", "--field"]).unwrap()
);
assert_eq!(
Opt {
arg: None,
field: None
},
Opt::try_parse_from(&["test"]).unwrap()
);
}
#[test]
fn vec_type_is_multiple_occurrences() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long)]
arg: Vec<i32>,
}
assert_eq!(
Opt { arg: vec![24] },
Opt::try_parse_from(&["test", "-a24"]).unwrap()
);
assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { arg: vec![24, 42] },
Opt::try_parse_from(&["test", "-a", "24", "-a", "42"]).unwrap()
);
}
#[test]
fn vec_type_with_required() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, required = true)]
arg: Vec<i32>,
}
assert_eq!(
Opt { arg: vec![24] },
Opt::try_parse_from(&["test", "-a24"]).unwrap()
);
assert!(Opt::try_parse_from(&["test"]).is_err());
assert_eq!(
Opt { arg: vec![24, 42] },
Opt::try_parse_from(&["test", "-a", "24", "-a", "42"]).unwrap()
);
}
#[test]
fn vec_type_with_multiple_values_only() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, multiple_values(true))]
arg: Vec<i32>,
}
assert_eq!(
Opt { arg: vec![24] },
Opt::try_parse_from(&["test", "-a24"]).unwrap()
);
assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { arg: vec![24, 42] },
Opt::try_parse_from(&["test", "-a", "24", "42"]).unwrap()
);
}
#[test]
fn ignore_qualified_vec_type() {
fn parser(s: &str) -> Result<Vec<String>, std::convert::Infallible> {
Ok(vec![s.to_string()])
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_parser = parser)]
arg: ::std::vec::Vec<String>,
}
assert_eq!(
Opt {
arg: vec!["success".into()]
},
Opt::try_parse_from(&["test", "success"]).unwrap()
);
}
#[test]
fn option_vec_type() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short)]
arg: Option<Vec<i32>>,
}
assert_eq!(
Opt { arg: Some(vec![1]) },
Opt::try_parse_from(&["test", "-a", "1"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(vec![1, 2])
},
Opt::try_parse_from(&["test", "-a", "1", "-a", "2"]).unwrap()
);
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
}
#[test]
fn option_vec_type_structopt_behavior() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, multiple_values(true), min_values(0))]
arg: Option<Vec<i32>>,
}
assert_eq!(
Opt { arg: Some(vec![1]) },
Opt::try_parse_from(&["test", "-a", "1"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(vec![1, 2])
},
Opt::try_parse_from(&["test", "-a", "1", "2"]).unwrap()
);
assert_eq!(
Opt { arg: Some(vec![]) },
Opt::try_parse_from(&["test", "-a"]).unwrap()
);
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
}
#[test]
fn two_option_vec_types() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short)]
arg: Option<Vec<i32>>,
#[clap(short)]
b: Option<Vec<i32>>,
}
assert_eq!(
Opt {
arg: Some(vec![1]),
b: None,
},
Opt::try_parse_from(&["test", "-a", "1"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(vec![1]),
b: Some(vec![1])
},
Opt::try_parse_from(&["test", "-a", "1", "-b", "1"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(vec![1, 2]),
b: Some(vec![1, 2])
},
Opt::try_parse_from(&["test", "-a", "1", "-a", "2", "-b", "1", "-b", "2"]).unwrap()
);
assert_eq!(
Opt { arg: None, b: None },
Opt::try_parse_from(&["test"]).unwrap()
);
}
#[test]
fn explicit_value_parser() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(long, value_parser = clap::value_parser!(i32))]
arg: i32,
}
assert_eq!(
Opt { arg: 42 },
Opt::try_parse_from(&["test", "--arg", "42"]).unwrap()
);
}
#[test]
fn implicit_value_parser() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(long)]
arg: i32,
}
assert_eq!(
Opt { arg: 42 },
Opt::try_parse_from(&["test", "--arg", "42"]).unwrap()
);
}

View file

@ -1,36 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
mod options {
use clap::Parser;
#[derive(Debug, Parser)]
pub struct Options {
#[clap(subcommand)]
pub subcommand: super::subcommands::SubCommand,
}
}
mod subcommands {
use clap::Subcommand;
#[derive(Debug, Subcommand)]
pub enum SubCommand {
/// foo
Foo {
/// foo
bars: String,
},
}
}

View file

@ -1,29 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use clap::Parser;
#[test]
fn raw_bool_literal() {
#[derive(Parser, Debug, PartialEq)]
#[clap(name = "raw_bool")]
struct Opt {
#[clap(raw(false))]
a: String,
#[clap(raw(true))]
b: String,
}
assert_eq!(
Opt {
a: "one".into(),
b: "--help".into()
},
Opt::try_parse_from(&["test", "one", "--", "--help"]).unwrap()
);
}

View file

@ -1,24 +0,0 @@
use clap::Parser;
#[test]
fn raw_idents() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(short, long)]
r#type: String,
}
assert_eq!(
Opt {
r#type: "long".into()
},
Opt::try_parse_from(&["test", "--type", "long"]).unwrap()
);
assert_eq!(
Opt {
r#type: "short".into()
},
Opt::try_parse_from(&["test", "-t", "short"]).unwrap()
);
}

View file

@ -1,47 +0,0 @@
#![cfg(feature = "env")]
use crate::utils;
use clap::Parser;
#[test]
fn it_works() {
#[derive(Debug, PartialEq, Parser)]
#[clap(rename_all_env = "kebab")]
struct BehaviorModel {
#[clap(env)]
be_nice: String,
}
let help = utils::get_help::<BehaviorModel>();
assert!(help.contains("[env: be-nice=]"));
}
#[test]
fn default_is_screaming() {
#[derive(Debug, PartialEq, Parser)]
struct BehaviorModel {
#[clap(env)]
be_nice: String,
}
let help = utils::get_help::<BehaviorModel>();
assert!(help.contains("[env: BE_NICE=]"));
}
#[test]
fn overridable() {
#[derive(Debug, PartialEq, Parser)]
#[clap(rename_all_env = "kebab")]
struct BehaviorModel {
#[clap(env)]
be_nice: String,
#[clap(rename_all_env = "pascal", env)]
be_aggressive: String,
}
let help = utils::get_help::<BehaviorModel>();
assert!(help.contains("[env: be-nice=]"));
assert!(help.contains("[env: BeAggressive=]"));
}

View file

@ -1,157 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use clap::Parser;
#[test]
fn skip_1() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(short)]
x: u32,
#[clap(skip)]
s: u32,
}
assert!(Opt::try_parse_from(&["test", "-x", "10", "20"]).is_err());
let mut opt = Opt::try_parse_from(&["test", "-x", "10"]).unwrap();
assert_eq!(
opt,
Opt {
x: 10,
s: 0, // default
}
);
opt.s = 42;
opt.update_from(&["test", "-x", "22"]);
assert_eq!(opt, Opt { x: 22, s: 42 });
}
#[test]
fn skip_2() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(short)]
x: u32,
#[clap(skip)]
ss: String,
#[clap(skip)]
sn: u8,
y: u32,
#[clap(skip)]
sz: u16,
t: u32,
}
assert_eq!(
Opt::try_parse_from(&["test", "-x", "10", "20", "30"]).unwrap(),
Opt {
x: 10,
ss: String::from(""),
sn: 0,
y: 20,
sz: 0,
t: 30,
}
);
}
#[test]
fn skip_enum() {
#[derive(Debug, PartialEq)]
#[allow(unused)]
enum Kind {
A,
B,
}
impl Default for Kind {
fn default() -> Self {
Kind::B
}
}
#[derive(Parser, Debug, PartialEq)]
pub struct Opt {
#[clap(long, short)]
number: u32,
#[clap(skip)]
k: Kind,
#[clap(skip)]
v: Vec<u32>,
}
assert_eq!(
Opt::try_parse_from(&["test", "-n", "10"]).unwrap(),
Opt {
number: 10,
k: Kind::B,
v: vec![],
}
);
}
#[test]
fn skip_help_doc_comments() {
#[derive(Parser, Debug, PartialEq)]
pub struct Opt {
#[clap(skip, help = "internal_stuff")]
a: u32,
#[clap(skip, long_help = "internal_stuff\ndo not touch")]
b: u32,
/// Not meant to be used by clap.
///
/// I want a default here.
#[clap(skip)]
c: u32,
#[clap(short)]
n: u32,
}
assert_eq!(
Opt::try_parse_from(&["test", "-n", "10"]).unwrap(),
Opt {
n: 10,
a: 0,
b: 0,
c: 0,
}
);
}
#[test]
fn skip_val() {
#[derive(Parser, Debug, PartialEq)]
pub struct Opt {
#[clap(long, short)]
number: u32,
#[clap(skip = "key")]
k: String,
#[clap(skip = vec![1, 2, 3])]
v: Vec<u32>,
}
assert_eq!(
Opt::try_parse_from(&["test", "-n", "10"]).unwrap(),
Opt {
number: 10,
k: "key".to_string(),
v: vec![1, 2, 3]
}
);
}

View file

@ -1,609 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use crate::utils;
use clap::{Args, Parser, Subcommand};
#[derive(Parser, PartialEq, Debug)]
enum Opt {
/// Fetch stuff from GitHub
Fetch {
#[clap(long)]
all: bool,
/// Overwrite local branches.
#[clap(short, long)]
force: bool,
repo: String,
},
Add {
#[clap(short, long)]
interactive: bool,
#[clap(short, long)]
verbose: bool,
},
}
#[test]
fn test_fetch() {
assert_eq!(
Opt::Fetch {
all: true,
force: false,
repo: "origin".to_string()
},
Opt::try_parse_from(&["test", "fetch", "--all", "origin"]).unwrap()
);
assert_eq!(
Opt::Fetch {
all: false,
force: true,
repo: "origin".to_string()
},
Opt::try_parse_from(&["test", "fetch", "-f", "origin"]).unwrap()
);
}
#[test]
fn test_add() {
assert_eq!(
Opt::Add {
interactive: false,
verbose: false
},
Opt::try_parse_from(&["test", "add"]).unwrap()
);
assert_eq!(
Opt::Add {
interactive: true,
verbose: true
},
Opt::try_parse_from(&["test", "add", "-i", "-v"]).unwrap()
);
}
#[test]
fn test_no_parse() {
let result = Opt::try_parse_from(&["test", "badcmd", "-i", "-v"]);
assert!(result.is_err());
let result = Opt::try_parse_from(&["test", "add", "--badoption"]);
assert!(result.is_err());
let result = Opt::try_parse_from(&["test"]);
assert!(result.is_err());
}
#[derive(Parser, PartialEq, Debug)]
enum Opt2 {
DoSomething { arg: String },
}
#[test]
/// This test is specifically to make sure that hyphenated subcommands get
/// processed correctly.
fn test_hyphenated_subcommands() {
assert_eq!(
Opt2::DoSomething {
arg: "blah".to_string()
},
Opt2::try_parse_from(&["test", "do-something", "blah"]).unwrap()
);
}
#[derive(Parser, PartialEq, Debug)]
enum Opt3 {
Add,
Init,
Fetch,
}
#[test]
fn test_null_commands() {
assert_eq!(Opt3::Add, Opt3::try_parse_from(&["test", "add"]).unwrap());
assert_eq!(Opt3::Init, Opt3::try_parse_from(&["test", "init"]).unwrap());
assert_eq!(
Opt3::Fetch,
Opt3::try_parse_from(&["test", "fetch"]).unwrap()
);
}
#[derive(Parser, PartialEq, Debug)]
#[clap(about = "Not shown")]
struct Add {
file: String,
}
/// Not shown
#[derive(Parser, PartialEq, Debug)]
struct Fetch {
remote: String,
}
#[derive(Parser, PartialEq, Debug)]
enum Opt4 {
// Not shown
/// Add a file
Add(Add),
Init,
/// download history from remote
Fetch(Fetch),
}
#[test]
fn test_tuple_commands() {
assert_eq!(
Opt4::Add(Add {
file: "f".to_string()
}),
Opt4::try_parse_from(&["test", "add", "f"]).unwrap()
);
assert_eq!(Opt4::Init, Opt4::try_parse_from(&["test", "init"]).unwrap());
assert_eq!(
Opt4::Fetch(Fetch {
remote: "origin".to_string()
}),
Opt4::try_parse_from(&["test", "fetch", "origin"]).unwrap()
);
let output = utils::get_long_help::<Opt4>();
assert!(output.contains("download history from remote"));
assert!(output.contains("Add a file"));
assert!(!output.contains("Not shown"));
}
#[test]
fn global_passed_down() {
#[derive(Debug, PartialEq, Parser)]
struct Opt {
#[clap(global = true, long)]
other: bool,
#[clap(subcommand)]
sub: Subcommands,
}
#[derive(Debug, PartialEq, Subcommand)]
enum Subcommands {
Add,
Global(GlobalCmd),
}
#[derive(Debug, PartialEq, Args)]
struct GlobalCmd {
#[clap(from_global)]
other: bool,
}
assert_eq!(
Opt::try_parse_from(&["test", "global"]).unwrap(),
Opt {
other: false,
sub: Subcommands::Global(GlobalCmd { other: false })
}
);
assert_eq!(
Opt::try_parse_from(&["test", "global", "--other"]).unwrap(),
Opt {
other: true,
sub: Subcommands::Global(GlobalCmd { other: true })
}
);
}
#[test]
fn external_subcommand() {
#[derive(Debug, PartialEq, Parser)]
struct Opt {
#[clap(subcommand)]
sub: Subcommands,
}
#[derive(Debug, PartialEq, Subcommand)]
enum Subcommands {
Add,
Remove,
#[clap(external_subcommand)]
Other(Vec<String>),
}
assert_eq!(
Opt::try_parse_from(&["test", "add"]).unwrap(),
Opt {
sub: Subcommands::Add
}
);
assert_eq!(
Opt::try_parse_from(&["test", "remove"]).unwrap(),
Opt {
sub: Subcommands::Remove
}
);
assert!(Opt::try_parse_from(&["test"]).is_err());
assert_eq!(
Opt::try_parse_from(&["test", "git", "status"]).unwrap(),
Opt {
sub: Subcommands::Other(vec!["git".into(), "status".into()])
}
);
}
#[test]
fn external_subcommand_os_string() {
use std::ffi::OsString;
#[derive(Debug, PartialEq, Parser)]
struct Opt {
#[clap(subcommand)]
sub: Subcommands,
}
#[derive(Debug, PartialEq, Subcommand)]
enum Subcommands {
#[clap(external_subcommand)]
Other(Vec<OsString>),
}
assert_eq!(
Opt::try_parse_from(&["test", "git", "status"]).unwrap(),
Opt {
sub: Subcommands::Other(vec!["git".into(), "status".into()])
}
);
assert!(Opt::try_parse_from(&["test"]).is_err());
}
#[test]
fn external_subcommand_optional() {
#[derive(Debug, PartialEq, Parser)]
struct Opt {
#[clap(subcommand)]
sub: Option<Subcommands>,
}
#[derive(Debug, PartialEq, Subcommand)]
enum Subcommands {
#[clap(external_subcommand)]
Other(Vec<String>),
}
assert_eq!(
Opt::try_parse_from(&["test", "git", "status"]).unwrap(),
Opt {
sub: Some(Subcommands::Other(vec!["git".into(), "status".into()]))
}
);
assert_eq!(Opt::try_parse_from(&["test"]).unwrap(), Opt { sub: None });
}
#[test]
fn enum_in_enum_subsubcommand() {
#[derive(Parser, Debug, PartialEq)]
pub enum Opt {
#[clap(alias = "l")]
List,
#[clap(subcommand, alias = "d")]
Daemon(DaemonCommand),
}
#[derive(Subcommand, Debug, PartialEq)]
pub enum DaemonCommand {
Start,
Stop,
}
let result = Opt::try_parse_from(&["test"]);
assert!(result.is_err());
let result = Opt::try_parse_from(&["test", "list"]).unwrap();
assert_eq!(Opt::List, result);
let result = Opt::try_parse_from(&["test", "l"]).unwrap();
assert_eq!(Opt::List, result);
let result = Opt::try_parse_from(&["test", "daemon"]);
assert!(result.is_err());
let result = Opt::try_parse_from(&["test", "daemon", "start"]).unwrap();
assert_eq!(Opt::Daemon(DaemonCommand::Start), result);
let result = Opt::try_parse_from(&["test", "d", "start"]).unwrap();
assert_eq!(Opt::Daemon(DaemonCommand::Start), result);
}
#[test]
fn update_subcommands() {
#[derive(Parser, PartialEq, Debug)]
enum Opt {
Command1(Command1),
Command2(Command2),
}
#[derive(Parser, PartialEq, Debug)]
struct Command1 {
arg1: i32,
arg2: i32,
}
#[derive(Parser, PartialEq, Debug)]
struct Command2 {
arg2: i32,
}
// Full subcommand update
let mut opt = Opt::Command1(Command1 { arg1: 12, arg2: 14 });
opt.try_update_from(&["test", "command1", "42", "44"])
.unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "command1", "42", "44"]).unwrap(),
opt
);
// Partial subcommand update
let mut opt = Opt::Command1(Command1 { arg1: 12, arg2: 14 });
opt.try_update_from(&["test", "command1", "42"]).unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "command1", "42", "14"]).unwrap(),
opt
);
// Change subcommand
let mut opt = Opt::Command1(Command1 { arg1: 12, arg2: 14 });
opt.try_update_from(&["test", "command2", "43"]).unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "command2", "43"]).unwrap(),
opt
);
}
#[test]
fn update_sub_subcommands() {
#[derive(Parser, PartialEq, Debug)]
enum Opt {
#[clap(subcommand)]
Child1(Child1),
#[clap(subcommand)]
Child2(Child2),
}
#[derive(Subcommand, PartialEq, Debug)]
enum Child1 {
Command1(Command1),
Command2(Command2),
}
#[derive(Subcommand, PartialEq, Debug)]
enum Child2 {
Command1(Command1),
Command2(Command2),
}
#[derive(Args, PartialEq, Debug)]
struct Command1 {
arg1: i32,
arg2: i32,
}
#[derive(Args, PartialEq, Debug)]
struct Command2 {
arg2: i32,
}
// Full subcommand update
let mut opt = Opt::Child1(Child1::Command1(Command1 { arg1: 12, arg2: 14 }));
opt.try_update_from(&["test", "child1", "command1", "42", "44"])
.unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "child1", "command1", "42", "44"]).unwrap(),
opt
);
// Partial subcommand update
let mut opt = Opt::Child1(Child1::Command1(Command1 { arg1: 12, arg2: 14 }));
opt.try_update_from(&["test", "child1", "command1", "42"])
.unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "child1", "command1", "42", "14"]).unwrap(),
opt
);
// Partial subcommand update
let mut opt = Opt::Child1(Child1::Command1(Command1 { arg1: 12, arg2: 14 }));
opt.try_update_from(&["test", "child1", "command2", "43"])
.unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "child1", "command2", "43"]).unwrap(),
opt
);
// Change subcommand
let mut opt = Opt::Child1(Child1::Command1(Command1 { arg1: 12, arg2: 14 }));
opt.try_update_from(&["test", "child2", "command2", "43"])
.unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "child2", "command2", "43"]).unwrap(),
opt
);
}
#[test]
fn update_ext_subcommand() {
#[derive(Parser, PartialEq, Debug)]
enum Opt {
Command1(Command1),
Command2(Command2),
#[clap(external_subcommand)]
Ext(Vec<String>),
}
#[derive(Args, PartialEq, Debug)]
struct Command1 {
arg1: i32,
arg2: i32,
}
#[derive(Args, PartialEq, Debug)]
struct Command2 {
arg2: i32,
}
// Full subcommand update
let mut opt = Opt::Ext(vec!["12".into(), "14".into()]);
opt.try_update_from(&["test", "ext", "42", "44"]).unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "ext", "42", "44"]).unwrap(),
opt
);
// No partial subcommand update
let mut opt = Opt::Ext(vec!["12".into(), "14".into()]);
opt.try_update_from(&["test", "ext", "42"]).unwrap();
assert_eq!(Opt::try_parse_from(&["test", "ext", "42"]).unwrap(), opt);
// Change subcommand
let mut opt = Opt::Ext(vec!["12".into(), "14".into()]);
opt.try_update_from(&["test", "command2", "43"]).unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "command2", "43"]).unwrap(),
opt
);
let mut opt = Opt::Command1(Command1 { arg1: 12, arg2: 14 });
opt.try_update_from(&["test", "ext", "42", "44"]).unwrap();
assert_eq!(
Opt::try_parse_from(&["test", "ext", "42", "44"]).unwrap(),
opt
);
}
#[test]
fn subcommand_name_not_literal() {
fn get_name() -> &'static str {
"renamed"
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(subcommand)]
subcmd: SubCmd,
}
#[derive(Subcommand, PartialEq, Debug)]
enum SubCmd {
#[clap(name = get_name())]
SubCmd1,
}
assert!(Opt::try_parse_from(&["test", "renamed"]).is_ok());
}
#[test]
fn skip_subcommand() {
#[derive(Debug, PartialEq, Parser)]
struct Opt {
#[clap(subcommand)]
sub: Subcommands,
}
#[derive(Debug, PartialEq, Subcommand)]
enum Subcommands {
Add,
Remove,
#[allow(dead_code)]
#[clap(skip)]
Skip,
}
assert_eq!(
Opt::try_parse_from(&["test", "add"]).unwrap(),
Opt {
sub: Subcommands::Add
}
);
assert_eq!(
Opt::try_parse_from(&["test", "remove"]).unwrap(),
Opt {
sub: Subcommands::Remove
}
);
let res = Opt::try_parse_from(&["test", "skip"]);
assert_eq!(res.unwrap_err().kind(), clap::ErrorKind::UnknownArgument,);
}
#[test]
#[cfg(feature = "unstable-v4")]
fn built_in_subcommand_escaped() {
#[derive(Debug, PartialEq, Parser)]
enum Command {
Install {
arg: Option<String>,
},
#[clap(external_subcommand)]
Custom(Vec<String>),
}
assert_eq!(
Command::try_parse_from(&["test", "install", "arg"]).unwrap(),
Command::Install {
arg: Some(String::from("arg"))
}
);
assert_eq!(
Command::try_parse_from(&["test", "--", "install"]).unwrap(),
Command::Custom(vec![String::from("install")])
);
assert_eq!(
Command::try_parse_from(&["test", "--", "install", "arg"]).unwrap(),
Command::Custom(vec![String::from("install"), String::from("arg")])
);
}
#[test]
#[cfg(not(feature = "unstable-v4"))]
fn built_in_subcommand_escaped() {
#[derive(Debug, PartialEq, Parser)]
enum Command {
Install {
arg: Option<String>,
},
#[clap(external_subcommand)]
Custom(Vec<String>),
}
assert_eq!(
Command::try_parse_from(&["test", "install", "arg"]).unwrap(),
Command::Install {
arg: Some(String::from("arg"))
}
);
assert_eq!(
Command::try_parse_from(&["test", "--", "install"]).unwrap(),
Command::Install { arg: None }
);
assert_eq!(
Command::try_parse_from(&["test", "--", "install", "arg"]).unwrap(),
Command::Install { arg: None }
);
}

View file

@ -1,35 +0,0 @@
//! Regression test to ensure that type aliases do not cause compilation failures.
#![allow(deprecated)]
use clap::{Parser, Subcommand, ValueEnum};
// Result type alias
#[allow(dead_code)]
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
type Option<T> = std::option::Option<T>;
#[derive(Parser)]
pub struct Opts {
another_string: String,
#[clap(subcommand)]
command: Command,
#[clap(short, long, value_enum)]
choice: ArgChoice,
}
#[derive(Subcommand, PartialEq, Debug)]
enum Command {
DoSomething { arg: Option<String> },
}
#[derive(ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
}
#[test]
fn type_alias_regressions() {
Opts::try_parse_from(["test", "value", "--choice=foo", "do-something"]).unwrap();
}

View file

@ -1,225 +0,0 @@
#![cfg(not(windows))]
use clap::{ErrorKind, Parser};
use std::ffi::OsString;
use std::os::unix::ffi::OsStringExt;
#[derive(Parser, Debug, PartialEq, Eq)]
struct Positional {
arg: String,
}
#[derive(Parser, Debug, PartialEq, Eq)]
struct Named {
#[clap(short, long)]
arg: String,
}
#[test]
fn invalid_utf8_strict_positional() {
let m = Positional::try_parse_from(vec![OsString::from(""), OsString::from_vec(vec![0xe9])]);
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}
#[test]
fn invalid_utf8_strict_option_short_space() {
let m = Named::try_parse_from(vec![
OsString::from(""),
OsString::from("-a"),
OsString::from_vec(vec![0xe9]),
]);
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}
#[test]
fn invalid_utf8_strict_option_short_equals() {
let m = Named::try_parse_from(vec![
OsString::from(""),
OsString::from_vec(vec![0x2d, 0x61, 0x3d, 0xe9]),
]);
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}
#[test]
fn invalid_utf8_strict_option_short_no_space() {
let m = Named::try_parse_from(vec![
OsString::from(""),
OsString::from_vec(vec![0x2d, 0x61, 0xe9]),
]);
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}
#[test]
fn invalid_utf8_strict_option_long_space() {
let m = Named::try_parse_from(vec![
OsString::from(""),
OsString::from("--arg"),
OsString::from_vec(vec![0xe9]),
]);
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}
#[test]
fn invalid_utf8_strict_option_long_equals() {
let m = Named::try_parse_from(vec![
OsString::from(""),
OsString::from_vec(vec![0x2d, 0x2d, 0x61, 0x72, 0x67, 0x3d, 0xe9]),
]);
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}
#[derive(Parser, Debug, PartialEq, Eq)]
struct PositionalOs {
arg: OsString,
}
#[derive(Parser, Debug, PartialEq, Eq)]
struct NamedOs {
#[clap(short, long)]
arg: OsString,
}
#[test]
fn invalid_utf8_positional() {
let r = PositionalOs::try_parse_from(vec![OsString::from(""), OsString::from_vec(vec![0xe9])]);
assert_eq!(
r.unwrap(),
PositionalOs {
arg: OsString::from_vec(vec![0xe9])
}
);
}
#[test]
fn invalid_utf8_option_short_space() {
let r = NamedOs::try_parse_from(vec![
OsString::from(""),
OsString::from("-a"),
OsString::from_vec(vec![0xe9]),
]);
assert_eq!(
r.unwrap(),
NamedOs {
arg: OsString::from_vec(vec![0xe9])
}
);
}
#[test]
fn invalid_utf8_option_short_equals() {
let r = NamedOs::try_parse_from(vec![
OsString::from(""),
OsString::from_vec(vec![0x2d, 0x61, 0x3d, 0xe9]),
]);
assert_eq!(
r.unwrap(),
NamedOs {
arg: OsString::from_vec(vec![0xe9])
}
);
}
#[test]
fn invalid_utf8_option_short_no_space() {
let r = NamedOs::try_parse_from(vec![
OsString::from(""),
OsString::from_vec(vec![0x2d, 0x61, 0xe9]),
]);
assert_eq!(
r.unwrap(),
NamedOs {
arg: OsString::from_vec(vec![0xe9])
}
);
}
#[test]
fn invalid_utf8_option_long_space() {
let r = NamedOs::try_parse_from(vec![
OsString::from(""),
OsString::from("--arg"),
OsString::from_vec(vec![0xe9]),
]);
assert_eq!(
r.unwrap(),
NamedOs {
arg: OsString::from_vec(vec![0xe9])
}
);
}
#[test]
fn invalid_utf8_option_long_equals() {
let r = NamedOs::try_parse_from(vec![
OsString::from(""),
OsString::from_vec(vec![0x2d, 0x2d, 0x61, 0x72, 0x67, 0x3d, 0xe9]),
]);
assert_eq!(
r.unwrap(),
NamedOs {
arg: OsString::from_vec(vec![0xe9])
}
);
}
#[derive(Debug, PartialEq, Parser)]
enum External {
#[clap(external_subcommand)]
Other(Vec<String>),
}
#[test]
fn refuse_invalid_utf8_subcommand_with_allow_external_subcommands() {
let m = External::try_parse_from(vec![
OsString::from(""),
OsString::from_vec(vec![0xe9]),
OsString::from("normal"),
]);
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}
#[test]
fn refuse_invalid_utf8_subcommand_args_with_allow_external_subcommands() {
let m = External::try_parse_from(vec![
OsString::from(""),
OsString::from("subcommand"),
OsString::from("normal"),
OsString::from_vec(vec![0xe9]),
OsString::from("--another_normal"),
]);
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}
#[derive(Debug, PartialEq, Parser)]
enum ExternalOs {
#[clap(external_subcommand)]
Other(Vec<OsString>),
}
#[test]
fn allow_invalid_utf8_subcommand_args_with_allow_external_subcommands() {
let m = ExternalOs::try_parse_from(vec![
OsString::from(""),
OsString::from("subcommand"),
OsString::from("normal"),
OsString::from_vec(vec![0xe9]),
OsString::from("--another_normal"),
]);
assert_eq!(
m.unwrap(),
ExternalOs::Other(vec![
OsString::from("subcommand"),
OsString::from("normal"),
OsString::from_vec(vec![0xe9]),
OsString::from("--another_normal"),
])
);
}

View file

@ -1,56 +0,0 @@
// Hi, future me (or whoever you are)!
//
// Yes, we do need this attr.
// No, the warnings cannot be fixed otherwise.
// Accept and endure. Do not touch.
#![allow(unused)]
use clap::CommandFactory;
pub fn get_help<T: CommandFactory>() -> String {
let mut output = Vec::new();
<T as CommandFactory>::command()
.write_help(&mut output)
.unwrap();
let output = String::from_utf8(output).unwrap();
eprintln!("\n%%% HELP %%%:=====\n{}\n=====\n", output);
eprintln!("\n%%% HELP (DEBUG) %%%:=====\n{:?}\n=====\n", output);
output
}
pub fn get_long_help<T: CommandFactory>() -> String {
let mut output = Vec::new();
<T as CommandFactory>::command()
.write_long_help(&mut output)
.unwrap();
let output = String::from_utf8(output).unwrap();
eprintln!("\n%%% LONG_HELP %%%:=====\n{}\n=====\n", output);
eprintln!("\n%%% LONG_HELP (DEBUG) %%%:=====\n{:?}\n=====\n", output);
output
}
pub fn get_subcommand_long_help<T: CommandFactory>(subcmd: &str) -> String {
let mut output = Vec::new();
<T as CommandFactory>::command()
.get_subcommands_mut()
.find(|s| s.get_name() == subcmd)
.unwrap()
.write_long_help(&mut output)
.unwrap();
let output = String::from_utf8(output).unwrap();
eprintln!(
"\n%%% SUBCOMMAND `{}` HELP %%%:=====\n{}\n=====\n",
subcmd, output
);
eprintln!(
"\n%%% SUBCOMMAND `{}` HELP (DEBUG) %%%:=====\n{:?}\n=====\n",
subcmd, output
);
output
}

View file

@ -1,526 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use clap::Parser;
#[test]
fn basic() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum)]
arg: ArgChoice,
}
assert_eq!(
Opt {
arg: ArgChoice::Foo
},
Opt::try_parse_from(&["", "foo"]).unwrap()
);
assert_eq!(
Opt {
arg: ArgChoice::Bar
},
Opt::try_parse_from(&["", "bar"]).unwrap()
);
assert!(Opt::try_parse_from(&["", "fOo"]).is_err());
}
#[test]
fn default_value() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
}
impl Default for ArgChoice {
fn default() -> Self {
Self::Bar
}
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, default_value_t)]
arg: ArgChoice,
}
assert_eq!(
Opt {
arg: ArgChoice::Foo
},
Opt::try_parse_from(&["", "foo"]).unwrap()
);
assert_eq!(
Opt {
arg: ArgChoice::Bar
},
Opt::try_parse_from(&["", "bar"]).unwrap()
);
assert_eq!(
Opt {
arg: ArgChoice::Bar
},
Opt::try_parse_from(&[""]).unwrap()
);
}
#[test]
fn multi_word_is_renamed_kebab() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
#[allow(non_camel_case_types)]
enum ArgChoice {
FooBar,
BAR_BAZ,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum)]
arg: ArgChoice,
}
assert_eq!(
Opt {
arg: ArgChoice::FooBar
},
Opt::try_parse_from(&["", "foo-bar"]).unwrap()
);
assert_eq!(
Opt {
arg: ArgChoice::BAR_BAZ
},
Opt::try_parse_from(&["", "bar-baz"]).unwrap()
);
assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
}
#[test]
fn variant_with_defined_casing() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
#[clap(rename_all = "screaming_snake")]
FooBar,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum)]
arg: ArgChoice,
}
assert_eq!(
Opt {
arg: ArgChoice::FooBar
},
Opt::try_parse_from(&["", "FOO_BAR"]).unwrap()
);
assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
}
#[test]
fn casing_is_propagated_from_parent() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
#[clap(rename_all = "screaming_snake")]
enum ArgChoice {
FooBar,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum)]
arg: ArgChoice,
}
assert_eq!(
Opt {
arg: ArgChoice::FooBar
},
Opt::try_parse_from(&["", "FOO_BAR"]).unwrap()
);
assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
}
#[test]
fn casing_propagation_is_overridden() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
#[clap(rename_all = "screaming_snake")]
enum ArgChoice {
#[clap(rename_all = "camel")]
FooBar,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum)]
arg: ArgChoice,
}
assert_eq!(
Opt {
arg: ArgChoice::FooBar
},
Opt::try_parse_from(&["", "fooBar"]).unwrap()
);
assert!(Opt::try_parse_from(&["", "FooBar"]).is_err());
assert!(Opt::try_parse_from(&["", "FOO_BAR"]).is_err());
}
#[test]
fn ignore_case() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, ignore_case(true))]
arg: ArgChoice,
}
assert_eq!(
Opt {
arg: ArgChoice::Foo
},
Opt::try_parse_from(&["", "foo"]).unwrap()
);
assert_eq!(
Opt {
arg: ArgChoice::Foo
},
Opt::try_parse_from(&["", "fOo"]).unwrap()
);
}
#[test]
fn ignore_case_set_to_false() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, ignore_case(false))]
arg: ArgChoice,
}
assert_eq!(
Opt {
arg: ArgChoice::Foo
},
Opt::try_parse_from(&["", "foo"]).unwrap()
);
assert!(Opt::try_parse_from(&["", "fOo"]).is_err());
}
#[test]
fn alias() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
#[clap(alias = "TOTP")]
Totp,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, ignore_case(false))]
arg: ArgChoice,
}
assert_eq!(
Opt {
arg: ArgChoice::Totp
},
Opt::try_parse_from(&["", "totp"]).unwrap()
);
assert_eq!(
Opt {
arg: ArgChoice::Totp
},
Opt::try_parse_from(&["", "TOTP"]).unwrap()
);
}
#[test]
fn multiple_alias() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
#[clap(alias = "TOTP", alias = "t")]
Totp,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, ignore_case(false))]
arg: ArgChoice,
}
assert_eq!(
Opt {
arg: ArgChoice::Totp
},
Opt::try_parse_from(&["", "totp"]).unwrap()
);
assert_eq!(
Opt {
arg: ArgChoice::Totp
},
Opt::try_parse_from(&["", "TOTP"]).unwrap()
);
assert_eq!(
Opt {
arg: ArgChoice::Totp
},
Opt::try_parse_from(&["", "t"]).unwrap()
);
}
#[test]
fn skip_variant() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
#[allow(dead_code)] // silence warning about `Baz` being unused
enum ArgChoice {
Foo,
Bar,
#[clap(skip)]
Baz,
}
assert_eq!(
<ArgChoice as clap::ValueEnum>::value_variants()
.iter()
.map(clap::ValueEnum::to_possible_value)
.map(Option::unwrap)
.collect::<Vec<_>>(),
vec![
clap::PossibleValue::new("foo"),
clap::PossibleValue::new("bar")
]
);
{
use clap::ValueEnum;
assert!(ArgChoice::from_str("foo", true).is_ok());
assert!(ArgChoice::from_str("bar", true).is_ok());
assert!(ArgChoice::from_str("baz", true).is_err());
}
}
#[test]
fn skip_non_unit_variant() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
#[allow(dead_code)] // silence warning about `Baz` being unused
enum ArgChoice {
Foo,
Bar,
#[clap(skip)]
Baz(usize),
}
assert_eq!(
<ArgChoice as clap::ValueEnum>::value_variants()
.iter()
.map(clap::ValueEnum::to_possible_value)
.map(Option::unwrap)
.collect::<Vec<_>>(),
vec![
clap::PossibleValue::new("foo"),
clap::PossibleValue::new("bar")
]
);
{
use clap::ValueEnum;
assert!(ArgChoice::from_str("foo", true).is_ok());
assert!(ArgChoice::from_str("bar", true).is_ok());
assert!(ArgChoice::from_str("baz", true).is_err());
}
}
#[test]
fn from_str_invalid() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
}
{
use clap::ValueEnum;
assert!(ArgChoice::from_str("bar", true).is_err());
}
}
#[test]
fn option_type() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum)]
arg: Option<ArgChoice>,
}
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&[""]).unwrap());
assert_eq!(
Opt {
arg: Some(ArgChoice::Foo)
},
Opt::try_parse_from(&["", "foo"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(ArgChoice::Bar)
},
Opt::try_parse_from(&["", "bar"]).unwrap()
);
assert!(Opt::try_parse_from(&["", "fOo"]).is_err());
}
#[test]
fn option_option_type() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, long)]
arg: Option<Option<ArgChoice>>,
}
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&[""]).unwrap());
assert_eq!(
Opt { arg: Some(None) },
Opt::try_parse_from(&["", "--arg"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(Some(ArgChoice::Foo))
},
Opt::try_parse_from(&["", "--arg", "foo"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(Some(ArgChoice::Bar))
},
Opt::try_parse_from(&["", "--arg", "bar"]).unwrap()
);
assert!(Opt::try_parse_from(&["", "--arg", "fOo"]).is_err());
}
#[test]
fn vec_type() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, short, long)]
arg: Vec<ArgChoice>,
}
assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&[""]).unwrap());
assert_eq!(
Opt {
arg: vec![ArgChoice::Foo]
},
Opt::try_parse_from(&["", "-a", "foo"]).unwrap()
);
assert_eq!(
Opt {
arg: vec![ArgChoice::Foo, ArgChoice::Bar]
},
Opt::try_parse_from(&["", "-a", "foo", "-a", "bar"]).unwrap()
);
assert!(Opt::try_parse_from(&["", "-a", "fOo"]).is_err());
}
#[test]
fn option_vec_type() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, short, long)]
arg: Option<Vec<ArgChoice>>,
}
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&[""]).unwrap());
assert_eq!(
Opt {
arg: Some(vec![ArgChoice::Foo])
},
Opt::try_parse_from(&["", "-a", "foo"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(vec![ArgChoice::Foo, ArgChoice::Bar])
},
Opt::try_parse_from(&["", "-a", "foo", "-a", "bar"]).unwrap()
);
assert!(Opt::try_parse_from(&["", "-a", "fOo"]).is_err());
}
#[test]
fn vec_type_default_value() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Bar,
Baz,
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(
value_enum,
short,
long,
default_value = "foo,bar",
value_delimiter = ','
)]
arg: Vec<ArgChoice>,
}
assert_eq!(
Opt {
arg: vec![ArgChoice::Foo, ArgChoice::Bar]
},
Opt::try_parse_from(&[""]).unwrap()
);
assert_eq!(
Opt {
arg: vec![ArgChoice::Foo, ArgChoice::Baz]
},
Opt::try_parse_from(&["", "-a", "foo,baz"]).unwrap()
);
}

View file

@ -27,17 +27,16 @@ struct Opt {
next_line_help = true,
default_value = "0",
require_equals = true,
value_parser
)]
x: i32,
#[clap(short = 'l', long = "level", value_parser, aliases = &["set-level", "lvl"])]
#[clap(short = 'l', long = "level", aliases = &["set-level", "lvl"])]
level: String,
#[clap(long("values"), value_parser)]
#[clap(long("values"))]
values: Vec<i32>,
#[clap(name = "FILE", value_parser, requires_if("FILE", "values"))]
#[clap(name = "FILE", requires_if("FILE", "values"))]
files: Vec<String>,
}

View file

@ -22,7 +22,7 @@ use clap::{Parser, Subcommand};
fn required_option() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, value_parser)]
#[clap(short, long)]
arg: i32,
}
assert_eq!(
@ -48,7 +48,7 @@ fn required_option() {
fn option_with_default() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, value_parser, default_value = "42")]
#[clap(short, default_value = "42")]
arg: i32,
}
assert_eq!(
@ -66,7 +66,7 @@ fn option_with_default() {
fn option_with_raw_default() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, value_parser, default_value = "42")]
#[clap(short, default_value = "42")]
arg: i32,
}
assert_eq!(
@ -95,7 +95,6 @@ fn option_from_str() {
#[derive(Debug, Parser, PartialEq)]
struct Opt {
#[clap(value_parser)]
a: Option<A>,
}
@ -121,7 +120,6 @@ fn vec_from_str() {
#[derive(Debug, Parser, PartialEq)]
struct Opt {
#[clap(value_parser)]
a: Vec<A>,
}
@ -150,7 +148,7 @@ fn option_vec_from_str() {
#[derive(Debug, Parser, PartialEq)]
struct Opt {
#[clap(short, value_parser)]
#[clap(short)]
a: Option<Vec<A>>,
}
@ -165,7 +163,7 @@ fn option_vec_from_str() {
fn option_type_is_optional() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, value_parser)]
#[clap(short)]
arg: Option<i32>,
}
assert_eq!(
@ -184,7 +182,7 @@ fn required_with_option_type() {
#[derive(Debug, PartialEq, Eq, Parser)]
#[clap(subcommand_negates_reqs = true)]
struct Opt {
#[clap(value_parser, required = true)]
#[clap(required = true)]
req_str: Option<String>,
#[clap(subcommand)]
@ -242,7 +240,7 @@ fn ignore_qualified_option_type() {
fn option_option_type_is_optional_value() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, value_parser)]
#[clap(short)]
#[allow(clippy::option_option)]
arg: Option<Option<i32>>,
}
@ -269,7 +267,7 @@ fn option_option_type_is_optional_value() {
fn option_option_type_help() {
#[derive(Parser, Debug)]
struct Opt {
#[clap(long, value_name = "val", value_parser)]
#[clap(long, value_name = "val")]
arg: Option<Option<i32>>,
}
let help = utils::get_help::<Opt>();
@ -281,10 +279,10 @@ fn option_option_type_help() {
fn two_option_option_types() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, value_parser)]
#[clap(short)]
arg: Option<Option<i32>>,
#[clap(long, value_parser)]
#[clap(long)]
field: Option<Option<String>>,
}
assert_eq!(
@ -335,7 +333,7 @@ fn two_option_option_types() {
fn vec_type_is_multiple_occurrences() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, value_parser)]
#[clap(short, long)]
arg: Vec<i32>,
}
assert_eq!(
@ -353,7 +351,7 @@ fn vec_type_is_multiple_occurrences() {
fn vec_type_with_required() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, required = true, value_parser)]
#[clap(short, long, required = true)]
arg: Vec<i32>,
}
assert_eq!(
@ -371,7 +369,7 @@ fn vec_type_with_required() {
fn vec_type_with_multiple_values_only() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, multiple_values(true), value_parser)]
#[clap(short, long, multiple_values(true))]
arg: Vec<i32>,
}
assert_eq!(
@ -409,7 +407,7 @@ fn ignore_qualified_vec_type() {
fn option_vec_type() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, value_parser)]
#[clap(short)]
arg: Option<Vec<i32>>,
}
assert_eq!(
@ -431,7 +429,7 @@ fn option_vec_type() {
fn option_vec_type_structopt_behavior() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, multiple_values(true), min_values(0), value_parser)]
#[clap(short, long, multiple_values(true), min_values(0))]
arg: Option<Vec<i32>>,
}
assert_eq!(
@ -458,10 +456,10 @@ fn option_vec_type_structopt_behavior() {
fn two_option_vec_types() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, value_parser)]
#[clap(short)]
arg: Option<Vec<i32>>,
#[clap(short, value_parser)]
#[clap(short)]
b: Option<Vec<i32>>,
}
@ -512,7 +510,7 @@ fn explicit_value_parser() {
fn implicit_value_parser() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(long, value_parser)]
#[clap(long)]
arg: i32,
}
assert_eq!(

View file

@ -30,7 +30,6 @@ mod subcommands {
/// foo
Foo {
/// foo
#[clap(value_parser)]
bars: String,
},
}

View file

@ -13,9 +13,9 @@ fn raw_bool_literal() {
#[derive(Parser, Debug, PartialEq)]
#[clap(name = "raw_bool")]
struct Opt {
#[clap(raw(false), value_parser)]
#[clap(raw(false))]
a: String,
#[clap(raw(true), value_parser)]
#[clap(raw(true))]
b: String,
}

View file

@ -4,7 +4,7 @@ use clap::Parser;
fn raw_idents() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(short, long, value_parser)]
#[clap(short, long)]
r#type: String,
}

View file

@ -9,7 +9,7 @@ fn it_works() {
#[derive(Debug, PartialEq, Parser)]
#[clap(rename_all_env = "kebab")]
struct BehaviorModel {
#[clap(env, value_parser)]
#[clap(env)]
be_nice: String,
}
@ -21,7 +21,7 @@ fn it_works() {
fn default_is_screaming() {
#[derive(Debug, PartialEq, Parser)]
struct BehaviorModel {
#[clap(env, value_parser)]
#[clap(env)]
be_nice: String,
}
@ -34,10 +34,10 @@ fn overridable() {
#[derive(Debug, PartialEq, Parser)]
#[clap(rename_all_env = "kebab")]
struct BehaviorModel {
#[clap(env, value_parser)]
#[clap(env)]
be_nice: String,
#[clap(rename_all_env = "pascal", env, value_parser)]
#[clap(rename_all_env = "pascal", env)]
be_aggressive: String,
}

View file

@ -12,7 +12,7 @@ use clap::Parser;
fn skip_1() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(short, value_parser)]
#[clap(short)]
x: u32,
#[clap(skip)]
s: u32,
@ -39,17 +39,17 @@ fn skip_1() {
fn skip_2() {
#[derive(Parser, Debug, PartialEq)]
struct Opt {
#[clap(short, value_parser)]
#[clap(short)]
x: u32,
#[clap(skip)]
ss: String,
#[clap(skip)]
sn: u8,
#[clap(value_parser)]
y: u32,
#[clap(skip)]
sz: u16,
#[clap(value_parser)]
t: u32,
}
@ -83,7 +83,7 @@ fn skip_enum() {
#[derive(Parser, Debug, PartialEq)]
pub struct Opt {
#[clap(long, short, value_parser)]
#[clap(long, short)]
number: u32,
#[clap(skip)]
k: Kind,
@ -117,7 +117,7 @@ fn skip_help_doc_comments() {
#[clap(skip)]
c: u32,
#[clap(short, value_parser)]
#[clap(short)]
n: u32,
}
@ -136,7 +136,7 @@ fn skip_help_doc_comments() {
fn skip_val() {
#[derive(Parser, Debug, PartialEq)]
pub struct Opt {
#[clap(long, short, value_parser)]
#[clap(long, short)]
number: u32,
#[clap(skip = "key")]

View file

@ -20,19 +20,19 @@ use clap::{Args, Parser, Subcommand};
enum Opt {
/// Fetch stuff from GitHub
Fetch {
#[clap(long, action)]
#[clap(long)]
all: bool,
/// Overwrite local branches.
#[clap(short, long, action)]
#[clap(short, long)]
force: bool,
#[clap(value_parser)]
repo: String,
},
Add {
#[clap(short, long, action)]
#[clap(short, long)]
interactive: bool,
#[clap(short, long, action)]
#[clap(short, long)]
verbose: bool,
},
}
@ -89,10 +89,7 @@ fn test_no_parse() {
#[derive(Parser, PartialEq, Debug)]
enum Opt2 {
DoSomething {
#[clap(value_parser)]
arg: String,
},
DoSomething { arg: String },
}
#[test]
@ -127,13 +124,11 @@ fn test_null_commands() {
#[derive(Parser, PartialEq, Debug)]
#[clap(about = "Not shown")]
struct Add {
#[clap(value_parser)]
file: String,
}
/// Not shown
#[derive(Parser, PartialEq, Debug)]
struct Fetch {
#[clap(value_parser)]
remote: String,
}
#[derive(Parser, PartialEq, Debug)]
@ -173,7 +168,7 @@ fn test_tuple_commands() {
fn global_passed_down() {
#[derive(Debug, PartialEq, Parser)]
struct Opt {
#[clap(global = true, long, action)]
#[clap(global = true, long)]
other: bool,
#[clap(subcommand)]
sub: Subcommands,
@ -187,7 +182,7 @@ fn global_passed_down() {
#[derive(Debug, PartialEq, Args)]
struct GlobalCmd {
#[clap(from_global, action)]
#[clap(from_global)]
other: bool,
}
@ -343,15 +338,13 @@ fn update_subcommands() {
#[derive(Parser, PartialEq, Debug)]
struct Command1 {
#[clap(value_parser)]
arg1: i32,
#[clap(value_parser)]
arg2: i32,
}
#[derive(Parser, PartialEq, Debug)]
struct Command2 {
#[clap(value_parser)]
arg2: i32,
}
@ -405,15 +398,13 @@ fn update_sub_subcommands() {
#[derive(Args, PartialEq, Debug)]
struct Command1 {
#[clap(value_parser)]
arg1: i32,
#[clap(value_parser)]
arg2: i32,
}
#[derive(Args, PartialEq, Debug)]
struct Command2 {
#[clap(value_parser)]
arg2: i32,
}
@ -466,15 +457,13 @@ fn update_ext_subcommand() {
#[derive(Args, PartialEq, Debug)]
struct Command1 {
#[clap(value_parser)]
arg1: i32,
#[clap(value_parser)]
arg2: i32,
}
#[derive(Args, PartialEq, Debug)]
struct Command2 {
#[clap(value_parser)]
arg2: i32,
}
@ -564,12 +553,10 @@ fn skip_subcommand() {
}
#[test]
#[cfg(feature = "unstable-v4")]
fn built_in_subcommand_escaped() {
#[derive(Debug, PartialEq, Parser)]
enum Command {
Install {
#[clap(value_parser)]
arg: Option<String>,
},
#[clap(external_subcommand)]
@ -591,32 +578,3 @@ fn built_in_subcommand_escaped() {
Command::Custom(vec![String::from("install"), String::from("arg")])
);
}
#[test]
#[cfg(not(feature = "unstable-v4"))]
fn built_in_subcommand_escaped() {
#[derive(Debug, PartialEq, Parser)]
enum Command {
Install {
#[clap(value_parser)]
arg: Option<String>,
},
#[clap(external_subcommand)]
Custom(Vec<String>),
}
assert_eq!(
Command::try_parse_from(&["test", "install", "arg"]).unwrap(),
Command::Install {
arg: Some(String::from("arg"))
}
);
assert_eq!(
Command::try_parse_from(&["test", "--", "install"]).unwrap(),
Command::Install { arg: None }
);
assert_eq!(
Command::try_parse_from(&["test", "--", "install", "arg"]).unwrap(),
Command::Install { arg: None }
);
}

View file

@ -11,20 +11,16 @@ type Option<T> = std::option::Option<T>;
#[derive(Parser)]
pub struct Opts {
#[clap(value_parser)]
another_string: String,
#[clap(subcommand)]
command: Command,
#[clap(short, long, value_enum, value_parser)]
#[clap(short, long, value_enum)]
choice: ArgChoice,
}
#[derive(Subcommand, PartialEq, Debug)]
enum Command {
DoSomething {
#[clap(value_parser)]
arg: Option<String>,
},
DoSomething { arg: Option<String> },
}
#[derive(ValueEnum, PartialEq, Debug, Clone)]

View file

@ -6,13 +6,12 @@ use std::os::unix::ffi::OsStringExt;
#[derive(Parser, Debug, PartialEq, Eq)]
struct Positional {
#[clap(value_parser)]
arg: String,
}
#[derive(Parser, Debug, PartialEq, Eq)]
struct Named {
#[clap(short, long, value_parser)]
#[clap(short, long)]
arg: String,
}
@ -77,13 +76,12 @@ fn invalid_utf8_strict_option_long_equals() {
#[derive(Parser, Debug, PartialEq, Eq)]
struct PositionalOs {
#[clap(value_parser)]
arg: OsString,
}
#[derive(Parser, Debug, PartialEq, Eq)]
struct NamedOs {
#[clap(short, long, value_parser)]
#[clap(short, long)]
arg: OsString,
}

View file

@ -19,7 +19,7 @@ fn basic() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, value_parser)]
#[clap(value_enum)]
arg: ArgChoice,
}
@ -54,7 +54,7 @@ fn default_value() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, value_parser, default_value_t)]
#[clap(value_enum, default_value_t)]
arg: ArgChoice,
}
@ -89,7 +89,7 @@ fn multi_word_is_renamed_kebab() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, value_parser)]
#[clap(value_enum)]
arg: ArgChoice,
}
@ -118,7 +118,7 @@ fn variant_with_defined_casing() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, value_parser)]
#[clap(value_enum)]
arg: ArgChoice,
}
@ -141,7 +141,7 @@ fn casing_is_propagated_from_parent() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, value_parser)]
#[clap(value_enum)]
arg: ArgChoice,
}
@ -165,7 +165,7 @@ fn casing_propagation_is_overridden() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, value_parser)]
#[clap(value_enum)]
arg: ArgChoice,
}
@ -188,7 +188,7 @@ fn ignore_case() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, value_parser, ignore_case(true))]
#[clap(value_enum, ignore_case(true))]
arg: ArgChoice,
}
@ -215,7 +215,7 @@ fn ignore_case_set_to_false() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, ignore_case(false), value_parser)]
#[clap(value_enum, ignore_case(false))]
arg: ArgChoice,
}
@ -238,7 +238,7 @@ fn alias() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, ignore_case(false), value_parser)]
#[clap(value_enum, ignore_case(false))]
arg: ArgChoice,
}
@ -266,7 +266,7 @@ fn multiple_alias() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, ignore_case(false), value_parser)]
#[clap(value_enum, ignore_case(false))]
arg: ArgChoice,
}
@ -375,7 +375,7 @@ fn option_type() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, value_parser)]
#[clap(value_enum)]
arg: Option<ArgChoice>,
}
@ -405,7 +405,7 @@ fn option_option_type() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, long, value_parser)]
#[clap(value_enum, long)]
arg: Option<Option<ArgChoice>>,
}
@ -439,7 +439,7 @@ fn vec_type() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, short, long, value_parser)]
#[clap(value_enum, short, long)]
arg: Vec<ArgChoice>,
}
@ -469,7 +469,7 @@ fn option_vec_type() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(value_enum, short, long, value_parser)]
#[clap(value_enum, short, long)]
arg: Option<Vec<ArgChoice>>,
}
@ -505,8 +505,7 @@ fn vec_type_default_value() {
short,
long,
default_value = "foo,bar",
value_delimiter = ',',
value_parser
value_delimiter = ','
)]
arg: Vec<ArgChoice>,
}

View file

@ -11,8 +11,4 @@
fn ui() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/derive_ui/*.rs");
#[cfg(feature = "unstable-v4")]
t.compile_fail("tests/derive_ui/next/*.rs");
#[cfg(not(feature = "unstable-v4"))]
t.compile_fail("tests/derive_ui/stable/*.rs");
}

View file

@ -1,5 +1,5 @@
error[E0277]: the trait bound `bool: ValueEnum` is not satisfied
--> tests/derive_ui/next/bool_value_enum.rs:6:31
--> tests/derive_ui/bool_value_enum.rs:6:31
|
6 | #[clap(short, value_enum, default_value_t)]
| ^^^^^^^^^^^^^^^ the trait `ValueEnum` is not implemented for `bool`

View file

@ -1,5 +1,5 @@
error[E0599]: no method named `non_existing_attribute` found for struct `Arg` in the current scope
--> tests/derive_ui/next/non_existent_attr.rs:14:19
--> tests/derive_ui/non_existent_attr.rs:14:19
|
14 | #[clap(short, non_existing_attribute = 1)]
| ^^^^^^^^^^^^^^^^^^^^^^ method not found in `Arg<'_>`

View file

@ -1,13 +0,0 @@
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(name = "basic")]
struct Opt {
#[clap(short, value_enum)]
opts: bool,
}
fn main() {
let opt = Opt::parse();
println!("{:?}", opt);
}

View file

@ -1,40 +0,0 @@
error[E0277]: the trait bound `bool: ValueEnum` is not satisfied
--> tests/derive_ui/stable/bool_value_enum.rs:7:11
|
7 | opts: bool,
| ^^^^ the trait `ValueEnum` is not implemented for `bool`
|
note: required by `clap::ValueEnum::from_str`
--> src/derive.rs
|
| fn from_str(input: &str, ignore_case: bool) -> Result<Self, String> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0618]: expected function, found enum variant `bool`
--> tests/derive_ui/stable/bool_value_enum.rs:7:11
|
7 | opts: bool,
| ^^^^ call expression requires function
|
help: `bool` is a unit variant, you need to write it without the parenthesis
|
7 | opts: bool,
| ~~~~
error[E0599]: no method named `possible_values` found for struct `Arg` in the current scope
--> tests/derive_ui/stable/bool_value_enum.rs:7:11
|
7 | opts: bool,
| ^^^^ help: there is an associated function with a similar name: `hide_possible_values`
error[E0277]: the trait bound `bool: ValueEnum` is not satisfied
--> tests/derive_ui/stable/bool_value_enum.rs:7:11
|
7 | opts: bool,
| ^^^^ the trait `ValueEnum` is not implemented for `bool`
|
note: required by `value_variants`
--> src/derive.rs
|
| fn value_variants<'a>() -> &'a [Self];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -1,21 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(name = "basic")]
struct Opt {
#[clap(short, non_existing_attribute = 1)]
debug: bool,
}
fn main() {
let opt = Opt::parse();
println!("{:?}", opt);
}

View file

@ -1,5 +0,0 @@
error[E0599]: no method named `non_existing_attribute` found for struct `Arg` in the current scope
--> tests/derive_ui/stable/non_existent_attr.rs:14:19
|
14 | #[clap(short, non_existing_attribute = 1)]
| ^^^^^^^^^^^^^^^^^^^^^^ method not found in `Arg<'_>`

View file

@ -1,18 +0,0 @@
// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(name = "basic")]
struct Opt(u32);
fn main() {
let opt = Opt::parse();
println!("{:?}", opt);
}

View file

@ -1,21 +0,0 @@
error: `#[derive(Parser)]` only supports non-tuple structs and enums
--> tests/derive_ui/stable/tuple_struct.rs:11:10
|
11 | #[derive(Parser, Debug)]
| ^^^^^^
|
= note: this error originates in the derive macro `Parser` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0599]: no function or associated item named `parse` found for struct `Opt` in the current scope
--> tests/derive_ui/stable/tuple_struct.rs:16:20
|
13 | struct Opt(u32);
| ---------------- function or associated item `parse` not found for this
...
16 | let opt = Opt::parse();
| ^^^^^ function or associated item not found in `Opt`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following traits define an item `parse`, perhaps you need to implement one of them:
candidate #1: `Parser`
candidate #2: `TypedValueParser`

View file

@ -1,5 +1,5 @@
error: `#[derive(Parser)]` only supports non-tuple structs and enums
--> tests/derive_ui/next/tuple_struct.rs:11:10
--> tests/derive_ui/tuple_struct.rs:11:10
|
11 | #[derive(Parser, Debug)]
| ^^^^^^
@ -7,7 +7,7 @@ error: `#[derive(Parser)]` only supports non-tuple structs and enums
= note: this error originates in the derive macro `Parser` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0599]: no function or associated item named `parse` found for struct `Opt` in the current scope
--> tests/derive_ui/next/tuple_struct.rs:16:20
--> tests/derive_ui/tuple_struct.rs:16:20
|
13 | struct Opt(u32);
| ---------------- function or associated item `parse` not found for this