mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 12:13:52 +00:00
CLI::IsMember (#222)
* Adding first draft of Sets Use IsMember now Using IsMember as backend for Set Non-const validator backend Move set tests to set Clearer inits * Drop shortcut Tighten up classes a bit for MSVC Check with GCC 4.8 too * Simpler templates, but more of them Dropping more type safety for older compilers Shortcut string set * Adding shortcut init Making g++ 4.7 docker image happy Fix Clang tidy issue with last commit Adding one more shortcut, adding a couple of tests * Dropping dual pointer versions of code * Smarter shortcut syntax * Adding slighly faster choices * Cleanup to make InMember simpler * Drop choices for now, adding some tests * ValidationError is now always the error from a validator * Support for other types of initializer lists, including enums * Factor out type utilities, single version of IsMember code * Adding a few tests for #224 * Minor cleanup for Validation Error * Adding tests, moved deprecated tests * Docs updates
This commit is contained in:
parent
3d7de7d25c
commit
c9123811d1
16
.travis.yml
16
.travis.yml
@ -24,7 +24,7 @@ matrix:
|
||||
- .ci/make_and_test.sh 17
|
||||
|
||||
# Check style/tidy
|
||||
- compiler: clang
|
||||
- compiler: clang
|
||||
env:
|
||||
- CHECK_STYLE=yes
|
||||
script:
|
||||
@ -104,6 +104,20 @@ matrix:
|
||||
conan upload "*" -c -r origin --all
|
||||
fi
|
||||
|
||||
# GCC 4.8
|
||||
- compiler: gcc
|
||||
env:
|
||||
- GCC_VER=4.8
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- g++-4.8
|
||||
install:
|
||||
- export CC=gcc-4.8
|
||||
- export CXX=g++-4.8
|
||||
script:
|
||||
- .ci/make_and_test.sh 11
|
||||
|
||||
# macOS and clang
|
||||
- os: osx
|
||||
compiler: clang
|
||||
|
16
CHANGELOG.md
16
CHANGELOG.md
@ -1,17 +1,29 @@
|
||||
## Version 1.8: Flags and Sets
|
||||
## Version 1.8: Sets and Flags
|
||||
|
||||
This version adds inverted flags, which can cancel or reduce the count of flags, and can also support basic number assignment. A new `add_option_fn` lets you more easily program CLI11 options with the types you choose. Vector options now support a custom separator. Apps can now be composed with unnamed subcommand support.
|
||||
Set handling has been completely replaced by a new backend that works as a Validator. This provides a single interface instead of the 16 different functions in App. It also allows ordered collections to be used, custom functions for filtering, and better help and error messages. Also new are inverted flags, which can cancel or reduce the count of flags, and can also support basic number assignment. A new `add_option_fn` lets you more easily program CLI11 options with the types you choose. Vector options now support a custom separator. Apps can now be composed with unnamed subcommand support.
|
||||
|
||||
* New `CL::IsMember` validator replaces set validation [#222]
|
||||
* Much more powerful flags with different values [#211]
|
||||
* `add_option` now supports bool due to unified bool handling [#211]
|
||||
* Support for composable unnamed subcommands [#216]
|
||||
* Custom vector separator [#209], [#221]
|
||||
* Validators added for IP4 addresses and positive numbers [#210]
|
||||
|
||||
> ### Converting from CLI11 1.7:
|
||||
>
|
||||
> * `app.add_set("--name", value, {"choice1", "choice2"})` should become `app.add_option("--name", value)->check(CLI::IsMember({"choice1", "choice2"}))`
|
||||
> * The `_mutable` versions of this can be replaced by passing a pointer or shared pointer into `IsMember`
|
||||
> * The `_ignore_case` version of this can be replaced by adding `CLI::ignore_case` to the argument list in `IsMember`
|
||||
> * The `_ignore_underscore` version of this can be replaced by adding `CLI::ignore_underscore` to the argument list in `IsMember`
|
||||
> * The `_ignore_case_underscore` version of this can be replaced by adding both functions listed above to the argument list in `IsMember`
|
||||
> * An error with sets now produces a `ValidationError` instead of a `ConversionError`
|
||||
|
||||
[#209]: https://github.com/CLIUtils/CLI11/pull/209
|
||||
[#210]: https://github.com/CLIUtils/CLI11/pull/210
|
||||
[#211]: https://github.com/CLIUtils/CLI11/pull/211
|
||||
[#216]: https://github.com/CLIUtils/CLI11/pull/216
|
||||
[#221]: https://github.com/CLIUtils/CLI11/pull/221
|
||||
[#222]: https://github.com/CLIUtils/CLI11/pull/222
|
||||
|
||||
|
||||
## Version 1.7.1: Quick patch
|
||||
|
47
README.md
47
README.md
@ -144,12 +144,15 @@ GTEST_COLOR=1 CTEST_OUTPUT_ON_FAILURE=1 make test
|
||||
To set up, add options, and run, your main function will look something like this:
|
||||
|
||||
```cpp
|
||||
CLI::App app{"App description"};
|
||||
int main(int charc, char** argv) {
|
||||
CLI::App app{"App description"};
|
||||
|
||||
std::string filename = "default";
|
||||
app.add_option("-f,--file", filename, "A help string");
|
||||
std::string filename = "default";
|
||||
app.add_option("-f,--file", filename, "A help string");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
<details><summary>Note: If you don't like macros, this is what that macro expands to: (click to expand)</summary><p>
|
||||
@ -175,7 +178,7 @@ While all options internally are the same type, there are several ways to add an
|
||||
|
||||
```cpp
|
||||
app.add_option(option_name,
|
||||
variable_to_bind_to, // int, float, vector, or string-like
|
||||
variable_to_bind_to, // bool, int, float, vector, or string-like
|
||||
help_string="",
|
||||
default=false)
|
||||
|
||||
@ -196,24 +199,10 @@ app.add_flag_function(option_name,
|
||||
function <void(int count)>,
|
||||
help_string="")
|
||||
|
||||
app.add_set(option_name,
|
||||
variable_to_bind_to, // Same type as stored by set
|
||||
set_of_possible_options, // Set will be copied, ignores changes
|
||||
help_string="",
|
||||
default=false)
|
||||
app.add_mutable_set(... // Set can change later, keeps reference
|
||||
|
||||
app.add_set_ignore_case(... // String only
|
||||
app.add_mutable_set_ignore_case(... // String only
|
||||
app.add_set_ignore_underscore(... // String only
|
||||
app.add_mutable_set_ignore_underscore(... // String only
|
||||
app.add_set_ignore_case_underscore(... // String only
|
||||
app.add_mutable_set_ignore_case_underscore(... // String only
|
||||
|
||||
App* subcom = app.add_subcommand(name, description);
|
||||
```
|
||||
|
||||
An option name must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option` or `add_set`. The set options allow your users to pick from a set of predefined options, and you can use an initializer list directly if you like. If you need to modify the set later, use the `mutable` forms.
|
||||
An option name must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option`.
|
||||
|
||||
The `add_option_function<type>(...` function will typically require the template parameter be given unless a `std::function` object with an exact match is passed. The type can be any type supported by the `add_option` function.
|
||||
|
||||
@ -229,7 +218,7 @@ app.add_flag_function(option_name,
|
||||
help_string="")
|
||||
```
|
||||
|
||||
allow a syntax for the option names to default particular options to a false value if some flags are passed. For example:
|
||||
which allow a syntax for the option names to default particular options to a false value if some flags are passed. For example:
|
||||
|
||||
```cpp
|
||||
app.add_flag("--flag,!--no-flag,result,"help for flag");`
|
||||
@ -271,6 +260,7 @@ Before parsing, you can set the following options:
|
||||
- `->ignore_underscore()`: Ignore any underscores in the options names (also works on subcommands, does not affect arguments). For example "option_one" will match with "optionone". This does not apply to short form options since they only have one character
|
||||
- `->description(str)`: Set/change the description.
|
||||
- `->multi_option_policy(CLI::MultiOptionPolicy::Throw)`: Set the multi-option policy. Shortcuts available: `->take_last()`, `->take_first()`, and `->join()`. This will only affect options expecting 1 argument or bool flags (which do not inherit their default but always start with a specific policy).
|
||||
- `->check(CLI::IsMember(...))`: Require an option be a member of a given set. See below for options.
|
||||
- `->check(CLI::ExistingFile)`: Requires that the file exists if given.
|
||||
- `->check(CLI::ExistingDirectory)`: Requires that the directory exists.
|
||||
- `->check(CLI::ExistingPath)`: Requires that the path (file or directory) exists.
|
||||
@ -282,8 +272,19 @@ Before parsing, you can set the following options:
|
||||
- `->each(void(std::string)>`: Run this function on each value received, as it is received.
|
||||
- `->configurable(false)`: Disable this option from being in a configuration file.
|
||||
|
||||
|
||||
These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. Check takes any function that has the signature `void(const std::string&)`; it should throw a `ValidationError` when validation fails. The help message will have the name of the parent option prepended. Since `check` and `transform` use the same underlying mechanism, you can chain as many as you want, and they will be executed in order. If you just want to see the unconverted values, use `.results()` to get the `std::vector<std::string>` of results. Validate can also be a subclass of `CLI::Validator`, in which case it can also set the type name and can be combined with `&` and `|` (all built-in validators are this sort).
|
||||
|
||||
The `IsMember` validator lets you specify a set of predefined options. You can pass any container or copyable pointer (including `std::shared_ptr`) to a container to this validator; the container just needs to be iterable and have a `::value_type`. The type should be convertible from a string. You can use an initializer list directly if you like. If you need to modify the set later, the pointer form lets you do that; the type message and check will correctly refer to the current version of the set.
|
||||
After specifying a set of options, you can also specify "filter" functions of the form `T(T)`, where `T` is the type of the values. The most common choices probably will be `CLI::ignore_case` an `CLI::ignore_underscore`.
|
||||
Here are some examples
|
||||
of `IsMember`:
|
||||
|
||||
- `CLI::IsMember({"choice1", "choice2"})`: Select from exact match to choices.
|
||||
- `CLI::IsMember({"choice1", "choice2"}, CLI::ignore_case, CLI::ignore_underscore)`: Match things like `Choice_1`, too.
|
||||
- `CLI::IsMember(std::set<int>({2,3,4}))`: Most containers and types work; you just need `std::begin`, `std::end`, and `::value_type`.
|
||||
- `auto p = std::make_shared<std::vector<std::string>>(std::initializer_list<std::string>("one", "two")); CLI::IsMember(p)`: You can modify `p` later.
|
||||
|
||||
On the command line, options can be given as:
|
||||
|
||||
- `-a` (flag)
|
||||
@ -296,7 +297,7 @@ On the command line, options can be given as:
|
||||
- `--file filename` (space)
|
||||
- `--file=filename` (equals)
|
||||
|
||||
If allow_windows_style_options() is specified in the application or subcommand options can also be given as:
|
||||
If `allow_windows_style_options()` is specified in the application or subcommand options can also be given as:
|
||||
- `/a` (flag)
|
||||
- `/f filename` (option)
|
||||
- `/long` (long flag)
|
||||
@ -359,7 +360,7 @@ There are several options that are supported on the main app and subcommands. Th
|
||||
- `.name(name)`: Add or change the name.
|
||||
- `.callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point.
|
||||
- `.allow_extras()`: Do not throw an error if extra arguments are left over.
|
||||
- `.positionals_at_end()`: Specify that positional arguments occur as the last arguments and throw an error if an unexpected positional is encountered.
|
||||
- `.positionals_at_end()`: Specify that positional arguments occur as the last arguments and throw an error if an unexpected positional is encountered.
|
||||
- `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognized item. It is ideal for allowing your app or subcommand to be a "prefix" to calling another app.
|
||||
- `.footer(message)`: Set text to appear at the bottom of the help string.
|
||||
- `.set_help_flag(name, message)`: Set the help flag name and message, returns a pointer to the created option.
|
||||
|
@ -130,7 +130,7 @@ add_cli_exe(enum enum.cpp)
|
||||
add_test(NAME enum_pass COMMAND enum -l 1)
|
||||
add_test(NAME enum_fail COMMAND enum -l 4)
|
||||
set_property(TEST enum_fail PROPERTY PASS_REGULAR_EXPRESSION
|
||||
"Could not convert: --level = 4")
|
||||
"--level: 4 not in {0,1,2}")
|
||||
|
||||
add_cli_exe(modhelp modhelp.cpp)
|
||||
add_test(NAME modhelp COMMAND modhelp -a test -h)
|
||||
|
@ -16,7 +16,8 @@ int main(int argc, char **argv) {
|
||||
CLI::App app;
|
||||
|
||||
Level level;
|
||||
app.add_set("-l,--level", level, {Level::High, Level::Medium, Level::Low}, "Level settings")
|
||||
app.add_option("-l,--level", level, "Level settings")
|
||||
->check(CLI::IsMember({Level::High, Level::Medium, Level::Low}))
|
||||
->type_name("enum/Level in {High=0, Medium=1, Low=2}");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
@ -644,51 +644,31 @@ class App {
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Add set of options (No default, temp reference, such as an inline set)
|
||||
/// Add set of options (No default, temp reference, such as an inline set) DEPRECATED
|
||||
template <typename T>
|
||||
Option *add_set(std::string option_name,
|
||||
T &member, ///< The selected member of the set
|
||||
std::set<T> options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
bool retval = detail::lexical_cast(res[0], member);
|
||||
if(!retval)
|
||||
throw ConversionError(res[0], simple_name);
|
||||
return std::find(std::begin(options), std::end(options), member) != std::end(options);
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), false);
|
||||
std::string typeval = detail::type_name<T>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
Option *opt = add_option(option_name, member, std::move(description));
|
||||
opt->check(IsMember{options});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options (No default, set can be changed afterwords - do not destroy the set)
|
||||
/// Add set of options (No default, set can be changed afterwords - do not destroy the set) DEPRECATED
|
||||
template <typename T>
|
||||
Option *add_mutable_set(std::string option_name,
|
||||
T &member, ///< The selected member of the set
|
||||
const std::set<T> &options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
bool retval = detail::lexical_cast(res[0], member);
|
||||
if(!retval)
|
||||
throw ConversionError(res[0], simple_name);
|
||||
return std::find(std::begin(options), std::end(options), member) != std::end(options);
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), false);
|
||||
opt->type_name_fn(
|
||||
[&options]() { return std::string(detail::type_name<T>()) + " in {" + detail::join(options) + "}"; });
|
||||
|
||||
Option *opt = add_option(option_name, member, std::move(description));
|
||||
opt->check(IsMember{&options});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options (with default, static set, such as an inline set)
|
||||
/// Add set of options (with default, static set, such as an inline set) DEPRECATED
|
||||
template <typename T>
|
||||
Option *add_set(std::string option_name,
|
||||
T &member, ///< The selected member of the set
|
||||
@ -696,27 +676,12 @@ class App {
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
bool retval = detail::lexical_cast(res[0], member);
|
||||
if(!retval)
|
||||
throw ConversionError(res[0], simple_name);
|
||||
return std::find(std::begin(options), std::end(options), member) != std::end(options);
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted);
|
||||
std::string typeval = detail::type_name<T>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
if(defaulted) {
|
||||
std::stringstream out;
|
||||
out << member;
|
||||
opt->default_str(out.str());
|
||||
}
|
||||
Option *opt = add_option(option_name, member, std::move(description), defaulted);
|
||||
opt->check(IsMember{options});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options (with default, set can be changed afterwards - do not destroy the set)
|
||||
/// Add set of options (with default, set can be changed afterwards - do not destroy the set) DEPRECATED
|
||||
template <typename T>
|
||||
Option *add_mutable_set(std::string option_name,
|
||||
T &member, ///< The selected member of the set
|
||||
@ -724,381 +689,166 @@ class App {
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
bool retval = detail::lexical_cast(res[0], member);
|
||||
if(!retval)
|
||||
throw ConversionError(res[0], simple_name);
|
||||
return std::find(std::begin(options), std::end(options), member) != std::end(options);
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted);
|
||||
opt->type_name_fn(
|
||||
[&options]() { return std::string(detail::type_name<T>()) + " in {" + detail::join(options) + "}"; });
|
||||
if(defaulted) {
|
||||
std::stringstream out;
|
||||
out << member;
|
||||
opt->default_str(out.str());
|
||||
}
|
||||
Option *opt = add_option(option_name, member, std::move(description), defaulted);
|
||||
opt->check(IsMember{&options});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore case (no default, static set)
|
||||
/// Add set of options, string only, ignore case (no default, static set) DEPRECATED
|
||||
CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_case)) instead")
|
||||
Option *add_set_ignore_case(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
std::set<std::string> options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), false);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
|
||||
Option *opt = add_option(option_name, member, std::move(description));
|
||||
opt->check(IsMember{options, CLI::ignore_case});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore case (no default, set can be changed afterwards - do not destroy the
|
||||
/// set)
|
||||
/// set) DEPRECATED
|
||||
CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_case)) with a (shared) pointer instead")
|
||||
Option *add_mutable_set_ignore_case(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), false);
|
||||
opt->type_name_fn([&options]() {
|
||||
return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
|
||||
});
|
||||
|
||||
Option *opt = add_option(option_name, member, std::move(description));
|
||||
opt->check(IsMember{&options, CLI::ignore_case});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore case (default, static set)
|
||||
/// Add set of options, string only, ignore case (default, static set) DEPRECATED
|
||||
CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_case)) instead")
|
||||
Option *add_set_ignore_case(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
std::set<std::string> options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
if(defaulted) {
|
||||
opt->default_str(member);
|
||||
}
|
||||
Option *opt = add_option(option_name, member, std::move(description), defaulted);
|
||||
opt->check(IsMember{options, CLI::ignore_case});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore case (default, set can be changed afterwards - do not destroy the set)
|
||||
/// DEPRECATED
|
||||
CLI11_DEPRECATED("Use ->check(CLI::IsMember(...)) with a (shared) pointer instead")
|
||||
Option *add_mutable_set_ignore_case(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted);
|
||||
opt->type_name_fn([&options]() {
|
||||
return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
|
||||
});
|
||||
if(defaulted) {
|
||||
opt->default_str(member);
|
||||
}
|
||||
Option *opt = add_option(option_name, member, std::move(description), defaulted);
|
||||
opt->check(IsMember{&options, CLI::ignore_case});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore (no default, static set)
|
||||
/// Add set of options, string only, ignore underscore (no default, static set) DEPRECATED
|
||||
CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_underscore)) instead")
|
||||
Option *add_set_ignore_underscore(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
std::set<std::string> options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
member = detail::remove_underscore(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::remove_underscore(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), false);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
|
||||
Option *opt = add_option(option_name, member, std::move(description));
|
||||
opt->check(IsMember{options, CLI::ignore_underscore});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore (no default, set can be changed afterwards - do not destroy
|
||||
/// the set)
|
||||
/// the set) DEPRECATED
|
||||
CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_underscore)) with a (shared) pointer instead")
|
||||
Option *add_mutable_set_ignore_underscore(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
member = detail::remove_underscore(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::remove_underscore(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), false);
|
||||
opt->type_name_fn([&options]() {
|
||||
return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
|
||||
});
|
||||
|
||||
Option *opt = add_option(option_name, member, std::move(description));
|
||||
opt->check(IsMember{options, CLI::ignore_underscore});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore (default, static set)
|
||||
/// Add set of options, string only, ignore underscore (default, static set) DEPRECATED
|
||||
CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_underscore)) instead")
|
||||
Option *add_set_ignore_underscore(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
std::set<std::string> options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
member = detail::remove_underscore(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::remove_underscore(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
if(defaulted) {
|
||||
opt->default_str(member);
|
||||
}
|
||||
Option *opt = add_option(option_name, member, std::move(description), defaulted);
|
||||
opt->check(IsMember{options, CLI::ignore_underscore});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore (default, set can be changed afterwards - do not destroy the
|
||||
/// set)
|
||||
/// set) DEPRECATED
|
||||
CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_underscore)) with a (shared) pointer instead")
|
||||
Option *add_mutable_set_ignore_underscore(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
member = detail::remove_underscore(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::remove_underscore(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted);
|
||||
opt->type_name_fn([&options]() {
|
||||
return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
|
||||
});
|
||||
if(defaulted) {
|
||||
opt->default_str(member);
|
||||
}
|
||||
Option *opt = add_option(option_name, member, std::move(description), defaulted);
|
||||
opt->check(IsMember{&options, CLI::ignore_underscore});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore and case (no default, static set)
|
||||
/// Add set of options, string only, ignore underscore and case (no default, static set) DEPRECATED
|
||||
CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) instead")
|
||||
Option *add_set_ignore_case_underscore(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
std::set<std::string> options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(detail::remove_underscore(res[0]));
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(detail::remove_underscore(val)) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), false);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
|
||||
Option *opt = add_option(option_name, member, std::move(description));
|
||||
opt->check(IsMember{options, CLI::ignore_underscore, CLI::ignore_case});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore and case (no default, set can be changed afterwards - do not
|
||||
/// destroy the set)
|
||||
/// destroy the set) DEPRECATED
|
||||
CLI11_DEPRECATED(
|
||||
"Use ->check(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) with a (shared) pointer instead")
|
||||
Option *add_mutable_set_ignore_case_underscore(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(detail::remove_underscore(res[0]));
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(detail::remove_underscore(val)) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), false);
|
||||
opt->type_name_fn([&options]() {
|
||||
return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
|
||||
});
|
||||
|
||||
Option *opt = add_option(option_name, member, std::move(description));
|
||||
opt->check(IsMember{&options, CLI::ignore_underscore, CLI::ignore_case});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore and case (default, static set)
|
||||
/// Add set of options, string only, ignore underscore and case (default, static set) DEPRECATED
|
||||
CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) instead")
|
||||
Option *add_set_ignore_case_underscore(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
std::set<std::string> options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(detail::remove_underscore(res[0]));
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(detail::remove_underscore(val)) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
if(defaulted) {
|
||||
opt->default_str(member);
|
||||
}
|
||||
Option *opt = add_option(option_name, member, std::move(description), defaulted);
|
||||
opt->check(IsMember{options, CLI::ignore_underscore, CLI::ignore_case});
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore and case (default, set can be changed afterwards - do not
|
||||
/// destroy the set)
|
||||
/// destroy the set) DEPRECATED
|
||||
CLI11_DEPRECATED(
|
||||
"Use ->check(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) with a (shared) pointer instead")
|
||||
Option *add_mutable_set_ignore_case_underscore(std::string option_name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(option_name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(detail::remove_underscore(res[0]));
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(detail::remove_underscore(val)) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted);
|
||||
opt->type_name_fn([&options]() {
|
||||
return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
|
||||
});
|
||||
if(defaulted) {
|
||||
opt->default_str(member);
|
||||
}
|
||||
Option *opt = add_option(option_name, member, std::move(description), defaulted);
|
||||
opt->check(IsMember{&options, CLI::ignore_underscore, CLI::ignore_case});
|
||||
return opt;
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,9 @@ class FormatterBase {
|
||||
FormatterBase() = default;
|
||||
FormatterBase(const FormatterBase &) = default;
|
||||
FormatterBase(FormatterBase &&) = default;
|
||||
virtual ~FormatterBase() = default;
|
||||
|
||||
/// Adding a destructor in this form to work around bug in GCC 4.7
|
||||
virtual ~FormatterBase() noexcept {} // NOLINT(modernize-use-equals-default)
|
||||
|
||||
/// This is the key method that puts together help
|
||||
virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0;
|
||||
@ -93,6 +95,9 @@ class FormatterLambda final : public FormatterBase {
|
||||
/// Create a FormatterLambda with a lambda function
|
||||
explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {}
|
||||
|
||||
/// Adding a destructor (mostly to make GCC 4.7 happy)
|
||||
~FormatterLambda() noexcept override {} // NOLINT(modernize-use-equals-default)
|
||||
|
||||
/// This will simply call the lambda function
|
||||
std::string make_help(const App *app, std::string name, AppFormatMode mode) const override {
|
||||
return lambda_(app, name, mode);
|
||||
|
@ -304,7 +304,9 @@ class Option : public OptionBase<Option> {
|
||||
/// Adds a validator with a built in type name
|
||||
Option *check(const Validator &validator) {
|
||||
validators_.emplace_back(validator.func);
|
||||
if(!validator.tname.empty())
|
||||
if(validator.tname_function)
|
||||
type_name_fn(validator.tname_function);
|
||||
else if(!validator.tname.empty())
|
||||
type_name(validator.tname);
|
||||
return this;
|
||||
}
|
||||
@ -606,7 +608,14 @@ class Option : public OptionBase<Option> {
|
||||
if(!validators_.empty()) {
|
||||
for(std::string &result : results_)
|
||||
for(const std::function<std::string(std::string &)> &vali : validators_) {
|
||||
std::string err_msg = vali(result);
|
||||
std::string err_msg;
|
||||
|
||||
try {
|
||||
err_msg = vali(result);
|
||||
} catch(const ValidationError &err) {
|
||||
throw ValidationError(err.what(), get_name());
|
||||
}
|
||||
|
||||
if(!err_msg.empty())
|
||||
throw ValidationError(get_name(), err_msg);
|
||||
}
|
||||
|
@ -343,4 +343,5 @@ inline std::string &add_quotes_if_needed(std::string &str) {
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace CLI
|
||||
|
@ -119,7 +119,7 @@ class AutoTimer : public Timer {
|
||||
AutoTimer(std::string title = "Timer", time_print_t time_print = Simple) : Timer(title, time_print) {}
|
||||
// GCC 4.7 does not support using inheriting constructors.
|
||||
|
||||
/// This desctructor prints the string
|
||||
/// This destructor prints the string
|
||||
~AutoTimer() { std::cout << to_string() << std::endl; }
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
|
||||
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
@ -12,6 +13,16 @@ namespace CLI {
|
||||
|
||||
// Type tools
|
||||
|
||||
// Utilities for type enabling
|
||||
namespace detail {
|
||||
// Based generally on https://rmf.io/cxx11/almost-static-if
|
||||
/// Simple empty scoped class
|
||||
enum class enabler {};
|
||||
|
||||
/// An instance to use in EnableIf
|
||||
constexpr enabler dummy = {};
|
||||
} // namespace detail
|
||||
|
||||
/// A copy of enable_if_t from C++14, compatible with C++11.
|
||||
///
|
||||
/// We could check to see if C++14 is being used, but it does not hurt to redefine this
|
||||
@ -21,24 +32,46 @@ namespace CLI {
|
||||
template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
||||
/// Check to see if something is a vector (fail check by default)
|
||||
template <typename T> struct is_vector { static const bool value = false; };
|
||||
template <typename T> struct is_vector : std::false_type {};
|
||||
|
||||
/// Check to see if something is a vector (true if actually a vector)
|
||||
template <class T, class A> struct is_vector<std::vector<T, A>> { static bool const value = true; };
|
||||
template <class T, class A> struct is_vector<std::vector<T, A>> : std::true_type {};
|
||||
|
||||
/// Check to see if something is bool (fail check by default)
|
||||
template <typename T> struct is_bool { static const bool value = false; };
|
||||
template <typename T> struct is_bool : std::false_type {};
|
||||
|
||||
/// Check to see if something is bool (true if actually a bool)
|
||||
template <> struct is_bool<bool> { static bool const value = true; };
|
||||
template <> struct is_bool<bool> : std::true_type {};
|
||||
|
||||
/// Check to see if something is a shared pointer
|
||||
template <typename T> struct is_shared_ptr : std::false_type {};
|
||||
|
||||
/// Check to see if something is a shared pointer (True if really a shared pointer)
|
||||
template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
|
||||
|
||||
/// Check to see if something is copyable pointer
|
||||
template <typename T> struct is_copyable_ptr {
|
||||
static bool const value = is_shared_ptr<T>::value || std::is_pointer<T>::value;
|
||||
};
|
||||
|
||||
/// Handy helper to access the element_type generically. This is not part of is_copyable_ptr because it requires that
|
||||
/// pointer_traits<T> be valid.
|
||||
template <typename T> struct element_type {
|
||||
using type =
|
||||
typename std::conditional<is_copyable_ptr<T>::value, typename std::pointer_traits<T>::element_type, T>::type;
|
||||
};
|
||||
|
||||
/// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of
|
||||
/// the container
|
||||
template <typename T> struct element_value_type { using type = typename element_type<T>::type::value_type; };
|
||||
|
||||
/// This can be specialized to override the type deduction for IsMember.
|
||||
template <typename T> struct IsMemberType { using type = T; };
|
||||
|
||||
/// The main custom type needed here is const char * should be a string.
|
||||
template <> struct IsMemberType<const char *> { using type = std::string; };
|
||||
|
||||
namespace detail {
|
||||
// Based generally on https://rmf.io/cxx11/almost-static-if
|
||||
/// Simple empty scoped class
|
||||
enum class enabler {};
|
||||
|
||||
/// An instance to use in EnableIf
|
||||
constexpr enabler dummy = {};
|
||||
|
||||
// Type name print
|
||||
|
||||
|
@ -3,10 +3,12 @@
|
||||
// Distributed under the 3-Clause BSD License. See accompanying
|
||||
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
|
||||
|
||||
#include "CLI/StringTools.hpp"
|
||||
#include "CLI/TypeTools.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// C standard library
|
||||
@ -17,6 +19,8 @@
|
||||
|
||||
namespace CLI {
|
||||
|
||||
class Option;
|
||||
|
||||
/// @defgroup validator_group Validators
|
||||
|
||||
/// @brief Some validators that are provided
|
||||
@ -27,17 +31,31 @@ namespace CLI {
|
||||
/// @{
|
||||
|
||||
///
|
||||
struct Validator {
|
||||
class Validator {
|
||||
friend Option;
|
||||
|
||||
protected:
|
||||
/// This is the type name, if empty the type name will not be changed
|
||||
std::string tname;
|
||||
|
||||
/// This is the type function, if empty the tname will be used
|
||||
std::function<std::string()> tname_function;
|
||||
|
||||
/// This it the base function that is to be called.
|
||||
/// Returns a string error message if validation fails.
|
||||
std::function<std::string(const std::string &)> func;
|
||||
std::function<std::string(std::string &)> func;
|
||||
|
||||
public:
|
||||
/// This is the required operator for a validator - provided to help
|
||||
/// users (CLI11 uses the member `func` directly)
|
||||
std::string operator()(std::string &str) const { return func(str); };
|
||||
|
||||
/// This is the required operator for a validator - provided to help
|
||||
/// users (CLI11 uses the member `func` directly)
|
||||
std::string operator()(const std::string &str) const { return func(str); };
|
||||
std::string operator()(const std::string &str) const {
|
||||
std::string value = str;
|
||||
return func(value);
|
||||
};
|
||||
|
||||
/// Combining validators is a new validator
|
||||
Validator operator&(const Validator &other) const {
|
||||
@ -45,10 +63,10 @@ struct Validator {
|
||||
newval.tname = (tname == other.tname ? tname : "");
|
||||
|
||||
// Give references (will make a copy in lambda function)
|
||||
const std::function<std::string(const std::string &filename)> &f1 = func;
|
||||
const std::function<std::string(const std::string &filename)> &f2 = other.func;
|
||||
const std::function<std::string(std::string & filename)> &f1 = func;
|
||||
const std::function<std::string(std::string & filename)> &f2 = other.func;
|
||||
|
||||
newval.func = [f1, f2](const std::string &filename) {
|
||||
newval.func = [f1, f2](std::string &filename) {
|
||||
std::string s1 = f1(filename);
|
||||
std::string s2 = f2(filename);
|
||||
if(!s1.empty() && !s2.empty())
|
||||
@ -65,10 +83,10 @@ struct Validator {
|
||||
newval.tname = (tname == other.tname ? tname : "");
|
||||
|
||||
// Give references (will make a copy in lambda function)
|
||||
const std::function<std::string(const std::string &filename)> &f1 = func;
|
||||
const std::function<std::string(const std::string &filename)> &f2 = other.func;
|
||||
const std::function<std::string(std::string & filename)> &f1 = func;
|
||||
const std::function<std::string(std::string & filename)> &f2 = other.func;
|
||||
|
||||
newval.func = [f1, f2](const std::string &filename) {
|
||||
newval.func = [f1, f2](std::string &filename) {
|
||||
std::string s1 = f1(filename);
|
||||
std::string s2 = f2(filename);
|
||||
if(s1.empty() || s2.empty())
|
||||
@ -86,10 +104,11 @@ struct Validator {
|
||||
namespace detail {
|
||||
|
||||
/// Check for an existing file (returns error message if check fails)
|
||||
struct ExistingFileValidator : public Validator {
|
||||
class ExistingFileValidator : public Validator {
|
||||
public:
|
||||
ExistingFileValidator() {
|
||||
tname = "FILE";
|
||||
func = [](const std::string &filename) {
|
||||
func = [](std::string &filename) {
|
||||
struct stat buffer;
|
||||
bool exist = stat(filename.c_str(), &buffer) == 0;
|
||||
bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
|
||||
@ -104,10 +123,11 @@ struct ExistingFileValidator : public Validator {
|
||||
};
|
||||
|
||||
/// Check for an existing directory (returns error message if check fails)
|
||||
struct ExistingDirectoryValidator : public Validator {
|
||||
class ExistingDirectoryValidator : public Validator {
|
||||
public:
|
||||
ExistingDirectoryValidator() {
|
||||
tname = "DIR";
|
||||
func = [](const std::string &filename) {
|
||||
func = [](std::string &filename) {
|
||||
struct stat buffer;
|
||||
bool exist = stat(filename.c_str(), &buffer) == 0;
|
||||
bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
|
||||
@ -122,10 +142,11 @@ struct ExistingDirectoryValidator : public Validator {
|
||||
};
|
||||
|
||||
/// Check for an existing path
|
||||
struct ExistingPathValidator : public Validator {
|
||||
class ExistingPathValidator : public Validator {
|
||||
public:
|
||||
ExistingPathValidator() {
|
||||
tname = "PATH";
|
||||
func = [](const std::string &filename) {
|
||||
func = [](std::string &filename) {
|
||||
struct stat buffer;
|
||||
bool const exist = stat(filename.c_str(), &buffer) == 0;
|
||||
if(!exist) {
|
||||
@ -137,10 +158,11 @@ struct ExistingPathValidator : public Validator {
|
||||
};
|
||||
|
||||
/// Check for an non-existing path
|
||||
struct NonexistentPathValidator : public Validator {
|
||||
class NonexistentPathValidator : public Validator {
|
||||
public:
|
||||
NonexistentPathValidator() {
|
||||
tname = "PATH";
|
||||
func = [](const std::string &filename) {
|
||||
func = [](std::string &filename) {
|
||||
struct stat buffer;
|
||||
bool exist = stat(filename.c_str(), &buffer) == 0;
|
||||
if(exist) {
|
||||
@ -152,10 +174,11 @@ struct NonexistentPathValidator : public Validator {
|
||||
};
|
||||
|
||||
/// Validate the given string is a legal ipv4 address
|
||||
struct IPV4Validator : public Validator {
|
||||
class IPV4Validator : public Validator {
|
||||
public:
|
||||
IPV4Validator() {
|
||||
tname = "IPV4";
|
||||
func = [](const std::string &ip_addr) {
|
||||
func = [](std::string &ip_addr) {
|
||||
auto result = CLI::detail::split(ip_addr, '.');
|
||||
if(result.size() != 4) {
|
||||
return "Invalid IPV4 address must have four parts " + ip_addr;
|
||||
@ -177,10 +200,11 @@ struct IPV4Validator : public Validator {
|
||||
};
|
||||
|
||||
/// Validate the argument is a number and greater than or equal to 0
|
||||
struct PositiveNumber : public Validator {
|
||||
class PositiveNumber : public Validator {
|
||||
public:
|
||||
PositiveNumber() {
|
||||
tname = "POSITIVE";
|
||||
func = [](const std::string &number_str) {
|
||||
func = [](std::string &number_str) {
|
||||
int number;
|
||||
if(!detail::lexical_cast(number_str, number)) {
|
||||
return "Failed parsing number " + number_str;
|
||||
@ -216,7 +240,8 @@ const detail::IPV4Validator ValidIPV4;
|
||||
const detail::PositiveNumber PositiveNumber;
|
||||
|
||||
/// Produce a range (factory). Min and max are inclusive.
|
||||
struct Range : public Validator {
|
||||
class Range : public Validator {
|
||||
public:
|
||||
/// This produces a range with min and max inclusive.
|
||||
///
|
||||
/// Note that the constructor is templated, but the struct is not, so C++17 is not
|
||||
@ -226,7 +251,7 @@ struct Range : public Validator {
|
||||
out << detail::type_name<T>() << " in [" << min << " - " << max << "]";
|
||||
|
||||
tname = out.str();
|
||||
func = [min, max](std::string input) {
|
||||
func = [min, max](std::string &input) {
|
||||
T val;
|
||||
detail::lexical_cast(input, val);
|
||||
if(val < min || val > max)
|
||||
@ -240,6 +265,99 @@ struct Range : public Validator {
|
||||
template <typename T> explicit Range(T max) : Range(static_cast<T>(0), max) {}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename T, enable_if_t<is_copyable_ptr<T>::value, detail::enabler> = detail::dummy>
|
||||
auto smart_deref(T value) -> decltype(*value) {
|
||||
return *value;
|
||||
}
|
||||
|
||||
template <typename T, enable_if_t<!is_copyable_ptr<T>::value, detail::enabler> = detail::dummy> T smart_deref(T value) {
|
||||
return value;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/// Verify items are in a set
|
||||
class IsMember : public Validator {
|
||||
public:
|
||||
using filter_fn_t = std::function<std::string(std::string)>;
|
||||
|
||||
/// This allows in-place construction using an initializer list
|
||||
template <typename T, typename... Args>
|
||||
explicit IsMember(std::initializer_list<T> values, Args &&... args)
|
||||
: IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
|
||||
|
||||
/// This checks to see if an item is in a set (empty function)
|
||||
template <typename T>
|
||||
explicit IsMember(T set)
|
||||
: IsMember(std::move(set),
|
||||
std::function<typename IsMemberType<typename element_value_type<T>::type>::type(
|
||||
typename IsMemberType<typename element_value_type<T>::type>::type)>()) {}
|
||||
|
||||
/// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter
|
||||
/// both sides of the comparison before computing the comparison.
|
||||
template <typename T, typename F> explicit IsMember(T set, F filter_function) {
|
||||
// Get the type of the contained item - requires a container have ::value_type
|
||||
using item_t = typename element_value_type<T>::type;
|
||||
using local_item_t = typename IsMemberType<item_t>::type;
|
||||
|
||||
// Make a local copy of the filter function, using a std::function if not one already
|
||||
std::function<local_item_t(local_item_t)> filter_fn = filter_function;
|
||||
|
||||
// This is the type name for help, it will take the current version of the set contents
|
||||
tname_function = [set]() {
|
||||
std::stringstream out;
|
||||
out << detail::type_name<item_t>() << " in {" << detail::join(detail::smart_deref(set), ",") << "}";
|
||||
return out.str();
|
||||
};
|
||||
|
||||
// This is the function that validates
|
||||
// It stores a copy of the set pointer-like, so shared_ptr will stay alive
|
||||
func = [set, filter_fn](std::string &input) {
|
||||
for(const item_t &v : detail::smart_deref(set)) {
|
||||
local_item_t a = v;
|
||||
local_item_t b;
|
||||
if(!detail::lexical_cast(input, b))
|
||||
throw ValidationError(input); // name is added later
|
||||
|
||||
// The filter function might be empty, so don't filter if it is.
|
||||
if(filter_fn) {
|
||||
a = filter_fn(a);
|
||||
b = filter_fn(b);
|
||||
}
|
||||
|
||||
if(a == b) {
|
||||
// Make sure the version in the input string is identical to the one in the set
|
||||
// Requires std::stringstream << be supported on T.
|
||||
if(filter_fn) {
|
||||
std::stringstream out;
|
||||
out << v;
|
||||
input = out.str();
|
||||
}
|
||||
|
||||
// Return empty error string (success)
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
// If you reach this point, the result was not found
|
||||
return input + " not in {" + detail::join(detail::smart_deref(set), ",") + "}";
|
||||
};
|
||||
}
|
||||
|
||||
/// You can pass in as many filter functions as you like, they nest
|
||||
template <typename T, typename... Args>
|
||||
IsMember(T set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
|
||||
: IsMember(std::move(set),
|
||||
[filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
|
||||
other...) {}
|
||||
};
|
||||
|
||||
/// Helper function to allow ignore_case to be passed to IsMember
|
||||
inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
|
||||
|
||||
/// Helper function to allow ignore_underscore to be passed to IsMember
|
||||
inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }
|
||||
|
||||
namespace detail {
|
||||
/// Split a string into a program name and command line arguments
|
||||
/// the string is assumed to contain a file name followed by other arguments
|
||||
@ -266,6 +384,7 @@ inline std::pair<std::string, std::string> split_program_name(std::string comman
|
||||
ltrim(vals.second);
|
||||
return vals;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
/// @}
|
||||
|
||||
|
@ -5,8 +5,8 @@ CLANG_FORMAT=saschpe/clang-format:5.0.1
|
||||
|
||||
set -evx
|
||||
|
||||
docker run -it ${CLANG_FORMAT} --version
|
||||
docker run -it -v "$(pwd)":/workdir -w /workdir ${CLANG_FORMAT} -style=file -sort-includes -i $(git ls-files -- '*.cpp' '*.hpp')
|
||||
docker run --rm -it ${CLANG_FORMAT} --version
|
||||
docker run --rm -it -v "$(pwd)":/workdir -w /workdir ${CLANG_FORMAT} -style=file -sort-includes -i $(git ls-files -- '*.cpp' '*.hpp')
|
||||
|
||||
git diff --exit-code --color
|
||||
|
||||
|
@ -1071,7 +1071,7 @@ TEST_F(TApp, CallbackFlagsFalse) {
|
||||
app.add_flag_function("-v,-f{false},--val,--fval{false}", func);
|
||||
|
||||
run();
|
||||
EXPECT_EQ(value, 0u);
|
||||
EXPECT_EQ(value, 0);
|
||||
|
||||
args = {"-f"};
|
||||
run();
|
||||
@ -1100,7 +1100,7 @@ TEST_F(TApp, CallbackFlagsFalseShortcut) {
|
||||
app.add_flag_function("-v,!-f,--val,!--fval", func);
|
||||
|
||||
run();
|
||||
EXPECT_EQ(value, 0u);
|
||||
EXPECT_EQ(value, 0);
|
||||
|
||||
args = {"-f"};
|
||||
run();
|
||||
@ -1317,233 +1317,6 @@ TEST_F(TApp, FileExists) {
|
||||
EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSet) {
|
||||
|
||||
std::string choice;
|
||||
app.add_set("-q,--quick", choice, {"one", "two", "three"});
|
||||
|
||||
args = {"--quick", "two"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ("two", choice);
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetWithDefault) {
|
||||
|
||||
std::string choice = "one";
|
||||
app.add_set("-q,--quick", choice, {"one", "two", "three"}, "", true);
|
||||
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ("two", choice);
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InCaselessSetWithDefault) {
|
||||
|
||||
std::string choice = "one";
|
||||
app.add_set_ignore_case("-q,--quick", choice, {"one", "two", "three"}, "", true);
|
||||
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "tWo"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ("two", choice);
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InIntSet) {
|
||||
|
||||
int choice;
|
||||
app.add_set("-q,--quick", choice, {1, 2, 3});
|
||||
|
||||
args = {"--quick", "2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(2, choice);
|
||||
|
||||
args = {"--quick", "4"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InIntSetWindows) {
|
||||
|
||||
int choice;
|
||||
app.add_set("-q,--quick", choice, {1, 2, 3});
|
||||
app.allow_windows_style_options();
|
||||
args = {"/q", "2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(2, choice);
|
||||
|
||||
args = {"/q4"};
|
||||
EXPECT_THROW(run(), CLI::ExtrasError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, FailSet) {
|
||||
|
||||
int choice;
|
||||
app.add_set("-q,--quick", choice, {1, 2, 3});
|
||||
|
||||
args = {"--quick", "3", "--quick=2"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
|
||||
args = {"--quick=hello"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, FailMutableSet) {
|
||||
|
||||
int choice;
|
||||
std::set<int> vals{1, 2, 3};
|
||||
app.add_mutable_set("-q,--quick", choice, vals);
|
||||
app.add_mutable_set("-s,--slow", choice, vals, "", true);
|
||||
|
||||
args = {"--quick=hello"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
args = {"--slow=hello"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCase) {
|
||||
|
||||
std::string choice;
|
||||
app.add_set_ignore_case("-q,--quick", choice, {"one", "Two", "THREE"});
|
||||
|
||||
args = {"--quick", "One"};
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
run();
|
||||
EXPECT_EQ("Two", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
args = {"--quick=one", "--quick=two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCaseMutableValue) {
|
||||
|
||||
std::set<std::string> options{"one", "Two", "THREE"};
|
||||
std::string choice;
|
||||
app.add_mutable_set_ignore_case("-q,--quick", choice, options);
|
||||
|
||||
args = {"--quick", "One"};
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
run();
|
||||
EXPECT_EQ("Two", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // Keeps caps from set
|
||||
|
||||
options.clear();
|
||||
args = {"--quick", "ThrEE"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCasePointer) {
|
||||
|
||||
std::set<std::string> *options = new std::set<std::string>{"one", "Two", "THREE"};
|
||||
std::string choice;
|
||||
app.add_set_ignore_case("-q,--quick", choice, *options);
|
||||
|
||||
args = {"--quick", "One"};
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
run();
|
||||
EXPECT_EQ("Two", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // Keeps caps from set
|
||||
|
||||
delete options;
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // this does not throw a segfault
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
args = {"--quick=one", "--quick=two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreUnderscore) {
|
||||
|
||||
std::string choice;
|
||||
app.add_set_ignore_underscore("-q,--quick", choice, {"option_one", "option_two", "optionthree"});
|
||||
|
||||
args = {"--quick", "option_one"};
|
||||
run();
|
||||
EXPECT_EQ("option_one", choice);
|
||||
|
||||
args = {"--quick", "optiontwo"};
|
||||
run();
|
||||
EXPECT_EQ("option_two", choice); // Keeps underscore from set
|
||||
|
||||
args = {"--quick", "_option_thr_ee"};
|
||||
run();
|
||||
EXPECT_EQ("optionthree", choice); // no underscore
|
||||
|
||||
args = {"--quick", "Option4"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
args = {"--quick=option_one", "--quick=option_two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCaseUnderscore) {
|
||||
|
||||
std::string choice;
|
||||
app.add_set_ignore_case_underscore("-q,--quick", choice, {"Option_One", "option_two", "OptionThree"});
|
||||
|
||||
args = {"--quick", "option_one"};
|
||||
run();
|
||||
EXPECT_EQ("Option_One", choice);
|
||||
|
||||
args = {"--quick", "OptionTwo"};
|
||||
run();
|
||||
EXPECT_EQ("option_two", choice); // Keeps underscore and case from set
|
||||
|
||||
args = {"--quick", "_OPTION_thr_ee"};
|
||||
run();
|
||||
EXPECT_EQ("OptionThree", choice); // no underscore
|
||||
|
||||
args = {"--quick", "Option4"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
args = {"--quick=option_one", "--quick=option_two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, VectorFixedString) {
|
||||
std::vector<std::string> strvec;
|
||||
std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
|
||||
@ -1895,33 +1668,6 @@ TEST_F(TApp, OptionWithDefaults) {
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, SetWithDefaults) {
|
||||
int someint = 2;
|
||||
app.add_set("-a", someint, {1, 2, 3, 4}, "", true);
|
||||
|
||||
args = {"-a1", "-a2"};
|
||||
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, SetWithDefaultsConversion) {
|
||||
int someint = 2;
|
||||
app.add_set("-a", someint, {1, 2, 3, 4}, "", true);
|
||||
|
||||
args = {"-a", "hi"};
|
||||
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, SetWithDefaultsIC) {
|
||||
std::string someint = "ho";
|
||||
app.add_set_ignore_case("-a", someint, {"Hi", "Ho"}, "", true);
|
||||
|
||||
args = {"-aHi", "-aHo"};
|
||||
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
// Added to test ->transform
|
||||
TEST_F(TApp, OrderedModifingTransforms) {
|
||||
std::vector<std::string> val;
|
||||
@ -1989,69 +1735,6 @@ TEST_F(TApp, CustomDoubleOption) {
|
||||
EXPECT_DOUBLE_EQ(custom_opt.second, 1.5);
|
||||
}
|
||||
|
||||
// #113
|
||||
TEST_F(TApp, AddRemoveSetItems) {
|
||||
std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
|
||||
|
||||
std::string type1, type2;
|
||||
app.add_mutable_set("--type1", type1, items);
|
||||
app.add_mutable_set("--type2", type2, items, "", true);
|
||||
|
||||
args = {"--type1", "TYPE1", "--type2", "TYPE2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE1");
|
||||
EXPECT_EQ(type2, "TYPE2");
|
||||
|
||||
items.insert("TYPE6");
|
||||
items.insert("TYPE7");
|
||||
|
||||
items.erase("TYPE1");
|
||||
items.erase("TYPE2");
|
||||
|
||||
args = {"--type1", "TYPE6", "--type2", "TYPE7"};
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE6");
|
||||
EXPECT_EQ(type2, "TYPE7");
|
||||
|
||||
args = {"--type1", "TYPE1"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
args = {"--type2", "TYPE2"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, AddRemoveSetItemsNoCase) {
|
||||
std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
|
||||
|
||||
std::string type1, type2;
|
||||
app.add_mutable_set_ignore_case("--type1", type1, items);
|
||||
app.add_mutable_set_ignore_case("--type2", type2, items, "", true);
|
||||
|
||||
args = {"--type1", "TYPe1", "--type2", "TyPE2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE1");
|
||||
EXPECT_EQ(type2, "TYPE2");
|
||||
|
||||
items.insert("TYPE6");
|
||||
items.insert("TYPE7");
|
||||
|
||||
items.erase("TYPE1");
|
||||
items.erase("TYPE2");
|
||||
|
||||
args = {"--type1", "TyPE6", "--type2", "tYPE7"};
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE6");
|
||||
EXPECT_EQ(type2, "TYPE7");
|
||||
|
||||
args = {"--type1", "TYPe1"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
args = {"--type2", "TYpE2"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
// #128
|
||||
TEST_F(TApp, RepeatingMultiArgumentOptions) {
|
||||
std::vector<std::string> entries;
|
||||
|
@ -26,6 +26,7 @@ set(CLI11_TESTS
|
||||
IniTest
|
||||
SimpleTest
|
||||
AppTest
|
||||
SetTest
|
||||
CreationTest
|
||||
SubcommandTest
|
||||
HelpTest
|
||||
@ -34,6 +35,7 @@ set(CLI11_TESTS
|
||||
OptionalTest
|
||||
DeprecatedTest
|
||||
StringParseTest
|
||||
TrueFalseTest
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
@ -1,12 +1,328 @@
|
||||
#ifdef CLI11_SINGLE_FILE
|
||||
#include "CLI11.hpp"
|
||||
#else
|
||||
#include "CLI/CLI.hpp"
|
||||
#endif
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "app_helper.hpp"
|
||||
|
||||
TEST(Deprecated, Emtpy) {
|
||||
// No deprecated features at this time.
|
||||
EXPECT_TRUE(true);
|
||||
}
|
||||
|
||||
// Classic sets
|
||||
|
||||
TEST_F(TApp, SetWithDefaults) {
|
||||
int someint = 2;
|
||||
app.add_set("-a", someint, {1, 2, 3, 4}, "", true);
|
||||
|
||||
args = {"-a1", "-a2"};
|
||||
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, SetWithDefaultsConversion) {
|
||||
int someint = 2;
|
||||
app.add_set("-a", someint, {1, 2, 3, 4}, "", true);
|
||||
|
||||
args = {"-a", "hi"};
|
||||
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, SetWithDefaultsIC) {
|
||||
std::string someint = "ho";
|
||||
app.add_set_ignore_case("-a", someint, {"Hi", "Ho"}, "", true);
|
||||
|
||||
args = {"-aHi", "-aHo"};
|
||||
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSet) {
|
||||
|
||||
std::string choice;
|
||||
app.add_set("-q,--quick", choice, {"one", "two", "three"});
|
||||
|
||||
args = {"--quick", "two"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ("two", choice);
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetWithDefault) {
|
||||
|
||||
std::string choice = "one";
|
||||
app.add_set("-q,--quick", choice, {"one", "two", "three"}, "", true);
|
||||
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ("two", choice);
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InCaselessSetWithDefault) {
|
||||
|
||||
std::string choice = "one";
|
||||
app.add_set_ignore_case("-q,--quick", choice, {"one", "two", "three"}, "", true);
|
||||
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "tWo"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ("two", choice);
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InIntSet) {
|
||||
|
||||
int choice;
|
||||
app.add_set("-q,--quick", choice, {1, 2, 3});
|
||||
|
||||
args = {"--quick", "2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(2, choice);
|
||||
|
||||
args = {"--quick", "4"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InIntSetWindows) {
|
||||
|
||||
int choice;
|
||||
app.add_set("-q,--quick", choice, {1, 2, 3});
|
||||
app.allow_windows_style_options();
|
||||
args = {"/q", "2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(2, choice);
|
||||
|
||||
args = {"/q", "4"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"/q4"};
|
||||
EXPECT_THROW(run(), CLI::ExtrasError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, FailSet) {
|
||||
|
||||
int choice;
|
||||
app.add_set("-q,--quick", choice, {1, 2, 3});
|
||||
|
||||
args = {"--quick", "3", "--quick=2"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
|
||||
args = {"--quick=hello"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, FailMutableSet) {
|
||||
|
||||
int choice;
|
||||
std::set<int> vals{1, 2, 3};
|
||||
app.add_mutable_set("-q,--quick", choice, vals);
|
||||
app.add_mutable_set("-s,--slow", choice, vals, "", true);
|
||||
|
||||
args = {"--quick=hello"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--slow=hello"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCase) {
|
||||
|
||||
std::string choice;
|
||||
app.add_set_ignore_case("-q,--quick", choice, {"one", "Two", "THREE"});
|
||||
|
||||
args = {"--quick", "One"};
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
run();
|
||||
EXPECT_EQ("Two", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--quick=one", "--quick=two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCaseMutableValue) {
|
||||
|
||||
std::set<std::string> options{"one", "Two", "THREE"};
|
||||
std::string choice;
|
||||
app.add_mutable_set_ignore_case("-q,--quick", choice, options);
|
||||
|
||||
args = {"--quick", "One"};
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
run();
|
||||
EXPECT_EQ("Two", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // Keeps caps from set
|
||||
|
||||
options.clear();
|
||||
args = {"--quick", "ThrEE"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCasePointer) {
|
||||
|
||||
auto options = std::make_shared<std::set<std::string>>(std::initializer_list<std::string>{"one", "Two", "THREE"});
|
||||
std::string choice;
|
||||
app.add_set_ignore_case("-q,--quick", choice, *options);
|
||||
|
||||
args = {"--quick", "One"};
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
run();
|
||||
EXPECT_EQ("Two", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // Keeps caps from set
|
||||
|
||||
options.reset();
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // this does not throw a segfault
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--quick=one", "--quick=two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreUnderscore) {
|
||||
|
||||
std::string choice;
|
||||
app.add_set_ignore_underscore("-q,--quick", choice, {"option_one", "option_two", "optionthree"});
|
||||
|
||||
args = {"--quick", "option_one"};
|
||||
run();
|
||||
EXPECT_EQ("option_one", choice);
|
||||
|
||||
args = {"--quick", "optiontwo"};
|
||||
run();
|
||||
EXPECT_EQ("option_two", choice); // Keeps underscore from set
|
||||
|
||||
args = {"--quick", "_option_thr_ee"};
|
||||
run();
|
||||
EXPECT_EQ("optionthree", choice); // no underscore
|
||||
|
||||
args = {"--quick", "Option4"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--quick=option_one", "--quick=option_two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCaseUnderscore) {
|
||||
|
||||
std::string choice;
|
||||
app.add_set_ignore_case_underscore("-q,--quick", choice, {"Option_One", "option_two", "OptionThree"});
|
||||
|
||||
args = {"--quick", "option_one"};
|
||||
run();
|
||||
EXPECT_EQ("Option_One", choice);
|
||||
|
||||
args = {"--quick", "OptionTwo"};
|
||||
run();
|
||||
EXPECT_EQ("option_two", choice); // Keeps underscore and case from set
|
||||
|
||||
args = {"--quick", "_OPTION_thr_ee"};
|
||||
run();
|
||||
EXPECT_EQ("OptionThree", choice); // no underscore
|
||||
|
||||
args = {"--quick", "Option4"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--quick=option_one", "--quick=option_two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
// #113
|
||||
TEST_F(TApp, AddRemoveSetItems) {
|
||||
std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
|
||||
|
||||
std::string type1, type2;
|
||||
app.add_mutable_set("--type1", type1, items);
|
||||
app.add_mutable_set("--type2", type2, items, "", true);
|
||||
|
||||
args = {"--type1", "TYPE1", "--type2", "TYPE2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE1");
|
||||
EXPECT_EQ(type2, "TYPE2");
|
||||
|
||||
items.insert("TYPE6");
|
||||
items.insert("TYPE7");
|
||||
|
||||
items.erase("TYPE1");
|
||||
items.erase("TYPE2");
|
||||
|
||||
args = {"--type1", "TYPE6", "--type2", "TYPE7"};
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE6");
|
||||
EXPECT_EQ(type2, "TYPE7");
|
||||
|
||||
args = {"--type1", "TYPE1"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--type2", "TYPE2"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, AddRemoveSetItemsNoCase) {
|
||||
std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
|
||||
|
||||
std::string type1, type2;
|
||||
app.add_mutable_set_ignore_case("--type1", type1, items);
|
||||
app.add_mutable_set_ignore_case("--type2", type2, items, "", true);
|
||||
|
||||
args = {"--type1", "TYPe1", "--type2", "TyPE2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE1");
|
||||
EXPECT_EQ(type2, "TYPE2");
|
||||
|
||||
items.insert("TYPE6");
|
||||
items.insert("TYPE7");
|
||||
|
||||
items.erase("TYPE1");
|
||||
items.erase("TYPE2");
|
||||
|
||||
args = {"--type1", "TyPE6", "--type2", "tYPE7"};
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE6");
|
||||
EXPECT_EQ(type2, "TYPE7");
|
||||
|
||||
args = {"--type1", "TYPe1"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--type2", "TYpE2"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
@ -280,7 +280,7 @@ TEST(THelp, IntDefaults) {
|
||||
|
||||
int one{1}, two{2};
|
||||
app.add_option("--one", one, "Help for one", true);
|
||||
app.add_set("--set", two, {2, 3, 4}, "Help for set", true);
|
||||
app.add_option("--set", two, "Help for set", true)->check(CLI::IsMember({2, 3, 4}));
|
||||
|
||||
std::string help = app.help();
|
||||
|
||||
@ -295,7 +295,7 @@ TEST(THelp, SetLower) {
|
||||
CLI::App app{"My prog"};
|
||||
|
||||
std::string def{"One"};
|
||||
app.add_set_ignore_case("--set", def, {"oNe", "twO", "THREE"}, "Help for set", true);
|
||||
app.add_option("--set", def, "Help for set", true)->check(CLI::IsMember({"oNe", "twO", "THREE"}));
|
||||
|
||||
std::string help = app.help();
|
||||
|
||||
@ -795,7 +795,7 @@ TEST(THelp, ChangingSet) {
|
||||
|
||||
std::set<int> vals{1, 2, 3};
|
||||
int val;
|
||||
app.add_mutable_set("--val", val, vals);
|
||||
app.add_option("--val", val)->check(CLI::IsMember(&vals));
|
||||
|
||||
std::string help = app.help();
|
||||
|
||||
@ -816,7 +816,7 @@ TEST(THelp, ChangingSetDefaulted) {
|
||||
|
||||
std::set<int> vals{1, 2, 3};
|
||||
int val = 2;
|
||||
app.add_mutable_set("--val", val, vals, "", true);
|
||||
app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals));
|
||||
|
||||
std::string help = app.help();
|
||||
|
||||
@ -836,7 +836,7 @@ TEST(THelp, ChangingCaselessSet) {
|
||||
|
||||
std::set<std::string> vals{"1", "2", "3"};
|
||||
std::string val;
|
||||
app.add_mutable_set_ignore_case("--val", val, vals);
|
||||
app.add_option("--val", val)->check(CLI::IsMember(&vals, CLI::ignore_case));
|
||||
|
||||
std::string help = app.help();
|
||||
|
||||
@ -857,7 +857,7 @@ TEST(THelp, ChangingCaselessSetDefaulted) {
|
||||
|
||||
std::set<std::string> vals{"1", "2", "3"};
|
||||
std::string val = "2";
|
||||
app.add_mutable_set_ignore_case("--val", val, vals, "", true);
|
||||
app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals, CLI::ignore_case));
|
||||
|
||||
std::string help = app.help();
|
||||
|
||||
|
@ -603,9 +603,9 @@ TEST_F(TApp, IniFlags) {
|
||||
run();
|
||||
|
||||
EXPECT_EQ(2, two);
|
||||
EXPECT_EQ(true, three);
|
||||
EXPECT_EQ(true, four);
|
||||
EXPECT_EQ(true, five);
|
||||
EXPECT_TRUE(three);
|
||||
EXPECT_TRUE(four);
|
||||
EXPECT_TRUE(five);
|
||||
}
|
||||
|
||||
TEST_F(TApp, IniFalseFlags) {
|
||||
@ -631,9 +631,9 @@ TEST_F(TApp, IniFalseFlags) {
|
||||
run();
|
||||
|
||||
EXPECT_EQ(-2, two);
|
||||
EXPECT_EQ(false, three);
|
||||
EXPECT_EQ(true, four);
|
||||
EXPECT_EQ(true, five);
|
||||
EXPECT_FALSE(three);
|
||||
EXPECT_TRUE(four);
|
||||
EXPECT_TRUE(five);
|
||||
}
|
||||
|
||||
TEST_F(TApp, IniFalseFlagsDef) {
|
||||
@ -659,9 +659,9 @@ TEST_F(TApp, IniFalseFlagsDef) {
|
||||
run();
|
||||
|
||||
EXPECT_EQ(-2, two);
|
||||
EXPECT_EQ(true, three);
|
||||
EXPECT_EQ(false, four);
|
||||
EXPECT_EQ(true, five);
|
||||
EXPECT_TRUE(three);
|
||||
EXPECT_FALSE(four);
|
||||
EXPECT_TRUE(five);
|
||||
}
|
||||
|
||||
TEST_F(TApp, IniOutputSimple) {
|
||||
@ -767,7 +767,7 @@ TEST_F(TApp, IniOutputFlag) {
|
||||
TEST_F(TApp, IniOutputSet) {
|
||||
|
||||
int v;
|
||||
app.add_set("--simple", v, {1, 2, 3});
|
||||
app.add_option("--simple", v)->check(CLI::IsMember({1, 2, 3}));
|
||||
|
||||
args = {"--simple=2"};
|
||||
|
||||
|
451
tests/SetTest.cpp
Normal file
451
tests/SetTest.cpp
Normal file
@ -0,0 +1,451 @@
|
||||
#include "app_helper.hpp"
|
||||
|
||||
static_assert(CLI::is_shared_ptr<std::shared_ptr<int>>::value == true, "is_shared_ptr should work on shared pointers");
|
||||
static_assert(CLI::is_shared_ptr<int *>::value == false, "is_shared_ptr should work on pointers");
|
||||
static_assert(CLI::is_shared_ptr<int>::value == false, "is_shared_ptr should work on non-pointers");
|
||||
|
||||
static_assert(CLI::is_copyable_ptr<std::shared_ptr<int>>::value == true,
|
||||
"is_copyable_ptr should work on shared pointers");
|
||||
static_assert(CLI::is_copyable_ptr<int *>::value == true, "is_copyable_ptr should work on pointers");
|
||||
static_assert(CLI::is_copyable_ptr<int>::value == false, "is_copyable_ptr should work on non-pointers");
|
||||
|
||||
TEST_F(TApp, SimpleSets) {
|
||||
std::string value;
|
||||
auto opt = app.add_option("-s,--set", value)->check(CLI::IsMember{std::set<std::string>({"one", "two", "three"})});
|
||||
args = {"-s", "one"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("-s"));
|
||||
EXPECT_EQ(1u, app.count("--set"));
|
||||
EXPECT_EQ(1u, opt->count());
|
||||
EXPECT_EQ(value, "one");
|
||||
}
|
||||
|
||||
TEST_F(TApp, SimpleSetsPtrs) {
|
||||
auto set = std::shared_ptr<std::set<std::string>>(new std::set<std::string>{"one", "two", "three"});
|
||||
std::string value;
|
||||
auto opt = app.add_option("-s,--set", value)->check(CLI::IsMember{set});
|
||||
args = {"-s", "one"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("-s"));
|
||||
EXPECT_EQ(1u, app.count("--set"));
|
||||
EXPECT_EQ(1u, opt->count());
|
||||
EXPECT_EQ(value, "one");
|
||||
|
||||
set->insert("four");
|
||||
|
||||
args = {"-s", "four"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("-s"));
|
||||
EXPECT_EQ(1u, app.count("--set"));
|
||||
EXPECT_EQ(1u, opt->count());
|
||||
EXPECT_EQ(value, "four");
|
||||
}
|
||||
|
||||
TEST_F(TApp, SimiShortcutSets) {
|
||||
std::string value;
|
||||
auto opt = app.add_option("--set", value)->check(CLI::IsMember({"one", "two", "three"}));
|
||||
args = {"--set", "one"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("--set"));
|
||||
EXPECT_EQ(1u, opt->count());
|
||||
EXPECT_EQ(value, "one");
|
||||
|
||||
std::string value2;
|
||||
auto opt2 = app.add_option("--set2", value2)->check(CLI::IsMember({"One", "two", "three"}, CLI::ignore_case));
|
||||
args = {"--set2", "onE"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("--set2"));
|
||||
EXPECT_EQ(1u, opt2->count());
|
||||
EXPECT_EQ(value2, "One");
|
||||
|
||||
std::string value3;
|
||||
auto opt3 = app.add_option("--set3", value3)
|
||||
->check(CLI::IsMember({"O_ne", "two", "three"}, CLI::ignore_case, CLI::ignore_underscore));
|
||||
args = {"--set3", "onE"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("--set3"));
|
||||
EXPECT_EQ(1u, opt3->count());
|
||||
EXPECT_EQ(value3, "O_ne");
|
||||
}
|
||||
|
||||
TEST_F(TApp, SetFromCharStarArrayVector) {
|
||||
constexpr const char *names[] = {"one", "two", "three"};
|
||||
std::string value;
|
||||
auto opt = app.add_option("-s,--set", value)
|
||||
->check(CLI::IsMember{std::vector<std::string>(std::begin(names), std::end(names))});
|
||||
args = {"-s", "one"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("-s"));
|
||||
EXPECT_EQ(1u, app.count("--set"));
|
||||
EXPECT_EQ(1u, opt->count());
|
||||
EXPECT_EQ(value, "one");
|
||||
}
|
||||
|
||||
TEST_F(TApp, OtherTypeSets) {
|
||||
int value;
|
||||
std::vector<int> set = {2, 3, 4};
|
||||
auto opt = app.add_option("--set", value)->check(CLI::IsMember(set));
|
||||
args = {"--set", "3"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("--set"));
|
||||
EXPECT_EQ(1u, opt->count());
|
||||
EXPECT_EQ(value, 3);
|
||||
|
||||
args = {"--set", "5"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
std::vector<int> set2 = {-2, 3, 4};
|
||||
auto opt2 = app.add_option("--set2", value)->check(CLI::IsMember(set2, [](int x) { return std::abs(x); }));
|
||||
args = {"--set2", "-3"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("--set2"));
|
||||
EXPECT_EQ(1u, opt2->count());
|
||||
EXPECT_EQ(value, 3);
|
||||
|
||||
args = {"--set2", "-3"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("--set2"));
|
||||
EXPECT_EQ(1u, opt2->count());
|
||||
EXPECT_EQ(value, 3);
|
||||
|
||||
args = {"--set2", "2"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("--set2"));
|
||||
EXPECT_EQ(1u, opt2->count());
|
||||
EXPECT_EQ(value, -2);
|
||||
}
|
||||
|
||||
TEST_F(TApp, NumericalSets) {
|
||||
int value;
|
||||
auto opt = app.add_option("-s,--set", value)->check(CLI::IsMember{std::set<int>({1, 2, 3})});
|
||||
args = {"-s", "1"};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("-s"));
|
||||
EXPECT_EQ(1u, app.count("--set"));
|
||||
EXPECT_EQ(1u, opt->count());
|
||||
EXPECT_EQ(value, 1);
|
||||
}
|
||||
|
||||
// Converted original set tests
|
||||
|
||||
TEST_F(TApp, SetWithDefaults) {
|
||||
int someint = 2;
|
||||
app.add_option("-a", someint, "", true)->check(CLI::IsMember({1, 2, 3, 4}));
|
||||
|
||||
args = {"-a1", "-a2"};
|
||||
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, SetWithDefaultsConversion) {
|
||||
int someint = 2;
|
||||
app.add_option("-a", someint, "", true)->check(CLI::IsMember({1, 2, 3, 4}));
|
||||
|
||||
args = {"-a", "hi"};
|
||||
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, SetWithDefaultsIC) {
|
||||
std::string someint = "ho";
|
||||
app.add_option("-a", someint, "", true)->check(CLI::IsMember({"Hi", "Ho"}));
|
||||
|
||||
args = {"-aHi", "-aHo"};
|
||||
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSet) {
|
||||
|
||||
std::string choice;
|
||||
app.add_option("-q,--quick", choice)->check(CLI::IsMember({"one", "two", "three"}));
|
||||
|
||||
args = {"--quick", "two"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ("two", choice);
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetWithDefault) {
|
||||
|
||||
std::string choice = "one";
|
||||
app.add_option("-q,--quick", choice, "", true)->check(CLI::IsMember({"one", "two", "three"}));
|
||||
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ("two", choice);
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InCaselessSetWithDefault) {
|
||||
|
||||
std::string choice = "one";
|
||||
app.add_option("-q,--quick", choice, "", true)->check(CLI::IsMember({"one", "two", "three"}, CLI::ignore_case));
|
||||
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "tWo"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ("two", choice);
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InIntSet) {
|
||||
|
||||
int choice;
|
||||
app.add_option("-q,--quick", choice)->check(CLI::IsMember({1, 2, 3}));
|
||||
|
||||
args = {"--quick", "2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(2, choice);
|
||||
|
||||
args = {"--quick", "4"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InIntSetWindows) {
|
||||
|
||||
int choice;
|
||||
app.add_option("-q,--quick", choice)->check(CLI::IsMember({1, 2, 3}));
|
||||
app.allow_windows_style_options();
|
||||
args = {"/q", "2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(2, choice);
|
||||
|
||||
args = {"/q", "4"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"/q4"};
|
||||
EXPECT_THROW(run(), CLI::ExtrasError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, FailSet) {
|
||||
|
||||
int choice;
|
||||
app.add_option("-q,--quick", choice)->check(CLI::IsMember({1, 2, 3}));
|
||||
|
||||
args = {"--quick", "3", "--quick=2"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
|
||||
args = {"--quick=hello"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, FailMutableSet) {
|
||||
|
||||
int choice;
|
||||
auto vals = std::shared_ptr<std::set<int>>(new std::set<int>({1, 2, 3}));
|
||||
app.add_option("-q,--quick", choice)->check(CLI::IsMember(vals));
|
||||
app.add_option("-s,--slow", choice, "", true)->check(CLI::IsMember(vals));
|
||||
|
||||
args = {"--quick=hello"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--slow=hello"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCase) {
|
||||
|
||||
std::string choice;
|
||||
app.add_option("-q,--quick", choice)->check(CLI::IsMember({"one", "Two", "THREE"}, CLI::ignore_case));
|
||||
|
||||
args = {"--quick", "One"};
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
run();
|
||||
EXPECT_EQ("Two", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--quick=one", "--quick=two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCaseMutableValue) {
|
||||
|
||||
std::set<std::string> options{"one", "Two", "THREE"};
|
||||
std::string choice;
|
||||
app.add_option("-q,--quick", choice)->check(CLI::IsMember(&options, CLI::ignore_case));
|
||||
|
||||
args = {"--quick", "One"};
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
run();
|
||||
EXPECT_EQ("Two", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // Keeps caps from set
|
||||
|
||||
options.clear();
|
||||
args = {"--quick", "ThrEE"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCasePointer) {
|
||||
|
||||
std::set<std::string> *options = new std::set<std::string>{"one", "Two", "THREE"};
|
||||
std::string choice;
|
||||
app.add_option("-q,--quick", choice)->check(CLI::IsMember(*options, CLI::ignore_case));
|
||||
|
||||
args = {"--quick", "One"};
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
run();
|
||||
EXPECT_EQ("Two", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // Keeps caps from set
|
||||
|
||||
delete options;
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // this does not throw a segfault
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--quick=one", "--quick=two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreUnderscore) {
|
||||
|
||||
std::string choice;
|
||||
app.add_option("-q,--quick", choice)
|
||||
->check(CLI::IsMember({"option_one", "option_two", "optionthree"}, CLI::ignore_underscore));
|
||||
|
||||
args = {"--quick", "option_one"};
|
||||
run();
|
||||
EXPECT_EQ("option_one", choice);
|
||||
|
||||
args = {"--quick", "optiontwo"};
|
||||
run();
|
||||
EXPECT_EQ("option_two", choice); // Keeps underscore from set
|
||||
|
||||
args = {"--quick", "_option_thr_ee"};
|
||||
run();
|
||||
EXPECT_EQ("optionthree", choice); // no underscore
|
||||
|
||||
args = {"--quick", "Option4"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--quick=option_one", "--quick=option_two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCaseUnderscore) {
|
||||
|
||||
std::string choice;
|
||||
app.add_option("-q,--quick", choice)
|
||||
->check(CLI::IsMember({"Option_One", "option_two", "OptionThree"}, CLI::ignore_case, CLI::ignore_underscore));
|
||||
|
||||
args = {"--quick", "option_one"};
|
||||
run();
|
||||
EXPECT_EQ("Option_One", choice);
|
||||
|
||||
args = {"--quick", "OptionTwo"};
|
||||
run();
|
||||
EXPECT_EQ("option_two", choice); // Keeps underscore and case from set
|
||||
|
||||
args = {"--quick", "_OPTION_thr_ee"};
|
||||
run();
|
||||
EXPECT_EQ("OptionThree", choice); // no underscore
|
||||
|
||||
args = {"--quick", "Option4"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--quick=option_one", "--quick=option_two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
// #113
|
||||
TEST_F(TApp, AddRemoveSetItems) {
|
||||
std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
|
||||
|
||||
std::string type1, type2;
|
||||
app.add_option("--type1", type1)->check(CLI::IsMember(&items));
|
||||
app.add_option("--type2", type2, "", true)->check(CLI::IsMember(&items));
|
||||
|
||||
args = {"--type1", "TYPE1", "--type2", "TYPE2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE1");
|
||||
EXPECT_EQ(type2, "TYPE2");
|
||||
|
||||
items.insert("TYPE6");
|
||||
items.insert("TYPE7");
|
||||
|
||||
items.erase("TYPE1");
|
||||
items.erase("TYPE2");
|
||||
|
||||
args = {"--type1", "TYPE6", "--type2", "TYPE7"};
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE6");
|
||||
EXPECT_EQ(type2, "TYPE7");
|
||||
|
||||
args = {"--type1", "TYPE1"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--type2", "TYPE2"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, AddRemoveSetItemsNoCase) {
|
||||
std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
|
||||
|
||||
std::string type1, type2;
|
||||
app.add_option("--type1", type1)->check(CLI::IsMember(&items, CLI::ignore_case));
|
||||
app.add_option("--type2", type2, "", true)->check(CLI::IsMember(&items, CLI::ignore_case));
|
||||
|
||||
args = {"--type1", "TYPe1", "--type2", "TyPE2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE1");
|
||||
EXPECT_EQ(type2, "TYPE2");
|
||||
|
||||
items.insert("TYPE6");
|
||||
items.insert("TYPE7");
|
||||
|
||||
items.erase("TYPE1");
|
||||
items.erase("TYPE2");
|
||||
|
||||
args = {"--type1", "TyPE6", "--type2", "tYPE7"};
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE6");
|
||||
EXPECT_EQ(type2, "TYPE7");
|
||||
|
||||
args = {"--type1", "TYPe1"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--type2", "TYpE2"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
}
|
@ -888,8 +888,8 @@ TEST_F(TApp, UnnamedSubMixExtras) {
|
||||
run();
|
||||
EXPECT_EQ(val, -3.0);
|
||||
EXPECT_EQ(val2, 5.93);
|
||||
EXPECT_EQ(app.remaining_size(), 2);
|
||||
EXPECT_EQ(sub->remaining_size(), 0);
|
||||
EXPECT_EQ(app.remaining_size(), 2u);
|
||||
EXPECT_EQ(sub->remaining_size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(TApp, UnnamedSubNoExtras) {
|
||||
@ -901,8 +901,8 @@ TEST_F(TApp, UnnamedSubNoExtras) {
|
||||
run();
|
||||
EXPECT_EQ(val, -3.0);
|
||||
EXPECT_EQ(val2, 5.93);
|
||||
EXPECT_EQ(app.remaining_size(), 0);
|
||||
EXPECT_EQ(sub->remaining_size(), 0);
|
||||
EXPECT_EQ(app.remaining_size(), 0u);
|
||||
EXPECT_EQ(sub->remaining_size(), 0u);
|
||||
}
|
||||
|
||||
TEST(SharedSubTests, SharedSubcommand) {
|
||||
|
30
tests/TrueFalseTest.cpp
Normal file
30
tests/TrueFalseTest.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "app_helper.hpp"
|
||||
|
||||
/// This allows a set of strings to be run over by a test
|
||||
struct TApp_TBO : public TApp, public ::testing::WithParamInterface<const char *> {};
|
||||
|
||||
TEST_P(TApp_TBO, TrueBoolOption) {
|
||||
bool value = false; // Not used, but set just in case
|
||||
app.add_option("-b,--bool", value);
|
||||
args = {"--bool", GetParam()};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("--bool"));
|
||||
EXPECT_TRUE(value);
|
||||
}
|
||||
|
||||
// Change to INSTANTIATE_TEST_SUITE_P in GTest master
|
||||
INSTANTIATE_TEST_CASE_P(TrueBoolOptions, TApp_TBO, ::testing::Values("true", "on", "True", "ON"), );
|
||||
|
||||
/// This allows a set of strings to be run over by a test
|
||||
struct TApp_FBO : public TApp, public ::testing::WithParamInterface<const char *> {};
|
||||
|
||||
TEST_P(TApp_FBO, FalseBoolOptions) {
|
||||
bool value = true; // Not used, but set just in case
|
||||
app.add_option("-b,--bool", value);
|
||||
args = {"--bool", GetParam()};
|
||||
run();
|
||||
EXPECT_EQ(1u, app.count("--bool"));
|
||||
EXPECT_FALSE(value);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(FalseBoolOptions, TApp_FBO, ::testing::Values("false", "off", "False", "OFF"), );
|
Loading…
x
Reference in New Issue
Block a user