1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-30 12:43:52 +00:00

Massive move to -a,--long,posit syntax

This commit is contained in:
Henry Fredrick Schreiner 2017-02-04 10:10:48 -05:00
parent 955dd950f0
commit 200d0f277f
5 changed files with 290 additions and 186 deletions

View File

@ -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;

View File

@ -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;

View File

@ -178,7 +178,6 @@ namespace detail {
struct Combiner {
int num;
bool positional;
bool required;
bool defaulted;
std::vector<std::function<bool(std::string)>> 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<std::string>,std::vector<std::string>> get_names(const std::vector<std::string> &input) {
inline std::tuple<std::vector<std::string>,std::vector<std::string>, std::string>
get_names(const std::vector<std::string> &input) {
std::vector<std::string> short_names;
std::vector<std::string> 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<std::string>,std::vector<std::string>>(short_names, long_names);
return std::tuple<std::vector<std::string>,std::vector<std::string>, 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<std::vector<std::string>> results_t;
typedef std::function<bool(results_t)> callback_t;
@ -399,6 +399,8 @@ protected:
// Config
std::vector<std::string> snames;
std::vector<std::string> 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<bool(results_t)> callback=[](results_t){return true;}) :
Option(std::string name, std::string discription = "", detail::Combiner opts=Nothing, std::function<bool(results_t)> 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<std::string> 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<std::string>& 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<std::string> flatten_results() const {
std::vector<std::string> output;
for(const std::vector<std::string> result : results)
@ -602,6 +651,7 @@ protected:
std::vector<std::unique_ptr<App>> subcommands;
bool parsed{false};
App* subcommand = nullptr;
std::string progname = "program";
std::function<void()> 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<typename T, enable_if_t<!is_vector<T>::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<typename T>
Option* add_option(
std::string name, ///< The name, long,short
std::vector<T> &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<T> &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<typename T, typename... Args>
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<typename T,
enable_if_t<std::is_integral<T>::value && !is_bool<T>::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<typename T,
enable_if_t<is_bool<T>::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<typename T>
Option* add_set(
std::string name, ///< The name, short,long
std::string name,
T &member, ///< The selected member of the set
std::set<T> options, ///< The set of posibilities
std::string discription="", ///< Discription string
detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
std::set<T> 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<typename T, typename... Args>
Option* add_set(
std::string name, ///< The name, short,long
T &member, ///< The selected member of the set
std::set<T> 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<T> options, ///< The set of posibilities
std::string discription,
detail::Combiner opts,
detail::Combiner opts2,
Args... args
) {
@ -862,13 +921,13 @@ public:
template<typename T = std::string,
enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
Value<T> 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<T> out(name);
std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
@ -890,7 +949,7 @@ public:
template<typename T = std::string, typename... Args>
Value<T> 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<typename T,
enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
Value<T> 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<T> out(name);
std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
@ -938,13 +997,13 @@ public:
template<typename T,
enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
Value<T> 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<T> out(name);
std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
@ -967,7 +1026,7 @@ public:
template<typename T, typename... Args>
Value<T> 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<int> 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<typename T>
Value<T> make_set(
std::string name, ///< The name, short,long
std::string name,
std::set<T> 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<T> out(name);
std::shared_ptr<std::unique_ptr<T>> 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<std::string> 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,

View File

@ -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<std::string> strvec;
std::vector<std::string> 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<std::string> strvec;
std::vector<std::string> 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<int>("first");
CLI::Value<int> value2 = app.make_option<int>("s");
auto value = app.make_option<int>("--first");
CLI::Value<int> value2 = app.make_option<int>("-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<std::vector<int>>("first", "", CLI::ARGS);
auto value2 = app.make_option<std::vector<std::string>>("second", "", CLI::ARGS);
auto value = app.make_option<std::vector<int>>("--first", "", CLI::Args);
auto value2 = app.make_option<std::vector<std::string>>("--second");
std::vector<int> i;
std::vector<std::string> 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<int>({12,3,9}), *value);
EXPECT_EQ(std::vector<std::string>({"thing", "try"}), *value2);
@ -524,7 +524,7 @@ TEST_F(TAppValue, Vector) {
}
TEST_F(TAppValue, DoubleVector) {
auto value = app.make_option<std::vector<double>>("simple", "", CLI::ARGS);
auto value = app.make_option<std::vector<double>>("--simple");
std::vector<double> 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<double>({1.2, 3.4, -1}), *value);
}

View File

@ -87,17 +87,20 @@ TEST(Regex, SplittingNew) {
std::vector<std::string> shorts;
std::vector<std::string> 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<std::string>({"long", "also-long"}), longs);
EXPECT_EQ(std::vector<std::string>({"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<std::string>({"long", "also-long"}), longs);
EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts);
EXPECT_NO_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"--long", "", "s", "-q", "", "also-long"}));
EXPECT_EQ(std::vector<std::string>({"long", "also-long"}), longs);
EXPECT_EQ(std::vector<std::string>({"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);
}