mirror of
https://github.com/clap-rs/clap
synced 2024-12-12 22:02:35 +00:00
Merge pull request #3783 from epage/refactor
refactor(derive): Prepare for action support
This commit is contained in:
commit
e8ad62b784
44 changed files with 5395 additions and 81 deletions
|
@ -64,10 +64,10 @@ impl Attrs {
|
|||
res.push_attrs(attrs);
|
||||
res.push_doc_comment(attrs, "about");
|
||||
|
||||
if let Some(parser) = res.value_parser.as_ref() {
|
||||
if let Some(value_parser) = res.value_parser.as_ref() {
|
||||
abort!(
|
||||
parser.span(),
|
||||
"`value_parse` attribute is only allowed on fields"
|
||||
value_parser.span(),
|
||||
"`value_parser` attribute is only allowed on fields"
|
||||
);
|
||||
}
|
||||
if let Some(parser) = res.parser.as_ref() {
|
||||
|
@ -104,10 +104,10 @@ impl Attrs {
|
|||
|
||||
match &*res.kind {
|
||||
Kind::Flatten => {
|
||||
if let Some(parser) = res.value_parser.as_ref() {
|
||||
if let Some(value_parser) = res.value_parser.as_ref() {
|
||||
abort!(
|
||||
parser.span(),
|
||||
"`value_parse` attribute is only allowed flattened entry"
|
||||
value_parser.span(),
|
||||
"`value_parser` attribute is not allowed for flattened entry"
|
||||
);
|
||||
}
|
||||
if let Some(parser) = res.parser.as_ref() {
|
||||
|
@ -130,10 +130,10 @@ impl Attrs {
|
|||
Kind::ExternalSubcommand => (),
|
||||
|
||||
Kind::Subcommand(_) => {
|
||||
if let Some(parser) = res.value_parser.as_ref() {
|
||||
if let Some(value_parser) = res.value_parser.as_ref() {
|
||||
abort!(
|
||||
parser.span(),
|
||||
"`value_parse` attribute is only allowed for subcommand"
|
||||
value_parser.span(),
|
||||
"`value_parser` attribute is not allowed for subcommand"
|
||||
);
|
||||
}
|
||||
if let Some(parser) = res.parser.as_ref() {
|
||||
|
@ -204,10 +204,10 @@ impl Attrs {
|
|||
res.push_attrs(&variant.attrs);
|
||||
res.push_doc_comment(&variant.attrs, "help");
|
||||
|
||||
if let Some(parser) = res.value_parser.as_ref() {
|
||||
if let Some(value_parser) = res.value_parser.as_ref() {
|
||||
abort!(
|
||||
parser.span(),
|
||||
"`value_parse` attribute is only allowed on fields"
|
||||
value_parser.span(),
|
||||
"`value_parser` attribute is only allowed on fields"
|
||||
);
|
||||
}
|
||||
if let Some(parser) = res.parser.as_ref() {
|
||||
|
@ -244,10 +244,10 @@ impl Attrs {
|
|||
|
||||
match &*res.kind {
|
||||
Kind::Flatten => {
|
||||
if let Some(parser) = res.value_parser.as_ref() {
|
||||
if let Some(value_parser) = res.value_parser.as_ref() {
|
||||
abort!(
|
||||
parser.span(),
|
||||
"`value_parse` attribute is not allowed for flattened entry"
|
||||
value_parser.span(),
|
||||
"`value_parser` attribute is not allowed for flattened entry"
|
||||
);
|
||||
}
|
||||
if let Some(parser) = res.parser.as_ref() {
|
||||
|
@ -274,10 +274,10 @@ impl Attrs {
|
|||
}
|
||||
|
||||
Kind::Subcommand(_) => {
|
||||
if let Some(parser) = res.value_parser.as_ref() {
|
||||
if let Some(value_parser) = res.value_parser.as_ref() {
|
||||
abort!(
|
||||
parser.span(),
|
||||
"`value_parse` attribute is not allowed for subcommand"
|
||||
value_parser.span(),
|
||||
"`value_parser` attribute is not allowed for subcommand"
|
||||
);
|
||||
}
|
||||
if let Some(parser) = res.parser.as_ref() {
|
||||
|
@ -330,7 +330,7 @@ impl Attrs {
|
|||
if let Some(value_parser) = res.value_parser.as_ref() {
|
||||
abort!(
|
||||
value_parser.span(),
|
||||
"`value_parse` attribute conflicts with `parse` attribute"
|
||||
"`value_parser` attribute conflicts with `parse` attribute"
|
||||
);
|
||||
}
|
||||
match *ty {
|
||||
|
@ -721,7 +721,7 @@ impl Attrs {
|
|||
.unwrap_or_else(|| self.parser(field_type).value_parser())
|
||||
}
|
||||
|
||||
pub fn custom_value_parser(&self) -> bool {
|
||||
pub fn ignore_parser(&self) -> bool {
|
||||
self.value_parser.is_some()
|
||||
}
|
||||
|
||||
|
@ -958,7 +958,7 @@ impl Parser {
|
|||
),
|
||||
ParserKind::FromFlag => Method::new(
|
||||
func,
|
||||
quote_spanned! { self.kind.span()=> clap::ValueParser::bool()},
|
||||
quote_spanned! { self.kind.span()=> clap::builder::ValueParser::bool()},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -244,14 +244,14 @@ pub fn gen_augment(
|
|||
let convert_type = inner_type(&field.ty);
|
||||
|
||||
let parser = attrs.parser(&field.ty);
|
||||
let occurrences = *parser.kind == ParserKind::FromOccurrences;
|
||||
let flag = *parser.kind == ParserKind::FromFlag;
|
||||
|
||||
let value_parser = attrs.value_parser(&field.ty);
|
||||
let func = &parser.func;
|
||||
|
||||
let mut occurrences = false;
|
||||
let mut flag = false;
|
||||
let validator = match *parser.kind {
|
||||
_ if attrs.custom_value_parser() || attrs.is_enum() => quote!(),
|
||||
_ if attrs.ignore_parser() || attrs.is_enum() => quote!(),
|
||||
ParserKind::TryFromStr => quote_spanned! { func.span()=>
|
||||
.validator(|s| {
|
||||
#func(s)
|
||||
|
@ -261,14 +261,19 @@ pub fn gen_augment(
|
|||
ParserKind::TryFromOsStr => quote_spanned! { func.span()=>
|
||||
.validator_os(|s| #func(s).map(|_: #convert_type| ()))
|
||||
},
|
||||
ParserKind::FromStr
|
||||
| ParserKind::FromOsStr
|
||||
| ParserKind::FromFlag
|
||||
| ParserKind::FromOccurrences => quote!(),
|
||||
ParserKind::FromStr | ParserKind::FromOsStr => quote!(),
|
||||
ParserKind::FromFlag => {
|
||||
flag = true;
|
||||
quote!()
|
||||
}
|
||||
ParserKind::FromOccurrences => {
|
||||
occurrences = true;
|
||||
quote!()
|
||||
}
|
||||
};
|
||||
|
||||
let value_name = attrs.value_name();
|
||||
let possible_values = if attrs.is_enum() && !attrs.custom_value_parser() {
|
||||
let possible_values = if attrs.is_enum() && !attrs.ignore_parser() {
|
||||
gen_arg_enum_possible_values(convert_type)
|
||||
} else {
|
||||
quote!()
|
||||
|
@ -524,25 +529,33 @@ fn gen_parsers(
|
|||
let span = parser.kind.span();
|
||||
let convert_type = inner_type(&field.ty);
|
||||
let id = attrs.id();
|
||||
let mut flag = false;
|
||||
let mut occurrences = false;
|
||||
let (get_one, get_many, deref, mut parse) = match *parser.kind {
|
||||
FromOccurrences => (
|
||||
quote_spanned!(span=> occurrences_of),
|
||||
quote!(),
|
||||
quote!(|s| ::std::ops::Deref::deref(s)),
|
||||
func.clone(),
|
||||
),
|
||||
FromFlag => (
|
||||
quote!(),
|
||||
quote!(),
|
||||
quote!(|s| ::std::ops::Deref::deref(s)),
|
||||
func.clone(),
|
||||
),
|
||||
_ if attrs.custom_value_parser() => (
|
||||
_ if attrs.ignore_parser() => (
|
||||
quote_spanned!(span=> remove_one::<#convert_type>),
|
||||
quote_spanned!(span=> remove_many::<#convert_type>),
|
||||
quote!(|s| s),
|
||||
quote_spanned!(func.span()=> |s| ::std::result::Result::Ok::<_, clap::Error>(s)),
|
||||
),
|
||||
FromOccurrences => {
|
||||
occurrences = true;
|
||||
(
|
||||
quote_spanned!(span=> occurrences_of),
|
||||
quote!(),
|
||||
quote!(|s| ::std::ops::Deref::deref(s)),
|
||||
func.clone(),
|
||||
)
|
||||
}
|
||||
FromFlag => {
|
||||
flag = true;
|
||||
(
|
||||
quote!(),
|
||||
quote!(),
|
||||
quote!(|s| ::std::ops::Deref::deref(s)),
|
||||
func.clone(),
|
||||
)
|
||||
}
|
||||
FromStr => (
|
||||
quote_spanned!(span=> get_one::<String>),
|
||||
quote_spanned!(span=> get_many::<String>),
|
||||
|
@ -568,7 +581,7 @@ fn gen_parsers(
|
|||
quote_spanned!(func.span()=> |s| #func(s).map_err(|err| clap::Error::raw(clap::ErrorKind::ValueValidation, format!("Invalid value for {}: {}", #id, err)))),
|
||||
),
|
||||
};
|
||||
if attrs.is_enum() && !attrs.custom_value_parser() {
|
||||
if attrs.is_enum() && !attrs.ignore_parser() {
|
||||
let ci = attrs.ignore_case();
|
||||
|
||||
parse = quote_spanned! { convert_type.span()=>
|
||||
|
@ -576,8 +589,6 @@ fn gen_parsers(
|
|||
}
|
||||
}
|
||||
|
||||
let flag = *parser.kind == ParserKind::FromFlag;
|
||||
let occurrences = *parser.kind == ParserKind::FromOccurrences;
|
||||
// Give this identifier the same hygiene
|
||||
// as the `arg_matches` parameter definition. This
|
||||
// allows us to refer to `arg_matches` within a `quote_spanned` block
|
||||
|
|
|
@ -26,7 +26,7 @@ enum Commands {
|
|||
/// does testing things
|
||||
Test {
|
||||
/// lists test values
|
||||
#[clap(short, long, value_parser)]
|
||||
#[clap(short, long)]
|
||||
list: bool,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use clap::Parser;
|
|||
#[derive(Parser)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[clap(short, long, value_parser)]
|
||||
#[clap(short, long)]
|
||||
verbose: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -18,11 +18,6 @@ pub enum MatchesError {
|
|||
UnknownArgument {
|
||||
// Missing `id` but blocked on a public id type which will hopefully come with `unstable-v4`
|
||||
},
|
||||
/// Present argument must have one value
|
||||
#[non_exhaustive]
|
||||
ExpectedOne {
|
||||
// Missing `id` but blocked on a public id type which will hopefully come with `unstable-v4`
|
||||
},
|
||||
}
|
||||
|
||||
impl MatchesError {
|
||||
|
@ -56,9 +51,6 @@ impl std::fmt::Display for MatchesError {
|
|||
Self::UnknownArgument {} => {
|
||||
writeln!(f, "Unknown argument or group id. Make sure you are using the argument id and not the short or long flags")
|
||||
}
|
||||
Self::ExpectedOne {} => {
|
||||
writeln!(f, "Present argument must have one value. Make sure you are using the correct lookup (`get_one` vs `get_many`)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1063,11 +1063,8 @@ impl ArgMatches {
|
|||
) -> Result<Option<&T>, MatchesError> {
|
||||
let id = Id::from(name);
|
||||
let arg = self.try_get_arg_t::<T>(&id)?;
|
||||
let value = match arg.map(|a| a.first()) {
|
||||
Some(Some(value)) => value,
|
||||
Some(None) => {
|
||||
return Err(MatchesError::ExpectedOne {});
|
||||
}
|
||||
let value = match arg.and_then(|a| a.first()) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
return Ok(None);
|
||||
}
|
||||
|
|
|
@ -38,10 +38,7 @@ fn env_bool_literal() {
|
|||
let m = r.unwrap();
|
||||
assert!(m.is_present("present"));
|
||||
assert_eq!(m.occurrences_of("present"), 0);
|
||||
assert!(matches!(
|
||||
m.try_get_one::<String>("present").unwrap_err(),
|
||||
clap::parser::MatchesError::ExpectedOne { .. }
|
||||
));
|
||||
assert_eq!(m.get_one::<String>("present").map(|v| v.as_str()), None);
|
||||
assert!(!m.is_present("negated"));
|
||||
assert!(!m.is_present("absent"));
|
||||
}
|
||||
|
|
|
@ -111,10 +111,7 @@ fn group_single_flag() {
|
|||
|
||||
let m = res.unwrap();
|
||||
assert!(m.is_present("grp"));
|
||||
assert!(matches!(
|
||||
m.try_get_one::<String>("grp").unwrap_err(),
|
||||
clap::parser::MatchesError::ExpectedOne { .. }
|
||||
));
|
||||
assert!(m.get_one::<String>("grp").map(|v| v.as_str()).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -49,10 +49,7 @@ fn multiple_args_and_final_arg_without_value() {
|
|||
Some("file")
|
||||
);
|
||||
assert!(m.is_present("f"));
|
||||
assert!(matches!(
|
||||
m.try_get_one::<String>("stuff").unwrap_err(),
|
||||
clap::parser::MatchesError::ExpectedOne { .. }
|
||||
));
|
||||
assert_eq!(m.get_one::<String>("stuff").map(|v| v.as_str()), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -79,10 +76,7 @@ fn multiple_args_and_intermittent_arg_without_value() {
|
|||
Some("file")
|
||||
);
|
||||
assert!(m.is_present("f"));
|
||||
assert!(matches!(
|
||||
m.try_get_one::<String>("stuff").unwrap_err(),
|
||||
clap::parser::MatchesError::ExpectedOne { .. }
|
||||
));
|
||||
assert_eq!(m.get_one::<String>("stuff").map(|v| v.as_str()), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -124,10 +118,7 @@ fn subcommand() {
|
|||
sub_m.is_present("test"),
|
||||
"expected subcommand to be present due to partial parsing"
|
||||
);
|
||||
assert!(matches!(
|
||||
sub_m.try_get_one::<String>("test").unwrap_err(),
|
||||
clap::parser::MatchesError::ExpectedOne { .. }
|
||||
));
|
||||
assert_eq!(sub_m.get_one::<String>("test").map(|v| v.as_str()), None);
|
||||
assert_eq!(
|
||||
sub_m.get_one::<String>("stuff").map(|v| v.as_str()),
|
||||
Some("some other val")
|
||||
|
|
97
tests/derive/legacy/app_name.rs
Normal file
97
tests/derive/legacy/app_name.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
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"));
|
||||
}
|
526
tests/derive/legacy/arg_enum.rs
Normal file
526
tests/derive/legacy/arg_enum.rs
Normal file
|
@ -0,0 +1,526 @@
|
|||
// 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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
impl Default for ArgChoice {
|
||||
fn default() -> Self {
|
||||
Self::Bar
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
enum ArgChoice {
|
||||
FooBar,
|
||||
BAR_BAZ,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
#[clap(rename_all = "screaming_snake")]
|
||||
FooBar,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
#[clap(rename_all = "screaming_snake")]
|
||||
enum ArgChoice {
|
||||
FooBar,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
#[clap(rename_all = "screaming_snake")]
|
||||
enum ArgChoice {
|
||||
#[clap(rename_all = "camel")]
|
||||
FooBar,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
#[clap(alias = "TOTP")]
|
||||
Totp,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
#[clap(alias = "TOTP", alias = "t")]
|
||||
Totp,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
#[allow(dead_code)] // silence warning about `Baz` being unused
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
Bar,
|
||||
#[clap(skip)]
|
||||
Baz,
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
<ArgChoice as clap::ArgEnum>::value_variants()
|
||||
.iter()
|
||||
.map(clap::ArgEnum::to_possible_value)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
clap::PossibleValue::new("foo"),
|
||||
clap::PossibleValue::new("bar")
|
||||
]
|
||||
);
|
||||
|
||||
{
|
||||
use clap::ArgEnum;
|
||||
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::ArgEnum, 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::ArgEnum>::value_variants()
|
||||
.iter()
|
||||
.map(clap::ArgEnum::to_possible_value)
|
||||
.map(Option::unwrap)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
clap::PossibleValue::new("foo"),
|
||||
clap::PossibleValue::new("bar")
|
||||
]
|
||||
);
|
||||
|
||||
{
|
||||
use clap::ArgEnum;
|
||||
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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
}
|
||||
|
||||
{
|
||||
use clap::ArgEnum;
|
||||
assert!(ArgChoice::from_str("bar", true).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_type() {
|
||||
#[derive(clap::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(arg_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::ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
Bar,
|
||||
Baz,
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(
|
||||
arg_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()
|
||||
);
|
||||
}
|
120
tests/derive/legacy/arguments.rs
Normal file
120
tests/derive/legacy/arguments.rs
Normal file
|
@ -0,0 +1,120 @@
|
|||
// 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()
|
||||
);
|
||||
}
|
51
tests/derive/legacy/author_version_about.rs
Normal file
51
tests/derive/legacy/author_version_about.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
// 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"));
|
||||
}
|
51
tests/derive/legacy/basic.rs
Normal file
51
tests/derive/legacy/basic.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
// 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());
|
||||
}
|
48
tests/derive/legacy/boxed.rs
Normal file
48
tests/derive/legacy/boxed.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
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
|
||||
);
|
||||
}
|
351
tests/derive/legacy/custom_string_parsers.rs
Normal file
351
tests/derive/legacy/custom_string_parsers.rs
Normal file
|
@ -0,0 +1,351 @@
|
|||
// 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;
|
||||
|
||||
use std::ffi::{CString, OsStr};
|
||||
use std::num::ParseIntError;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct PathOpt {
|
||||
#[clap(short, long, parse(from_os_str))]
|
||||
path: PathBuf,
|
||||
|
||||
#[clap(short, default_value = "../", parse(from_os_str))]
|
||||
default_path: PathBuf,
|
||||
|
||||
#[clap(short, parse(from_os_str), multiple_occurrences(true))]
|
||||
vector_path: Vec<PathBuf>,
|
||||
|
||||
#[clap(short, parse(from_os_str))]
|
||||
option_path_1: Option<PathBuf>,
|
||||
|
||||
#[clap(short = 'q', parse(from_os_str))]
|
||||
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, parse(try_from_str = 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
|
||||
);
|
||||
}
|
||||
|
||||
fn custom_parser_1(_: &str) -> &'static str {
|
||||
"A"
|
||||
}
|
||||
#[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")
|
||||
}
|
||||
fn custom_parser_3(_: &OsStr) -> &'static str {
|
||||
"C"
|
||||
}
|
||||
fn custom_parser_4(_: &OsStr) -> Result<&'static str, String> {
|
||||
Ok("D")
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct NoOpOpt {
|
||||
#[clap(short, parse(from_str = custom_parser_1))]
|
||||
a: &'static str,
|
||||
#[clap(short, parse(try_from_str = custom_parser_2))]
|
||||
b: &'static str,
|
||||
#[clap(short, parse(from_os_str = custom_parser_3))]
|
||||
c: &'static str,
|
||||
#[clap(short, parse(try_from_os_str = custom_parser_4))]
|
||||
d: &'static str,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_every_custom_parser() {
|
||||
assert_eq!(
|
||||
NoOpOpt {
|
||||
a: "A",
|
||||
b: "B",
|
||||
c: "C",
|
||||
d: "D"
|
||||
},
|
||||
NoOpOpt::try_parse_from(&["test", "-a=?", "-b=?", "-c=?", "-d=?"]).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_every_custom_parser() {
|
||||
let mut opt = NoOpOpt {
|
||||
a: "0",
|
||||
b: "0",
|
||||
c: "0",
|
||||
d: "D",
|
||||
};
|
||||
|
||||
opt.try_update_from(&["test", "-a=?", "-b=?", "-d=?"])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
NoOpOpt {
|
||||
a: "A",
|
||||
b: "B",
|
||||
c: "0",
|
||||
d: "D"
|
||||
},
|
||||
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, parse(try_from_str))]
|
||||
integer: u64,
|
||||
|
||||
#[clap(short, parse(from_os_str))]
|
||||
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()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_bool() {
|
||||
fn parse_bool(s: &str) -> Result<bool, String> {
|
||||
match s {
|
||||
"true" => Ok(true),
|
||||
"false" => Ok(false),
|
||||
_ => Err(format!("invalid bool {}", s)),
|
||||
}
|
||||
}
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, parse(try_from_str = parse_bool))]
|
||||
debug: bool,
|
||||
#[clap(
|
||||
short,
|
||||
default_value = "false",
|
||||
parse(try_from_str = parse_bool)
|
||||
)]
|
||||
verbose: bool,
|
||||
#[clap(short, parse(try_from_str = parse_bool))]
|
||||
tribool: Option<bool>,
|
||||
#[clap(short, parse(try_from_str = parse_bool), multiple_occurrences(true))]
|
||||
bitset: Vec<bool>,
|
||||
}
|
||||
|
||||
assert!(Opt::try_parse_from(&["test"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-d"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-dfoo"]).is_err());
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: false,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::try_parse_from(&["test", "-dfalse"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::try_parse_from(&["test", "-dtrue"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::try_parse_from(&["test", "-dtrue", "-vfalse"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: true,
|
||||
tribool: None,
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::try_parse_from(&["test", "-dtrue", "-vtrue"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: Some(false),
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::try_parse_from(&["test", "-dtrue", "-tfalse"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: Some(true),
|
||||
bitset: vec![],
|
||||
},
|
||||
Opt::try_parse_from(&["test", "-dtrue", "-ttrue"]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Opt {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
tribool: None,
|
||||
bitset: vec![false, true, false, false],
|
||||
},
|
||||
Opt::try_parse_from(&["test", "-dtrue", "-bfalse", "-btrue", "-bfalse", "-bfalse"])
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cstring() {
|
||||
#[derive(Parser)]
|
||||
struct Opt {
|
||||
#[clap(parse(try_from_str = CString::new))]
|
||||
c_string: CString,
|
||||
}
|
||||
|
||||
assert!(Opt::try_parse_from(&["test"]).is_err());
|
||||
assert_eq!(
|
||||
Opt::try_parse_from(&["test", "bla"])
|
||||
.unwrap()
|
||||
.c_string
|
||||
.to_bytes(),
|
||||
b"bla"
|
||||
);
|
||||
assert!(Opt::try_parse_from(&["test", "bla\0bla"]).is_err());
|
||||
}
|
57
tests/derive/legacy/default_value.rs
Normal file
57
tests/derive/legacy/default_value.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
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();
|
||||
}
|
53
tests/derive/legacy/deny_warnings.rs
Normal file
53
tests/derive/legacy/deny_warnings.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
// 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(parse(try_from_str = 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(parse(try_from_str = try_str), default_value_t)]
|
||||
s: String,
|
||||
},
|
||||
}
|
||||
assert_eq!(
|
||||
Opt::Foo {
|
||||
s: "foo".to_string()
|
||||
},
|
||||
Opt::try_parse_from(&["test", "foo", "foo"]).unwrap()
|
||||
);
|
||||
}
|
240
tests/derive/legacy/doc_comments_help.rs
Normal file
240
tests/derive/legacy/doc_comments_help.rs
Normal file
|
@ -0,0 +1,240 @@
|
|||
// 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::{ArgEnum, CommandFactory, Parser};
|
||||
|
||||
#[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(ArgEnum, 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);
|
||||
}
|
36
tests/derive/legacy/explicit_name_no_renaming.rs
Normal file
36
tests/derive/legacy/explicit_name_no_renaming.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
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>"))
|
||||
}
|
189
tests/derive/legacy/flags.rs
Normal file
189
tests/derive/legacy/flags.rs
Normal file
|
@ -0,0 +1,189 @@
|
|||
// 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 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", "--alice"]).unwrap()
|
||||
);
|
||||
assert!(Opt::try_parse_from(&["test", "-i"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a", "foo"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a", "-a"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a", "--alice"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_occurrences() {
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
alice: u64,
|
||||
#[clap(short, long, parse(from_occurrences))]
|
||||
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, parse(from_occurrences))]
|
||||
bob: u64,
|
||||
}
|
||||
|
||||
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)]
|
||||
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()
|
||||
);
|
||||
}
|
256
tests/derive/legacy/flatten.rs
Normal file
256
tests/derive/legacy/flatten.rs
Normal file
|
@ -0,0 +1,256 @@
|
|||
// 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"));
|
||||
}
|
145
tests/derive/legacy/generic.rs
Normal file
145
tests/derive/legacy/generic.rs
Normal file
|
@ -0,0 +1,145 @@
|
|||
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,
|
||||
<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,
|
||||
<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"])
|
||||
)
|
||||
}
|
473
tests/derive/legacy/help.rs
Normal file
473
tests/derive/legacy/help.rs
Normal file
|
@ -0,0 +1,473 @@
|
|||
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, parse(from_occurrences))]
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
assert_eq!(help, HELP);
|
||||
}
|
130
tests/derive/legacy/issues.rs
Normal file
130
tests/derive/legacy/issues.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
// https://github.com/TeXitoi/structopt/issues/{NUMBER}
|
||||
|
||||
use crate::utils;
|
||||
|
||||
use clap::{ArgGroup, Args, Parser, Subcommand};
|
||||
|
||||
#[test]
|
||||
fn issue_151() {
|
||||
#[derive(Args, Debug)]
|
||||
#[clap(group = ArgGroup::new("verb").required(true).multiple(true))]
|
||||
struct Opt {
|
||||
#[clap(long, group = "verb")]
|
||||
foo: bool,
|
||||
#[clap(long, group = "verb")]
|
||||
bar: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Cli {
|
||||
#[clap(flatten)]
|
||||
a: Opt,
|
||||
}
|
||||
|
||||
assert!(Cli::try_parse_from(&["test"]).is_err());
|
||||
assert!(Cli::try_parse_from(&["test", "--foo"]).is_ok());
|
||||
assert!(Cli::try_parse_from(&["test", "--bar"]).is_ok());
|
||||
assert!(Cli::try_parse_from(&["test", "--zebra"]).is_err());
|
||||
assert!(Cli::try_parse_from(&["test", "--foo", "--bar"]).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
|
||||
}
|
53
tests/derive/legacy/macros.rs
Normal file
53
tests/derive/legacy/macros.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
// 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>)
|
||||
]}
|
||||
}
|
33
tests/derive/legacy/mod.rs
Normal file
33
tests/derive/legacy/mod.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
#![allow(deprecated)]
|
||||
|
||||
mod app_name;
|
||||
mod arg_enum;
|
||||
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 structopt;
|
||||
mod subcommands;
|
||||
mod type_alias_regressions;
|
||||
mod utf8;
|
||||
mod utils;
|
354
tests/derive/legacy/naming.rs
Normal file
354
tests/derive/legacy/naming.rs
Normal file
|
@ -0,0 +1,354 @@
|
|||
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()
|
||||
);
|
||||
}
|
194
tests/derive/legacy/nested_subcommands.rs
Normal file
194
tests/derive/legacy/nested_subcommands.rs
Normal file
|
@ -0,0 +1,194 @@
|
|||
// 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, parse(from_occurrences))]
|
||||
verbose: u64,
|
||||
#[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, parse(from_occurrences))]
|
||||
verbose: u64,
|
||||
#[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"]));
|
||||
}
|
156
tests/derive/legacy/non_literal_attributes.rs
Normal file
156
tests/derive/legacy/non_literal_attributes.rs
Normal file
|
@ -0,0 +1,156 @@
|
|||
// 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, parse(try_from_str = 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
|
||||
);
|
||||
}
|
422
tests/derive/legacy/options.rs
Normal file
422
tests/derive/legacy/options.rs
Normal file
|
@ -0,0 +1,422 @@
|
|||
// 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!(Opt::try_parse_from(&["test"]).is_err());
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).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"]).unwrap());
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[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"]).unwrap());
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_from_str() {
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct A;
|
||||
|
||||
impl<'a> From<&'a str> for A {
|
||||
fn from(_: &str) -> A {
|
||||
A
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser, PartialEq)]
|
||||
struct Opt {
|
||||
#[clap(parse(from_str))]
|
||||
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 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: None }, Opt::try_parse_from(&["test"]).unwrap());
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[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, parse(from_occurrences))]
|
||||
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) -> Option<String> {
|
||||
Some(s.to_string())
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(parse(from_str = 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, multiple_occurrences(true))]
|
||||
#[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: None }, Opt::try_parse_from(&["test"]).unwrap());
|
||||
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
|
||||
}
|
||||
|
||||
#[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), multiple_occurrences(false))]
|
||||
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) -> Vec<String> {
|
||||
vec![s.to_string()]
|
||||
}
|
||||
|
||||
#[derive(Parser, PartialEq, Debug)]
|
||||
struct Opt {
|
||||
#[clap(parse(from_str = 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()
|
||||
);
|
||||
}
|
36
tests/derive/legacy/privacy.rs
Normal file
36
tests/derive/legacy/privacy.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
// 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,
|
||||
},
|
||||
}
|
||||
}
|
29
tests/derive/legacy/raw_bool_literal.rs
Normal file
29
tests/derive/legacy/raw_bool_literal.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// 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()
|
||||
);
|
||||
}
|
24
tests/derive/legacy/raw_idents.rs
Normal file
24
tests/derive/legacy/raw_idents.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
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()
|
||||
);
|
||||
}
|
47
tests/derive/legacy/rename_all_env.rs
Normal file
47
tests/derive/legacy/rename_all_env.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
#![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=]"));
|
||||
}
|
155
tests/derive/legacy/skip.rs
Normal file
155
tests/derive/legacy/skip.rs
Normal file
|
@ -0,0 +1,155 @@
|
|||
// 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, parse(try_from_str))]
|
||||
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]
|
||||
}
|
||||
);
|
||||
}
|
23
tests/derive/legacy/structopt.rs
Normal file
23
tests/derive/legacy/structopt.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
#![allow(deprecated)]
|
||||
|
||||
use clap::{AppSettings, StructOpt};
|
||||
|
||||
#[test]
|
||||
fn compatible() {
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(author, version, about)]
|
||||
#[structopt(global_setting(AppSettings::PropagateVersion))]
|
||||
struct Cli {
|
||||
#[structopt(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(setting(AppSettings::SubcommandRequiredElseHelp))]
|
||||
enum Commands {
|
||||
/// Adds files to myapp
|
||||
Add { name: Option<String> },
|
||||
}
|
||||
|
||||
Cli::from_iter(["test", "add"]);
|
||||
}
|
605
tests/derive/legacy/subcommands.rs
Normal file
605
tests/derive/legacy/subcommands.rs
Normal file
|
@ -0,0 +1,605 @@
|
|||
// 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,
|
||||
#[clap(short, long)]
|
||||
/// Overwrite local branches.
|
||||
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 }
|
||||
);
|
||||
}
|
47
tests/derive/legacy/type_alias_regressions.rs
Normal file
47
tests/derive/legacy/type_alias_regressions.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
/// Regression test to ensure that type aliases do not cause compilation failures.
|
||||
use std::str::FromStr;
|
||||
|
||||
use clap::{ArgEnum, Parser, Subcommand};
|
||||
|
||||
// Result type alias
|
||||
#[allow(dead_code)]
|
||||
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||
|
||||
// Wrapper to use for Option type alias
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Wrapper<T>(T);
|
||||
|
||||
impl<T: FromStr> FromStr for Wrapper<T> {
|
||||
type Err = <T as FromStr>::Err;
|
||||
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
T::from_str(s).map(Wrapper)
|
||||
}
|
||||
}
|
||||
|
||||
type Option<T> = std::option::Option<Wrapper<T>>;
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct Opts {
|
||||
another_string: String,
|
||||
#[clap(subcommand)]
|
||||
command: Command,
|
||||
#[clap(short, long, arg_enum)]
|
||||
choice: ArgChoice,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, PartialEq, Debug)]
|
||||
enum Command {
|
||||
DoSomething { arg: Option<String> },
|
||||
}
|
||||
|
||||
#[derive(ArgEnum, PartialEq, Debug, Clone)]
|
||||
enum ArgChoice {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_alias_regressions() {
|
||||
Opts::try_parse_from(["test", "value", "--choice=foo", "do-something"]).unwrap();
|
||||
}
|
226
tests/derive/legacy/utf8.rs
Normal file
226
tests/derive/legacy/utf8.rs
Normal file
|
@ -0,0 +1,226 @@
|
|||
#![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 {
|
||||
#[clap(parse(from_os_str))]
|
||||
arg: OsString,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, PartialEq, Eq)]
|
||||
struct NamedOs {
|
||||
#[clap(short, long, parse(from_os_str))]
|
||||
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"),
|
||||
])
|
||||
);
|
||||
}
|
56
tests/derive/legacy/utils.rs
Normal file
56
tests/derive/legacy/utils.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
// 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
|
||||
}
|
|
@ -16,6 +16,7 @@ mod flatten;
|
|||
mod generic;
|
||||
mod help;
|
||||
mod issues;
|
||||
mod legacy;
|
||||
mod macros;
|
||||
mod naming;
|
||||
mod nested_subcommands;
|
||||
|
|
|
@ -20,3 +20,48 @@ help: `bool` is a unit variant, you need to write it without the parenthesis
|
|||
|
|
||||
7 | opts: bool,
|
||||
| ~~~~
|
||||
|
||||
warning: use of deprecated associated function `clap::Arg::<'help>::possible_values`: Replaced with `Arg::value_parser(PossibleValuesParser::new(...)).takes_value(true)`
|
||||
--> tests/derive_ui/bool_arg_enum.rs:7:11
|
||||
|
|
||||
7 | opts: bool,
|
||||
| ^^^^
|
||||
|
|
||||
= note: `#[warn(deprecated)]` on by default
|
||||
|
||||
error[E0277]: the trait bound `bool: ArgEnum` is not satisfied
|
||||
--> tests/derive_ui/bool_arg_enum.rs:7:11
|
||||
|
|
||||
7 | opts: bool,
|
||||
| ^^^^ the trait `ArgEnum` is not implemented for `bool`
|
||||
|
|
||||
note: required by `value_variants`
|
||||
--> src/derive.rs
|
||||
|
|
||||
| fn value_variants<'a>() -> &'a [Self];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0277]: the trait bound `bool: ArgEnum` is not satisfied
|
||||
--> tests/derive_ui/bool_arg_enum.rs:6:5
|
||||
|
|
||||
6 | / #[clap(short, arg_enum)]
|
||||
7 | | opts: bool,
|
||||
| |______________^ the trait `ArgEnum` is not implemented for `bool`
|
||||
|
|
||||
note: required by a bound in `ArgEnum`
|
||||
--> src/derive.rs
|
||||
|
|
||||
| / pub trait ArgEnum: Sized + Clone {
|
||||
| | /// All possible argument values, in display order.
|
||||
| | fn value_variants<'a>() -> &'a [Self];
|
||||
| |
|
||||
... |
|
||||
| | fn to_possible_value<'a>(&self) -> Option<PossibleValue<'a>>;
|
||||
| | }
|
||||
| |_^ required by this bound in `ArgEnum`
|
||||
|
||||
warning: use of deprecated associated function `clap::Arg::<'help>::possible_values`: Replaced with `Arg::value_parser(PossibleValuesParser::new(...)).takes_value(true)`
|
||||
--> tests/derive_ui/bool_arg_enum.rs:7:11
|
||||
|
|
||||
7 | opts: bool,
|
||||
| ^^^^
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
error: `value_parse` attribute conflicts with `parse` attribute
|
||||
error: `value_parser` attribute conflicts with `parse` attribute
|
||||
--> tests/derive_ui/parse_with_value_parser.rs:14:29
|
||||
|
|
||||
14 | #[clap(parse(from_str), value_parser = clap::value_parser!(String))]
|
||||
|
|
Loading…
Reference in a new issue