Commit 4d5bff2393eae8348e7abdada6af3f4e5e075593

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

Adding ArgumentMismatch, changable improvement

include/CLI/App.hpp
... ... @@ -274,8 +274,6 @@ class App {
274 274  
275 275 std::string simple_name = CLI::detail::split(name, ',').at(0);
276 276 CLI::callback_t fun = [&variable, simple_name](CLI::results_t res) {
277   - if(res.size() != 1)
278   - throw ConversionError("Only one " + simple_name + " allowed");
279 277 return detail::lexical_cast(res[0], variable);
280 278 };
281 279  
... ... @@ -293,8 +291,6 @@ class App {
293 291  
294 292 std::string simple_name = CLI::detail::split(name, ',').at(0);
295 293 CLI::callback_t fun = [&variable, simple_name](CLI::results_t res) {
296   - if(res.size() != 1)
297   - throw ConversionError("Only one " + simple_name + " allowed");
298 294 return detail::lexical_cast(res[0], variable);
299 295 };
300 296  
... ... @@ -325,7 +321,7 @@ class App {
325 321 };
326 322  
327 323 Option *opt = add_option(name, fun, description, false);
328   - opt->set_custom_option(detail::type_name<T>(), -1, true);
  324 + opt->set_custom_option(detail::type_name<T>(), -1);
329 325 return opt;
330 326 }
331 327  
... ... @@ -347,7 +343,7 @@ class App {
347 343 };
348 344  
349 345 Option *opt = add_option(name, fun, description, defaulted);
350   - opt->set_custom_option(detail::type_name<T>(), -1, true);
  346 + opt->set_custom_option(detail::type_name<T>(), -1);
351 347 if(defaulted)
352 348 opt->set_default_str("[" + detail::join(variable) + "]");
353 349 return opt;
... ... @@ -454,9 +450,6 @@ class App {
454 450  
455 451 std::string simple_name = CLI::detail::split(name, ',').at(0);
456 452 CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
457   - if(res.size() != 1) {
458   - throw ConversionError("Only one " + simple_name + " allowed");
459   - }
460 453 bool retval = detail::lexical_cast(res[0], member);
461 454 if(!retval)
462 455 throw ConversionError("The value " + res[0] + "is not an allowed value for " + simple_name);
... ... @@ -480,9 +473,6 @@ class App {
480 473  
481 474 std::string simple_name = CLI::detail::split(name, ',').at(0);
482 475 CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
483   - if(res.size() != 1) {
484   - throw ConversionError("Only one " + simple_name + " allowed");
485   - }
486 476 bool retval = detail::lexical_cast(res[0], member);
487 477 if(!retval)
488 478 throw ConversionError("The value " + res[0] + "is not an allowed value for " + simple_name);
... ... @@ -509,9 +499,6 @@ class App {
509 499  
510 500 std::string simple_name = CLI::detail::split(name, ',').at(0);
511 501 CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
512   - if(res.size() != 1) {
513   - throw ConversionError("Only one " + simple_name + " allowed");
514   - }
515 502 member = detail::to_lower(res[0]);
516 503 auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
517 504 return detail::to_lower(val) == member;
... ... @@ -541,9 +528,6 @@ class App {
541 528  
542 529 std::string simple_name = CLI::detail::split(name, ',').at(0);
543 530 CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
544   - if(res.size() != 1) {
545   - throw ConversionError("Only one " + simple_name + " allowed");
546   - }
547 531 member = detail::to_lower(res[0]);
548 532 auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
549 533 return detail::to_lower(val) == member;
... ...
include/CLI/Error.hpp
... ... @@ -43,6 +43,7 @@ enum class ExitCodes {
43 43 InvalidError,
44 44 HorribleError,
45 45 OptionNotFound,
  46 + ArgumentMismatch,
46 47 BaseClass = 127
47 48 };
48 49  
... ... @@ -146,6 +147,17 @@ class RequiredError : public ParseError {
146 147 CLI11_ERROR_SIMPLE(RequiredError)
147 148 };
148 149  
  150 +/// Thrown when the wrong number of arguments has been recieved
  151 +class ArgumentMismatch : ParseError {
  152 + CLI11_ERROR_DEF(ParseError, ArgumentMismatch)
  153 + ArgumentMismatch(std::string name, int expected, size_t recieved)
  154 + : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name +
  155 + ", got " + std::to_string(recieved))
  156 + : ("Expected at least " + std::to_string(-expected) + " arguments to " + name +
  157 + ", got " + std::to_string(recieved)),
  158 + ExitCodes::ArgumentMismatch) {}
  159 +};
  160 +
149 161 /// Thrown when a requires option is missing
150 162 class RequiresError : public ParseError {
151 163 CLI11_ERROR_DEF(ParseError, RequiresError)
... ...
include/CLI/Option.hpp
... ... @@ -207,14 +207,15 @@ class Option : public OptionBase&lt;Option&gt; {
207 207  
208 208 /// Set the number of expected arguments (Flags bypass this)
209 209 Option *expected(int value) {
210   - if(value == 0)
  210 + if(expected_ == value)
  211 + return this;
  212 + else if(value == 0)
211 213 throw IncorrectConstruction("Cannot set 0 expected, use a flag instead");
212   - else if(expected_ == 0)
213   - throw IncorrectConstruction("Cannot make a flag take arguments!");
214 214 else if(!changeable_)
215 215 throw IncorrectConstruction("You can only change the expected arguments for vectors");
216 216 else if(last_)
217 217 throw IncorrectConstruction("You can't change expected arguments after you've set take_last!");
  218 +
218 219 expected_ = value;
219 220 return this;
220 221 }
... ... @@ -447,7 +448,11 @@ class Option : public OptionBase&lt;Option&gt; {
447 448 results_t partial_result = {results_.back()};
448 449 local_result = !callback_(partial_result);
449 450 } else {
450   - local_result = !callback_(results_);
  451 + if((expected_ > 0 && results_.size() != static_cast<size_t>(expected_)) ||
  452 + (expected_ < 0 && results_.size() < static_cast<size_t>(-expected_)))
  453 + throw ArgumentMismatch(single_name(), expected_, results_.size());
  454 + else
  455 + local_result = !callback_(results_);
451 456 }
452 457  
453 458 if(local_result)
... ... @@ -527,13 +532,13 @@ class Option : public OptionBase&lt;Option&gt; {
527 532 /// @name Custom options
528 533 ///@{
529 534  
530   - /// Set a custom option, typestring, expected, and changeable
531   - void set_custom_option(std::string typeval, int expected = 1, bool changeable = false) {
  535 + /// Set a custom option, typestring, expected; locks changeable unless expected is -1
  536 + void set_custom_option(std::string typeval, int expected = 1) {
532 537 typeval_ = typeval;
533 538 expected_ = expected;
534 539 if(expected == 0)
535 540 required_ = false;
536   - changeable_ = changeable;
  541 + changeable_ = expected < 0;
537 542 }
538 543  
539 544 /// Set the default value string representation
... ...
tests/AppTest.cpp
... ... @@ -119,7 +119,7 @@ TEST_F(TApp, DualOptions) {
119 119 EXPECT_EQ(ans, vstr);
120 120  
121 121 args = {"--string=one", "--string=two"};
122   - EXPECT_THROW(run(), CLI::ConversionError);
  122 + EXPECT_THROW(run(), CLI::ArgumentMismatch);
123 123 }
124 124  
125 125 TEST_F(TApp, LotsOfFlags) {
... ... @@ -737,7 +737,7 @@ TEST_F(TApp, FailSet) {
737 737 app.add_set("-q,--quick", choice, {1, 2, 3});
738 738  
739 739 args = {"--quick", "3", "--quick=2"};
740   - EXPECT_THROW(run(), CLI::ConversionError);
  740 + EXPECT_THROW(run(), CLI::ArgumentMismatch);
741 741  
742 742 app.reset();
743 743  
... ... @@ -770,7 +770,7 @@ TEST_F(TApp, InSetIgnoreCase) {
770 770  
771 771 app.reset();
772 772 args = {"--quick=one", "--quick=two"};
773   - EXPECT_THROW(run(), CLI::ConversionError);
  773 + EXPECT_THROW(run(), CLI::ArgumentMismatch);
774 774 }
775 775  
776 776 TEST_F(TApp, VectorFixedString) {
... ... @@ -1145,27 +1145,24 @@ TEST_F(TApp, CheckSubcomFail) {
1145 1145 EXPECT_THROW(CLI::detail::AppFriend::parse_subcommand(&app, args), CLI::HorribleError);
1146 1146 }
1147 1147  
1148   -// Added to test defaults on dual method
1149 1148 TEST_F(TApp, OptionWithDefaults) {
1150 1149 int someint = 2;
1151 1150 app.add_option("-a", someint, "", true);
1152 1151  
1153 1152 args = {"-a1", "-a2"};
1154 1153  
1155   - EXPECT_THROW(run(), CLI::ConversionError);
  1154 + EXPECT_THROW(run(), CLI::ArgumentMismatch);
1156 1155 }
1157 1156  
1158   -// Added to test defaults on dual method
1159 1157 TEST_F(TApp, SetWithDefaults) {
1160 1158 int someint = 2;
1161 1159 app.add_set("-a", someint, {1, 2, 3, 4}, "", true);
1162 1160  
1163 1161 args = {"-a1", "-a2"};
1164 1162  
1165   - EXPECT_THROW(run(), CLI::ConversionError);
  1163 + EXPECT_THROW(run(), CLI::ArgumentMismatch);
1166 1164 }
1167 1165  
1168   -// Added to test defaults on dual method
1169 1166 TEST_F(TApp, SetWithDefaultsConversion) {
1170 1167 int someint = 2;
1171 1168 app.add_set("-a", someint, {1, 2, 3, 4}, "", true);
... ... @@ -1175,14 +1172,13 @@ TEST_F(TApp, SetWithDefaultsConversion) {
1175 1172 EXPECT_THROW(run(), CLI::ConversionError);
1176 1173 }
1177 1174  
1178   -// Added to test defaults on dual method
1179 1175 TEST_F(TApp, SetWithDefaultsIC) {
1180 1176 std::string someint = "ho";
1181 1177 app.add_set_ignore_case("-a", someint, {"Hi", "Ho"}, "", true);
1182 1178  
1183 1179 args = {"-aHi", "-aHo"};
1184 1180  
1185   - EXPECT_THROW(run(), CLI::ConversionError);
  1181 + EXPECT_THROW(run(), CLI::ArgumentMismatch);
1186 1182 }
1187 1183  
1188 1184 // Added to test ->transform
... ...