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

Adding take_last

This commit is contained in:
Henry Fredrick Schreiner 2017-11-18 16:02:33 -05:00 committed by Henry Schreiner
parent 0a35db8f00
commit 20cccfc353
4 changed files with 58 additions and 5 deletions

View File

@ -155,6 +155,7 @@ The add commands return a pointer to an internally stored `Option`. If you set t
* `->envname(name)`: Gets the value from the environment if present and not passed on the command line. * `->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"`. `"Hidden"` will not show up in the help print. * `->group(name)`: The help group to put the option in. No effect for positional options. Defaults to `"Options"`. `"Hidden"` will not show up in the help print.
* `->ignore_case()`: Ignore the case on the command line (also works on subcommands, does not affect arguments). * `->ignore_case()`: Ignore the case on the command line (also works on subcommands, does not affect arguments).
* `->take_last()`: Only take the last option/flag given on the command line, automatically true for bool flags
* `->check(CLI::ExistingFile)`: Requires that the file exists if given. * `->check(CLI::ExistingFile)`: Requires that the file exists if given.
* `->check(CLI::ExistingDirectory)`: Requires that the directory exists. * `->check(CLI::ExistingDirectory)`: Requires that the directory exists.
* `->check(CLI::NonexistentPath)`: Requires that the path does not exist. * `->check(CLI::NonexistentPath)`: Requires that the path does not exist.

View File

@ -351,22 +351,23 @@ class App {
return opt; return opt;
} }
/// Bool version /// Bool version - defaults to allowing multiple passings, but can be forced to one if `take_last(false)` is used.
template <typename T, enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy> template <typename T, enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy>
Option *add_flag(std::string name, Option *add_flag(std::string name,
T &count, ///< A varaible holding true if passed T &count, ///< A varaible holding true if passed
std::string description = "") { std::string description = "") {
count = false; count = false;
CLI::callback_t fun = [&count](CLI::results_t) { CLI::callback_t fun = [&count](CLI::results_t res) {
count = true; count = true;
return true; return res.size() == 1;
}; };
Option *opt = add_option(name, fun, description, false); Option *opt = add_option(name, fun, description, false);
if(opt->get_positional()) if(opt->get_positional())
throw IncorrectConstruction("Flags cannot be positional"); throw IncorrectConstruction("Flags cannot be positional");
opt->set_custom_option("", 0); opt->set_custom_option("", 0);
opt->take_last();
return opt; return opt;
} }

View File

@ -74,6 +74,9 @@ class Option {
/// The number of expected values, 0 for flag, -1 for unlimited vector /// The number of expected values, 0 for flag, -1 for unlimited vector
int expected_{1}; int expected_{1};
/// Only take the last argument (requires `expected_ == 1`)
bool last_{false};
/// A private setting to allow args to not be able to accept incorrect expected values /// A private setting to allow args to not be able to accept incorrect expected values
bool changeable_{false}; bool changeable_{false};
@ -155,10 +158,20 @@ class Option {
throw IncorrectConstruction("Cannot make a flag take arguments!"); throw IncorrectConstruction("Cannot make a flag take arguments!");
else if(!changeable_) else if(!changeable_)
throw IncorrectConstruction("You can only change the expected arguments for vectors"); throw IncorrectConstruction("You can only change the expected arguments for vectors");
else if(last_)
throw IncorrectConstruction("You can't change expected arguments after you've set take_last!");
expected_ = value; expected_ = value;
return this; return this;
} }
/// Take the last argument if given multiple times
Option *take_last(bool value = true) {
if(expected_ != 0 && expected_ != 1)
throw IncorrectConstruction("take_last only works for flags and single value options!");
last_ = value;
return this;
}
/// Adds a validator /// Adds a validator
Option *check(std::function<bool(std::string)> validator) { Option *check(std::function<bool(std::string)> validator) {
@ -243,6 +256,9 @@ class Option {
/// The number of arguments the option expects /// The number of arguments the option expects
int get_expected() const { return expected_; } int get_expected() const { return expected_; }
/// The status of the take last flag
bool get_take_last() const { return last_; }
/// True if this has a default value /// True if this has a default value
int get_default() const { return default_; } int get_default() const { return default_; }
@ -340,7 +356,16 @@ class Option {
/// Process the callback /// Process the callback
void run_callback() const { void run_callback() const {
if(!callback_(results_)) bool result;
// If take_last, only operate on the final item
if(last_) {
results_t partial_result = {results_.back()};
result = !callback_(partial_result);
} else {
result = !callback_(results_);
}
if(result)
throw ConversionError(get_name() + "=" + detail::join(results_)); throw ConversionError(get_name() + "=" + detail::join(results_));
if(!validators_.empty()) { if(!validators_.empty()) {
for(const std::string &result : results_) for(const std::string &result : results_)
@ -407,7 +432,7 @@ class Option {
return std::find(std::begin(lnames_), std::end(lnames_), name) != std::end(lnames_); return std::find(std::begin(lnames_), std::end(lnames_), name) != std::end(lnames_);
} }
/// Puts a result at position r /// Puts a result at the end, unless last_ is set, in which case it just keeps the last one
void add_result(std::string s) { void add_result(std::string s) {
results_.push_back(s); results_.push_back(s);
callback_run_ = false; callback_run_ = false;

View File

@ -167,6 +167,20 @@ TEST_F(TApp, BoolAndIntFlags) {
EXPECT_EQ((unsigned int)2, uflag); EXPECT_EQ((unsigned int)2, uflag);
} }
TEST_F(TApp, BoolOnlyFlag) {
bool bflag;
app.add_flag("-b", bflag)->take_last(false);
args = {"-b"};
EXPECT_NO_THROW(run());
EXPECT_TRUE(bflag);
app.reset();
args = {"-b", "-b"};
EXPECT_THROW(run(), CLI::ConversionError);
}
TEST_F(TApp, ShortOpts) { TEST_F(TApp, ShortOpts) {
unsigned long long funnyint; unsigned long long funnyint;
@ -204,6 +218,18 @@ TEST_F(TApp, DefaultOpts) {
EXPECT_EQ("9", s); EXPECT_EQ("9", s);
} }
TEST_F(TApp, TakeLastOpt) {
std::string str;
app.add_option("--str", str)->take_last();
args = {"--str=one", "--str=two"};
run();
EXPECT_EQ(str, "two");
}
TEST_F(TApp, EnumTest) { TEST_F(TApp, EnumTest) {
enum Level : std::int32_t { High, Medium, Low }; enum Level : std::int32_t { High, Medium, Low };
Level level = Level::Low; Level level = Level::Low;