Commit 447bda047ff97b15feb9f3e4af4edbf526fdf05b
Committed by
Henry Schreiner
1 parent
de06d50f
Adding size instead of expected + unchangable
Showing
4 changed files
with
66 additions
and
32 deletions
include/CLI/App.hpp
| @@ -835,7 +835,7 @@ class App { | @@ -835,7 +835,7 @@ class App { | ||
| 835 | std::string value; | 835 | std::string value; |
| 836 | 836 | ||
| 837 | // Non-flags | 837 | // Non-flags |
| 838 | - if(opt->get_expected() != 0) { | 838 | + if(opt->get_type_size() != 0) { |
| 839 | 839 | ||
| 840 | // If the option was found on command line | 840 | // If the option was found on command line |
| 841 | if(opt->count() > 0) | 841 | if(opt->count() > 0) |
| @@ -1069,7 +1069,7 @@ class App { | @@ -1069,7 +1069,7 @@ class App { | ||
| 1069 | /// Currently checks to see if multiple positionals exist with -1 args | 1069 | /// Currently checks to see if multiple positionals exist with -1 args |
| 1070 | void _validate() const { | 1070 | void _validate() const { |
| 1071 | auto count = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) { | 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 | if(count > 1) | 1074 | if(count > 1) |
| 1075 | throw InvalidError(name_); | 1075 | throw InvalidError(name_); |
| @@ -1189,8 +1189,8 @@ class App { | @@ -1189,8 +1189,8 @@ class App { | ||
| 1189 | // Required or partially filled | 1189 | // Required or partially filled |
| 1190 | if(opt->get_required() || opt->count() != 0) { | 1190 | if(opt->get_required() || opt->count() != 0) { |
| 1191 | // Make sure enough -N arguments parsed (+N is already handled in parsing function) | 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 | // Required but empty | 1195 | // Required but empty |
| 1196 | if(opt->get_required() && opt->count() == 0) | 1196 | if(opt->get_required() && opt->count() == 0) |
| @@ -1260,7 +1260,7 @@ class App { | @@ -1260,7 +1260,7 @@ class App { | ||
| 1260 | 1260 | ||
| 1261 | if(op->results_.empty()) { | 1261 | if(op->results_.empty()) { |
| 1262 | // Flag parsing | 1262 | // Flag parsing |
| 1263 | - if(op->get_expected() == 0) { | 1263 | + if(op->get_type_size() == 0) { |
| 1264 | if(current.inputs.size() == 1) { | 1264 | if(current.inputs.size() == 1) { |
| 1265 | std::string val = current.inputs.at(0); | 1265 | std::string val = current.inputs.at(0); |
| 1266 | val = detail::to_lower(val); | 1266 | val = detail::to_lower(val); |
| @@ -1320,9 +1320,9 @@ class App { | @@ -1320,9 +1320,9 @@ class App { | ||
| 1320 | size_t _count_remaining_positionals(bool required = false) const { | 1320 | size_t _count_remaining_positionals(bool required = false) const { |
| 1321 | size_t retval = 0; | 1321 | size_t retval = 0; |
| 1322 | for(const Option_p &opt : options_) | 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 | return retval; | 1327 | return retval; |
| 1328 | } | 1328 | } |
| @@ -1334,7 +1334,7 @@ class App { | @@ -1334,7 +1334,7 @@ class App { | ||
| 1334 | for(const Option_p &opt : options_) { | 1334 | for(const Option_p &opt : options_) { |
| 1335 | // Eat options, one by one, until done | 1335 | // Eat options, one by one, until done |
| 1336 | if(opt->get_positional() && | 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 | opt->add_result(positional); | 1339 | opt->add_result(positional); |
| 1340 | parse_order_.push_back(opt.get()); | 1340 | parse_order_.push_back(opt.get()); |
| @@ -1421,7 +1421,7 @@ class App { | @@ -1421,7 +1421,7 @@ class App { | ||
| 1421 | // Get a reference to the pointer to make syntax bearable | 1421 | // Get a reference to the pointer to make syntax bearable |
| 1422 | Option_p &op = *op_ptr; | 1422 | Option_p &op = *op_ptr; |
| 1423 | 1423 | ||
| 1424 | - int num = op->get_expected(); | 1424 | + int num = op->get_items_expected(); |
| 1425 | 1425 | ||
| 1426 | // Make sure we always eat the minimum for unlimited vectors | 1426 | // Make sure we always eat the minimum for unlimited vectors |
| 1427 | int collected = 0; | 1427 | int collected = 0; |
| @@ -1459,7 +1459,7 @@ class App { | @@ -1459,7 +1459,7 @@ class App { | ||
| 1459 | 1459 | ||
| 1460 | // If there are any unlimited positionals, those also take priority | 1460 | // If there are any unlimited positionals, those also take priority |
| 1461 | if(std::any_of(std::begin(options_), std::end(options_), [](const Option_p &opt) { | 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 | break; | 1464 | break; |
| 1465 | } | 1465 | } |
include/CLI/Error.hpp
| @@ -91,6 +91,9 @@ class IncorrectConstruction : public ConstructionError { | @@ -91,6 +91,9 @@ class IncorrectConstruction : public ConstructionError { | ||
| 91 | static IncorrectConstruction Set0Opt(std::string name) { | 91 | static IncorrectConstruction Set0Opt(std::string name) { |
| 92 | return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead"); | 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 | static IncorrectConstruction ChangeNotVector(std::string name) { | 97 | static IncorrectConstruction ChangeNotVector(std::string name) { |
| 95 | return IncorrectConstruction(name + ": You can only change the expected arguments for vectors"); | 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<Option> { | @@ -180,11 +180,13 @@ class Option : public OptionBase<Option> { | ||
| 180 | /// @name Configuration | 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 | /// A list of validators to run on each value parsed | 191 | /// A list of validators to run on each value parsed |
| 190 | std::vector<std::function<std::string(std::string &)>> validators_; | 192 | std::vector<std::function<std::string(std::string &)>> validators_; |
| @@ -244,14 +246,25 @@ class Option : public OptionBase<Option> { | @@ -244,14 +246,25 @@ class Option : public OptionBase<Option> { | ||
| 244 | /// @name Setting options | 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 | Option *expected(int value) { | 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 | else if(value == 0) | 256 | else if(value == 0) |
| 252 | throw IncorrectConstruction::Set0Opt(single_name()); | 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 | throw IncorrectConstruction::ChangeNotVector(single_name()); | 265 | throw IncorrectConstruction::ChangeNotVector(single_name()); |
| 266 | + | ||
| 267 | + // TODO: Can support multioption for non-1 values (except for join) | ||
| 255 | else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw) | 268 | else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw) |
| 256 | throw IncorrectConstruction::AfterMultiOpt(single_name()); | 269 | throw IncorrectConstruction::AfterMultiOpt(single_name()); |
| 257 | 270 | ||
| @@ -368,9 +381,10 @@ class Option : public OptionBase<Option> { | @@ -368,9 +381,10 @@ class Option : public OptionBase<Option> { | ||
| 368 | return this; | 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 | Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) { | 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 | throw IncorrectConstruction::MultiOptionPolicy(single_name()); | 388 | throw IncorrectConstruction::MultiOptionPolicy(single_name()); |
| 375 | multi_option_policy_ = value; | 389 | multi_option_policy_ = value; |
| 376 | return this; | 390 | return this; |
| @@ -381,8 +395,19 @@ class Option : public OptionBase<Option> { | @@ -381,8 +395,19 @@ class Option : public OptionBase<Option> { | ||
| 381 | ///@{ | 395 | ///@{ |
| 382 | 396 | ||
| 383 | /// The number of arguments the option expects | 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 | int get_expected() const { return expected_; } | 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 | /// True if this has a default value | 411 | /// True if this has a default value |
| 387 | int get_default() const { return default_; } | 412 | int get_default() const { return default_; } |
| 388 | 413 | ||
| @@ -428,7 +453,7 @@ class Option : public OptionBase<Option> { | @@ -428,7 +453,7 @@ class Option : public OptionBase<Option> { | ||
| 428 | return out; | 453 | return out; |
| 429 | } | 454 | } |
| 430 | 455 | ||
| 431 | - /// The most discriptive name available | 456 | + /// The most descriptive name available |
| 432 | std::string single_name() const { | 457 | std::string single_name() const { |
| 433 | if(!lnames_.empty()) | 458 | if(!lnames_.empty()) |
| 434 | return std::string("--") + lnames_[0]; | 459 | return std::string("--") + lnames_[0]; |
| @@ -456,7 +481,7 @@ class Option : public OptionBase<Option> { | @@ -456,7 +481,7 @@ class Option : public OptionBase<Option> { | ||
| 456 | std::string help_aftername() const { | 481 | std::string help_aftername() const { |
| 457 | std::stringstream out; | 482 | std::stringstream out; |
| 458 | 483 | ||
| 459 | - if(get_expected() != 0) { | 484 | + if(get_type_size() != 0) { |
| 460 | if(!typeval_.empty()) | 485 | if(!typeval_.empty()) |
| 461 | out << " " << typeval_; | 486 | out << " " << typeval_; |
| 462 | if(!defaultval_.empty()) | 487 | if(!defaultval_.empty()) |
| @@ -502,18 +527,23 @@ class Option : public OptionBase<Option> { | @@ -502,18 +527,23 @@ class Option : public OptionBase<Option> { | ||
| 502 | 527 | ||
| 503 | // Operation depends on the policy setting | 528 | // Operation depends on the policy setting |
| 504 | if(multi_option_policy_ == MultiOptionPolicy::TakeLast) { | 529 | if(multi_option_policy_ == MultiOptionPolicy::TakeLast) { |
| 530 | + // TODO: add non-1 size arguments here | ||
| 505 | results_t partial_result = {results_.back()}; | 531 | results_t partial_result = {results_.back()}; |
| 506 | local_result = !callback_(partial_result); | 532 | local_result = !callback_(partial_result); |
| 533 | + | ||
| 507 | } else if(multi_option_policy_ == MultiOptionPolicy::TakeFirst) { | 534 | } else if(multi_option_policy_ == MultiOptionPolicy::TakeFirst) { |
| 508 | results_t partial_result = {results_.at(0)}; | 535 | results_t partial_result = {results_.at(0)}; |
| 509 | local_result = !callback_(partial_result); | 536 | local_result = !callback_(partial_result); |
| 537 | + | ||
| 510 | } else if(multi_option_policy_ == MultiOptionPolicy::Join) { | 538 | } else if(multi_option_policy_ == MultiOptionPolicy::Join) { |
| 511 | results_t partial_result = {detail::join(results_, "\n")}; | 539 | results_t partial_result = {detail::join(results_, "\n")}; |
| 512 | local_result = !callback_(partial_result); | 540 | local_result = !callback_(partial_result); |
| 541 | + | ||
| 513 | } else { | 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 | else | 547 | else |
| 518 | local_result = !callback_(results_); | 548 | local_result = !callback_(results_); |
| 519 | } | 549 | } |
| @@ -595,13 +625,14 @@ class Option : public OptionBase<Option> { | @@ -595,13 +625,14 @@ class Option : public OptionBase<Option> { | ||
| 595 | /// @name Custom options | 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 | typeval_ = typeval; | 630 | typeval_ = typeval; |
| 601 | - expected_ = expected; | ||
| 602 | - if(expected == 0) | 631 | + type_size_ = type_size; |
| 632 | + if(type_size_ == 0) | ||
| 603 | required_ = false; | 633 | required_ = false; |
| 604 | - changeable_ = expected < 0; | 634 | + if(type_size < 0) |
| 635 | + expected_ = -1; | ||
| 605 | } | 636 | } |
| 606 | 637 | ||
| 607 | /// Set the default value string representation | 638 | /// Set the default value string representation |
tests/CreationTest.cpp
| @@ -125,7 +125,7 @@ TEST_F(TApp, IncorrectConstructionFlagPositional3) { | @@ -125,7 +125,7 @@ TEST_F(TApp, IncorrectConstructionFlagPositional3) { | ||
| 125 | 125 | ||
| 126 | TEST_F(TApp, IncorrectConstructionFlagExpected) { | 126 | TEST_F(TApp, IncorrectConstructionFlagExpected) { |
| 127 | auto cat = app.add_flag("--cat"); | 127 | auto cat = app.add_flag("--cat"); |
| 128 | - EXPECT_NO_THROW(cat->expected(0)); | 128 | + EXPECT_THROW(cat->expected(0), CLI::IncorrectConstruction); |
| 129 | EXPECT_THROW(cat->expected(1), CLI::IncorrectConstruction); | 129 | EXPECT_THROW(cat->expected(1), CLI::IncorrectConstruction); |
| 130 | } | 130 | } |
| 131 | 131 |