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

Adding size instead of expected + unchangable

This commit is contained in:
Henry Fredrick Schreiner 2018-03-20 09:32:21 +01:00 committed by Henry Schreiner
parent de06d50f34
commit 447bda047f
4 changed files with 66 additions and 32 deletions

View File

@ -835,7 +835,7 @@ class App {
std::string value;
// Non-flags
if(opt->get_expected() != 0) {
if(opt->get_type_size() != 0) {
// If the option was found on command line
if(opt->count() > 0)
@ -1069,7 +1069,7 @@ class App {
/// Currently checks to see if multiple positionals exist with -1 args
void _validate() const {
auto count = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
return opt->get_expected() == -1 && opt->get_positional();
return opt->get_items_expected() < 0 && opt->get_positional();
});
if(count > 1)
throw InvalidError(name_);
@ -1189,8 +1189,8 @@ class App {
// Required or partially filled
if(opt->get_required() || opt->count() != 0) {
// Make sure enough -N arguments parsed (+N is already handled in parsing function)
if(opt->get_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_expected()))
throw ArgumentMismatch::AtLeast(opt->single_name(), -opt->get_expected());
if(opt->get_items_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_items_expected()))
throw ArgumentMismatch::AtLeast(opt->single_name(), -opt->get_items_expected());
// Required but empty
if(opt->get_required() && opt->count() == 0)
@ -1260,7 +1260,7 @@ class App {
if(op->results_.empty()) {
// Flag parsing
if(op->get_expected() == 0) {
if(op->get_type_size() == 0) {
if(current.inputs.size() == 1) {
std::string val = current.inputs.at(0);
val = detail::to_lower(val);
@ -1320,9 +1320,9 @@ class App {
size_t _count_remaining_positionals(bool required = false) const {
size_t retval = 0;
for(const Option_p &opt : options_)
if(opt->get_positional() && (!required || opt->get_required()) && opt->get_expected() > 0 &&
static_cast<int>(opt->count()) < opt->get_expected())
retval = static_cast<size_t>(opt->get_expected()) - opt->count();
if(opt->get_positional() && (!required || opt->get_required()) && opt->get_items_expected() > 0 &&
static_cast<int>(opt->count()) < opt->get_items_expected())
retval = static_cast<size_t>(opt->get_items_expected()) - opt->count();
return retval;
}
@ -1334,7 +1334,7 @@ class App {
for(const Option_p &opt : options_) {
// Eat options, one by one, until done
if(opt->get_positional() &&
(static_cast<int>(opt->count()) < opt->get_expected() || opt->get_expected() < 0)) {
(static_cast<int>(opt->count()) < opt->get_items_expected() || opt->get_items_expected() < 0)) {
opt->add_result(positional);
parse_order_.push_back(opt.get());
@ -1421,7 +1421,7 @@ class App {
// Get a reference to the pointer to make syntax bearable
Option_p &op = *op_ptr;
int num = op->get_expected();
int num = op->get_items_expected();
// Make sure we always eat the minimum for unlimited vectors
int collected = 0;
@ -1459,7 +1459,7 @@ class App {
// If there are any unlimited positionals, those also take priority
if(std::any_of(std::begin(options_), std::end(options_), [](const Option_p &opt) {
return opt->get_positional() && opt->get_expected() < 0;
return opt->get_positional() && opt->get_items_expected() < 0;
}))
break;
}

View File

@ -91,6 +91,9 @@ class IncorrectConstruction : public ConstructionError {
static IncorrectConstruction Set0Opt(std::string name) {
return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");
}
static IncorrectConstruction SetFlag(std::string name) {
return IncorrectConstruction(name + ": Cannot set an expected number for flags");
}
static IncorrectConstruction ChangeNotVector(std::string name) {
return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");
}

View File

@ -180,11 +180,13 @@ class Option : public OptionBase<Option> {
/// @name Configuration
///@{
/// The number of expected values, 0 for flag, -1 for unlimited vector
int expected_{1};
/// The number of arguments that make up one option. -1=unlimited (vector-like), 0=flag, 1=normal option,
/// 2=complex/pair, etc. Set only when the option is created; this is intrinsic to the type. Eventually, -2 may mean
/// vector of pairs.
int type_size_{1};
/// A private setting to allow args to not be able to accept incorrect expected values
bool changeable_{false};
/// The number of expected values, type_size_ must be < 0. Ignored for flag. N < 0 means at least -N values.
int expected_{1};
/// A list of validators to run on each value parsed
std::vector<std::function<std::string(std::string &)>> validators_;
@ -244,14 +246,25 @@ class Option : public OptionBase<Option> {
/// @name Setting options
///@{
/// Set the number of expected arguments (Flags bypass this)
/// Set the number of expected arguments (Flags don't use this)
Option *expected(int value) {
if(expected_ == value)
return this;
// Break if this is a flag
if(type_size_ == 0)
throw IncorrectConstruction::SetFlag(single_name());
// Setting 0 is not allowed
else if(value == 0)
throw IncorrectConstruction::Set0Opt(single_name());
else if(!changeable_)
// No change is okay, quit now
else if(expected_ == value)
return this;
// Type must be a vector
else if(type_size_ >= 0)
throw IncorrectConstruction::ChangeNotVector(single_name());
// TODO: Can support multioption for non-1 values (except for join)
else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw)
throw IncorrectConstruction::AfterMultiOpt(single_name());
@ -368,9 +381,10 @@ class Option : public OptionBase<Option> {
return this;
}
/// Take the last argument if given multiple times
/// Take the last argument if given multiple times (or another policy)
Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
if(get_expected() != 0 && get_expected() != 1)
// TODO: This can support multiple options
if(get_type_size() != 0 && get_expected() != 1)
throw IncorrectConstruction::MultiOptionPolicy(single_name());
multi_option_policy_ = value;
return this;
@ -381,8 +395,19 @@ class Option : public OptionBase<Option> {
///@{
/// The number of arguments the option expects
int get_type_size() const { return type_size_; }
/// The number of times the option expects to be included
int get_expected() const { return expected_; }
/// The total number of expected values (including the type)
int get_items_expected() const {
// type_size == 0, return 0
// type_size > 1, return type_size_
// type_size < 0, return -type_size * expected;
return type_size_ < 0 ? -1 * type_size_ * expected_ : type_size_;
}
/// True if this has a default value
int get_default() const { return default_; }
@ -428,7 +453,7 @@ class Option : public OptionBase<Option> {
return out;
}
/// The most discriptive name available
/// The most descriptive name available
std::string single_name() const {
if(!lnames_.empty())
return std::string("--") + lnames_[0];
@ -456,7 +481,7 @@ class Option : public OptionBase<Option> {
std::string help_aftername() const {
std::stringstream out;
if(get_expected() != 0) {
if(get_type_size() != 0) {
if(!typeval_.empty())
out << " " << typeval_;
if(!defaultval_.empty())
@ -502,18 +527,23 @@ class Option : public OptionBase<Option> {
// Operation depends on the policy setting
if(multi_option_policy_ == MultiOptionPolicy::TakeLast) {
// TODO: add non-1 size arguments here
results_t partial_result = {results_.back()};
local_result = !callback_(partial_result);
} else if(multi_option_policy_ == MultiOptionPolicy::TakeFirst) {
results_t partial_result = {results_.at(0)};
local_result = !callback_(partial_result);
} else if(multi_option_policy_ == MultiOptionPolicy::Join) {
results_t partial_result = {detail::join(results_, "\n")};
local_result = !callback_(partial_result);
} else {
if((expected_ > 0 && results_.size() != static_cast<size_t>(expected_)) ||
(expected_ < 0 && results_.size() < static_cast<size_t>(-expected_)))
throw ArgumentMismatch(single_name(), expected_, results_.size());
// For now, vector of non size 1 types are not supported but possibility included here
if((get_items_expected() > 0 && results_.size() != static_cast<size_t>(get_items_expected())) ||
(get_items_expected() < 0 && results_.size() < static_cast<size_t>(-get_items_expected())))
throw ArgumentMismatch(single_name(), get_items_expected(), results_.size());
else
local_result = !callback_(results_);
}
@ -595,13 +625,14 @@ class Option : public OptionBase<Option> {
/// @name Custom options
///@{
/// Set a custom option, typestring, expected; locks changeable unless expected is -1
void set_custom_option(std::string typeval, int expected = 1) {
/// Set a custom option, typestring, type_size
void set_custom_option(std::string typeval, int type_size = 1) {
typeval_ = typeval;
expected_ = expected;
if(expected == 0)
type_size_ = type_size;
if(type_size_ == 0)
required_ = false;
changeable_ = expected < 0;
if(type_size < 0)
expected_ = -1;
}
/// Set the default value string representation

View File

@ -125,7 +125,7 @@ TEST_F(TApp, IncorrectConstructionFlagPositional3) {
TEST_F(TApp, IncorrectConstructionFlagExpected) {
auto cat = app.add_flag("--cat");
EXPECT_NO_THROW(cat->expected(0));
EXPECT_THROW(cat->expected(0), CLI::IncorrectConstruction);
EXPECT_THROW(cat->expected(1), CLI::IncorrectConstruction);
}