Commit 447bda047ff97b15feb9f3e4af4edbf526fdf05b

Authored by Henry Fredrick Schreiner
Committed by Henry Schreiner
1 parent de06d50f

Adding size instead of expected + unchangable

include/CLI/App.hpp
... ... @@ -835,7 +835,7 @@ class App {
835 835 std::string value;
836 836  
837 837 // Non-flags
838   - if(opt->get_expected() != 0) {
  838 + if(opt->get_type_size() != 0) {
839 839  
840 840 // If the option was found on command line
841 841 if(opt->count() > 0)
... ... @@ -1069,7 +1069,7 @@ class App {
1069 1069 /// Currently checks to see if multiple positionals exist with -1 args
1070 1070 void _validate() const {
1071 1071 auto count = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
1072   - return opt->get_expected() == -1 && opt->get_positional();
  1072 + return opt->get_items_expected() < 0 && opt->get_positional();
1073 1073 });
1074 1074 if(count > 1)
1075 1075 throw InvalidError(name_);
... ... @@ -1189,8 +1189,8 @@ class App {
1189 1189 // Required or partially filled
1190 1190 if(opt->get_required() || opt->count() != 0) {
1191 1191 // Make sure enough -N arguments parsed (+N is already handled in parsing function)
1192   - if(opt->get_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_expected()))
1193   - throw ArgumentMismatch::AtLeast(opt->single_name(), -opt->get_expected());
  1192 + if(opt->get_items_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_items_expected()))
  1193 + throw ArgumentMismatch::AtLeast(opt->single_name(), -opt->get_items_expected());
1194 1194  
1195 1195 // Required but empty
1196 1196 if(opt->get_required() && opt->count() == 0)
... ... @@ -1260,7 +1260,7 @@ class App {
1260 1260  
1261 1261 if(op->results_.empty()) {
1262 1262 // Flag parsing
1263   - if(op->get_expected() == 0) {
  1263 + if(op->get_type_size() == 0) {
1264 1264 if(current.inputs.size() == 1) {
1265 1265 std::string val = current.inputs.at(0);
1266 1266 val = detail::to_lower(val);
... ... @@ -1320,9 +1320,9 @@ class App {
1320 1320 size_t _count_remaining_positionals(bool required = false) const {
1321 1321 size_t retval = 0;
1322 1322 for(const Option_p &opt : options_)
1323   - if(opt->get_positional() && (!required || opt->get_required()) && opt->get_expected() > 0 &&
1324   - static_cast<int>(opt->count()) < opt->get_expected())
1325   - retval = static_cast<size_t>(opt->get_expected()) - opt->count();
  1323 + if(opt->get_positional() && (!required || opt->get_required()) && opt->get_items_expected() > 0 &&
  1324 + static_cast<int>(opt->count()) < opt->get_items_expected())
  1325 + retval = static_cast<size_t>(opt->get_items_expected()) - opt->count();
1326 1326  
1327 1327 return retval;
1328 1328 }
... ... @@ -1334,7 +1334,7 @@ class App {
1334 1334 for(const Option_p &opt : options_) {
1335 1335 // Eat options, one by one, until done
1336 1336 if(opt->get_positional() &&
1337   - (static_cast<int>(opt->count()) < opt->get_expected() || opt->get_expected() < 0)) {
  1337 + (static_cast<int>(opt->count()) < opt->get_items_expected() || opt->get_items_expected() < 0)) {
1338 1338  
1339 1339 opt->add_result(positional);
1340 1340 parse_order_.push_back(opt.get());
... ... @@ -1421,7 +1421,7 @@ class App {
1421 1421 // Get a reference to the pointer to make syntax bearable
1422 1422 Option_p &op = *op_ptr;
1423 1423  
1424   - int num = op->get_expected();
  1424 + int num = op->get_items_expected();
1425 1425  
1426 1426 // Make sure we always eat the minimum for unlimited vectors
1427 1427 int collected = 0;
... ... @@ -1459,7 +1459,7 @@ class App {
1459 1459  
1460 1460 // If there are any unlimited positionals, those also take priority
1461 1461 if(std::any_of(std::begin(options_), std::end(options_), [](const Option_p &opt) {
1462   - return opt->get_positional() && opt->get_expected() < 0;
  1462 + return opt->get_positional() && opt->get_items_expected() < 0;
1463 1463 }))
1464 1464 break;
1465 1465 }
... ...
include/CLI/Error.hpp
... ... @@ -91,6 +91,9 @@ class IncorrectConstruction : public ConstructionError {
91 91 static IncorrectConstruction Set0Opt(std::string name) {
92 92 return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");
93 93 }
  94 + static IncorrectConstruction SetFlag(std::string name) {
  95 + return IncorrectConstruction(name + ": Cannot set an expected number for flags");
  96 + }
94 97 static IncorrectConstruction ChangeNotVector(std::string name) {
95 98 return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");
96 99 }
... ...
include/CLI/Option.hpp
... ... @@ -180,11 +180,13 @@ class Option : public OptionBase&lt;Option&gt; {
180 180 /// @name Configuration
181 181 ///@{
182 182  
183   - /// The number of expected values, 0 for flag, -1 for unlimited vector
184   - int expected_{1};
  183 + /// The number of arguments that make up one option. -1=unlimited (vector-like), 0=flag, 1=normal option,
  184 + /// 2=complex/pair, etc. Set only when the option is created; this is intrinsic to the type. Eventually, -2 may mean
  185 + /// vector of pairs.
  186 + int type_size_{1};
185 187  
186   - /// A private setting to allow args to not be able to accept incorrect expected values
187   - bool changeable_{false};
  188 + /// The number of expected values, type_size_ must be < 0. Ignored for flag. N < 0 means at least -N values.
  189 + int expected_{1};
188 190  
189 191 /// A list of validators to run on each value parsed
190 192 std::vector<std::function<std::string(std::string &)>> validators_;
... ... @@ -244,14 +246,25 @@ class Option : public OptionBase&lt;Option&gt; {
244 246 /// @name Setting options
245 247 ///@{
246 248  
247   - /// Set the number of expected arguments (Flags bypass this)
  249 + /// Set the number of expected arguments (Flags don't use this)
248 250 Option *expected(int value) {
249   - if(expected_ == value)
250   - return this;
  251 + // Break if this is a flag
  252 + if(type_size_ == 0)
  253 + throw IncorrectConstruction::SetFlag(single_name());
  254 +
  255 + // Setting 0 is not allowed
251 256 else if(value == 0)
252 257 throw IncorrectConstruction::Set0Opt(single_name());
253   - else if(!changeable_)
  258 +
  259 + // No change is okay, quit now
  260 + else if(expected_ == value)
  261 + return this;
  262 +
  263 + // Type must be a vector
  264 + else if(type_size_ >= 0)
254 265 throw IncorrectConstruction::ChangeNotVector(single_name());
  266 +
  267 + // TODO: Can support multioption for non-1 values (except for join)
255 268 else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw)
256 269 throw IncorrectConstruction::AfterMultiOpt(single_name());
257 270  
... ... @@ -368,9 +381,10 @@ class Option : public OptionBase&lt;Option&gt; {
368 381 return this;
369 382 }
370 383  
371   - /// Take the last argument if given multiple times
  384 + /// Take the last argument if given multiple times (or another policy)
372 385 Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
373   - if(get_expected() != 0 && get_expected() != 1)
  386 + // TODO: This can support multiple options
  387 + if(get_type_size() != 0 && get_expected() != 1)
374 388 throw IncorrectConstruction::MultiOptionPolicy(single_name());
375 389 multi_option_policy_ = value;
376 390 return this;
... ... @@ -381,8 +395,19 @@ class Option : public OptionBase&lt;Option&gt; {
381 395 ///@{
382 396  
383 397 /// The number of arguments the option expects
  398 + int get_type_size() const { return type_size_; }
  399 +
  400 + /// The number of times the option expects to be included
384 401 int get_expected() const { return expected_; }
385 402  
  403 + /// The total number of expected values (including the type)
  404 + int get_items_expected() const {
  405 + // type_size == 0, return 0
  406 + // type_size > 1, return type_size_
  407 + // type_size < 0, return -type_size * expected;
  408 + return type_size_ < 0 ? -1 * type_size_ * expected_ : type_size_;
  409 + }
  410 +
386 411 /// True if this has a default value
387 412 int get_default() const { return default_; }
388 413  
... ... @@ -428,7 +453,7 @@ class Option : public OptionBase&lt;Option&gt; {
428 453 return out;
429 454 }
430 455  
431   - /// The most discriptive name available
  456 + /// The most descriptive name available
432 457 std::string single_name() const {
433 458 if(!lnames_.empty())
434 459 return std::string("--") + lnames_[0];
... ... @@ -456,7 +481,7 @@ class Option : public OptionBase&lt;Option&gt; {
456 481 std::string help_aftername() const {
457 482 std::stringstream out;
458 483  
459   - if(get_expected() != 0) {
  484 + if(get_type_size() != 0) {
460 485 if(!typeval_.empty())
461 486 out << " " << typeval_;
462 487 if(!defaultval_.empty())
... ... @@ -502,18 +527,23 @@ class Option : public OptionBase&lt;Option&gt; {
502 527  
503 528 // Operation depends on the policy setting
504 529 if(multi_option_policy_ == MultiOptionPolicy::TakeLast) {
  530 + // TODO: add non-1 size arguments here
505 531 results_t partial_result = {results_.back()};
506 532 local_result = !callback_(partial_result);
  533 +
507 534 } else if(multi_option_policy_ == MultiOptionPolicy::TakeFirst) {
508 535 results_t partial_result = {results_.at(0)};
509 536 local_result = !callback_(partial_result);
  537 +
510 538 } else if(multi_option_policy_ == MultiOptionPolicy::Join) {
511 539 results_t partial_result = {detail::join(results_, "\n")};
512 540 local_result = !callback_(partial_result);
  541 +
513 542 } else {
514   - if((expected_ > 0 && results_.size() != static_cast<size_t>(expected_)) ||
515   - (expected_ < 0 && results_.size() < static_cast<size_t>(-expected_)))
516   - throw ArgumentMismatch(single_name(), expected_, results_.size());
  543 + // For now, vector of non size 1 types are not supported but possibility included here
  544 + if((get_items_expected() > 0 && results_.size() != static_cast<size_t>(get_items_expected())) ||
  545 + (get_items_expected() < 0 && results_.size() < static_cast<size_t>(-get_items_expected())))
  546 + throw ArgumentMismatch(single_name(), get_items_expected(), results_.size());
517 547 else
518 548 local_result = !callback_(results_);
519 549 }
... ... @@ -595,13 +625,14 @@ class Option : public OptionBase&lt;Option&gt; {
595 625 /// @name Custom options
596 626 ///@{
597 627  
598   - /// Set a custom option, typestring, expected; locks changeable unless expected is -1
599   - void set_custom_option(std::string typeval, int expected = 1) {
  628 + /// Set a custom option, typestring, type_size
  629 + void set_custom_option(std::string typeval, int type_size = 1) {
600 630 typeval_ = typeval;
601   - expected_ = expected;
602   - if(expected == 0)
  631 + type_size_ = type_size;
  632 + if(type_size_ == 0)
603 633 required_ = false;
604   - changeable_ = expected < 0;
  634 + if(type_size < 0)
  635 + expected_ = -1;
605 636 }
606 637  
607 638 /// Set the default value string representation
... ...
tests/CreationTest.cpp
... ... @@ -125,7 +125,7 @@ TEST_F(TApp, IncorrectConstructionFlagPositional3) {
125 125  
126 126 TEST_F(TApp, IncorrectConstructionFlagExpected) {
127 127 auto cat = app.add_flag("--cat");
128   - EXPECT_NO_THROW(cat->expected(0));
  128 + EXPECT_THROW(cat->expected(0), CLI::IncorrectConstruction);
129 129 EXPECT_THROW(cat->expected(1), CLI::IncorrectConstruction);
130 130 }
131 131  
... ...