Commit cc3fa6009d04a0f6d1d70c81334d45018542c1f4

Authored by Henry Fredrick Schreiner
1 parent a80ac7b7

Support for multiple names on option

include/CLI.hpp
@@ -287,34 +287,6 @@ inline void cleanup_names(const std::vector<std::string> &input) { @@ -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 const Combiner NOTHING {0, false,false,false, {}}; 291 const Combiner NOTHING {0, false,false,false, {}};
320 const Combiner REQUIRED {1, false,true, false, {}}; 292 const Combiner REQUIRED {1, false,true, false, {}};
@@ -339,8 +311,8 @@ class Option { @@ -339,8 +311,8 @@ class Option {
339 public: 311 public:
340 protected: 312 protected:
341 // Config 313 // Config
342 - std::string sname;  
343 - std::string lname; 314 + std::vector<std::string> snames;
  315 + std::vector<std::string> lnames;
344 Combiner opts; 316 Combiner opts;
345 std::string discription; 317 std::string discription;
346 callback_t callback; 318 callback_t callback;
@@ -352,7 +324,7 @@ protected: @@ -352,7 +324,7 @@ protected:
352 public: 324 public:
353 Option(std::string name, std::string discription = "", Combiner opts=NOTHING, std::function<bool(results_t)> callback=[](results_t){return true;}) : 325 Option(std::string name, std::string discription = "", Combiner opts=NOTHING, std::function<bool(results_t)> callback=[](results_t){return true;}) :
354 opts(opts), discription(discription), callback(callback){ 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 void clear() { 330 void clear() {
@@ -386,44 +358,44 @@ public: @@ -386,44 +358,44 @@ public:
386 return callback(results); 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 bool operator== (const Option& other) const { 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 std::string get_name() const { 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 bool check_sname(const std::string& name) const { 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 bool check_lname(const std::string& name) const { 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 void add_result(int r, std::string s) { 400 void add_result(int r, std::string s) {
429 logit("Adding result: " + s); 401 logit("Adding result: " + s);
@@ -976,7 +948,7 @@ public: @@ -976,7 +948,7 @@ public:
976 auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_sname(name);}); 948 auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_sname(name);});
977 949
978 if(op == std::end(options)) { 950 if(op == std::end(options)) {
979 - missing_options.push_back("-" + op->get_sname()); 951 + missing_options.push_back("-" + name);
980 return; 952 return;
981 } 953 }
982 954
@@ -1044,7 +1016,7 @@ public: @@ -1044,7 +1016,7 @@ public:
1044 auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_lname(name);}); 1016 auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_lname(name);});
1045 1017
1046 if(op == std::end(options)) { 1018 if(op == std::end(options)) {
1047 - missing_options.push_back("--" + op->get_lname()); 1019 + missing_options.push_back("--" + name);
1048 return; 1020 return;
1049 } 1021 }
1050 1022
tests/CLITest.cpp
@@ -52,6 +52,21 @@ TEST_F(TApp, OneFlagLong) { @@ -52,6 +52,21 @@ TEST_F(TApp, OneFlagLong) {
52 EXPECT_EQ(1, app.count("count")); 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 TEST_F(TApp, OneFlagRef) { 70 TEST_F(TApp, OneFlagRef) {
56 int ref; 71 int ref;
57 app.add_flag("c,count", ref); 72 app.add_flag("c,count", ref);
@@ -454,9 +469,6 @@ TEST_F(TAppValue, DoubleVector) { @@ -454,9 +469,6 @@ TEST_F(TAppValue, DoubleVector) {
454 EXPECT_EQ(std::vector<double>({1.2, 3.4, -1}), *value); 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 // TODO: Check help output, better formatting 472 // TODO: Check help output, better formatting
459 // TODO: Add default/type info to help 473 // TODO: Add default/type info to help
460 -// TODO: Add regex replacement function (GCC 4.8 support)  
461 // TODO: Add README 474 // TODO: Add README
462 -// TODO: Change format of option specifications?  
tests/SmallTest.cpp
@@ -4,44 +4,6 @@ @@ -4,44 +4,6 @@
4 #include <fstream> 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 TEST(Validators, FileExists) { 8 TEST(Validators, FileExists) {
47 std::string myfile{"TestFileNotUsed.txt"}; 9 std::string myfile{"TestFileNotUsed.txt"};