Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding take_last #40

Merged
merged 2 commits into from
Nov 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
14 changes: 14 additions & 0 deletions tests/CreationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,20 @@ TEST_F(TApp, IncorrectConstructionVectorAsFlag) {
EXPECT_THROW(cat->expected(0), CLI::IncorrectConstruction);
}

TEST_F(TApp, IncorrectConstructionVectorTakeLast) {
std::vector<int> vec;
auto cat = app.add_option("--vec", vec);
EXPECT_THROW(cat->take_last(), CLI::IncorrectConstruction);
}

TEST_F(TApp, IncorrectConstructionTakeLastExpected) {
std::vector<int> vec;
auto cat = app.add_option("--vec", vec);
cat->expected(1);
ASSERT_NO_THROW(cat->take_last());
EXPECT_THROW(cat->expected(2), CLI::IncorrectConstruction);
}

TEST_F(TApp, IncorrectConstructionRequiresCannotFind) {
auto cat = app.add_flag("--cat");
EXPECT_THROW(cat->requires("--nothing"), CLI::IncorrectConstruction);
Expand Down