1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-05-03 14:03:52 +00:00

Adding string based method for requires/excludes

This commit is contained in:
Henry Fredrick Schreiner 2017-02-24 16:59:53 -05:00
parent 0ccd814ac9
commit 3f4c165ea9
5 changed files with 86 additions and 9 deletions

View File

@ -1,5 +1,6 @@
## Version 0.6
* Added string versions of `->requires()` and `->excludes()` for consistency.
* Renamed protected members for internal consistency, grouped docs.
* Added the ability to add a number to `.require_subcommand()`.

View File

@ -41,8 +41,7 @@ This library was built to supply the Application object for the GooFit CUDA/OMP
* Collect user feedback
* Ini configuration support is basic (long options only, no vector support), is more needed?
* Evaluate compatibility with [ROOT](https://root.cern.ch)'s TApplication object.
* Expand `required`/`exluded` to take strings
* Document adding callback (maybe add C++14 only switch method?)
* Document adding callback (maybe add C++14 only `add_switch` method?)
* Test "adding to cmake" method
See the [changelog](./CHANGELOG.md) or [GitHub releases](https://github.com/henryiii/CLI11/releases) for details.

View File

@ -440,7 +440,8 @@ public:
/// @name Extras for subclassing
///@{
/// This allows subclasses to inject code before callbacks but after parse
/// This allows subclasses to inject code before callbacks but after parse.
///
/// This does not run if any errors or help is thrown.
virtual void pre_callback() {}

View File

@ -185,9 +185,19 @@ public:
return this;
}
/// Any number supported
template<typename... ARG>
Option* requires(Option* opt, Option* opt1, ARG... args) {
/// Can find a string if needed
template<typename T=App>
Option* requires(std::string opt_name) {
for(const Option_p& opt : dynamic_cast<T*>(parent_)->options_)
if(opt.get() != this && opt->check_name(opt_name))
return requires(opt.get());
throw IncorrectConstruction("Option " + opt_name + " is not defined");
}
/// Any number supported, any mix of string and Opt
template<typename A, typename B, typename... ARG>
Option* requires(A opt, B opt1, ARG... args) {
requires(opt);
return requires(opt1, args...);
}
@ -200,9 +210,18 @@ public:
return this;
}
/// Any number supported
template<typename... ARG>
Option* excludes(Option* opt, Option* opt1, ARG... args) {
/// Can find a string if needed
template<typename T=App>
Option* excludes(std::string opt_name) {
for(const Option_p& opt : dynamic_cast<T*>(parent_)->options_)
if(opt.get() != this && opt->check_name(opt_name))
return excludes(opt.get());
throw IncorrectConstruction("Option " + opt_name + " is not defined");
}
/// Any number supported, any mix of string and Opt
template<typename A, typename B, typename... ARG>
Option* excludes(A opt, B opt1, ARG... args) {
excludes(opt);
return excludes(opt1, args...);
}

View File

@ -458,6 +458,30 @@ TEST_F(TApp, ExcludesFlags) {
EXPECT_THROW(run(), CLI::ExcludesError);
}
TEST_F(TApp, ExcludesMixedFlags) {
CLI::Option* opt1 = app.add_flag("--opt1");
app.add_flag("--opt2");
CLI::Option* opt3 = app.add_flag("--opt3");
app.add_flag("--no")->excludes(opt1, "--opt2", opt3);
EXPECT_NO_THROW(run());
app.reset();
args = {"--no"};
EXPECT_NO_THROW(run());
app.reset();
args = {"--opt2"};
EXPECT_NO_THROW(run());
app.reset();
args = {"--no", "--opt1"};
EXPECT_THROW(run(), CLI::ExcludesError);
app.reset();
args = {"--no", "--opt2"};
EXPECT_THROW(run(), CLI::ExcludesError);
}
TEST_F(TApp, RequiresMultiFlags) {
CLI::Option* opt1 = app.add_flag("--opt1");
@ -492,6 +516,39 @@ TEST_F(TApp, RequiresMultiFlags) {
EXPECT_NO_THROW(run());
}
TEST_F(TApp, RequiresMixedFlags) {
CLI::Option* opt1 = app.add_flag("--opt1");
CLI::Option* opt2 = app.add_flag("--opt2");
CLI::Option* opt3 = app.add_flag("--opt3");
app.add_flag("--optall")->requires(opt1, "--opt2", "--opt3");
EXPECT_NO_THROW(run());
app.reset();
args = {"--opt1"};
EXPECT_NO_THROW(run());
app.reset();
args = {"--opt2"};
EXPECT_NO_THROW(run());
app.reset();
args = {"--optall"};
EXPECT_THROW(run(), CLI::RequiresError);
app.reset();
args = {"--optall", "--opt1"};
EXPECT_THROW(run(), CLI::RequiresError);
app.reset();
args = {"--optall", "--opt2", "--opt1"};
EXPECT_THROW(run(), CLI::RequiresError);
app.reset();
args = {"--optall", "--opt1", "--opt2", "--opt3"};
EXPECT_NO_THROW(run());
}
TEST_F(TApp, RequiresChainedFlags) {
CLI::Option* opt1 = app.add_flag("--opt1");
CLI::Option* opt2 = app.add_flag("--opt2")->requires(opt1);