From 32e766957bba962efbe9f27450d29b29b7746603 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Tue, 2 Jun 2020 11:15:15 -0700 Subject: Import structopt-0.3.14 Change-Id: I03e7eaf9f442092701ce3175cb78c8ea2237f616 --- tests/argument_naming.rs | 311 +++++++++++++++++++++++ tests/arguments.rs | 86 +++++++ tests/author_version_about.rs | 58 +++++ tests/custom-string-parsers.rs | 306 ++++++++++++++++++++++ tests/default_value.rs | 19 ++ tests/deny-warnings.rs | 47 ++++ tests/doc-comments-help.rs | 185 ++++++++++++++ tests/explicit_name_no_renaming.rs | 32 +++ tests/flags.rs | 162 ++++++++++++ tests/flatten.rs | 129 ++++++++++ tests/issues.rs | 117 +++++++++ tests/macro-errors.rs | 13 + tests/nested-subcommands.rs | 193 ++++++++++++++ tests/non_literal_attributes.rs | 147 +++++++++++ tests/options.rs | 336 +++++++++++++++++++++++++ tests/privacy.rs | 32 +++ tests/raw_bool_literal.rs | 29 +++ tests/raw_idents.rs | 17 ++ tests/rename_all_env.rs | 46 ++++ tests/skip.rs | 148 +++++++++++ tests/special_types.rs | 73 ++++++ tests/subcommands.rs | 303 ++++++++++++++++++++++ tests/ui/bool_default_value.rs | 21 ++ tests/ui/bool_default_value.stderr | 5 + tests/ui/bool_required.rs | 21 ++ tests/ui/bool_required.stderr | 5 + tests/ui/enum_flatten.rs | 21 ++ tests/ui/enum_flatten.stderr | 6 + tests/ui/external_subcommand_wrong_type.rs | 19 ++ tests/ui/external_subcommand_wrong_type.stderr | 8 + tests/ui/flatten_and_doc.rs | 30 +++ tests/ui/flatten_and_doc.stderr | 5 + tests/ui/flatten_and_methods.rs | 29 +++ tests/ui/flatten_and_methods.stderr | 5 + tests/ui/flatten_and_parse.rs | 29 +++ tests/ui/flatten_and_parse.stderr | 5 + tests/ui/multiple_external_subcommand.rs | 21 ++ tests/ui/multiple_external_subcommand.stderr | 5 + tests/ui/non_existent_attr.rs | 21 ++ tests/ui/non_existent_attr.stderr | 5 + tests/ui/opt_opt_nonpositional.rs | 20 ++ tests/ui/opt_opt_nonpositional.stderr | 5 + tests/ui/opt_vec_nonpositional.rs | 20 ++ tests/ui/opt_vec_nonpositional.stderr | 5 + tests/ui/option_default_value.rs | 21 ++ tests/ui/option_default_value.stderr | 5 + tests/ui/option_required.rs | 21 ++ tests/ui/option_required.stderr | 5 + tests/ui/parse_empty_try_from_os.rs | 21 ++ tests/ui/parse_empty_try_from_os.stderr | 5 + tests/ui/parse_function_is_not_path.rs | 21 ++ tests/ui/parse_function_is_not_path.stderr | 5 + tests/ui/parse_literal_spec.rs | 21 ++ tests/ui/parse_literal_spec.stderr | 5 + tests/ui/parse_not_zero_args.rs | 21 ++ tests/ui/parse_not_zero_args.stderr | 5 + tests/ui/positional_bool.rs | 10 + tests/ui/positional_bool.stderr | 10 + tests/ui/raw.rs | 25 ++ tests/ui/raw.stderr | 19 ++ tests/ui/rename_all_wrong_casing.rs | 21 ++ tests/ui/rename_all_wrong_casing.stderr | 5 + tests/ui/skip_flatten.rs | 42 ++++ tests/ui/skip_flatten.stderr | 5 + tests/ui/skip_subcommand.rs | 42 ++++ tests/ui/skip_subcommand.stderr | 5 + tests/ui/skip_with_other_options.rs | 15 ++ tests/ui/skip_with_other_options.stderr | 5 + tests/ui/skip_without_default.rs | 29 +++ tests/ui/skip_without_default.stderr | 9 + tests/ui/struct_parse.rs | 21 ++ tests/ui/struct_parse.stderr | 5 + tests/ui/struct_subcommand.rs | 21 ++ tests/ui/struct_subcommand.stderr | 5 + tests/ui/structopt_empty_attr.rs | 22 ++ tests/ui/structopt_empty_attr.stderr | 5 + tests/ui/structopt_name_value_attr.rs | 22 ++ tests/ui/structopt_name_value_attr.stderr | 5 + tests/ui/subcommand_and_flatten.rs | 36 +++ tests/ui/subcommand_and_flatten.stderr | 5 + tests/ui/subcommand_and_methods.rs | 36 +++ tests/ui/subcommand_and_methods.stderr | 5 + tests/ui/subcommand_and_parse.rs | 36 +++ tests/ui/subcommand_and_parse.stderr | 5 + tests/ui/subcommand_opt_opt.rs | 36 +++ tests/ui/subcommand_opt_opt.stderr | 5 + tests/ui/subcommand_opt_vec.rs | 36 +++ tests/ui/subcommand_opt_vec.stderr | 5 + tests/ui/tuple_struct.rs | 18 ++ tests/ui/tuple_struct.stderr | 5 + tests/utils.rs | 45 ++++ tests/we_need_syn_full.rs | 19 ++ 92 files changed, 3896 insertions(+) create mode 100644 tests/argument_naming.rs create mode 100644 tests/arguments.rs create mode 100644 tests/author_version_about.rs create mode 100644 tests/custom-string-parsers.rs create mode 100644 tests/default_value.rs create mode 100644 tests/deny-warnings.rs create mode 100644 tests/doc-comments-help.rs create mode 100644 tests/explicit_name_no_renaming.rs create mode 100644 tests/flags.rs create mode 100644 tests/flatten.rs create mode 100644 tests/issues.rs create mode 100644 tests/macro-errors.rs create mode 100644 tests/nested-subcommands.rs create mode 100644 tests/non_literal_attributes.rs create mode 100644 tests/options.rs create mode 100644 tests/privacy.rs create mode 100644 tests/raw_bool_literal.rs create mode 100644 tests/raw_idents.rs create mode 100644 tests/rename_all_env.rs create mode 100644 tests/skip.rs create mode 100644 tests/special_types.rs create mode 100644 tests/subcommands.rs create mode 100644 tests/ui/bool_default_value.rs create mode 100644 tests/ui/bool_default_value.stderr create mode 100644 tests/ui/bool_required.rs create mode 100644 tests/ui/bool_required.stderr create mode 100644 tests/ui/enum_flatten.rs create mode 100644 tests/ui/enum_flatten.stderr create mode 100644 tests/ui/external_subcommand_wrong_type.rs create mode 100644 tests/ui/external_subcommand_wrong_type.stderr create mode 100644 tests/ui/flatten_and_doc.rs create mode 100644 tests/ui/flatten_and_doc.stderr create mode 100644 tests/ui/flatten_and_methods.rs create mode 100644 tests/ui/flatten_and_methods.stderr create mode 100644 tests/ui/flatten_and_parse.rs create mode 100644 tests/ui/flatten_and_parse.stderr create mode 100644 tests/ui/multiple_external_subcommand.rs create mode 100644 tests/ui/multiple_external_subcommand.stderr create mode 100644 tests/ui/non_existent_attr.rs create mode 100644 tests/ui/non_existent_attr.stderr create mode 100644 tests/ui/opt_opt_nonpositional.rs create mode 100644 tests/ui/opt_opt_nonpositional.stderr create mode 100644 tests/ui/opt_vec_nonpositional.rs create mode 100644 tests/ui/opt_vec_nonpositional.stderr create mode 100644 tests/ui/option_default_value.rs create mode 100644 tests/ui/option_default_value.stderr create mode 100644 tests/ui/option_required.rs create mode 100644 tests/ui/option_required.stderr create mode 100644 tests/ui/parse_empty_try_from_os.rs create mode 100644 tests/ui/parse_empty_try_from_os.stderr create mode 100644 tests/ui/parse_function_is_not_path.rs create mode 100644 tests/ui/parse_function_is_not_path.stderr create mode 100644 tests/ui/parse_literal_spec.rs create mode 100644 tests/ui/parse_literal_spec.stderr create mode 100644 tests/ui/parse_not_zero_args.rs create mode 100644 tests/ui/parse_not_zero_args.stderr create mode 100644 tests/ui/positional_bool.rs create mode 100644 tests/ui/positional_bool.stderr create mode 100644 tests/ui/raw.rs create mode 100644 tests/ui/raw.stderr create mode 100644 tests/ui/rename_all_wrong_casing.rs create mode 100644 tests/ui/rename_all_wrong_casing.stderr create mode 100644 tests/ui/skip_flatten.rs create mode 100644 tests/ui/skip_flatten.stderr create mode 100644 tests/ui/skip_subcommand.rs create mode 100644 tests/ui/skip_subcommand.stderr create mode 100644 tests/ui/skip_with_other_options.rs create mode 100644 tests/ui/skip_with_other_options.stderr create mode 100644 tests/ui/skip_without_default.rs create mode 100644 tests/ui/skip_without_default.stderr create mode 100644 tests/ui/struct_parse.rs create mode 100644 tests/ui/struct_parse.stderr create mode 100644 tests/ui/struct_subcommand.rs create mode 100644 tests/ui/struct_subcommand.stderr create mode 100644 tests/ui/structopt_empty_attr.rs create mode 100644 tests/ui/structopt_empty_attr.stderr create mode 100644 tests/ui/structopt_name_value_attr.rs create mode 100644 tests/ui/structopt_name_value_attr.stderr create mode 100644 tests/ui/subcommand_and_flatten.rs create mode 100644 tests/ui/subcommand_and_flatten.stderr create mode 100644 tests/ui/subcommand_and_methods.rs create mode 100644 tests/ui/subcommand_and_methods.stderr create mode 100644 tests/ui/subcommand_and_parse.rs create mode 100644 tests/ui/subcommand_and_parse.stderr create mode 100644 tests/ui/subcommand_opt_opt.rs create mode 100644 tests/ui/subcommand_opt_opt.stderr create mode 100644 tests/ui/subcommand_opt_vec.rs create mode 100644 tests/ui/subcommand_opt_vec.stderr create mode 100644 tests/ui/tuple_struct.rs create mode 100644 tests/ui/tuple_struct.stderr create mode 100644 tests/utils.rs create mode 100644 tests/we_need_syn_full.rs (limited to 'tests') diff --git a/tests/argument_naming.rs b/tests/argument_naming.rs new file mode 100644 index 0000000..e7fe3d5 --- /dev/null +++ b/tests/argument_naming.rs @@ -0,0 +1,311 @@ +use structopt::StructOpt; + +#[test] +fn test_single_word_enum_variant_is_default_renamed_into_kebab_case() { + #[derive(StructOpt, Debug, PartialEq)] + enum Opt { + Command { foo: u32 }, + } + + assert_eq!( + Opt::Command { foo: 0 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "command", "0"])) + ); +} + +#[test] +fn test_multi_word_enum_variant_is_renamed() { + #[derive(StructOpt, Debug, PartialEq)] + enum Opt { + FirstCommand { foo: u32 }, + } + + assert_eq!( + Opt::FirstCommand { foo: 0 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "first-command", "0"])) + ); +} + +#[test] +fn test_standalone_long_generates_kebab_case() { + #[derive(StructOpt, Debug, PartialEq)] + #[allow(non_snake_case)] + struct Opt { + #[structopt(long)] + FOO_OPTION: bool, + } + + assert_eq!( + Opt { FOO_OPTION: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--foo-option"])) + ); +} + +#[test] +fn test_custom_long_overwrites_default_name() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(long = "foo")] + foo_option: bool, + } + + assert_eq!( + Opt { foo_option: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--foo"])) + ); +} + +#[test] +fn test_standalone_long_uses_previous_defined_custom_name() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(name = "foo", long)] + foo_option: bool, + } + + assert_eq!( + Opt { foo_option: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--foo"])) + ); +} + +#[test] +fn test_standalone_long_ignores_afterwards_defined_custom_name() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(long, name = "foo")] + foo_option: bool, + } + + assert_eq!( + Opt { foo_option: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--foo-option"])) + ); +} + +#[test] +fn test_standalone_short_generates_kebab_case() { + #[derive(StructOpt, Debug, PartialEq)] + #[allow(non_snake_case)] + struct Opt { + #[structopt(short)] + FOO_OPTION: bool, + } + + assert_eq!( + Opt { FOO_OPTION: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-f"])) + ); +} + +#[test] +fn test_custom_short_overwrites_default_name() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(short = "o")] + foo_option: bool, + } + + assert_eq!( + Opt { foo_option: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-o"])) + ); +} + +#[test] +fn test_standalone_short_uses_previous_defined_custom_name() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(name = "option", short)] + foo_option: bool, + } + + assert_eq!( + Opt { foo_option: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-o"])) + ); +} + +#[test] +fn test_standalone_short_ignores_afterwards_defined_custom_name() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(short, name = "option")] + foo_option: bool, + } + + assert_eq!( + Opt { foo_option: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-f"])) + ); +} + +#[test] +fn test_standalone_long_uses_previous_defined_casing() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(rename_all = "screaming_snake", long)] + foo_option: bool, + } + + assert_eq!( + Opt { foo_option: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--FOO_OPTION"])) + ); +} + +#[test] +fn test_standalone_short_uses_previous_defined_casing() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(rename_all = "screaming_snake", short)] + foo_option: bool, + } + + assert_eq!( + Opt { foo_option: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-F"])) + ); +} + +#[test] +fn test_standalone_long_works_with_verbatim_casing() { + #[derive(StructOpt, Debug, PartialEq)] + #[allow(non_snake_case)] + struct Opt { + #[structopt(rename_all = "verbatim", long)] + _fOO_oPtiON: bool, + } + + assert_eq!( + Opt { _fOO_oPtiON: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--_fOO_oPtiON"])) + ); +} + +#[test] +fn test_standalone_short_works_with_verbatim_casing() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(rename_all = "verbatim", short)] + _foo: bool, + } + + assert_eq!( + Opt { _foo: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-_"])) + ); +} + +#[test] +fn test_rename_all_is_propagated_from_struct_to_fields() { + #[derive(StructOpt, Debug, PartialEq)] + #[structopt(rename_all = "screaming_snake")] + struct Opt { + #[structopt(long)] + foo: bool, + } + + assert_eq!( + Opt { foo: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--FOO"])) + ); +} + +#[test] +fn test_rename_all_is_not_propagated_from_struct_into_flattened() { + #[derive(StructOpt, Debug, PartialEq)] + #[structopt(rename_all = "screaming_snake")] + struct Opt { + #[structopt(flatten)] + foo: Foo, + } + + #[derive(StructOpt, Debug, PartialEq)] + struct Foo { + #[structopt(long)] + foo: bool, + } + + assert_eq!( + Opt { + foo: Foo { foo: true } + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--foo"])) + ); +} + +#[test] +fn test_rename_all_is_not_propagated_from_struct_into_subcommand() { + #[derive(StructOpt, Debug, PartialEq)] + #[structopt(rename_all = "screaming_snake")] + struct Opt { + #[structopt(subcommand)] + foo: Foo, + } + + #[derive(StructOpt, Debug, PartialEq)] + enum Foo { + Command { + #[structopt(long)] + foo: bool, + }, + } + + assert_eq!( + Opt { + foo: Foo::Command { foo: true } + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "command", "--foo"])) + ); +} + +#[test] +fn test_rename_all_is_propagated_from_enum_to_variants_and_their_fields() { + #[derive(StructOpt, Debug, PartialEq)] + #[structopt(rename_all = "screaming_snake")] + enum Opt { + FirstVariant, + SecondVariant { + #[structopt(long)] + foo: bool, + }, + } + + assert_eq!( + Opt::FirstVariant, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "FIRST_VARIANT"])) + ); + + assert_eq!( + Opt::SecondVariant { foo: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "SECOND_VARIANT", "--FOO"])) + ); +} + +#[test] +fn test_rename_all_is_propagation_can_be_overridden() { + #[derive(StructOpt, Debug, PartialEq)] + #[structopt(rename_all = "screaming_snake")] + enum Opt { + #[structopt(rename_all = "kebab_case")] + FirstVariant { + #[structopt(long)] + foo_option: bool, + }, + SecondVariant { + #[structopt(rename_all = "kebab_case", long)] + foo_option: bool, + }, + } + + assert_eq!( + Opt::FirstVariant { foo_option: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "first-variant", "--foo-option"])) + ); + + assert_eq!( + Opt::SecondVariant { foo_option: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "SECOND_VARIANT", "--foo-option"])) + ); +} diff --git a/tests/arguments.rs b/tests/arguments.rs new file mode 100644 index 0000000..96a0938 --- /dev/null +++ b/tests/arguments.rs @@ -0,0 +1,86 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::clap; +use structopt::StructOpt; + +#[test] +fn required_argument() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + arg: i32, + } + assert_eq!(Opt { arg: 42 }, Opt::from_iter(&["test", "42"])); + assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err()); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "42", "24"]) + .is_err()); +} + +#[test] +fn optional_argument() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + arg: Option, + } + assert_eq!(Opt { arg: Some(42) }, Opt::from_iter(&["test", "42"])); + assert_eq!(Opt { arg: None }, Opt::from_iter(&["test"])); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "42", "24"]) + .is_err()); +} + +#[test] +fn argument_with_default() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(default_value = "42")] + arg: i32, + } + assert_eq!(Opt { arg: 24 }, Opt::from_iter(&["test", "24"])); + assert_eq!(Opt { arg: 42 }, Opt::from_iter(&["test"])); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "42", "24"]) + .is_err()); +} + +#[test] +fn arguments() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + arg: Vec, + } + assert_eq!(Opt { arg: vec![24] }, Opt::from_iter(&["test", "24"])); + assert_eq!(Opt { arg: vec![] }, Opt::from_iter(&["test"])); + assert_eq!( + Opt { arg: vec![24, 42] }, + Opt::from_iter(&["test", "24", "42"]) + ); +} + +#[test] +fn arguments_safe() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + arg: Vec, + } + assert_eq!( + Opt { arg: vec![24] }, + Opt::from_iter_safe(&["test", "24"]).unwrap() + ); + assert_eq!(Opt { arg: vec![] }, Opt::from_iter_safe(&["test"]).unwrap()); + assert_eq!( + Opt { arg: vec![24, 42] }, + Opt::from_iter_safe(&["test", "24", "42"]).unwrap() + ); + + assert_eq!( + clap::ErrorKind::ValueValidation, + Opt::from_iter_safe(&["test", "NOPE"]).err().unwrap().kind + ); +} diff --git a/tests/author_version_about.rs b/tests/author_version_about.rs new file mode 100644 index 0000000..0f1c8b5 --- /dev/null +++ b/tests/author_version_about.rs @@ -0,0 +1,58 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod utils; + +use structopt::StructOpt; +use utils::*; + +#[test] +fn no_author_version_about() { + #[derive(StructOpt, PartialEq, Debug)] + #[structopt(name = "foo", no_version)] + struct Opt {} + + let output = get_long_help::(); + assert!(output.starts_with("foo \n\nUSAGE:")); +} + +#[test] +fn use_env() { + #[derive(StructOpt, PartialEq, Debug)] + #[structopt(author, about)] + struct Opt {} + + let output = get_long_help::(); + assert!(output.starts_with("structopt 0.")); + assert!(output.contains("Guillaume Pinot , others")); + assert!(output.contains("Parse command line argument by defining a struct.")); +} + +#[test] +fn explicit_version_not_str() { + const VERSION: &str = "custom version"; + + #[derive(StructOpt)] + #[structopt(version = VERSION)] + pub struct Opt {} + + let output = get_long_help::(); + assert!(output.contains("custom version")); +} + +#[test] +fn no_version_gets_propagated() { + #[derive(StructOpt, PartialEq, Debug)] + #[structopt(no_version)] + enum Action { + Move, + } + + let output = get_subcommand_long_help::("move"); + assert_eq!(output.lines().next(), Some("test-move ")); +} diff --git a/tests/custom-string-parsers.rs b/tests/custom-string-parsers.rs new file mode 100644 index 0000000..89070ed --- /dev/null +++ b/tests/custom-string-parsers.rs @@ -0,0 +1,306 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +use std::ffi::{CString, OsStr, OsString}; +use std::num::ParseIntError; +use std::path::PathBuf; + +#[derive(StructOpt, PartialEq, Debug)] +struct PathOpt { + #[structopt(short, long, parse(from_os_str))] + path: PathBuf, + + #[structopt(short, default_value = "../", parse(from_os_str))] + default_path: PathBuf, + + #[structopt(short, parse(from_os_str))] + vector_path: Vec, + + #[structopt(short, parse(from_os_str))] + option_path_1: Option, + + #[structopt(short = "q", parse(from_os_str))] + option_path_2: Option, +} + +#[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::from_clap(&PathOpt::clap().get_matches_from(&[ + "test", "-p", "/usr/bin", "-v", "/a/b/c", "-v", "/d/e/f", "-v", "/g/h/i", "-q", + "j.zip", + ])) + ); +} + +fn parse_hex(input: &str) -> Result { + u64::from_str_radix(input, 16) +} + +#[derive(StructOpt, PartialEq, Debug)] +struct HexOpt { + #[structopt(short, parse(try_from_str = parse_hex))] + number: u64, +} + +#[test] +#[allow(clippy::unreadable_literal)] +fn test_parse_hex() { + assert_eq!( + HexOpt { number: 5 }, + HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "5"])) + ); + assert_eq!( + HexOpt { number: 0xabcdef }, + HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "abcdef"])) + ); + + let err = HexOpt::clap() + .get_matches_from_safe(&["test", "-n", "gg"]) + .unwrap_err(); + assert!(err.message.contains("invalid digit found in string"), err); +} + +fn custom_parser_1(_: &str) -> &'static str { + "A" +} +fn custom_parser_2(_: &str) -> Result<&'static str, u32> { + Ok("B") +} +fn custom_parser_3(_: &OsStr) -> &'static str { + "C" +} +fn custom_parser_4(_: &OsStr) -> Result<&'static str, OsString> { + Ok("D") +} + +#[derive(StructOpt, PartialEq, Debug)] +struct NoOpOpt { + #[structopt(short, parse(from_str = custom_parser_1))] + a: &'static str, + #[structopt(short, parse(try_from_str = custom_parser_2))] + b: &'static str, + #[structopt(short, parse(from_os_str = custom_parser_3))] + c: &'static str, + #[structopt(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::from_clap( + &NoOpOpt::clap().get_matches_from(&["test", "-a=?", "-b=?", "-c=?", "-d=?"]) + ) + ); +} + +// Note: can't use `Vec` directly, as structopt would instead look for +// conversion function from `&str` to `u8`. +type Bytes = Vec; + +#[derive(StructOpt, PartialEq, Debug)] +struct DefaultedOpt { + #[structopt(short, parse(from_str))] + bytes: Bytes, + + #[structopt(short, parse(try_from_str))] + integer: u64, + + #[structopt(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::from_clap(&DefaultedOpt::clap().get_matches_from(&[ + "test", + "-b", + "E²=p²c²+m²c⁴", + "-i", + "9000", + "-p", + "src/lib.rs", + ])) + ); +} + +#[derive(PartialEq, Debug)] +struct Foo(u8); + +fn foo(value: u64) -> Foo { + Foo(value as u8) +} + +#[derive(StructOpt, PartialEq, Debug)] +struct Occurrences { + #[structopt(short, long, parse(from_occurrences))] + signed: i32, + + #[structopt(short, parse(from_occurrences))] + little_signed: i8, + + #[structopt(short, parse(from_occurrences))] + unsigned: usize, + + #[structopt(short = "r", parse(from_occurrences))] + little_unsigned: u8, + + #[structopt(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::from_clap(&Occurrences::clap().get_matches_from(&[ + "test", "-s", "--signed", "--signed", "-l", "-rrrr", "-cccc", "--custom", + ])) + ); +} + +#[test] +fn test_custom_bool() { + fn parse_bool(s: &str) -> Result { + match s { + "true" => Ok(true), + "false" => Ok(false), + _ => Err(format!("invalid bool {}", s)), + } + } + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short, parse(try_from_str = parse_bool))] + debug: bool, + #[structopt( + short, + default_value = "false", + parse(try_from_str = parse_bool) + )] + verbose: bool, + #[structopt(short, parse(try_from_str = parse_bool))] + tribool: Option, + #[structopt(short, parse(try_from_str = parse_bool))] + bitset: Vec, + } + + assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err()); + assert!(Opt::clap().get_matches_from_safe(&["test", "-d"]).is_err()); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "-dfoo"]) + .is_err()); + assert_eq!( + Opt { + debug: false, + verbose: false, + tribool: None, + bitset: vec![], + }, + Opt::from_iter(&["test", "-dfalse"]) + ); + assert_eq!( + Opt { + debug: true, + verbose: false, + tribool: None, + bitset: vec![], + }, + Opt::from_iter(&["test", "-dtrue"]) + ); + assert_eq!( + Opt { + debug: true, + verbose: false, + tribool: None, + bitset: vec![], + }, + Opt::from_iter(&["test", "-dtrue", "-vfalse"]) + ); + assert_eq!( + Opt { + debug: true, + verbose: true, + tribool: None, + bitset: vec![], + }, + Opt::from_iter(&["test", "-dtrue", "-vtrue"]) + ); + assert_eq!( + Opt { + debug: true, + verbose: false, + tribool: Some(false), + bitset: vec![], + }, + Opt::from_iter(&["test", "-dtrue", "-tfalse"]) + ); + assert_eq!( + Opt { + debug: true, + verbose: false, + tribool: Some(true), + bitset: vec![], + }, + Opt::from_iter(&["test", "-dtrue", "-ttrue"]) + ); + assert_eq!( + Opt { + debug: true, + verbose: false, + tribool: None, + bitset: vec![false, true, false, false], + }, + Opt::from_iter(&["test", "-dtrue", "-bfalse", "-btrue", "-bfalse", "-bfalse"]) + ); +} + +#[test] +fn test_cstring() { + #[derive(StructOpt)] + struct Opt { + #[structopt(parse(try_from_str = CString::new))] + c_string: CString, + } + assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err()); + assert_eq!(Opt::from_iter(&["test", "bla"]).c_string.to_bytes(), b"bla"); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "bla\0bla"]) + .is_err()); +} diff --git a/tests/default_value.rs b/tests/default_value.rs new file mode 100644 index 0000000..383bd23 --- /dev/null +++ b/tests/default_value.rs @@ -0,0 +1,19 @@ +use structopt::StructOpt; + +mod utils; + +use utils::*; + +#[test] +fn auto_default_value() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(default_value)] + arg: i32, + } + assert_eq!(Opt { arg: 0 }, Opt::from_iter(&["test"])); + assert_eq!(Opt { arg: 1 }, Opt::from_iter(&["test", "1"])); + + let help = get_long_help::(); + assert!(help.contains("[default: 0]")); +} diff --git a/tests/deny-warnings.rs b/tests/deny-warnings.rs new file mode 100644 index 0000000..721204a --- /dev/null +++ b/tests/deny-warnings.rs @@ -0,0 +1,47 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(warnings)] + +use structopt::StructOpt; + +fn try_str(s: &str) -> Result { + Ok(s.into()) +} + +#[test] +fn warning_never_struct() { + #[derive(Debug, PartialEq, StructOpt)] + struct Opt { + #[structopt(parse(try_from_str = try_str))] + s: String, + } + assert_eq!( + Opt { + s: "foo".to_string() + }, + Opt::from_iter(&["test", "foo"]) + ); +} + +#[test] +fn warning_never_enum() { + #[derive(Debug, PartialEq, StructOpt)] + enum Opt { + Foo { + #[structopt(parse(try_from_str = try_str))] + s: String, + }, + } + assert_eq!( + Opt::Foo { + s: "foo".to_string() + }, + Opt::from_iter(&["test", "foo", "foo"]) + ); +} diff --git a/tests/doc-comments-help.rs b/tests/doc-comments-help.rs new file mode 100644 index 0000000..1d31683 --- /dev/null +++ b/tests/doc-comments-help.rs @@ -0,0 +1,185 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod utils; + +use structopt::StructOpt; +use utils::*; + +#[test] +fn doc_comments() { + /// Lorem ipsum + #[derive(StructOpt, PartialEq, Debug)] + struct LoremIpsum { + /// Fooify a bar + /// and a baz + #[structopt(short, long)] + foo: bool, + } + + let help = get_long_help::(); + assert!(help.contains("Lorem ipsum")); + assert!(help.contains("Fooify a bar and a baz")); +} + +#[test] +fn help_is_better_than_comments() { + /// Lorem ipsum + #[derive(StructOpt, PartialEq, Debug)] + #[structopt(name = "lorem-ipsum", about = "Dolor sit amet")] + struct LoremIpsum { + /// Fooify a bar + #[structopt(short, long, help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES")] + foo: bool, + } + + let help = get_long_help::(); + 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(StructOpt, PartialEq, Debug)] + #[structopt(name = "lorem-ipsum", no_version)] + struct LoremIpsum {} + + let help = get_long_help::(); + assert!(help.starts_with("lorem-ipsum \nFoo.\n\nBar\n\nUSAGE:")); +} + +#[test] +fn field_long_doc_comment_both_help_long_help() { + /// Lorem ipsumclap + #[derive(StructOpt, PartialEq, Debug)] + #[structopt(name = "lorem-ipsum", about = "Dolor sit amet")] + struct LoremIpsum { + /// Dot is removed from multiline comments. + /// + /// Long help + #[structopt(long)] + foo: bool, + + /// Dot is removed from one short comment. + #[structopt(long)] + bar: bool, + } + + let short_help = get_help::(); + let long_help = get_long_help::(); + + 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(StructOpt, Debug)] + #[structopt(name = "lorem-ipsum", about = "Dolor sit amet")] + struct LoremIpsum { + #[structopt(subcommand)] + foo: SubCommand, + } + + #[derive(StructOpt, Debug)] + pub enum SubCommand { + /// DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES + /// + /// Or something else + Foo { + #[structopt(help = "foo")] + bars: Vec, + }, + } + + let short_help = get_help::(); + let long_help = get_subcommand_long_help::("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(StructOpt, Debug)] + #[structopt(verbatim_doc_comment)] + struct SeeFigure1 { + #[structopt(long)] + foo: bool, + } + + let help = get_long_help::(); + let sample = r#" + () + | + ( () ) + ) ________ // ) + () |\ \ // +( \\__ \ ______\// + \__) | | + | | | + \ | | + \|_______| + // \\ + (( || + \\ || + ( () || + ( () ) )"#; + + assert!(help.contains(sample)) +} + +#[test] +fn verbatim_doc_comment_field() { + #[derive(StructOpt, Debug)] + struct App { + /// This help ends in a period. + #[structopt(long, verbatim_doc_comment)] + foo: bool, + /// This help does not end in a period. + #[structopt(long)] + bar: bool, + } + + let help = get_long_help::(); + let sample = r#" + --bar + This help does not end in a period + + --foo + This help ends in a period."#; + + assert!(help.contains(sample)) +} diff --git a/tests/explicit_name_no_renaming.rs b/tests/explicit_name_no_renaming.rs new file mode 100644 index 0000000..eff7a86 --- /dev/null +++ b/tests/explicit_name_no_renaming.rs @@ -0,0 +1,32 @@ +mod utils; + +use structopt::StructOpt; +use utils::*; + +#[test] +fn explicit_short_long_no_rename() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short = ".", long = ".foo")] + foo: Vec, + } + + assert_eq!( + Opt { + foo: vec!["short".into(), "long".into()] + }, + Opt::from_iter(&["test", "-.", "short", "--.foo", "long"]) + ); +} + +#[test] +fn explicit_name_no_rename() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(name = ".options")] + foo: Vec, + } + + let help = get_long_help::(); + assert!(help.contains("[.options]...")) +} diff --git a/tests/flags.rs b/tests/flags.rs new file mode 100644 index 0000000..39a5dc3 --- /dev/null +++ b/tests/flags.rs @@ -0,0 +1,162 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[test] +fn unique_flag() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short, long)] + alice: bool, + } + + assert_eq!( + Opt { alice: false }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test"])) + ); + assert_eq!( + Opt { alice: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"])) + ); + assert_eq!( + Opt { alice: true }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--alice"])) + ); + assert!(Opt::clap().get_matches_from_safe(&["test", "-i"]).is_err()); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "-a", "foo"]) + .is_err()); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "-a", "-a"]) + .is_err()); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "-a", "--alice"]) + .is_err()); +} + +#[test] +fn multiple_flag() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short, long, parse(from_occurrences))] + alice: u64, + #[structopt(short, long, parse(from_occurrences))] + bob: u8, + } + + assert_eq!( + Opt { alice: 0, bob: 0 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test"])) + ); + assert_eq!( + Opt { alice: 1, bob: 0 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"])) + ); + assert_eq!( + Opt { alice: 2, bob: 0 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "-a"])) + ); + assert_eq!( + Opt { alice: 2, bob: 2 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "--alice", "-bb"])) + ); + assert_eq!( + Opt { alice: 3, bob: 1 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-aaa", "--bob"])) + ); + assert!(Opt::clap().get_matches_from_safe(&["test", "-i"]).is_err()); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "-a", "foo"]) + .is_err()); +} + +fn parse_from_flag(b: bool) -> std::sync::atomic::AtomicBool { + std::sync::atomic::AtomicBool::new(b) +} + +#[test] +fn non_bool_flags() { + #[derive(StructOpt, Debug)] + struct Opt { + #[structopt(short, long, parse(from_flag = parse_from_flag))] + alice: std::sync::atomic::AtomicBool, + #[structopt(short, long, parse(from_flag))] + bob: std::sync::atomic::AtomicBool, + } + + let falsey = Opt::from_clap(&Opt::clap().get_matches_from(&["test"])); + assert!(!falsey.alice.load(std::sync::atomic::Ordering::Relaxed)); + assert!(!falsey.bob.load(std::sync::atomic::Ordering::Relaxed)); + + let alice = Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"])); + assert!(alice.alice.load(std::sync::atomic::Ordering::Relaxed)); + assert!(!alice.bob.load(std::sync::atomic::Ordering::Relaxed)); + + let bob = Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-b"])); + assert!(!bob.alice.load(std::sync::atomic::Ordering::Relaxed)); + assert!(bob.bob.load(std::sync::atomic::Ordering::Relaxed)); + + let both = Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-b", "-a"])); + assert!(both.alice.load(std::sync::atomic::Ordering::Relaxed)); + assert!(both.bob.load(std::sync::atomic::Ordering::Relaxed)); +} + +#[test] +fn combined_flags() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short, long)] + alice: bool, + #[structopt(short, long, parse(from_occurrences))] + bob: u64, + } + + assert_eq!( + Opt { + alice: false, + bob: 0 + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test"])) + ); + assert_eq!( + Opt { + alice: true, + bob: 0 + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"])) + ); + assert_eq!( + Opt { + alice: true, + bob: 0 + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"])) + ); + assert_eq!( + Opt { + alice: false, + bob: 1 + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-b"])) + ); + assert_eq!( + Opt { + alice: true, + bob: 1 + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--alice", "--bob"])) + ); + assert_eq!( + Opt { + alice: true, + bob: 4 + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-bb", "-a", "-bb"])) + ); +} diff --git a/tests/flatten.rs b/tests/flatten.rs new file mode 100644 index 0000000..f01e44e --- /dev/null +++ b/tests/flatten.rs @@ -0,0 +1,129 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[test] +fn flatten() { + #[derive(StructOpt, PartialEq, Debug)] + struct Common { + arg: i32, + } + + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(flatten)] + common: Common, + } + assert_eq!( + Opt { + common: Common { arg: 42 } + }, + Opt::from_iter(&["test", "42"]) + ); + assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err()); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "42", "24"]) + .is_err()); +} + +#[test] +#[should_panic] +fn flatten_twice() { + #[derive(StructOpt, PartialEq, Debug)] + struct Common { + arg: i32, + } + + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(flatten)] + c1: Common, + // Defines "arg" twice, so this should not work. + #[structopt(flatten)] + c2: Common, + } + Opt::from_iter(&["test", "42", "43"]); +} + +#[test] +fn flatten_in_subcommand() { + #[derive(StructOpt, PartialEq, Debug)] + struct Common { + arg: i32, + } + + #[derive(StructOpt, PartialEq, Debug)] + struct Add { + #[structopt(short)] + interactive: bool, + #[structopt(flatten)] + common: Common, + } + + #[derive(StructOpt, PartialEq, Debug)] + enum Opt { + Fetch { + #[structopt(short)] + all: bool, + #[structopt(flatten)] + common: Common, + }, + + Add(Add), + } + + assert_eq!( + Opt::Fetch { + all: false, + common: Common { arg: 42 } + }, + Opt::from_iter(&["test", "fetch", "42"]) + ); + assert_eq!( + Opt::Add(Add { + interactive: true, + common: Common { arg: 43 } + }), + Opt::from_iter(&["test", "add", "-i", "43"]) + ); +} + +#[test] +fn merge_subcommands_with_flatten() { + #[derive(StructOpt, PartialEq, Debug)] + enum BaseCli { + Command1(Command1), + } + + #[derive(StructOpt, PartialEq, Debug)] + struct Command1 { + arg1: i32, + } + + #[derive(StructOpt, PartialEq, Debug)] + struct Command2 { + arg2: i32, + } + + #[derive(StructOpt, PartialEq, Debug)] + enum Opt { + #[structopt(flatten)] + BaseCli(BaseCli), + Command2(Command2), + } + + assert_eq!( + Opt::BaseCli(BaseCli::Command1(Command1 { arg1: 42 })), + Opt::from_iter(&["test", "command1", "42"]) + ); + assert_eq!( + Opt::Command2(Command2 { arg2: 43 }), + Opt::from_iter(&["test", "command2", "43"]) + ); +} diff --git a/tests/issues.rs b/tests/issues.rs new file mode 100644 index 0000000..8b4ac4b --- /dev/null +++ b/tests/issues.rs @@ -0,0 +1,117 @@ +// https://github.com/TeXitoi/structopt/issues/{NUMBER} + +mod utils; +use utils::*; + +use structopt::StructOpt; + +#[test] +fn issue_151() { + use structopt::{clap::ArgGroup, StructOpt}; + + #[derive(StructOpt, Debug)] + #[structopt(group = ArgGroup::with_name("verb").required(true).multiple(true))] + struct Opt { + #[structopt(long, group = "verb")] + foo: bool, + #[structopt(long, group = "verb")] + bar: bool, + } + + #[derive(Debug, StructOpt)] + struct Cli { + #[structopt(flatten)] + a: Opt, + } + + assert!(Cli::clap().get_matches_from_safe(&["test"]).is_err()); + assert!(Cli::clap() + .get_matches_from_safe(&["test", "--foo"]) + .is_ok()); + assert!(Cli::clap() + .get_matches_from_safe(&["test", "--bar"]) + .is_ok()); + assert!(Cli::clap() + .get_matches_from_safe(&["test", "--zebra"]) + .is_err()); + assert!(Cli::clap() + .get_matches_from_safe(&["test", "--foo", "--bar"]) + .is_ok()); +} + +#[test] +fn issue_289() { + use structopt::{clap::AppSettings, StructOpt}; + + #[derive(StructOpt)] + #[structopt(setting = AppSettings::InferSubcommands)] + enum Args { + SomeCommand(SubSubCommand), + AnotherCommand, + } + + #[derive(StructOpt)] + #[structopt(setting = AppSettings::InferSubcommands)] + enum SubSubCommand { + TestCommand, + } + + assert!(Args::clap() + .get_matches_from_safe(&["test", "some-command", "test-command"]) + .is_ok()); + assert!(Args::clap() + .get_matches_from_safe(&["test", "some", "test-command"]) + .is_ok()); + assert!(Args::clap() + .get_matches_from_safe(&["test", "some-command", "test"]) + .is_ok()); + assert!(Args::clap() + .get_matches_from_safe(&["test", "some", "test"]) + .is_ok()); +} + +#[test] +fn issue_324() { + fn my_version() -> &'static str { + "MY_VERSION" + } + + #[derive(StructOpt)] + #[structopt(version = my_version())] + struct Opt { + #[structopt(subcommand)] + _cmd: Option, + } + + #[derive(StructOpt)] + enum SubCommand { + Start, + } + + let help = get_long_help::(); + assert!(help.contains("MY_VERSION")); +} + +#[test] +fn issue_359() { + #[derive(Debug, PartialEq, StructOpt)] + struct Opt { + #[structopt(subcommand)] + sub: Subcommands, + } + + #[derive(Debug, PartialEq, StructOpt)] + enum Subcommands { + Add, + + #[structopt(external_subcommand)] + Other(Vec), + } + + assert_eq!( + Opt { + sub: Subcommands::Other(vec!["only_one_arg".into()]) + }, + Opt::from_iter(&["test", "only_one_arg"]) + ); +} diff --git a/tests/macro-errors.rs b/tests/macro-errors.rs new file mode 100644 index 0000000..ae4f5a2 --- /dev/null +++ b/tests/macro-errors.rs @@ -0,0 +1,13 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed + +#[rustversion::attr(any(not(stable), before(1.39)), ignore)] +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +} diff --git a/tests/nested-subcommands.rs b/tests/nested-subcommands.rs new file mode 100644 index 0000000..1fbd166 --- /dev/null +++ b/tests/nested-subcommands.rs @@ -0,0 +1,193 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, PartialEq, Debug)] +struct Opt { + #[structopt(short, long)] + force: bool, + #[structopt(short, long, parse(from_occurrences))] + verbose: u64, + #[structopt(subcommand)] + cmd: Sub, +} + +#[derive(StructOpt, PartialEq, Debug)] +enum Sub { + Fetch {}, + Add {}, +} + +#[derive(StructOpt, PartialEq, Debug)] +struct Opt2 { + #[structopt(short, long)] + force: bool, + #[structopt(short, long, parse(from_occurrences))] + verbose: u64, + #[structopt(subcommand)] + cmd: Option, +} + +#[test] +fn test_no_cmd() { + let result = Opt::clap().get_matches_from_safe(&["test"]); + assert!(result.is_err()); + + assert_eq!( + Opt2 { + force: false, + verbose: 0, + cmd: None + }, + Opt2::from_clap(&Opt2::clap().get_matches_from(&["test"])) + ); +} + +#[test] +fn test_fetch() { + assert_eq!( + Opt { + force: false, + verbose: 3, + cmd: Sub::Fetch {} + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-vvv", "fetch"])) + ); + assert_eq!( + Opt { + force: true, + verbose: 0, + cmd: Sub::Fetch {} + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--force", "fetch"])) + ); +} + +#[test] +fn test_add() { + assert_eq!( + Opt { + force: false, + verbose: 0, + cmd: Sub::Add {} + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add"])) + ); + assert_eq!( + Opt { + force: false, + verbose: 2, + cmd: Sub::Add {} + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-vv", "add"])) + ); +} + +#[test] +fn test_badinput() { + let result = Opt::clap().get_matches_from_safe(&["test", "badcmd"]); + assert!(result.is_err()); + let result = Opt::clap().get_matches_from_safe(&["test", "add", "--verbose"]); + assert!(result.is_err()); + let result = Opt::clap().get_matches_from_safe(&["test", "--badopt", "add"]); + assert!(result.is_err()); + let result = Opt::clap().get_matches_from_safe(&["test", "add", "--badopt"]); + assert!(result.is_err()); +} + +#[derive(StructOpt, PartialEq, Debug)] +struct Opt3 { + #[structopt(short, long)] + all: bool, + #[structopt(subcommand)] + cmd: Sub2, +} + +#[derive(StructOpt, PartialEq, Debug)] +enum Sub2 { + Foo { + file: String, + #[structopt(subcommand)] + cmd: Sub3, + }, + Bar {}, +} + +#[derive(StructOpt, 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::from_clap( + &Opt3::clap().get_matches_from(&["test", "--all", "foo", "lib.rs", "quux"]) + ) + ); +} + +#[derive(StructOpt, PartialEq, Debug)] +enum SubSubCmdWithOption { + Remote { + #[structopt(subcommand)] + cmd: Option, + }, + Stash { + #[structopt(subcommand)] + cmd: Stash, + }, +} +#[derive(StructOpt, PartialEq, Debug)] +enum Remote { + Add { name: String, url: String }, + Remove { name: String }, +} + +#[derive(StructOpt, PartialEq, Debug)] +enum Stash { + Save, + Pop, +} + +#[test] +fn sub_sub_cmd_with_option() { + fn make(args: &[&str]) -> Option { + SubSubCmdWithOption::clap() + .get_matches_from_safe(args) + .ok() + .map(|m| SubSubCmdWithOption::from_clap(&m)) + } + 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"])); +} diff --git a/tests/non_literal_attributes.rs b/tests/non_literal_attributes.rs new file mode 100644 index 0000000..75b6b71 --- /dev/null +++ b/tests/non_literal_attributes.rs @@ -0,0 +1,147 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::clap::AppSettings; +use structopt::StructOpt; + +pub const DISPLAY_ORDER: usize = 2; + +// Check if the global settings compile +#[derive(StructOpt, Debug, PartialEq, Eq)] +#[structopt(global_settings = &[AppSettings::ColoredHelp])] +struct Opt { + #[structopt( + long = "x", + display_order = DISPLAY_ORDER, + next_line_help = true, + default_value = "0", + require_equals = true + )] + x: i32, + + #[structopt(short = "l", long = "level", aliases = &["set-level", "lvl"])] + level: String, + + #[structopt(long("values"))] + values: Vec, + + #[structopt(name = "FILE", requires_if("FILE", "values"))] + files: Vec, +} + +#[test] +fn test_slice() { + assert_eq!( + Opt { + x: 0, + level: "1".to_string(), + files: Vec::new(), + values: vec![], + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1"])) + ); + assert_eq!( + Opt { + x: 0, + level: "1".to_string(), + files: Vec::new(), + values: vec![], + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--level", "1"])) + ); + assert_eq!( + Opt { + x: 0, + level: "1".to_string(), + files: Vec::new(), + values: vec![], + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--set-level", "1"])) + ); + assert_eq!( + Opt { + x: 0, + level: "1".to_string(), + files: Vec::new(), + values: vec![], + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--lvl", "1"])) + ); +} + +#[test] +fn test_multi_args() { + assert_eq!( + Opt { + x: 0, + level: "1".to_string(), + files: vec!["file".to_string()], + values: vec![], + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1", "file"])) + ); + assert_eq!( + Opt { + x: 0, + level: "1".to_string(), + files: vec!["FILE".to_string()], + values: vec![1], + }, + Opt::from_clap( + &Opt::clap().get_matches_from(&["test", "-l", "1", "--values", "1", "--", "FILE"]), + ) + ); +} + +#[test] +fn test_multi_args_fail() { + let result = Opt::clap().get_matches_from_safe(&["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::from_clap(&Opt::clap().get_matches_from(&["test", "-l", "1", "--x=1"])) + ); + let result = Opt::clap().get_matches_from_safe(&["test", "-l", "1", "--x", "1"]); + assert!(result.is_err()); +} + +fn parse_hex(input: &str) -> Result { + u64::from_str_radix(input, 16) +} + +#[derive(StructOpt, PartialEq, Debug)] +struct HexOpt { + #[structopt(short = "n", parse(try_from_str = parse_hex))] + number: u64, +} + +#[test] +fn test_parse_hex_function_path() { + assert_eq!( + HexOpt { number: 5 }, + HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "5"])) + ); + assert_eq!( + HexOpt { number: 0xabcdef }, + HexOpt::from_clap(&HexOpt::clap().get_matches_from(&["test", "-n", "abcdef"])) + ); + + let err = HexOpt::clap() + .get_matches_from_safe(&["test", "-n", "gg"]) + .unwrap_err(); + assert!(err.message.contains("invalid digit found in string"), err); +} diff --git a/tests/options.rs b/tests/options.rs new file mode 100644 index 0000000..803abb4 --- /dev/null +++ b/tests/options.rs @@ -0,0 +1,336 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[test] +fn required_option() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short, long)] + arg: i32, + } + assert_eq!( + Opt { arg: 42 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42"])) + ); + assert_eq!( + Opt { arg: 42 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "42"])) + ); + assert_eq!( + Opt { arg: 42 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--arg", "42"])) + ); + assert!(Opt::clap().get_matches_from_safe(&["test"]).is_err()); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "-a42", "-a24"]) + .is_err()); +} + +#[test] +fn optional_option() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short)] + arg: Option, + } + assert_eq!( + Opt { arg: Some(42) }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42"])) + ); + assert_eq!( + Opt { arg: None }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test"])) + ); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "-a42", "-a24"]) + .is_err()); +} + +#[test] +fn option_with_default() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short, default_value = "42")] + arg: i32, + } + assert_eq!( + Opt { arg: 24 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"])) + ); + assert_eq!( + Opt { arg: 42 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test"])) + ); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "-a42", "-a24"]) + .is_err()); +} + +#[test] +fn option_with_raw_default() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short, default_value = "42")] + arg: i32, + } + assert_eq!( + Opt { arg: 24 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"])) + ); + assert_eq!( + Opt { arg: 42 }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test"])) + ); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "-a42", "-a24"]) + .is_err()); +} + +#[test] +fn options() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short, long)] + arg: Vec, + } + assert_eq!( + Opt { arg: vec![24] }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24"])) + ); + assert_eq!( + Opt { arg: vec![] }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test"])) + ); + assert_eq!( + Opt { arg: vec![24, 42] }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a24", "--arg", "42"])) + ); +} + +#[test] +fn empy_default_value() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short, default_value = "")] + arg: String, + } + assert_eq!(Opt { arg: "".into() }, Opt::from_iter(&["test"])); + assert_eq!( + Opt { arg: "foo".into() }, + Opt::from_iter(&["test", "-afoo"]) + ); +} + +#[test] +fn option_from_str() { + #[derive(Debug, PartialEq)] + struct A; + + impl<'a> From<&'a str> for A { + fn from(_: &str) -> A { + A + } + } + + #[derive(Debug, StructOpt, PartialEq)] + struct Opt { + #[structopt(parse(from_str))] + a: Option, + } + + assert_eq!(Opt { a: None }, Opt::from_iter(&["test"])); + assert_eq!(Opt { a: Some(A) }, Opt::from_iter(&["test", "foo"])); +} + +#[test] +fn optional_argument_for_optional_option() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short)] + #[allow(clippy::option_option)] + arg: Option>, + } + assert_eq!( + Opt { + arg: Some(Some(42)) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42"])) + ); + assert_eq!( + Opt { arg: Some(None) }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"])) + ); + assert_eq!( + Opt { arg: None }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test"])) + ); + assert!(Opt::clap() + .get_matches_from_safe(&["test", "-a42", "-a24"]) + .is_err()); +} + +#[test] +fn two_option_options() { + #[derive(StructOpt, PartialEq, Debug)] + #[allow(clippy::option_option)] + struct Opt { + #[structopt(short)] + arg: Option>, + + #[structopt(long)] + field: Option>, + } + assert_eq!( + Opt { + arg: Some(Some(42)), + field: Some(Some("f".into())) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42", "--field", "f"])) + ); + assert_eq!( + Opt { + arg: Some(Some(42)), + field: Some(None) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a42", "--field"])) + ); + assert_eq!( + Opt { + arg: Some(None), + field: Some(None) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "--field"])) + ); + assert_eq!( + Opt { + arg: Some(None), + field: Some(Some("f".into())) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "--field", "f"])) + ); + assert_eq!( + Opt { + arg: None, + field: Some(None) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "--field"])) + ); + assert_eq!( + Opt { + arg: None, + field: None + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test"])) + ); +} + +#[test] +fn optional_vec() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short)] + arg: Option>, + } + assert_eq!( + Opt { arg: Some(vec![1]) }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "1"])) + ); + + assert_eq!( + Opt { + arg: Some(vec![1, 2]) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a1", "-a2"])) + ); + + assert_eq!( + Opt { + arg: Some(vec![1, 2]) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a1", "-a2", "-a"])) + ); + + assert_eq!( + Opt { + arg: Some(vec![1, 2]) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a1", "-a", "-a2"])) + ); + + assert_eq!( + Opt { + arg: Some(vec![1, 2]) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "1", "2"])) + ); + + assert_eq!( + Opt { + arg: Some(vec![1, 2, 3]) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "1", "2", "-a", "3"])) + ); + + assert_eq!( + Opt { arg: Some(vec![]) }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a"])) + ); + + assert_eq!( + Opt { arg: Some(vec![]) }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "-a"])) + ); + + assert_eq!( + Opt { arg: None }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test"])) + ); +} + +#[test] +fn two_optional_vecs() { + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(short)] + arg: Option>, + + #[structopt(short)] + b: Option>, + } + + assert_eq!( + Opt { + arg: Some(vec![1]), + b: Some(vec![]) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "1", "-b"])) + ); + + assert_eq!( + Opt { + arg: Some(vec![1]), + b: Some(vec![]) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a", "-b", "-a1"])) + ); + + assert_eq!( + Opt { + arg: Some(vec![1, 2]), + b: Some(vec![1, 2]) + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "-a1", "-a2", "-b1", "-b2"])) + ); + + assert_eq!( + Opt { arg: None, b: None }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test"])) + ); +} diff --git a/tests/privacy.rs b/tests/privacy.rs new file mode 100644 index 0000000..730bbce --- /dev/null +++ b/tests/privacy.rs @@ -0,0 +1,32 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +mod options { + use super::StructOpt; + + #[derive(Debug, StructOpt)] + pub struct Options { + #[structopt(subcommand)] + pub subcommand: super::subcommands::SubCommand, + } +} + +mod subcommands { + use super::StructOpt; + + #[derive(Debug, StructOpt)] + pub enum SubCommand { + /// foo + Foo { + /// foo + bars: Vec, + }, + } +} diff --git a/tests/raw_bool_literal.rs b/tests/raw_bool_literal.rs new file mode 100644 index 0000000..faf8628 --- /dev/null +++ b/tests/raw_bool_literal.rs @@ -0,0 +1,29 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[test] +fn raw_bool_literal() { + #[derive(StructOpt, Debug, PartialEq)] + #[structopt(no_version, name = "raw_bool")] + struct Opt { + #[structopt(raw(false))] + a: String, + #[structopt(raw(true))] + b: String, + } + + assert_eq!( + Opt { + a: "one".into(), + b: "--help".into() + }, + Opt::from_iter(&["test", "one", "--", "--help"]) + ); +} diff --git a/tests/raw_idents.rs b/tests/raw_idents.rs new file mode 100644 index 0000000..c00ff66 --- /dev/null +++ b/tests/raw_idents.rs @@ -0,0 +1,17 @@ +use structopt::StructOpt; + +#[test] +fn raw_idents() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(short, long)] + r#type: Vec, + } + + assert_eq!( + Opt { + r#type: vec!["long".into(), "short".into()] + }, + Opt::from_iter(&["test", "--type", "long", "-t", "short"]) + ); +} diff --git a/tests/rename_all_env.rs b/tests/rename_all_env.rs new file mode 100644 index 0000000..1979e84 --- /dev/null +++ b/tests/rename_all_env.rs @@ -0,0 +1,46 @@ +mod utils; + +use structopt::StructOpt; +use utils::*; + +#[test] +fn it_works() { + #[derive(Debug, PartialEq, StructOpt)] + #[structopt(rename_all_env = "kebab")] + struct BehaviorModel { + #[structopt(env)] + be_nice: String, + } + + let help = get_help::(); + assert!(help.contains("[env: be-nice=]")); +} + +#[test] +fn default_is_screaming() { + #[derive(Debug, PartialEq, StructOpt)] + struct BehaviorModel { + #[structopt(env)] + be_nice: String, + } + + let help = get_help::(); + assert!(help.contains("[env: BE_NICE=]")); +} + +#[test] +fn overridable() { + #[derive(Debug, PartialEq, StructOpt)] + #[structopt(rename_all_env = "kebab")] + struct BehaviorModel { + #[structopt(env)] + be_nice: String, + + #[structopt(rename_all_env = "pascal", env)] + be_agressive: String, + } + + let help = get_help::(); + assert!(help.contains("[env: be-nice=]")); + assert!(help.contains("[env: BeAgressive=]")); +} diff --git a/tests/skip.rs b/tests/skip.rs new file mode 100644 index 0000000..47682d8 --- /dev/null +++ b/tests/skip.rs @@ -0,0 +1,148 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[test] +fn skip_1() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(short)] + x: u32, + #[structopt(skip)] + s: u32, + } + + assert!(Opt::from_iter_safe(&["test", "-x", "10", "20"]).is_err()); + assert_eq!( + Opt::from_iter(&["test", "-x", "10"]), + Opt { + x: 10, + s: 0, // default + } + ); +} + +#[test] +fn skip_2() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(short)] + x: u32, + #[structopt(skip)] + ss: String, + #[structopt(skip)] + sn: u8, + y: u32, + #[structopt(skip)] + sz: u16, + t: u32, + } + + assert_eq!( + Opt::from_iter(&["test", "-x", "10", "20", "30"]), + 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 { + return Kind::B; + } + } + + #[derive(StructOpt, Debug, PartialEq)] + pub struct Opt { + #[structopt(long, short)] + number: u32, + #[structopt(skip)] + k: Kind, + #[structopt(skip)] + v: Vec, + } + + assert_eq!( + Opt::from_iter(&["test", "-n", "10"]), + Opt { + number: 10, + k: Kind::B, + v: vec![], + } + ); +} + +#[test] +fn skip_help_doc_comments() { + #[derive(StructOpt, Debug, PartialEq)] + pub struct Opt { + #[structopt(skip, help = "internal_stuff")] + a: u32, + + #[structopt(skip, long_help = "internal_stuff\ndo not touch")] + b: u32, + + /// Not meant to be used by clap. + /// + /// I want a default here. + #[structopt(skip)] + c: u32, + + #[structopt(short, parse(try_from_str))] + n: u32, + } + + assert_eq!( + Opt::from_iter(&["test", "-n", "10"]), + Opt { + n: 10, + a: 0, + b: 0, + c: 0, + } + ); +} + +#[test] +fn skip_val() { + #[derive(StructOpt, Debug, PartialEq)] + pub struct Opt { + #[structopt(long, short)] + number: u32, + + #[structopt(skip = "key")] + k: String, + + #[structopt(skip = vec![1, 2, 3])] + v: Vec, + } + + assert_eq!( + Opt::from_iter(&["test", "-n", "10"]), + Opt { + number: 10, + k: "key".into(), + v: vec![1, 2, 3] + } + ); +} diff --git a/tests/special_types.rs b/tests/special_types.rs new file mode 100644 index 0000000..ffed5e2 --- /dev/null +++ b/tests/special_types.rs @@ -0,0 +1,73 @@ +//! Checks that types like `::std::option::Option` are not special + +use structopt::StructOpt; + +#[rustversion::since(1.37)] +#[test] +fn special_types_bool() { + 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 { + Ok(self::bool(s.into())) + } + } + }; + + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + arg: inner::bool, + } + + assert_eq!( + Opt { + arg: inner::bool("success".into()) + }, + Opt::from_iter(&["test", "success"]) + ); +} + +#[test] +fn special_types_option() { + fn parser(s: &str) -> Option { + Some(s.to_string()) + } + + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(parse(from_str = parser))] + arg: ::std::option::Option, + } + + assert_eq!( + Opt { + arg: Some("success".into()) + }, + Opt::from_iter(&["test", "success"]) + ); +} + +#[test] +fn special_types_vec() { + fn parser(s: &str) -> Vec { + vec![s.to_string()] + } + + #[derive(StructOpt, PartialEq, Debug)] + struct Opt { + #[structopt(parse(from_str = parser))] + arg: ::std::vec::Vec, + } + + assert_eq!( + Opt { + arg: vec!["success".into()] + }, + Opt::from_iter(&["test", "success"]) + ); +} diff --git a/tests/subcommands.rs b/tests/subcommands.rs new file mode 100644 index 0000000..1fc8e76 --- /dev/null +++ b/tests/subcommands.rs @@ -0,0 +1,303 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod utils; + +use structopt::StructOpt; +use utils::*; + +#[derive(StructOpt, PartialEq, Debug)] +enum Opt { + /// Fetch stuff from GitHub + Fetch { + #[structopt(long)] + all: bool, + #[structopt(short, long)] + /// Overwrite local branches. + force: bool, + repo: String, + }, + + Add { + #[structopt(short, long)] + interactive: bool, + #[structopt(short, long)] + verbose: bool, + }, +} + +#[test] +fn test_fetch() { + assert_eq!( + Opt::Fetch { + all: true, + force: false, + repo: "origin".to_string() + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "fetch", "--all", "origin"])) + ); + assert_eq!( + Opt::Fetch { + all: false, + force: true, + repo: "origin".to_string() + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "fetch", "-f", "origin"])) + ); +} + +#[test] +fn test_add() { + assert_eq!( + Opt::Add { + interactive: false, + verbose: false + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add"])) + ); + assert_eq!( + Opt::Add { + interactive: true, + verbose: true + }, + Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add", "-i", "-v"])) + ); +} + +#[test] +fn test_no_parse() { + let result = Opt::clap().get_matches_from_safe(&["test", "badcmd", "-i", "-v"]); + assert!(result.is_err()); + + let result = Opt::clap().get_matches_from_safe(&["test", "add", "--badoption"]); + assert!(result.is_err()); + + let result = Opt::clap().get_matches_from_safe(&["test"]); + assert!(result.is_err()); +} + +#[derive(StructOpt, 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::from_clap(&Opt2::clap().get_matches_from(&["test", "do-something", "blah"])) + ); +} + +#[derive(StructOpt, PartialEq, Debug)] +enum Opt3 { + Add, + Init, + Fetch, +} + +#[test] +fn test_null_commands() { + assert_eq!( + Opt3::Add, + Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "add"])) + ); + assert_eq!( + Opt3::Init, + Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "init"])) + ); + assert_eq!( + Opt3::Fetch, + Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "fetch"])) + ); +} + +#[derive(StructOpt, PartialEq, Debug)] +#[structopt(about = "Not shown")] +struct Add { + file: String, +} +/// Not shown +#[derive(StructOpt, PartialEq, Debug)] +struct Fetch { + remote: String, +} +#[derive(StructOpt, 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::from_clap(&Opt4::clap().get_matches_from(&["test", "add", "f"])) + ); + assert_eq!( + Opt4::Init, + Opt4::from_clap(&Opt4::clap().get_matches_from(&["test", "init"])) + ); + assert_eq!( + Opt4::Fetch(Fetch { + remote: "origin".to_string() + }), + Opt4::from_clap(&Opt4::clap().get_matches_from(&["test", "fetch", "origin"])) + ); + + let output = get_long_help::(); + + assert!(output.contains("download history from remote")); + assert!(output.contains("Add a file")); + assert!(!output.contains("Not shown")); +} + +#[test] +fn enum_in_enum_subsubcommand() { + #[derive(StructOpt, Debug, PartialEq)] + pub enum Opt { + Daemon(DaemonCommand), + } + + #[derive(StructOpt, Debug, PartialEq)] + pub enum DaemonCommand { + Start, + Stop, + } + + let result = Opt::clap().get_matches_from_safe(&["test"]); + assert!(result.is_err()); + + let result = Opt::clap().get_matches_from_safe(&["test", "daemon"]); + assert!(result.is_err()); + + let result = Opt::from_iter(&["test", "daemon", "start"]); + assert_eq!(Opt::Daemon(DaemonCommand::Start), result); +} + +#[test] +fn flatten_enum() { + #[derive(StructOpt, Debug, PartialEq)] + struct Opt { + #[structopt(flatten)] + sub_cmd: SubCmd, + } + #[derive(StructOpt, Debug, PartialEq)] + enum SubCmd { + Foo, + Bar, + } + + assert!(Opt::from_iter_safe(&["test"]).is_err()); + assert_eq!( + Opt::from_iter(&["test", "foo"]), + Opt { + sub_cmd: SubCmd::Foo + } + ); +} + +#[test] +fn external_subcommand() { + #[derive(Debug, PartialEq, StructOpt)] + struct Opt { + #[structopt(subcommand)] + sub: Subcommands, + } + + #[derive(Debug, PartialEq, StructOpt)] + enum Subcommands { + Add, + Remove, + #[structopt(external_subcommand)] + Other(Vec), + } + + assert_eq!( + Opt::from_iter(&["test", "add"]), + Opt { + sub: Subcommands::Add + } + ); + + assert_eq!( + Opt::from_iter(&["test", "remove"]), + Opt { + sub: Subcommands::Remove + } + ); + + assert_eq!( + Opt::from_iter(&["test", "git", "status"]), + Opt { + sub: Subcommands::Other(vec!["git".into(), "status".into()]) + } + ); + + assert!(Opt::from_iter_safe(&["test"]).is_err()); +} + +#[test] +fn external_subcommand_os_string() { + use std::ffi::OsString; + + #[derive(Debug, PartialEq, StructOpt)] + struct Opt { + #[structopt(subcommand)] + sub: Subcommands, + } + + #[derive(Debug, PartialEq, StructOpt)] + enum Subcommands { + #[structopt(external_subcommand)] + Other(Vec), + } + + assert_eq!( + Opt::from_iter(&["test", "git", "status"]), + Opt { + sub: Subcommands::Other(vec!["git".into(), "status".into()]) + } + ); + + assert!(Opt::from_iter_safe(&["test"]).is_err()); +} + +#[test] +fn external_subcommand_optional() { + #[derive(Debug, PartialEq, StructOpt)] + struct Opt { + #[structopt(subcommand)] + sub: Option, + } + + #[derive(Debug, PartialEq, StructOpt)] + enum Subcommands { + #[structopt(external_subcommand)] + Other(Vec), + } + + assert_eq!( + Opt::from_iter(&["test", "git", "status"]), + Opt { + sub: Some(Subcommands::Other(vec!["git".into(), "status".into()])) + } + ); + + assert_eq!(Opt::from_iter(&["test"]), Opt { sub: None }); +} diff --git a/tests/ui/bool_default_value.rs b/tests/ui/bool_default_value.rs new file mode 100644 index 0000000..9bdb0c9 --- /dev/null +++ b/tests/ui/bool_default_value.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt(short, default_value = true)] + b: bool, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/bool_default_value.stderr b/tests/ui/bool_default_value.stderr new file mode 100644 index 0000000..1e26a2d --- /dev/null +++ b/tests/ui/bool_default_value.stderr @@ -0,0 +1,5 @@ +error: default_value is meaningless for bool + --> $DIR/bool_default_value.rs:14:24 + | +14 | #[structopt(short, default_value = true)] + | ^^^^^^^^^^^^^ diff --git a/tests/ui/bool_required.rs b/tests/ui/bool_required.rs new file mode 100644 index 0000000..018223c --- /dev/null +++ b/tests/ui/bool_required.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt(short, required = true)] + b: bool, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/bool_required.stderr b/tests/ui/bool_required.stderr new file mode 100644 index 0000000..0b80d48 --- /dev/null +++ b/tests/ui/bool_required.stderr @@ -0,0 +1,5 @@ +error: required is meaningless for bool + --> $DIR/bool_required.rs:14:24 + | +14 | #[structopt(short, required = true)] + | ^^^^^^^^ diff --git a/tests/ui/enum_flatten.rs b/tests/ui/enum_flatten.rs new file mode 100644 index 0000000..768de76 --- /dev/null +++ b/tests/ui/enum_flatten.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +enum Opt { + #[structopt(flatten)] + Variant1, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/enum_flatten.stderr b/tests/ui/enum_flatten.stderr new file mode 100644 index 0000000..d74fa85 --- /dev/null +++ b/tests/ui/enum_flatten.stderr @@ -0,0 +1,6 @@ +error: `flatten` is usable only with single-typed tuple variants + --> $DIR/enum_flatten.rs:14:5 + | +14 | / #[structopt(flatten)] +15 | | Variant1, + | |____________^ diff --git a/tests/ui/external_subcommand_wrong_type.rs b/tests/ui/external_subcommand_wrong_type.rs new file mode 100644 index 0000000..ad62e73 --- /dev/null +++ b/tests/ui/external_subcommand_wrong_type.rs @@ -0,0 +1,19 @@ +use structopt::StructOpt; +use std::ffi::CString; + +#[derive(StructOpt, Debug)] +struct Opt { + #[structopt(subcommand)] + cmd: Command, +} + +#[derive(StructOpt, Debug)] +enum Command { + #[structopt(external_subcommand)] + Other(Vec) +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} \ No newline at end of file diff --git a/tests/ui/external_subcommand_wrong_type.stderr b/tests/ui/external_subcommand_wrong_type.stderr new file mode 100644 index 0000000..1966225 --- /dev/null +++ b/tests/ui/external_subcommand_wrong_type.stderr @@ -0,0 +1,8 @@ +error[E0308]: mismatched types + --> $DIR/external_subcommand_wrong_type.rs:13:15 + | +13 | Other(Vec) + | ^^^^^^^ expected struct `std::ffi::CString`, found struct `std::ffi::OsString` + | + = note: expected struct `std::vec::Vec` + found struct `std::vec::Vec` diff --git a/tests/ui/flatten_and_doc.rs b/tests/ui/flatten_and_doc.rs new file mode 100644 index 0000000..2dc154d --- /dev/null +++ b/tests/ui/flatten_and_doc.rs @@ -0,0 +1,30 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +struct DaemonOpts { + #[structopt(short)] + user: String, + #[structopt(short)] + group: String, +} + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + /// Opts. + #[structopt(flatten)] + opts: DaemonOpts, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/flatten_and_doc.stderr b/tests/ui/flatten_and_doc.stderr new file mode 100644 index 0000000..2724dbb --- /dev/null +++ b/tests/ui/flatten_and_doc.stderr @@ -0,0 +1,5 @@ +error: methods and doc comments are not allowed for flattened entry + --> $DIR/flatten_and_doc.rs:23:17 + | +23 | #[structopt(flatten)] + | ^^^^^^^ diff --git a/tests/ui/flatten_and_methods.rs b/tests/ui/flatten_and_methods.rs new file mode 100644 index 0000000..ff1af2e --- /dev/null +++ b/tests/ui/flatten_and_methods.rs @@ -0,0 +1,29 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +struct DaemonOpts { + #[structopt(short)] + user: String, + #[structopt(short)] + group: String, +} + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt(short, flatten)] + opts: DaemonOpts, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/flatten_and_methods.stderr b/tests/ui/flatten_and_methods.stderr new file mode 100644 index 0000000..f058eb3 --- /dev/null +++ b/tests/ui/flatten_and_methods.stderr @@ -0,0 +1,5 @@ +error: methods and doc comments are not allowed for flattened entry + --> $DIR/flatten_and_methods.rs:22:24 + | +22 | #[structopt(short, flatten)] + | ^^^^^^^ diff --git a/tests/ui/flatten_and_parse.rs b/tests/ui/flatten_and_parse.rs new file mode 100644 index 0000000..3317272 --- /dev/null +++ b/tests/ui/flatten_and_parse.rs @@ -0,0 +1,29 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +struct DaemonOpts { + #[structopt(short)] + user: String, + #[structopt(short)] + group: String, +} + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt(flatten, parse(from_occurrences))] + opts: DaemonOpts, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/flatten_and_parse.stderr b/tests/ui/flatten_and_parse.stderr new file mode 100644 index 0000000..e217a84 --- /dev/null +++ b/tests/ui/flatten_and_parse.stderr @@ -0,0 +1,5 @@ +error: parse attribute is not allowed for flattened entry + --> $DIR/flatten_and_parse.rs:22:26 + | +22 | #[structopt(flatten, parse(from_occurrences))] + | ^^^^^ diff --git a/tests/ui/multiple_external_subcommand.rs b/tests/ui/multiple_external_subcommand.rs new file mode 100644 index 0000000..986261b --- /dev/null +++ b/tests/ui/multiple_external_subcommand.rs @@ -0,0 +1,21 @@ +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +struct Opt { + #[structopt(subcommand)] + cmd: Command, +} + +#[derive(StructOpt, Debug)] +enum Command { + #[structopt(external_subcommand)] + Run(Vec), + + #[structopt(external_subcommand)] + Other(Vec) +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/multiple_external_subcommand.stderr b/tests/ui/multiple_external_subcommand.stderr new file mode 100644 index 0000000..0c80c2e --- /dev/null +++ b/tests/ui/multiple_external_subcommand.stderr @@ -0,0 +1,5 @@ +error: Only one variant can be marked with `external_subcommand`, this is the second + --> $DIR/multiple_external_subcommand.rs:14:17 + | +14 | #[structopt(external_subcommand)] + | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/non_existent_attr.rs b/tests/ui/non_existent_attr.rs new file mode 100644 index 0000000..96daf45 --- /dev/null +++ b/tests/ui/non_existent_attr.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt(short, non_existing_attribute = 1)] + debug: bool, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/non_existent_attr.stderr b/tests/ui/non_existent_attr.stderr new file mode 100644 index 0000000..99dc781 --- /dev/null +++ b/tests/ui/non_existent_attr.stderr @@ -0,0 +1,5 @@ +error[E0599]: no method named `non_existing_attribute` found for struct `clap::args::arg::Arg<'_, '_>` in the current scope + --> $DIR/non_existent_attr.rs:14:24 + | +14 | #[structopt(short, non_existing_attribute = 1)] + | ^^^^^^^^^^^^^^^^^^^^^^ method not found in `clap::args::arg::Arg<'_, '_>` diff --git a/tests/ui/opt_opt_nonpositional.rs b/tests/ui/opt_opt_nonpositional.rs new file mode 100644 index 0000000..2a08105 --- /dev/null +++ b/tests/ui/opt_opt_nonpositional.rs @@ -0,0 +1,20 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + n: Option>, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/opt_opt_nonpositional.stderr b/tests/ui/opt_opt_nonpositional.stderr new file mode 100644 index 0000000..cb9f172 --- /dev/null +++ b/tests/ui/opt_opt_nonpositional.stderr @@ -0,0 +1,5 @@ +error: Option> type is meaningless for positional argument + --> $DIR/opt_opt_nonpositional.rs:14:8 + | +14 | n: Option>, + | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/opt_vec_nonpositional.rs b/tests/ui/opt_vec_nonpositional.rs new file mode 100644 index 0000000..0f6f078 --- /dev/null +++ b/tests/ui/opt_vec_nonpositional.rs @@ -0,0 +1,20 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + n: Option>, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/opt_vec_nonpositional.stderr b/tests/ui/opt_vec_nonpositional.stderr new file mode 100644 index 0000000..c6b343f --- /dev/null +++ b/tests/ui/opt_vec_nonpositional.stderr @@ -0,0 +1,5 @@ +error: Option> type is meaningless for positional argument + --> $DIR/opt_vec_nonpositional.rs:14:8 + | +14 | n: Option>, + | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/option_default_value.rs b/tests/ui/option_default_value.rs new file mode 100644 index 0000000..a86bc0e --- /dev/null +++ b/tests/ui/option_default_value.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt(short, default_value = 1)] + n: Option, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/option_default_value.stderr b/tests/ui/option_default_value.stderr new file mode 100644 index 0000000..2215497 --- /dev/null +++ b/tests/ui/option_default_value.stderr @@ -0,0 +1,5 @@ +error: default_value is meaningless for Option + --> $DIR/option_default_value.rs:14:24 + | +14 | #[structopt(short, default_value = 1)] + | ^^^^^^^^^^^^^ diff --git a/tests/ui/option_required.rs b/tests/ui/option_required.rs new file mode 100644 index 0000000..d91afbf --- /dev/null +++ b/tests/ui/option_required.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt(short, required = true)] + n: Option, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/option_required.stderr b/tests/ui/option_required.stderr new file mode 100644 index 0000000..0230d57 --- /dev/null +++ b/tests/ui/option_required.stderr @@ -0,0 +1,5 @@ +error: required is meaningless for Option + --> $DIR/option_required.rs:14:24 + | +14 | #[structopt(short, required = true)] + | ^^^^^^^^ diff --git a/tests/ui/parse_empty_try_from_os.rs b/tests/ui/parse_empty_try_from_os.rs new file mode 100644 index 0000000..acfef0b --- /dev/null +++ b/tests/ui/parse_empty_try_from_os.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt(parse(try_from_os_str))] + s: String, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/parse_empty_try_from_os.stderr b/tests/ui/parse_empty_try_from_os.stderr new file mode 100644 index 0000000..3dc9f24 --- /dev/null +++ b/tests/ui/parse_empty_try_from_os.stderr @@ -0,0 +1,5 @@ +error: you must set parser for `try_from_os_str` explicitly + --> $DIR/parse_empty_try_from_os.rs:14:23 + | +14 | #[structopt(parse(try_from_os_str))] + | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/parse_function_is_not_path.rs b/tests/ui/parse_function_is_not_path.rs new file mode 100644 index 0000000..5eebc57 --- /dev/null +++ b/tests/ui/parse_function_is_not_path.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt(parse(from_str = "2"))] + s: String, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/parse_function_is_not_path.stderr b/tests/ui/parse_function_is_not_path.stderr new file mode 100644 index 0000000..7cf7444 --- /dev/null +++ b/tests/ui/parse_function_is_not_path.stderr @@ -0,0 +1,5 @@ +error: `parse` argument must be a function path + --> $DIR/parse_function_is_not_path.rs:14:34 + | +14 | #[structopt(parse(from_str = "2"))] + | ^^^ diff --git a/tests/ui/parse_literal_spec.rs b/tests/ui/parse_literal_spec.rs new file mode 100644 index 0000000..b6f125a --- /dev/null +++ b/tests/ui/parse_literal_spec.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt(parse("from_str"))] + s: String, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/parse_literal_spec.stderr b/tests/ui/parse_literal_spec.stderr new file mode 100644 index 0000000..6e99e8b --- /dev/null +++ b/tests/ui/parse_literal_spec.stderr @@ -0,0 +1,5 @@ +error: parser specification must start with identifier + --> $DIR/parse_literal_spec.rs:14:23 + | +14 | #[structopt(parse("from_str"))] + | ^^^^^^^^^^ diff --git a/tests/ui/parse_not_zero_args.rs b/tests/ui/parse_not_zero_args.rs new file mode 100644 index 0000000..8729178 --- /dev/null +++ b/tests/ui/parse_not_zero_args.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt(parse(from_str, from_str))] + s: String, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/parse_not_zero_args.stderr b/tests/ui/parse_not_zero_args.stderr new file mode 100644 index 0000000..34b99a4 --- /dev/null +++ b/tests/ui/parse_not_zero_args.stderr @@ -0,0 +1,5 @@ +error: `parse` must have exactly one argument + --> $DIR/parse_not_zero_args.rs:14:17 + | +14 | #[structopt(parse(from_str, from_str))] + | ^^^^^ diff --git a/tests/ui/positional_bool.rs b/tests/ui/positional_bool.rs new file mode 100644 index 0000000..4dbf538 --- /dev/null +++ b/tests/ui/positional_bool.rs @@ -0,0 +1,10 @@ +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +struct Opt { + verbose: bool, +} + +fn main() { + Opt::from_args(); +} \ No newline at end of file diff --git a/tests/ui/positional_bool.stderr b/tests/ui/positional_bool.stderr new file mode 100644 index 0000000..c3ed1ad --- /dev/null +++ b/tests/ui/positional_bool.stderr @@ -0,0 +1,10 @@ +error: `bool` cannot be used as positional parameter with default parser + + = help: if you want to create a flag add `long` or `short` + = help: If you really want a boolean parameter add an explicit parser, for example `parse(try_from_str)` + = note: see also https://github.com/TeXitoi/structopt/tree/master/examples/true_or_false.rs + + --> $DIR/positional_bool.rs:5:14 + | +5 | verbose: bool, + | ^^^^ diff --git a/tests/ui/raw.rs b/tests/ui/raw.rs new file mode 100644 index 0000000..b94f783 --- /dev/null +++ b/tests/ui/raw.rs @@ -0,0 +1,25 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +struct Opt { + #[structopt(raw(case_insensitive = "true"))] + s: String, +} + +#[derive(StructOpt, Debug)] +struct Opt2 { + #[structopt(raw(requires_if = r#""one", "two""#))] + s: String, +} +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/raw.stderr b/tests/ui/raw.stderr new file mode 100644 index 0000000..93b5e38 --- /dev/null +++ b/tests/ui/raw.stderr @@ -0,0 +1,19 @@ +error: `#[structopt(raw(...))` attributes are removed in structopt 0.3, they are replaced with raw methods + + = help: if you meant to call `clap::Arg::raw()` method you should use bool literal, like `raw(true)` or `raw(false)` + = note: if you need to call `clap::Arg/App::case_insensitive` method you can do it like this: #[structopt(case_insensitive = true)] + + --> $DIR/raw.rs:13:17 + | +13 | #[structopt(raw(case_insensitive = "true"))] + | ^^^ + +error: `#[structopt(raw(...))` attributes are removed in structopt 0.3, they are replaced with raw methods + + = help: if you meant to call `clap::Arg::raw()` method you should use bool literal, like `raw(true)` or `raw(false)` + = note: if you need to call `clap::Arg/App::requires_if` method you can do it like this: #[structopt(requires_if("one", "two"))] + + --> $DIR/raw.rs:19:17 + | +19 | #[structopt(raw(requires_if = r#""one", "two""#))] + | ^^^ diff --git a/tests/ui/rename_all_wrong_casing.rs b/tests/ui/rename_all_wrong_casing.rs new file mode 100644 index 0000000..4dabe14 --- /dev/null +++ b/tests/ui/rename_all_wrong_casing.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic", rename_all = "fail")] +struct Opt { + #[structopt(short)] + s: String, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/rename_all_wrong_casing.stderr b/tests/ui/rename_all_wrong_casing.stderr new file mode 100644 index 0000000..2a72080 --- /dev/null +++ b/tests/ui/rename_all_wrong_casing.stderr @@ -0,0 +1,5 @@ +error: unsupported casing: `fail` + --> $DIR/rename_all_wrong_casing.rs:12:42 + | +12 | #[structopt(name = "basic", rename_all = "fail")] + | ^^^^^^ diff --git a/tests/ui/skip_flatten.rs b/tests/ui/skip_flatten.rs new file mode 100644 index 0000000..8668ec2 --- /dev/null +++ b/tests/ui/skip_flatten.rs @@ -0,0 +1,42 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "make-cookie")] +struct MakeCookie { + #[structopt(short)] + s: String, + + #[structopt(skip, flatten)] + cmd: Command, +} + +#[derive(StructOpt, Debug)] +enum Command { + #[structopt(name = "pound")] + /// Pound acorns into flour for cookie dough. + Pound { acorns: u32 }, + + Sparkle { + #[structopt(short)] + color: String, + }, +} + +impl Default for Command { + fn default() -> Self { + Command::Pound { acorns: 0 } + } +} + +fn main() { + let opt = MakeCookie::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/skip_flatten.stderr b/tests/ui/skip_flatten.stderr new file mode 100644 index 0000000..76477a3 --- /dev/null +++ b/tests/ui/skip_flatten.stderr @@ -0,0 +1,5 @@ +error: subcommand, flatten and skip cannot be used together + --> $DIR/skip_flatten.rs:17:23 + | +17 | #[structopt(skip, flatten)] + | ^^^^^^^ diff --git a/tests/ui/skip_subcommand.rs b/tests/ui/skip_subcommand.rs new file mode 100644 index 0000000..5d21426 --- /dev/null +++ b/tests/ui/skip_subcommand.rs @@ -0,0 +1,42 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "make-cookie")] +struct MakeCookie { + #[structopt(short)] + s: String, + + #[structopt(subcommand, skip)] + cmd: Command, +} + +#[derive(StructOpt, Debug)] +enum Command { + #[structopt(name = "pound")] + /// Pound acorns into flour for cookie dough. + Pound { acorns: u32 }, + + Sparkle { + #[structopt(short)] + color: String, + }, +} + +impl Default for Command { + fn default() -> Self { + Command::Pound { acorns: 0 } + } +} + +fn main() { + let opt = MakeCookie::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/skip_subcommand.stderr b/tests/ui/skip_subcommand.stderr new file mode 100644 index 0000000..aba2d69 --- /dev/null +++ b/tests/ui/skip_subcommand.stderr @@ -0,0 +1,5 @@ +error: subcommand, flatten and skip cannot be used together + --> $DIR/skip_subcommand.rs:17:29 + | +17 | #[structopt(subcommand, skip)] + | ^^^^ diff --git a/tests/ui/skip_with_other_options.rs b/tests/ui/skip_with_other_options.rs new file mode 100644 index 0000000..73c5342 --- /dev/null +++ b/tests/ui/skip_with_other_options.rs @@ -0,0 +1,15 @@ +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "test")] +pub struct Opt { + #[structopt(long)] + a: u32, + #[structopt(skip, long)] + b: u32, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/skip_with_other_options.stderr b/tests/ui/skip_with_other_options.stderr new file mode 100644 index 0000000..3345f92 --- /dev/null +++ b/tests/ui/skip_with_other_options.stderr @@ -0,0 +1,5 @@ +error: methods are not allowed for skipped fields + --> $DIR/skip_with_other_options.rs:8:17 + | +8 | #[structopt(skip, long)] + | ^^^^ diff --git a/tests/ui/skip_without_default.rs b/tests/ui/skip_without_default.rs new file mode 100644 index 0000000..bc47511 --- /dev/null +++ b/tests/ui/skip_without_default.rs @@ -0,0 +1,29 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(Debug)] +enum Kind { + A, + B, +} + +#[derive(StructOpt, Debug)] +#[structopt(name = "test")] +pub struct Opt { + #[structopt(short)] + number: u32, + #[structopt(skip)] + k: Kind, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/skip_without_default.stderr b/tests/ui/skip_without_default.stderr new file mode 100644 index 0000000..330898f --- /dev/null +++ b/tests/ui/skip_without_default.stderr @@ -0,0 +1,9 @@ +error[E0277]: the trait bound `Kind: std::default::Default` is not satisfied + --> $DIR/skip_without_default.rs:22:17 + | +22 | #[structopt(skip)] + | ^^^^ the trait `std::default::Default` is not implemented for `Kind` + | + = note: required by `std::default::Default::default` + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/struct_parse.rs b/tests/ui/struct_parse.rs new file mode 100644 index 0000000..e428b23 --- /dev/null +++ b/tests/ui/struct_parse.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic", parse(from_str))] +struct Opt { + #[structopt(short)] + s: String, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/struct_parse.stderr b/tests/ui/struct_parse.stderr new file mode 100644 index 0000000..5518214 --- /dev/null +++ b/tests/ui/struct_parse.stderr @@ -0,0 +1,5 @@ +error: `parse` attribute is only allowed on fields + --> $DIR/struct_parse.rs:12:29 + | +12 | #[structopt(name = "basic", parse(from_str))] + | ^^^^^ diff --git a/tests/ui/struct_subcommand.rs b/tests/ui/struct_subcommand.rs new file mode 100644 index 0000000..ac0b145 --- /dev/null +++ b/tests/ui/struct_subcommand.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic", subcommand)] +struct Opt { + #[structopt(short)] + s: String, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/struct_subcommand.stderr b/tests/ui/struct_subcommand.stderr new file mode 100644 index 0000000..438f6f8 --- /dev/null +++ b/tests/ui/struct_subcommand.stderr @@ -0,0 +1,5 @@ +error: subcommand is only allowed on fields + --> $DIR/struct_subcommand.rs:12:29 + | +12 | #[structopt(name = "basic", subcommand)] + | ^^^^^^^^^^ diff --git a/tests/ui/structopt_empty_attr.rs b/tests/ui/structopt_empty_attr.rs new file mode 100644 index 0000000..a7fc0b9 --- /dev/null +++ b/tests/ui/structopt_empty_attr.rs @@ -0,0 +1,22 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt] + debug: bool, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} + diff --git a/tests/ui/structopt_empty_attr.stderr b/tests/ui/structopt_empty_attr.stderr new file mode 100644 index 0000000..bd3b3ed --- /dev/null +++ b/tests/ui/structopt_empty_attr.stderr @@ -0,0 +1,5 @@ +error: expected attribute arguments in parentheses: #[structopt(...)] + --> $DIR/structopt_empty_attr.rs:14:5 + | +14 | #[structopt] + | ^^^^^^^^^^^^ diff --git a/tests/ui/structopt_name_value_attr.rs b/tests/ui/structopt_name_value_attr.rs new file mode 100644 index 0000000..3d9388f --- /dev/null +++ b/tests/ui/structopt_name_value_attr.rs @@ -0,0 +1,22 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt { + #[structopt = "short"] + debug: bool, +} + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} + diff --git a/tests/ui/structopt_name_value_attr.stderr b/tests/ui/structopt_name_value_attr.stderr new file mode 100644 index 0000000..373a3b8 --- /dev/null +++ b/tests/ui/structopt_name_value_attr.stderr @@ -0,0 +1,5 @@ +error: expected parentheses: #[structopt(...)] + --> $DIR/structopt_name_value_attr.rs:14:17 + | +14 | #[structopt = "short"] + | ^ diff --git a/tests/ui/subcommand_and_flatten.rs b/tests/ui/subcommand_and_flatten.rs new file mode 100644 index 0000000..742ee6d --- /dev/null +++ b/tests/ui/subcommand_and_flatten.rs @@ -0,0 +1,36 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "make-cookie")] +struct MakeCookie { + #[structopt(short)] + s: String, + + #[structopt(subcommand, flatten)] + cmd: Command, +} + +#[derive(StructOpt, Debug)] +enum Command { + #[structopt(name = "pound")] + /// Pound acorns into flour for cookie dough. + Pound { acorns: u32 }, + + Sparkle { + #[structopt(short)] + color: String, + }, +} + +fn main() { + let opt = MakeCookie::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/subcommand_and_flatten.stderr b/tests/ui/subcommand_and_flatten.stderr new file mode 100644 index 0000000..cacea5e --- /dev/null +++ b/tests/ui/subcommand_and_flatten.stderr @@ -0,0 +1,5 @@ +error: subcommand, flatten and skip cannot be used together + --> $DIR/subcommand_and_flatten.rs:17:29 + | +17 | #[structopt(subcommand, flatten)] + | ^^^^^^^ diff --git a/tests/ui/subcommand_and_methods.rs b/tests/ui/subcommand_and_methods.rs new file mode 100644 index 0000000..890f10c --- /dev/null +++ b/tests/ui/subcommand_and_methods.rs @@ -0,0 +1,36 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "make-cookie")] +struct MakeCookie { + #[structopt(short)] + s: String, + + #[structopt(subcommand, long)] + cmd: Command, +} + +#[derive(StructOpt, Debug)] +enum Command { + #[structopt(name = "pound")] + /// Pound acorns into flour for cookie dough. + Pound { acorns: u32 }, + + Sparkle { + #[structopt(short)] + color: String, + }, +} + +fn main() { + let opt = MakeCookie::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/subcommand_and_methods.stderr b/tests/ui/subcommand_and_methods.stderr new file mode 100644 index 0000000..ccaf28d --- /dev/null +++ b/tests/ui/subcommand_and_methods.stderr @@ -0,0 +1,5 @@ +error: methods in attributes are not allowed for subcommand + --> $DIR/subcommand_and_methods.rs:17:17 + | +17 | #[structopt(subcommand, long)] + | ^^^^^^^^^^ diff --git a/tests/ui/subcommand_and_parse.rs b/tests/ui/subcommand_and_parse.rs new file mode 100644 index 0000000..f24e4bc --- /dev/null +++ b/tests/ui/subcommand_and_parse.rs @@ -0,0 +1,36 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "make-cookie")] +struct MakeCookie { + #[structopt(short)] + s: String, + + #[structopt(subcommand, parse(from_occurrences))] + cmd: Command, +} + +#[derive(StructOpt, Debug)] +enum Command { + #[structopt(name = "pound")] + /// Pound acorns into flour for cookie dough. + Pound { acorns: u32 }, + + Sparkle { + #[structopt(short)] + color: String, + }, +} + +fn main() { + let opt = MakeCookie::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/subcommand_and_parse.stderr b/tests/ui/subcommand_and_parse.stderr new file mode 100644 index 0000000..4070056 --- /dev/null +++ b/tests/ui/subcommand_and_parse.stderr @@ -0,0 +1,5 @@ +error: parse attribute is not allowed for subcommand + --> $DIR/subcommand_and_parse.rs:17:29 + | +17 | #[structopt(subcommand, parse(from_occurrences))] + | ^^^^^ diff --git a/tests/ui/subcommand_opt_opt.rs b/tests/ui/subcommand_opt_opt.rs new file mode 100644 index 0000000..1dd84e5 --- /dev/null +++ b/tests/ui/subcommand_opt_opt.rs @@ -0,0 +1,36 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "make-cookie")] +struct MakeCookie { + #[structopt(short)] + s: String, + + #[structopt(subcommand)] + cmd: Option>, +} + +#[derive(StructOpt, Debug)] +enum Command { + #[structopt(name = "pound")] + /// Pound acorns into flour for cookie dough. + Pound { acorns: u32 }, + + Sparkle { + #[structopt(short)] + color: String, + }, +} + +fn main() { + let opt = MakeCookie::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/subcommand_opt_opt.stderr b/tests/ui/subcommand_opt_opt.stderr new file mode 100644 index 0000000..25b37e5 --- /dev/null +++ b/tests/ui/subcommand_opt_opt.stderr @@ -0,0 +1,5 @@ +error: Option> type is not allowed for subcommand + --> $DIR/subcommand_opt_opt.rs:18:10 + | +18 | cmd: Option>, + | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/subcommand_opt_vec.rs b/tests/ui/subcommand_opt_vec.rs new file mode 100644 index 0000000..17bffbf --- /dev/null +++ b/tests/ui/subcommand_opt_vec.rs @@ -0,0 +1,36 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "make-cookie")] +struct MakeCookie { + #[structopt(short)] + s: String, + + #[structopt(subcommand)] + cmd: Option>, +} + +#[derive(StructOpt, Debug)] +enum Command { + #[structopt(name = "pound")] + /// Pound acorns into flour for cookie dough. + Pound { acorns: u32 }, + + Sparkle { + #[structopt(short)] + color: String, + }, +} + +fn main() { + let opt = MakeCookie::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/subcommand_opt_vec.stderr b/tests/ui/subcommand_opt_vec.stderr new file mode 100644 index 0000000..a36071b --- /dev/null +++ b/tests/ui/subcommand_opt_vec.stderr @@ -0,0 +1,5 @@ +error: Option> type is not allowed for subcommand + --> $DIR/subcommand_opt_vec.rs:18:10 + | +18 | cmd: Option>, + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/tuple_struct.rs b/tests/ui/tuple_struct.rs new file mode 100644 index 0000000..af9b1d5 --- /dev/null +++ b/tests/ui/tuple_struct.rs @@ -0,0 +1,18 @@ +// Copyright 2018 Guillaume Pinot (@TeXitoi) +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +#[structopt(name = "basic")] +struct Opt(u32); + +fn main() { + let opt = Opt::from_args(); + println!("{:?}", opt); +} diff --git a/tests/ui/tuple_struct.stderr b/tests/ui/tuple_struct.stderr new file mode 100644 index 0000000..9f2876f --- /dev/null +++ b/tests/ui/tuple_struct.stderr @@ -0,0 +1,5 @@ +error: structopt only supports non-tuple structs and enums + --> $DIR/tuple_struct.rs:11:10 + | +11 | #[derive(StructOpt, Debug)] + | ^^^^^^^^^ diff --git a/tests/utils.rs b/tests/utils.rs new file mode 100644 index 0000000..c0684a2 --- /dev/null +++ b/tests/utils.rs @@ -0,0 +1,45 @@ +#![allow(unused)] + +use structopt::StructOpt; + +pub fn get_help() -> String { + let mut output = Vec::new(); + ::clap().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() -> String { + let mut output = Vec::new(); + ::clap() + .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(subcmd: &str) -> String { + let output = ::clap() + .get_matches_from_safe(vec!["test", subcmd, "--help"]) + .expect_err("") + .message; + + eprintln!( + "\n%%% SUBCOMMAND `{}` HELP %%%:=====\n{}\n=====\n", + subcmd, output + ); + eprintln!( + "\n%%% SUBCOMMAND `{}` HELP (DEBUG) %%%:=====\n{:?}\n=====\n", + subcmd, output + ); + + output +} diff --git a/tests/we_need_syn_full.rs b/tests/we_need_syn_full.rs new file mode 100644 index 0000000..cc6eca8 --- /dev/null +++ b/tests/we_need_syn_full.rs @@ -0,0 +1,19 @@ +// See https://github.com/TeXitoi/structopt/issues/354 + +use structopt::StructOpt; + +#[test] +fn we_need_syn_full() { + #[allow(unused)] + #[derive(Debug, StructOpt, Clone)] + struct Args { + #[structopt( + short = "c", + long = "colour", + help = "Output colouring", + default_value = "auto", + possible_values = &["always", "auto", "never"] + )] + colour: String, + } +} -- cgit v1.2.3