diff --git a/examples/try.cpp b/examples/try.cpp index 38a40de4..30b7a1ba 100644 --- a/examples/try.cpp +++ b/examples/try.cpp @@ -6,13 +6,13 @@ int main (int argc, char** argv) { CLI::App app("K3Pi goofit fitter"); std::string file; - app.add_option("f,file", file, "File name"); + app.add_option("-f,--file", file, "File name"); int count; - app.add_flag("c,count", count, "Counter"); + app.add_flag("-c,--count", count, "Counter"); double value = 3.14; - app.add_option("-d,--double", value, "Some Value", CLI::DEFAULT); + app.add_option("-d,--double", value, "Some Value", CLI::Default); try { app.run(argc, argv); @@ -20,8 +20,8 @@ int main (int argc, char** argv) { return app.exit(e); } - std::cout << "Working on file: " << file << ", direct count: " << app.count("file") << std::endl; - std::cout << "Working on count: " << count << ", direct count: " << app.count("count") << std::endl; + std::cout << "Working on file: " << file << ", direct count: " << app.count("--file") << std::endl; + std::cout << "Working on count: " << count << ", direct count: " << app.count("--count") << std::endl; std::cout << "Some value: " << value << std::endl; return 0; diff --git a/examples/try1.cpp b/examples/try1.cpp index 76804445..6b74cb6a 100644 --- a/examples/try1.cpp +++ b/examples/try1.cpp @@ -9,10 +9,10 @@ int main (int argc, char** argv) { std::cout << app.help(); std::string file; - start->add_option("f,file", file, "File name"); + start->add_option("-f,--file", file, "File name"); int count; - stop->add_flag("c,count", count, "Counter"); + stop->add_flag("-c,--count", count, "Counter"); try { app.run(argc, argv); @@ -20,8 +20,8 @@ int main (int argc, char** argv) { return app.exit(e); } - std::cout << "Working on file: " << file << ", direct count: " << start->count("file") << std::endl; - std::cout << "Working on count: " << count << ", direct count: " << stop->count("count") << std::endl; + std::cout << "Working on file: " << file << ", direct count: " << start->count("--file") << std::endl; + std::cout << "Working on count: " << count << ", direct count: " << stop->count("--count") << std::endl; if(app.get_subcommand() != nullptr) std::cout << "Subcommand:" << app.get_subcommand()->get_name() << std::endl; diff --git a/include/CLI.hpp b/include/CLI.hpp index ec8880c4..550caa84 100644 --- a/include/CLI.hpp +++ b/include/CLI.hpp @@ -178,7 +178,6 @@ namespace detail { struct Combiner { int num; - bool positional; bool required; bool defaulted; std::vector> validators; @@ -187,7 +186,6 @@ namespace detail { Combiner operator | (Combiner b) const { Combiner self; self.num = std::min(num, b.num) == -1 ? -1 : std::max(num, b.num); - self.positional = positional || b.positional; self.required = required || b.required; self.defaulted = defaulted || b.defaulted; self.validators.reserve(validators.size() + b.validators.size()); @@ -290,36 +288,39 @@ namespace detail { } - inline std::tuple,std::vector> get_names(const std::vector &input) { + inline std::tuple,std::vector, std::string> + get_names(const std::vector &input) { + std::vector short_names; std::vector long_names; + std::string pos_name; for(std::string name : input) { if(name.length() == 0) continue; - else if(name.length() == 1) - if(valid_first_char(name[0])) - short_names.push_back(name); - else - throw BadNameString("Invalid one char name: "+name); - else if(name.length() == 2 && name[0] == '-' && name[1] != '-') { - if(valid_first_char(name[1])) + else if(name.length() > 1 && name[0] == '-' && name[1] != '-') { + if(name.length()==2 && valid_first_char(name[1])) short_names.push_back(std::string(1,name[1])); else throw BadNameString("Invalid one char name: "+name); - } else { - - if(name.substr(0,2) == "--") - name = name.substr(2); + } else if(name.length() > 2 && name.substr(0,2) == "--") { + name = name.substr(2); if(valid_name_string(name)) long_names.push_back(name); else - throw BadNameString("Bad long name"+name); + throw BadNameString("Bad long name: "+name); + } else if(name == "-" || name == "--") { + throw BadNameString("Must have a name, not just dashes"); + } else { + if(pos_name.length() > 0) + throw BadNameString("Only one positional name allowed, remove: "+name); + pos_name = name; } } - return std::tuple,std::vector>(short_names, long_names); + return std::tuple,std::vector, std::string> + (short_names, long_names, pos_name); } // Integers @@ -373,19 +374,18 @@ namespace detail { // Defines for common Combiners (don't use combiners directly) -const detail::Combiner NOTHING {0, false,false,false, {}}; -const detail::Combiner REQUIRED {1, false,true, false, {}}; -const detail::Combiner DEFAULT {1, false,false,true, {}}; -const detail::Combiner POSITIONAL {1, true, false,false, {}}; -const detail::Combiner ARGS {-1, false,false,false, {}}; -const detail::Combiner VALIDATORS {1, false, false, false, {}}; +const detail::Combiner Nothing {0, false, false, {}}; +const detail::Combiner Required {1, true, false, {}}; +const detail::Combiner Default {1, false, true, {}}; +const detail::Combiner Args {-1, false, false, {}}; +const detail::Combiner Validators {1, false, false, {}}; // Warning about using these validators: // The files could be added/deleted after the validation. This is not common, // but if this is a possibility, check the file you open afterwards -const detail::Combiner ExistingFile {1, false, false, false, {detail::_ExistingFile}}; -const detail::Combiner ExistingDirectory {1, false, false, false, {detail::_ExistingDirectory}}; -const detail::Combiner NonexistentPath {1, false, false, false, {detail::_NonexistentPath}}; +const detail::Combiner ExistingFile {1, false, false, {detail::_ExistingFile}}; +const detail::Combiner ExistingDirectory {1, false, false, {detail::_ExistingDirectory}}; +const detail::Combiner NonexistentPath {1, false, false, {detail::_NonexistentPath}}; typedef std::vector> results_t; typedef std::function callback_t; @@ -399,6 +399,8 @@ protected: // Config std::vector snames; std::vector lnames; + std::string pname; + detail::Combiner opts; std::string discription; callback_t callback; @@ -412,31 +414,62 @@ protected: public: - Option(std::string name, std::string discription = "", detail::Combiner opts=NOTHING, std::function callback=[](results_t){return true;}) : + Option(std::string name, std::string discription = "", detail::Combiner opts=Nothing, std::function callback=[](results_t){return true;}) : opts(opts), discription(discription), callback(callback){ - std::tie(snames, lnames) = detail::get_names(detail::split_names(name)); + std::tie(snames, lnames, pname) = detail::get_names(detail::split_names(name)); } + /// Clear the parsed results (mostly for testing) void clear() { results.clear(); } + /// True if option is required bool required() const { return opts.required; } + /// The number of arguments the option expects int expected() const { return opts.num; } + /// True if the argument can be given directly bool positional() const { - return opts.positional; + return pname.length() > 0; } + /// True if option has at least one non-positional name + bool nonpositional() const { + return (snames.size() + lnames.size()) > 0; + } + + /// True if this should print the default string bool defaulted() const { return opts.defaulted; } + /// True if option has discription + bool has_discription() const { + return discription.length() > 0; + } + + /// Get the discription + const std::string& get_discription() const { + return discription; + } + + /// The name and any extras needed for positionals + std::string help_positional() const { + std::string out = pname; + if(expected()<1) + out = out + "x" + std::to_string(expected()); + else if(expected()==-1) + out = out + "..."; + out = required() ? out : "["+out+"]"; + return out; + } + /// Process the callback bool run_callback() const { if(opts.validators.size()>0) { @@ -448,7 +481,7 @@ public: return callback(results); } - /// If options share any of the same names, they are equal + /// If options share any of the same names, they are equal (not counting positional) bool operator== (const Option& other) const { for(const std::string &sname : snames) for(const std::string &othersname : other.snames) @@ -461,6 +494,7 @@ public: return false; } + /// Gets a , sep list of names. Does not include the positional name. std::string get_name() const { std::vector name_list; for(const std::string& sname : snames) @@ -470,30 +504,40 @@ public: return detail::join(name_list); } + /// Check a name. Requires "-" or "--" for short / long, supports positional name bool check_name(std::string name) const { - for(int i=0; i<2; i++) - if(name.length()>2 && name[0] == '-') - name = name.substr(1); - return check_sname(name) || check_lname(name); + if(name.length()>2 && name.substr(0,2) == "--") + return check_lname(name.substr(2)); + else if (name.length()>1 && name.substr(0,1) == "-") + return check_sname(name.substr(1)); + else + return name == pname; } + /// Requires "-" to be removed from string bool check_sname(const std::string& name) const { return std::find(std::begin(snames), std::end(snames), name) != std::end(snames); } + /// Requires "--" to be removed from string bool check_lname(const std::string& name) const { return std::find(std::begin(lnames), std::end(lnames), name) != std::end(lnames); } + /// Puts a result at position r void add_result(int r, std::string s) { results.at(r).push_back(s); } + + /// Starts a new results vector (used for r in add_result) int get_new() { results.emplace_back(); return results.size() - 1; } + + /// Count the total number of times an option was passed int count() const { int out = 0; for(const std::vector& v : results) @@ -501,6 +545,7 @@ public: return out; } + /// Diagnostic representation std::string string() const { std::string val = "Option: " + get_name() + "\n" + " " + discription + "\n" @@ -514,6 +559,7 @@ public: return val; } + /// The first half of the help print, name plus default, etc std::string help_name() const { std::stringstream out; out << " " << get_name(); @@ -530,10 +576,12 @@ public: return out.str(); } + /// The length of the name part of the help, for formatting int help_len() const { return help_name().length(); } + /// Make a help string, adjustable len. std::string help(int len = 0) const { std::stringstream out; if(help_len() > len) { @@ -547,6 +595,7 @@ public: return out.str(); } + /// Produce a flattened vector of results, vs. a vector of vectors. std::vector flatten_results() const { std::vector output; for(const std::vector result : results) @@ -602,6 +651,7 @@ protected: std::vector> subcommands; bool parsed{false}; App* subcommand = nullptr; + std::string progname = "program"; std::function app_callback; @@ -639,7 +689,7 @@ public: App(std::string prog_discription="") : prog_discription(prog_discription) { - add_flag("h,help", "Print this help message and exit"); + add_flag("-h,--help", "Print this help message and exit"); } @@ -657,7 +707,7 @@ public: * After start is called, you can use count to see if the value was passed, and * the value will be initialized properly. * - * Program::REQUIRED, Program::DEFAULT, and Program::POSITIONAL are options, and can be `|` + * Program::Required, Program::Default, and the validators are options, and can be `|` * together. The positional options take an optional number of arguments. * * For example, @@ -666,10 +716,10 @@ public: * program.add_option("filename", filename, "discription of filename"); */ Option* add_option( - std::string name, ///< The name, long,short - callback_t callback, ///< The callback - std::string discription="", ///< Discription string - detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) + std::string name, + callback_t callback, + std::string discription="", + detail::Combiner opts=Validators ) { Option myopt{name, discription, opts, callback}; if(std::find(std::begin(options), std::end(options), myopt) == std::end(options)) @@ -683,15 +733,15 @@ public: /// Add option for string template::value, detail::enabler> = detail::dummy> Option* add_option( - std::string name, ///< The name, long,short + std::string name, T &variable, ///< The variable to set - std::string discription="", ///< Discription string - detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) + std::string discription="", + detail::Combiner opts=Validators ) { if(opts.num!=1) - throw IncorrectConstruction("Must have ARGS(1) or be a vector."); + throw IncorrectConstruction("Must have Args(1) or be a vector."); CLI::callback_t fun = [&variable](CLI::results_t res){ if(res.size()!=1) { return false; @@ -715,14 +765,14 @@ public: /// Add option for vector of results template Option* add_option( - std::string name, ///< The name, long,short - std::vector &variable, ///< The variable to set - std::string discription="", ///< Discription string - detail::Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) + std::string name, + std::vector &variable, ///< The variable vector to set + std::string discription="", + detail::Combiner opts=Args ) { if(opts.num==0) - throw IncorrectConstruction("Must have ARGS or be a vector."); + throw IncorrectConstruction("Must have Args or be a vector."); CLI::callback_t fun = [&variable](CLI::results_t res){ bool retval = true; variable.clear(); @@ -746,10 +796,10 @@ public: /// Multiple options are supported template Option* add_option( - std::string name, ///< The name, long,short + std::string name, T &variable, ///< The variable to set - std::string discription, ///< Discription string - detail::Combiner opts, ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) + std::string discription, + detail::Combiner opts, detail::Combiner opts2, Args... args ///< More options ) { @@ -757,23 +807,26 @@ public: } /// Add option for flag Option* add_flag( - std::string name, ///< The name, short,long - std::string discription="" ///< Discription string + std::string name, + std::string discription="" ) { CLI::callback_t fun = [](CLI::results_t){ return true; }; - return add_option(name, fun, discription, NOTHING); + Option* opt = add_option(name, fun, discription, Nothing); + if(opt->positional()) + throw IncorrectConstruction("Flags cannot be positional"); + return opt; } /// Add option for flag template::value && !is_bool::value, detail::enabler> = detail::dummy> Option* add_flag( - std::string name, ///< The name, short,long + std::string name, T &count, ///< A varaible holding the count - std::string discription="" ///< Discription string + std::string discription="" ) { count = 0; @@ -782,16 +835,19 @@ public: return true; }; - return add_option(name, fun, discription, NOTHING); + Option* opt = add_option(name, fun, discription, Nothing); + if(opt->positional()) + throw IncorrectConstruction("Flags cannot be positional"); + return opt; } /// Bool version only allows the flag once template::value, detail::enabler> = detail::dummy> Option* add_flag( - std::string name, ///< The name, short,long - T &count, ///< A varaible holding true if passed - std::string discription="" ///< Discription string + std::string name, + T &count, ///< A varaible holding true if passed + std::string discription="" ) { count = false; @@ -800,22 +856,25 @@ public: return res.size() == 1; }; - return add_option(name, fun, discription, NOTHING); + Option* opt = add_option(name, fun, discription, Nothing); + if(opt->positional()) + throw IncorrectConstruction("Flags cannot be positional"); + return opt; } /// Add set of options template Option* add_set( - std::string name, ///< The name, short,long + std::string name, T &member, ///< The selected member of the set - std::set options, ///< The set of posibilities - std::string discription="", ///< Discription string - detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) + std::set options, ///< The set of posibilities + std::string discription="", + detail::Combiner opts=Validators ) { if(opts.num!=1) - throw IncorrectConstruction("Must have ARGS(1)."); + throw IncorrectConstruction("Must have Args(1)."); CLI::callback_t fun = [&member, options](CLI::results_t res){ if(res.size()!=1) { @@ -844,11 +903,11 @@ public: template Option* add_set( - std::string name, ///< The name, short,long - T &member, ///< The selected member of the set - std::set options, ///< The set of posibilities - std::string discription, ///< Discription string - detail::Combiner opts, ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) + std::string name, + T &member, + std::set options, ///< The set of posibilities + std::string discription, + detail::Combiner opts, detail::Combiner opts2, Args... args ) { @@ -862,13 +921,13 @@ public: template::value, detail::enabler> = detail::dummy> Value make_option( - std::string name, ///< The name, short,long + std::string name, std::string discription="", - detail::Combiner opts=VALIDATORS + detail::Combiner opts=Validators ) { if(opts.num!=1) - throw IncorrectConstruction("Must have ARGS(1)."); + throw IncorrectConstruction("Must have Args(1)."); Value out(name); std::shared_ptr> ptr = out.value; @@ -890,7 +949,7 @@ public: template Value make_option( - std::string name, ///< The name, short,long + std::string name, std::string discription, detail::Combiner opts, detail::Combiner opts2, @@ -903,14 +962,14 @@ public: template::value, detail::enabler> = detail::dummy> Value make_option( - std::string name, ///< The name, short,long + std::string name, const T& default_value, std::string discription="", - detail::Combiner opts=VALIDATORS + detail::Combiner opts=Validators ) { if(opts.num!=1) - throw IncorrectConstruction("Must have ARGS(1)."); + throw IncorrectConstruction("Must have Args(1)."); Value out(name); std::shared_ptr> ptr = out.value; @@ -938,13 +997,13 @@ public: template::value, detail::enabler> = detail::dummy> Value make_option( - std::string name, ///< The name, short,long + std::string name, std::string discription="", - detail::Combiner opts=VALIDATORS + detail::Combiner opts=Args ) { if(opts.num==0) - throw IncorrectConstruction("Must have ARGS or be a vector."); + throw IncorrectConstruction("Must have Args or be a vector."); Value out(name); std::shared_ptr> ptr = out.value; @@ -967,7 +1026,7 @@ public: template Value make_option( - std::string name, ///< The name, short,long + std::string name, const T& default_value, std::string discription, detail::Combiner opts, @@ -979,7 +1038,7 @@ public: /// Prototype for new output style: flag Value make_flag( - std::string name, ///< The name, short,long + std::string name, std::string discription="" ) { @@ -992,24 +1051,27 @@ public: **ptr = (int) res.size(); return true; }; - add_option(name, fun, discription, NOTHING); + + Option* opt = add_option(name, fun, discription, Nothing); + if(opt->positional()) + throw IncorrectConstruction("Flags cannot be positional"); return out; } /// Add set of options template Value make_set( - std::string name, ///< The name, short,long + std::string name, std::set options, ///< The set of posibilities - std::string discription="", ///< Discription string - detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) + std::string discription="", + detail::Combiner opts=Validators ) { Value out(name); std::shared_ptr> ptr = out.value; if(opts.num!=1) - throw IncorrectConstruction("Must have ARGS(1)."); + throw IncorrectConstruction("Must have Args(1)."); CLI::callback_t fun = [ptr, options](CLI::results_t res){ if(res.size()!=1) { @@ -1046,6 +1108,7 @@ public: /// Parses the command line - throws errors void parse(int argc, char **argv) { + progname = argv[0]; std::vector args; for(int i=argc-1; i>0; i--) args.push_back(argv[i]); @@ -1081,7 +1144,7 @@ public: } } - if (count("help") > 0) { + if (count("--help") > 0) { throw CallForHelp(); } @@ -1259,12 +1322,50 @@ public: if(name != "") out << "Subcommand: " << name << " "; out << prog_discription << std::endl; + out << "Usage: " << progname; + + // Positionals + bool pos=false; + for(const Option &opt : options) + if(opt.positional()) { + out << " " << opt.help_positional(); + if(opt.has_discription()) + pos=true; + } + out << std::endl << std::endl; + + // Positional discriptions + if(pos) { + out << "Positionals:" << std::endl; + for(const Option &opt : options) + if(opt.positional() && opt.has_discription()) { + out << " " << std::setw(30) << std::right << opt.help_positional(); + out << opt.get_discription() << std::endl; + } + out << std::endl; + + } + + + // Options int len = std::accumulate(std::begin(options), std::end(options), 0, [](int val, const Option &opt){ return std::max(opt.help_len()+3, val);}); + + bool npos = false; for(const Option &opt : options) { - out << opt.help(len) << std::endl; + if(opt.nonpositional()) { + if(!npos) { + out << "Options:" << std::endl; + npos=true; + } + out << opt.help(len) << std::endl; + } + if(npos) + out << std::endl; } + + // Subcommands if(subcommands.size()> 0) { out << "Subcommands:" << std::endl; int max = std::accumulate(std::begin(subcommands), std::end(subcommands), 0, diff --git a/tests/CLITest.cpp b/tests/CLITest.cpp index d1bf22e6..23bfaea4 100644 --- a/tests/CLITest.cpp +++ b/tests/CLITest.cpp @@ -37,19 +37,19 @@ struct TApp : public ::testing::Test { }; TEST_F(TApp, OneFlagShort) { - app.add_flag("c,count"); + app.add_flag("-c,--count"); args = {"-c"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(1, app.count("c")); - EXPECT_EQ(1, app.count("count")); + EXPECT_EQ(1, app.count("-c")); + EXPECT_EQ(1, app.count("--count")); } TEST_F(TApp, OneFlagLong) { - app.add_flag("c,count"); + app.add_flag("-c,--count"); args = {"--count"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(1, app.count("c")); - EXPECT_EQ(1, app.count("count")); + EXPECT_EQ(1, app.count("-c")); + EXPECT_EQ(1, app.count("--count")); } TEST_F(TApp, DashedOptions) { @@ -59,9 +59,9 @@ TEST_F(TApp, DashedOptions) { args = {"-c", "--q", "--this", "--that"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(1, app.count("c")); - EXPECT_EQ(1, app.count("q")); - EXPECT_EQ(2, app.count("this")); + EXPECT_EQ(1, app.count("-c")); + EXPECT_EQ(1, app.count("--q")); + EXPECT_EQ(2, app.count("--this")); EXPECT_EQ(2, app.count("--that")); } @@ -69,76 +69,76 @@ TEST_F(TApp, DashedOptions) { TEST_F(TApp, OneFlagRef) { int ref; - app.add_flag("c,count", ref); + app.add_flag("-c,--count", ref); args = {"--count"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(1, app.count("c")); - EXPECT_EQ(1, app.count("count")); + EXPECT_EQ(1, app.count("-c")); + EXPECT_EQ(1, app.count("--count")); EXPECT_EQ(1, ref); } TEST_F(TApp, OneString) { std::string str; - app.add_option("s,string", str); + app.add_option("-s,--string", str); args = {"--string", "mystring"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(1, app.count("s")); - EXPECT_EQ(1, app.count("string")); + EXPECT_EQ(1, app.count("-s")); + EXPECT_EQ(1, app.count("--string")); EXPECT_EQ(str, "mystring"); } TEST_F(TApp, TogetherInt) { int i; - app.add_option("i,int", i); + app.add_option("-i,--int", i); args = {"-i4"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(1, app.count("int")); - EXPECT_EQ(1, app.count("i")); + EXPECT_EQ(1, app.count("--int")); + EXPECT_EQ(1, app.count("-i")); EXPECT_EQ(i, 4); } TEST_F(TApp, SepInt) { int i; - app.add_option("i,int", i); + app.add_option("-i,--int", i); args = {"-i","4"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(1, app.count("int")); - EXPECT_EQ(1, app.count("i")); + EXPECT_EQ(1, app.count("--int")); + EXPECT_EQ(1, app.count("-i")); EXPECT_EQ(i, 4); } TEST_F(TApp, OneStringAgain) { std::string str; - app.add_option("s,string", str); + app.add_option("-s,--string", str); args = {"--string", "mystring"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(1, app.count("s")); - EXPECT_EQ(1, app.count("string")); + EXPECT_EQ(1, app.count("-s")); + EXPECT_EQ(1, app.count("--string")); EXPECT_EQ(str, "mystring"); } TEST_F(TApp, DefaultStringAgain) { std::string str = "previous"; - app.add_option("s,string", str); + app.add_option("-s,--string", str); EXPECT_NO_THROW(run()); - EXPECT_EQ(0, app.count("s")); - EXPECT_EQ(0, app.count("string")); + EXPECT_EQ(0, app.count("-s")); + EXPECT_EQ(0, app.count("--string")); EXPECT_EQ(str, "previous"); } TEST_F(TApp, LotsOfFlags) { - app.add_flag("a"); - app.add_flag("A"); - app.add_flag("b"); + app.add_flag("-a"); + app.add_flag("-A"); + app.add_flag("-b"); args = {"-a","-b","-aA"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(2, app.count("a")); - EXPECT_EQ(1, app.count("b")); - EXPECT_EQ(1, app.count("A")); + EXPECT_EQ(2, app.count("-a")); + EXPECT_EQ(1, app.count("-b")); + EXPECT_EQ(1, app.count("-A")); } @@ -148,9 +148,9 @@ TEST_F(TApp, BoolAndIntFlags) { int iflag; unsigned int uflag; - app.add_flag("b", bflag); - app.add_flag("i", iflag); - app.add_flag("u", uflag); + app.add_flag("-b", bflag); + app.add_flag("-i", iflag); + app.add_flag("-u", uflag); args = {"-b", "-i", "-u"}; EXPECT_NO_THROW(run()); @@ -177,15 +177,15 @@ TEST_F(TApp, ShortOpts) { unsigned long long funnyint; std::string someopt; - app.add_flag("z", funnyint); - app.add_option("y", someopt); + app.add_flag("-z", funnyint); + app.add_option("-y", someopt); args = {"-zzyzyz",}; EXPECT_NO_THROW(run()); - EXPECT_EQ(2, app.count("z")); - EXPECT_EQ(1, app.count("y")); + EXPECT_EQ(2, app.count("-z")); + EXPECT_EQ(1, app.count("-y")); EXPECT_EQ((unsigned long long) 2, funnyint); EXPECT_EQ("zyz", someopt); } @@ -195,15 +195,15 @@ TEST_F(TApp, Flags) { int i = 3; std::string s = "HI"; - app.add_option("-i", i, "", CLI::DEFAULT, CLI::POSITIONAL); - app.add_option("-s", s, "", CLI::DEFAULT, CLI::POSITIONAL); + app.add_option("-i,i", i, "", CLI::Default); + app.add_option("-s,s", s, "", CLI::Default); args = {"-i2", "9"}; EXPECT_NO_THROW(run()); EXPECT_EQ(1, app.count("i")); - EXPECT_EQ(1, app.count("s")); + EXPECT_EQ(1, app.count("-s")); EXPECT_EQ(2, i); EXPECT_EQ("9", s); } @@ -212,8 +212,8 @@ TEST_F(TApp, Positionals) { std::string posit1; std::string posit2; - app.add_option("posit1", posit1, "", CLI::POSITIONAL); - app.add_option("posit2", posit2, "", CLI::POSITIONAL); + app.add_option("posit1", posit1); + app.add_option("posit2", posit2); args = {"thing1","thing2"}; @@ -229,42 +229,42 @@ TEST_F(TApp, MixedPositionals) { int positional_int; std::string positional_string; - app.add_option("posit1", positional_int, "", CLI::POSITIONAL); - app.add_option("posit2", positional_string, "", CLI::POSITIONAL); + app.add_option("posit1,--posit1", positional_int, ""); + app.add_option("posit2,--posit2", positional_string, ""); args = {"--posit2","thing2","7"}; EXPECT_NO_THROW(run()); EXPECT_EQ(1, app.count("posit2")); - EXPECT_EQ(1, app.count("posit1")); + EXPECT_EQ(1, app.count("--posit1")); EXPECT_EQ(7, positional_int); EXPECT_EQ("thing2", positional_string); } TEST_F(TApp, Reset) { - app.add_flag("simple"); + app.add_flag("--simple"); double doub; - app.add_option("d,double", doub); + app.add_option("-d,--double", doub); args = {"--simple", "--double", "1.2"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(1, app.count("simple")); - EXPECT_EQ(1, app.count("d")); + EXPECT_EQ(1, app.count("--simple")); + EXPECT_EQ(1, app.count("-d")); EXPECT_FLOAT_EQ(1.2, doub); app.reset(); - EXPECT_EQ(0, app.count("simple")); - EXPECT_EQ(0, app.count("d")); + EXPECT_EQ(0, app.count("--simple")); + EXPECT_EQ(0, app.count("-d")); EXPECT_NO_THROW(run()); - EXPECT_EQ(1, app.count("simple")); - EXPECT_EQ(1, app.count("d")); + EXPECT_EQ(1, app.count("--simple")); + EXPECT_EQ(1, app.count("-d")); EXPECT_FLOAT_EQ(1.2, doub); } @@ -275,7 +275,7 @@ TEST_F(TApp, FileNotExists) { EXPECT_TRUE(CLI::detail::_NonexistentPath(myfile)); std::string filename; - app.add_option("file", filename, "", CLI::NonexistentPath); + app.add_option("--file", filename, "", CLI::NonexistentPath); args = {"--file", myfile}; EXPECT_NO_THROW(run()); @@ -297,7 +297,7 @@ TEST_F(TApp, FileExists) { EXPECT_FALSE(CLI::detail::_ExistingFile(myfile)); std::string filename = "Failed"; - app.add_option("file", filename, "", CLI::ExistingFile); + app.add_option("--file", filename, "", CLI::ExistingFile); args = {"--file", myfile}; EXPECT_THROW(run(), CLI::ParseError); @@ -317,7 +317,7 @@ TEST_F(TApp, FileExists) { TEST_F(TApp, InSet) { std::string choice; - app.add_set("q,quick", choice, {"one", "two", "three"}); + app.add_set("-q,--quick", choice, {"one", "two", "three"}); args = {"--quick", "two"}; @@ -334,12 +334,12 @@ TEST_F(TApp, VectorFixedString) { std::vector strvec; std::vector answer{"mystring", "mystring2", "mystring3"}; - CLI::Option* opt = app.add_option("s,string", strvec, "", CLI::ARGS(3)); + CLI::Option* opt = app.add_option("-s,--string", strvec, "", CLI::Args(3)); EXPECT_EQ(3, opt->expected()); args = {"--string", "mystring", "mystring2", "mystring3"}; run(); - EXPECT_EQ(3, app.count("string")); + EXPECT_EQ(3, app.count("--string")); EXPECT_EQ(answer, strvec); } @@ -349,12 +349,12 @@ TEST_F(TApp, VectorUnlimString) { std::vector strvec; std::vector answer{"mystring", "mystring2", "mystring3"}; - CLI::Option* opt = app.add_option("s,string", strvec, "", CLI::ARGS); + CLI::Option* opt = app.add_option("-s,--string", strvec); EXPECT_EQ(-1, opt->expected()); args = {"--string", "mystring", "mystring2", "mystring3"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(3, app.count("string")); + EXPECT_EQ(3, app.count("--string")); EXPECT_EQ(answer, strvec); } @@ -417,9 +417,9 @@ struct SubcommandProgram : public TApp { start = app.add_subcommand("start", "Start prog"); stop = app.add_subcommand("stop", "Stop prog"); - app.add_flag("d", dummy, "My dummy var"); - start->add_option("f,file", file, "File name"); - stop->add_flag("c,count", count, "Some flag opt"); + app.add_flag("-d", dummy, "My dummy var"); + start->add_option("-f,--file", file, "File name"); + stop->add_flag("-c,--count", count, "Some flag opt"); } }; @@ -449,7 +449,7 @@ TEST_F(SubcommandProgram, SpareSub) { class TAppValue : public TApp {}; TEST_F(TAppValue, OneString) { - auto str = app.make_option("s,string"); + auto str = app.make_option("-s,--string"); std::string v; args = {"--string", "mystring"}; EXPECT_FALSE((bool) str); @@ -461,15 +461,15 @@ TEST_F(TAppValue, OneString) { EXPECT_NO_THROW(v = *str); EXPECT_NO_THROW(v = str); - EXPECT_EQ(1, app.count("s")); - EXPECT_EQ(1, app.count("string")); + EXPECT_EQ(1, app.count("-s")); + EXPECT_EQ(1, app.count("--string")); EXPECT_EQ(*str, "mystring"); } TEST_F(TAppValue, SeveralInts) { - auto value = app.make_option("first"); - CLI::Value value2 = app.make_option("s"); + auto value = app.make_option("--first"); + CLI::Value value2 = app.make_option("-s"); int v; args = {"--first", "12", "-s", "19"}; EXPECT_FALSE((bool) value); @@ -482,16 +482,16 @@ TEST_F(TAppValue, SeveralInts) { EXPECT_NO_THROW(v = *value); EXPECT_NO_THROW(v = value); - EXPECT_EQ(1, app.count("s")); - EXPECT_EQ(1, app.count("first")); + EXPECT_EQ(1, app.count("-s")); + EXPECT_EQ(1, app.count("--first")); EXPECT_EQ(*value, 12); EXPECT_EQ(*value2, 19); } TEST_F(TAppValue, Vector) { - auto value = app.make_option>("first", "", CLI::ARGS); - auto value2 = app.make_option>("second", "", CLI::ARGS); + auto value = app.make_option>("--first", "", CLI::Args); + auto value2 = app.make_option>("--second"); std::vector i; std::vector s; @@ -515,8 +515,8 @@ TEST_F(TAppValue, Vector) { EXPECT_NO_THROW(s = *value2); //EXPECT_NO_THROW(s = value2); - EXPECT_EQ(3, app.count("first")); - EXPECT_EQ(2, app.count("second")); + EXPECT_EQ(3, app.count("--first")); + EXPECT_EQ(2, app.count("--second")); EXPECT_EQ(std::vector({12,3,9}), *value); EXPECT_EQ(std::vector({"thing", "try"}), *value2); @@ -524,7 +524,7 @@ TEST_F(TAppValue, Vector) { } TEST_F(TAppValue, DoubleVector) { - auto value = app.make_option>("simple", "", CLI::ARGS); + auto value = app.make_option>("--simple"); std::vector d; args = {"--simple", "1.2", "3.4", "-1"}; @@ -535,7 +535,7 @@ TEST_F(TAppValue, DoubleVector) { EXPECT_NO_THROW(d = *value); - EXPECT_EQ(3, app.count("simple")); + EXPECT_EQ(3, app.count("--simple")); EXPECT_EQ(std::vector({1.2, 3.4, -1}), *value); } diff --git a/tests/SmallTest.cpp b/tests/SmallTest.cpp index a4934d71..08a4f87c 100644 --- a/tests/SmallTest.cpp +++ b/tests/SmallTest.cpp @@ -87,17 +87,20 @@ TEST(Regex, SplittingNew) { std::vector shorts; std::vector longs; + std::string pname; - EXPECT_NO_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"--long", "s", "-q", "also-long"})); + EXPECT_NO_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"--long", "-s", "-q", "--also-long"})); + EXPECT_EQ(std::vector({"long", "also-long"}), longs); + EXPECT_EQ(std::vector({"s", "q"}), shorts); + EXPECT_EQ("", pname); + + EXPECT_NO_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"--long", "", "-s", "-q", "", "--also-long"})); EXPECT_EQ(std::vector({"long", "also-long"}), longs); EXPECT_EQ(std::vector({"s", "q"}), shorts); - EXPECT_NO_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"--long", "", "s", "-q", "", "also-long"})); - EXPECT_EQ(std::vector({"long", "also-long"}), longs); - EXPECT_EQ(std::vector({"s", "q"}), shorts); - - EXPECT_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"-"}), CLI::BadNameString); - EXPECT_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"--"}), CLI::BadNameString); - EXPECT_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"-hi"}), CLI::BadNameString); + EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"-"}), CLI::BadNameString); + EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"--"}), CLI::BadNameString); + EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"-hi"}), CLI::BadNameString); + EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"one","two"}), CLI::BadNameString); }