Commit cf4f60862169407902696801a6066dd0946f3a5e

Authored by Henry Fredrick Schreiner
1 parent af79da5d

Adding check for invalid positionals

include/CLI/App.hpp
@@ -198,7 +198,7 @@ public: @@ -198,7 +198,7 @@ public:
198 /// 198 ///
199 /// For example, 199 /// For example,
200 /// 200 ///
201 - /// std::string filename 201 + /// std::string filename;
202 /// program.add_option("filename", filename, "description of filename"); 202 /// program.add_option("filename", filename, "description of filename");
203 /// 203 ///
204 Option* add_option( 204 Option* add_option(
@@ -474,6 +474,7 @@ public: @@ -474,6 +474,7 @@ public:
474 /// The real work is done here. Expects a reversed vector. 474 /// The real work is done here. Expects a reversed vector.
475 /// Changes the vector to the remaining options. 475 /// Changes the vector to the remaining options.
476 std::vector<std::string>& parse(std::vector<std::string> &args) { 476 std::vector<std::string>& parse(std::vector<std::string> &args) {
  477 + _validate();
477 _parse(args); 478 _parse(args);
478 run_callback(); 479 run_callback();
479 return args; 480 return args;
@@ -678,6 +679,18 @@ public: @@ -678,6 +679,18 @@ public:
678 679
679 protected: 680 protected:
680 681
  682 + /// Check the options to make sure there are no conficts.
  683 + ///
  684 + /// Currenly checks to see if mutiple positionals exist with -1 args
  685 + void _validate() const {
  686 + auto count = std::count_if(std::begin(options_), std::end(options_),
  687 + [](const Option_p& opt){return opt->get_expected() == -1 && opt->get_positional();});
  688 + if(count > 1)
  689 + throw InvalidError(name_ + ": Too many positional arguments with unlimited expected args");
  690 + for(const App_p& app : subcommands_)
  691 + app->_validate();
  692 + }
  693 +
681 694
682 /// Return missing from the master 695 /// Return missing from the master
683 missing_t* missing() { 696 missing_t* missing() {
include/CLI/Error.hpp
@@ -100,6 +100,11 @@ struct ExtrasError : public ParseError { @@ -100,6 +100,11 @@ struct ExtrasError : public ParseError {
100 ExtrasError(std::string name) : ParseError("ExtrasError", name, 6) {} 100 ExtrasError(std::string name) : ParseError("ExtrasError", name, 6) {}
101 }; 101 };
102 102
  103 +/// Thrown when validation fails before parsing
  104 +struct InvalidError : public ParseError {
  105 + InvalidError(std::string name) : ParseError("InvalidError", name, 15) {}
  106 +};
  107 +
103 /// This is just a safety check to verify selection and parsing match 108 /// This is just a safety check to verify selection and parsing match
104 struct HorribleError : public ParseError { 109 struct HorribleError : public ParseError {
105 HorribleError(std::string name) : ParseError("HorribleError", "(You should never see this error) " + name, 7) {} 110 HorribleError(std::string name) : ParseError("HorribleError", "(You should never see this error) " + name, 7) {}
tests/CreationTest.cpp
@@ -35,6 +35,24 @@ TEST_F(TApp, AddingExistingWithCaseAfter2) { @@ -35,6 +35,24 @@ TEST_F(TApp, AddingExistingWithCaseAfter2) {
35 EXPECT_THROW(cat->ignore_case(), CLI::OptionAlreadyAdded); 35 EXPECT_THROW(cat->ignore_case(), CLI::OptionAlreadyAdded);
36 } 36 }
37 37
  38 +TEST_F(TApp, AddingMultipleInfPositionals) {
  39 + std::vector<std::string> one, two;
  40 + app.add_option("one", one);
  41 + app.add_option("two", two);
  42 +
  43 + EXPECT_THROW(run(), CLI::InvalidError);
  44 +}
  45 +
  46 +
  47 +TEST_F(TApp, AddingMultipleInfPositionalsSubcom) {
  48 + std::vector<std::string> one, two;
  49 + CLI::App* below = app.add_subcommand("below");
  50 + below->add_option("one", one);
  51 + below->add_option("two", two);
  52 +
  53 + EXPECT_THROW(run(), CLI::InvalidError);
  54 +}
  55 +
38 TEST_F(TApp, MultipleSubcomMatching) { 56 TEST_F(TApp, MultipleSubcomMatching) {
39 app.add_subcommand("first"); 57 app.add_subcommand("first");
40 app.add_subcommand("second"); 58 app.add_subcommand("second");