mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 20:23:55 +00:00
Adding requires, excludes, and getenv (untested)
This commit is contained in:
parent
00acc84b52
commit
f4bf6d7226
@ -103,8 +103,11 @@ Adding a configuration option is special. If it is present, it will be read alon
|
||||
|
||||
The add commands return a pointer to an internally stored `Option`. If you set the final argument to true, the default value is captured and printed on the command line with the help flag. This option can be used direcly to check for the count (`->count()`) after parsing to avoid a string based lookup. Before parsing, you can set the following options:
|
||||
|
||||
* `->required()`: The program will quit if this option is not present
|
||||
* `->required()`: The program will quit if this option is not present. This is `manditory` in Plumbum, but required options seems to be a more standard term.
|
||||
* `->expected(N)`: Take `N` values instead of as many as possible, only for vector args
|
||||
* `->requires(opt)`: This option requires another option to also be present, opt is an `Option` pointer
|
||||
* `->excludes(opt)`: This option cannot be given with `opt` present, opt is an `Option` pointer
|
||||
* `->envname(name)`: Gets the value from the environment if present and not passed on the command line
|
||||
* `->group(name)`: The help group to put the option in. No effect for positional options. Defaults to `"Options"`.
|
||||
* `->check(CLI::ExistingFile)`: Requires that the file exists if given
|
||||
* `->check(CLI::ExistingDirectory)`: Requires that the directory exists
|
||||
|
@ -55,6 +55,7 @@ protected:
|
||||
std::string ini_file;
|
||||
bool ini_required {false};
|
||||
Option* ini_setting {nullptr};
|
||||
|
||||
|
||||
public:
|
||||
|
||||
@ -379,12 +380,23 @@ public:
|
||||
opt->add_result(0, positionals.front());
|
||||
positionals.pop_front();
|
||||
}
|
||||
|
||||
if (first_parse && opt->count() == 0 && opt->_envname != "") {
|
||||
// Will not interact very well with ini files
|
||||
char *ename = std::getenv(opt->_envname.c_str());
|
||||
if(ename != nullptr) {
|
||||
opt->get_new();
|
||||
opt->add_result(0, std::string(ename));
|
||||
}
|
||||
}
|
||||
|
||||
if (opt->count() > 0) {
|
||||
if(!opt->run_callback())
|
||||
throw ConversionError(opt->get_name() + "=" + detail::join(opt->flatten_results()));
|
||||
}
|
||||
}
|
||||
|
||||
// Process an INI file
|
||||
if (first_parse && ini_setting != nullptr && ini_file != "") {
|
||||
try {
|
||||
std::vector<std::string> values = detail::parse_ini(ini_file);
|
||||
@ -398,9 +410,19 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Verify required options
|
||||
for(const Option_p& opt : options) {
|
||||
// Required
|
||||
if (opt->get_required() && opt->count() < opt->get_expected())
|
||||
throw RequiredError(opt->get_name());
|
||||
// Requires
|
||||
for (const Option* opt_req : opt->_requires)
|
||||
if (opt_req->count() == 0)
|
||||
throw RequiresError(opt->get_name(), opt_req->get_name());
|
||||
// Excludes
|
||||
for (const Option* opt_ex : opt->_excludes)
|
||||
if (opt_ex->count() != 0)
|
||||
throw ExcludesError(opt->get_name(), opt_ex->get_name());
|
||||
}
|
||||
|
||||
if(positionals.size()>0)
|
||||
|
@ -75,6 +75,16 @@ struct RequiredError : public ParseError {
|
||||
RequiredError(std::string name) : ParseError("RequiredError", name, 5) {}
|
||||
};
|
||||
|
||||
/// Thrown when a requires option is missing
|
||||
struct RequiresError : public ParseError {
|
||||
RequiresError(std::string name, std::string subname) : ParseError("RequiresError", name + " requires " + subname, 13) {}
|
||||
};
|
||||
|
||||
/// Thrown when a exludes option is present
|
||||
struct ExcludesError : public ParseError {
|
||||
ExcludesError(std::string name, std::string subname) : ParseError("ExcludesError", name + " excludes " + subname, 14) {}
|
||||
};
|
||||
|
||||
/// Thrown when too many positionals are found
|
||||
struct PositionalError : public ParseError {
|
||||
PositionalError(std::string name) : ParseError("PositionalError", name, 6) {}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#include "CLI/Error.hpp"
|
||||
#include "CLI/StringTools.hpp"
|
||||
@ -42,6 +43,10 @@ protected:
|
||||
bool allow_vector {false};
|
||||
std::vector<std::function<bool(std::string)>> _validators;
|
||||
|
||||
std::set<Option*> _requires;
|
||||
std::set<Option*> _excludes;
|
||||
std::string _envname;
|
||||
|
||||
// Results
|
||||
results_t results;
|
||||
|
||||
@ -69,6 +74,7 @@ public:
|
||||
return this;
|
||||
}
|
||||
|
||||
/// True if this is a required option
|
||||
bool get_required() const {
|
||||
return _required;
|
||||
}
|
||||
@ -115,11 +121,13 @@ public:
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Changes the group membership
|
||||
Option* group(std::string name) {
|
||||
_group = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Get the group of this option
|
||||
const std::string& get_group() const {
|
||||
return _group;
|
||||
}
|
||||
@ -129,6 +137,42 @@ public:
|
||||
return description;
|
||||
}
|
||||
|
||||
/// Sets required options
|
||||
Option* requires(Option* opt) {
|
||||
auto tup = _requires.insert(opt);
|
||||
if(!tup.second)
|
||||
throw OptionAlreadyAdded(get_name() + " requires " + opt->get_name());
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Any number supported
|
||||
template<typename... ARG>
|
||||
Option* requires(Option* opt, Option* opt1, ARG... args) {
|
||||
requires(opt);
|
||||
return requires(opt1, args...);
|
||||
}
|
||||
|
||||
/// Sets excluded options
|
||||
Option* excludes(Option* opt) {
|
||||
auto tup = _excludes.insert(opt);
|
||||
if(!tup.second)
|
||||
throw OptionAlreadyAdded(get_name() + " excludes " + opt->get_name());
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Any number supported
|
||||
template<typename... ARG>
|
||||
Option* excludes(Option* opt, Option* opt1, ARG... args) {
|
||||
excludes(opt);
|
||||
return excludes(opt1, args...);
|
||||
}
|
||||
|
||||
/// Sets environment variable to read if no option given
|
||||
Option* envname(std::string name) {
|
||||
_envname = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// The name and any extras needed for positionals
|
||||
std::string help_positional() const {
|
||||
std::string out = pname;
|
||||
@ -144,6 +188,8 @@ public:
|
||||
std::string get_pname() const {
|
||||
return pname;
|
||||
}
|
||||
|
||||
|
||||
/// Process the callback
|
||||
bool run_callback() const {
|
||||
if(_validators.size()>0) {
|
||||
|
@ -537,4 +537,4 @@ TEST_F(SubcommandProgram, SpareSub) {
|
||||
}
|
||||
|
||||
|
||||
// TODO: Check help output and formatting
|
||||
// TODO: add tests for requires, excludes, envname
|
||||
|
Loading…
x
Reference in New Issue
Block a user