Skip to content

Commit

Permalink
Adding take_last
Browse files Browse the repository at this point in the history
  • Loading branch information
henryiii committed Nov 19, 2017
1 parent 0a35db8 commit 20cccfc
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 5 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* `->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).
* `->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::ExistingDirectory)`: Requires that the directory exists.
* `->check(CLI::NonexistentPath)`: Requires that the path does not exist.
Expand Down
7 changes: 4 additions & 3 deletions include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,22 +351,23 @@ class App {
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>
Option *add_flag(std::string name,
T &count, ///< A varaible holding true if passed
std::string description = "") {

count = false;
CLI::callback_t fun = [&count](CLI::results_t) {
CLI::callback_t fun = [&count](CLI::results_t res) {
count = true;
return true;
return res.size() == 1;
};

Option *opt = add_option(name, fun, description, false);
if(opt->get_positional())
throw IncorrectConstruction("Flags cannot be positional");
opt->set_custom_option("", 0);
opt->take_last();
return opt;
}

Expand Down
29 changes: 27 additions & 2 deletions include/CLI/Option.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ class Option {
/// The number of expected values, 0 for flag, -1 for unlimited vector
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
bool changeable_{false};

Expand Down Expand Up @@ -155,10 +158,20 @@ class Option {
throw IncorrectConstruction("Cannot make a flag take arguments!");
else if(!changeable_)
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;
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
Option *check(std::function<bool(std::string)> validator) {

Expand Down Expand Up @@ -243,6 +256,9 @@ class Option {
/// The number of arguments the option expects
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
int get_default() const { return default_; }

Expand Down Expand Up @@ -340,7 +356,16 @@ class Option {

/// Process the callback
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_));
if(!validators_.empty()) {
for(const std::string &result : results_)
Expand Down Expand Up @@ -407,7 +432,7 @@ class Option {
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) {
results_.push_back(s);
callback_run_ = false;
Expand Down
26 changes: 26 additions & 0 deletions tests/AppTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,20 @@ TEST_F(TApp, BoolAndIntFlags) {
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) {

unsigned long long funnyint;
Expand Down Expand Up @@ -204,6 +218,18 @@ TEST_F(TApp, DefaultOpts) {
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) {
enum Level : std::int32_t { High, Medium, Low };
Level level = Level::Low;
Expand Down

0 comments on commit 20cccfc

Please sign in to comment.