Commit cc3fa6009d04a0f6d1d70c81334d45018542c1f4
1 parent
a80ac7b7
Support for multiple names on option
Showing
3 changed files
with
44 additions
and
98 deletions
include/CLI.hpp
| ... | ... | @@ -287,34 +287,6 @@ inline void cleanup_names(const std::vector<std::string> &input) { |
| 287 | 287 | } |
| 288 | 288 | } |
| 289 | 289 | |
| 290 | -// Splits a string into long and short names | |
| 291 | -inline std::tuple<std::string, std::string> split(std::string fullname) { | |
| 292 | - if(fullname.find(" ") != std::string::npos) | |
| 293 | - throw BadNameString("Cannot have space in name string: "+fullname); | |
| 294 | - std::vector<std::string> output = split_names(fullname); | |
| 295 | - cleanup_names(output); | |
| 296 | - | |
| 297 | - if(output.size() > 2) | |
| 298 | - throw BadNameString(fullname); | |
| 299 | - else if (output.size() == 2) { | |
| 300 | - if(output[0].length()==0 && output[1].length()==0) | |
| 301 | - throw BadNameString("EMPTY"); | |
| 302 | - else if(output[0].length()<2) | |
| 303 | - return std::tuple<std::string,std::string>(output[0], output[1]); | |
| 304 | - else if(output[1].length() < 2) | |
| 305 | - return std::tuple<std::string,std::string>(output[0], output[1]); | |
| 306 | - else | |
| 307 | - throw BadNameString(fullname); | |
| 308 | - } else { | |
| 309 | - if(output[0].length()==0) | |
| 310 | - throw BadNameString("EMPTY"); | |
| 311 | - else if(output[0].length() == 1) | |
| 312 | - return std::tuple<std::string,std::string>(output[0], ""); | |
| 313 | - else | |
| 314 | - return std::tuple<std::string,std::string>("", output[0]); | |
| 315 | - } | |
| 316 | -} | |
| 317 | - | |
| 318 | 290 | |
| 319 | 291 | const Combiner NOTHING {0, false,false,false, {}}; |
| 320 | 292 | const Combiner REQUIRED {1, false,true, false, {}}; |
| ... | ... | @@ -339,8 +311,8 @@ class Option { |
| 339 | 311 | public: |
| 340 | 312 | protected: |
| 341 | 313 | // Config |
| 342 | - std::string sname; | |
| 343 | - std::string lname; | |
| 314 | + std::vector<std::string> snames; | |
| 315 | + std::vector<std::string> lnames; | |
| 344 | 316 | Combiner opts; |
| 345 | 317 | std::string discription; |
| 346 | 318 | callback_t callback; |
| ... | ... | @@ -352,7 +324,7 @@ protected: |
| 352 | 324 | public: |
| 353 | 325 | Option(std::string name, std::string discription = "", Combiner opts=NOTHING, std::function<bool(results_t)> callback=[](results_t){return true;}) : |
| 354 | 326 | opts(opts), discription(discription), callback(callback){ |
| 355 | - std::tie(sname, lname) = split(name); | |
| 327 | + std::tie(snames, lnames) = get_names(split_names(name)); | |
| 356 | 328 | } |
| 357 | 329 | |
| 358 | 330 | void clear() { |
| ... | ... | @@ -386,44 +358,44 @@ public: |
| 386 | 358 | return callback(results); |
| 387 | 359 | } |
| 388 | 360 | |
| 389 | - /// Indistinguishible options are equal | |
| 361 | + /// If options share any of the same names, they are equal | |
| 390 | 362 | bool operator== (const Option& other) const { |
| 391 | - if(sname=="" && other.sname=="") | |
| 392 | - return lname==other.lname; | |
| 393 | - else if(lname=="" && other.lname=="") | |
| 394 | - return sname==other.sname; | |
| 395 | - else | |
| 396 | - return sname==other.sname || lname==other.lname; | |
| 363 | + for(const std::string &sname : snames) | |
| 364 | + for(const std::string &othersname : other.snames) | |
| 365 | + if(sname == othersname) | |
| 366 | + return true; | |
| 367 | + for(const std::string &lname : lnames) | |
| 368 | + for(const std::string &otherlname : other.lnames) | |
| 369 | + if(lname == otherlname) | |
| 370 | + return true; | |
| 371 | + return false; | |
| 397 | 372 | } |
| 398 | 373 | |
| 399 | 374 | std::string get_name() const { |
| 400 | - if(sname=="") | |
| 401 | - return "--" + lname; | |
| 402 | - else if (lname=="") | |
| 403 | - return "-" + sname; | |
| 404 | - else | |
| 405 | - return "-" + sname + ", --" + lname; | |
| 375 | + std::vector<std::string> name_list; | |
| 376 | + for(const std::string& sname : snames) | |
| 377 | + name_list.push_back("-"+sname); | |
| 378 | + for(const std::string& lname : lnames) | |
| 379 | + name_list.push_back("--"+lname); | |
| 380 | + return join(name_list); | |
| 406 | 381 | } |
| 407 | 382 | |
| 408 | - bool check_name(const std::string& name) const { | |
| 409 | - return name == sname || name == lname || name == sname + "," + lname; | |
| 383 | + bool check_name(std::string name) const { | |
| 384 | + for(int i=0; i<2; i++) | |
| 385 | + if(name.length()>2 && name[0] == '-') | |
| 386 | + name = name.substr(1); | |
| 387 | + | |
| 388 | + return check_sname(name) || check_lname(name); | |
| 410 | 389 | } |
| 411 | 390 | |
| 412 | 391 | bool check_sname(const std::string& name) const { |
| 413 | - return name == sname; | |
| 392 | + return std::find(std::begin(snames), std::end(snames), name) != std::end(snames); | |
| 414 | 393 | } |
| 415 | 394 | |
| 416 | 395 | bool check_lname(const std::string& name) const { |
| 417 | - return name == lname; | |
| 396 | + return std::find(std::begin(lnames), std::end(lnames), name) != std::end(lnames); | |
| 418 | 397 | } |
| 419 | 398 | |
| 420 | - std::string get_sname() const { | |
| 421 | - return sname; | |
| 422 | - } | |
| 423 | - | |
| 424 | - std::string get_lname() const { | |
| 425 | - return lname; | |
| 426 | - } | |
| 427 | 399 | |
| 428 | 400 | void add_result(int r, std::string s) { |
| 429 | 401 | logit("Adding result: " + s); |
| ... | ... | @@ -976,7 +948,7 @@ public: |
| 976 | 948 | auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_sname(name);}); |
| 977 | 949 | |
| 978 | 950 | if(op == std::end(options)) { |
| 979 | - missing_options.push_back("-" + op->get_sname()); | |
| 951 | + missing_options.push_back("-" + name); | |
| 980 | 952 | return; |
| 981 | 953 | } |
| 982 | 954 | |
| ... | ... | @@ -1044,7 +1016,7 @@ public: |
| 1044 | 1016 | auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_lname(name);}); |
| 1045 | 1017 | |
| 1046 | 1018 | if(op == std::end(options)) { |
| 1047 | - missing_options.push_back("--" + op->get_lname()); | |
| 1019 | + missing_options.push_back("--" + name); | |
| 1048 | 1020 | return; |
| 1049 | 1021 | } |
| 1050 | 1022 | ... | ... |
tests/CLITest.cpp
| ... | ... | @@ -52,6 +52,21 @@ TEST_F(TApp, OneFlagLong) { |
| 52 | 52 | EXPECT_EQ(1, app.count("count")); |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | +TEST_F(TApp, DashedOptions) { | |
| 56 | + app.add_flag("-c"); | |
| 57 | + app.add_flag("--q"); | |
| 58 | + app.add_flag("--this,--that"); | |
| 59 | + | |
| 60 | + args = {"-c", "--q", "--this", "--that"}; | |
| 61 | + EXPECT_NO_THROW(run()); | |
| 62 | + EXPECT_EQ(1, app.count("c")); | |
| 63 | + EXPECT_EQ(1, app.count("q")); | |
| 64 | + EXPECT_EQ(2, app.count("this")); | |
| 65 | + EXPECT_EQ(2, app.count("--that")); | |
| 66 | + | |
| 67 | +} | |
| 68 | + | |
| 69 | + | |
| 55 | 70 | TEST_F(TApp, OneFlagRef) { |
| 56 | 71 | int ref; |
| 57 | 72 | app.add_flag("c,count", ref); |
| ... | ... | @@ -454,9 +469,6 @@ TEST_F(TAppValue, DoubleVector) { |
| 454 | 469 | EXPECT_EQ(std::vector<double>({1.2, 3.4, -1}), *value); |
| 455 | 470 | } |
| 456 | 471 | |
| 457 | -// TODO: Maybe add function to call on subcommand parse? Stashed. | |
| 458 | 472 | // TODO: Check help output, better formatting |
| 459 | 473 | // TODO: Add default/type info to help |
| 460 | -// TODO: Add regex replacement function (GCC 4.8 support) | |
| 461 | 474 | // TODO: Add README |
| 462 | -// TODO: Change format of option specifications? | ... | ... |
tests/SmallTest.cpp
| ... | ... | @@ -4,44 +4,6 @@ |
| 4 | 4 | #include <fstream> |
| 5 | 5 | |
| 6 | 6 | |
| 7 | -TEST(Split, GoodStrings) { | |
| 8 | - std::string s, l; | |
| 9 | - | |
| 10 | - std::tie(s, l) = CLI::split("a,boo"); | |
| 11 | - EXPECT_EQ("a", s); | |
| 12 | - EXPECT_EQ("boo", l); | |
| 13 | - | |
| 14 | - std::tie(s, l) = CLI::split(",coo"); | |
| 15 | - EXPECT_EQ("", s); | |
| 16 | - EXPECT_EQ("coo", l); | |
| 17 | - | |
| 18 | - std::tie(s, l) = CLI::split("d,"); | |
| 19 | - EXPECT_EQ("d", s); | |
| 20 | - EXPECT_EQ("", l); | |
| 21 | - | |
| 22 | - std::tie(s, l) = CLI::split("Q,this-is"); | |
| 23 | - EXPECT_EQ("Q", s); | |
| 24 | - EXPECT_EQ("this-is", l); | |
| 25 | - | |
| 26 | - std::tie(s, l) = CLI::split("s"); | |
| 27 | - EXPECT_EQ("s", s); | |
| 28 | - EXPECT_EQ("", l); | |
| 29 | - | |
| 30 | - std::tie(s, l) = CLI::split("single"); | |
| 31 | - EXPECT_EQ("", s); | |
| 32 | - EXPECT_EQ("single", l); | |
| 33 | - } | |
| 34 | - | |
| 35 | -TEST(Split, BadStrings) { | |
| 36 | - | |
| 37 | - EXPECT_THROW(CLI::split("a,,boo"), CLI::BadNameString); | |
| 38 | - EXPECT_THROW(CLI::split("a,b,c"), CLI::BadNameString); | |
| 39 | - EXPECT_THROW(CLI::split("ssd,sfd"), CLI::BadNameString); | |
| 40 | - EXPECT_THROW(CLI::split("-a"), CLI::BadNameString); | |
| 41 | - EXPECT_THROW(CLI::split(""), CLI::BadNameString); | |
| 42 | - EXPECT_THROW(CLI::split(","), CLI::BadNameString); | |
| 43 | - EXPECT_THROW(CLI::split("one two"), CLI::BadNameString); | |
| 44 | -} | |
| 45 | 7 | |
| 46 | 8 | TEST(Validators, FileExists) { |
| 47 | 9 | std::string myfile{"TestFileNotUsed.txt"}; | ... | ... |