1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-05-03 14:03:52 +00:00

Clean up, move more to detail

This commit is contained in:
Henry Fredrick Schreiner 2017-02-01 16:16:03 -05:00
parent e0ca21f435
commit 653d8b70fd
3 changed files with 298 additions and 329 deletions

View File

@ -18,6 +18,7 @@
#include <iomanip> #include <iomanip>
#include <numeric> #include <numeric>
#include <vector> #include <vector>
#include <locale>
// C standard library // C standard library
// Only needed for existence checking // Only needed for existence checking
@ -25,42 +26,59 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <locale>
//#define CLI_LOG 1
namespace CLI { namespace CLI {
/// Log a message, can be enabled with CLI_LOG
void logit(std::string) {
#ifdef CLI_LOG
std::cout << "\033[1;31m" << output << "\033[0m" << std::endl;
#endif
}
/// Simple fucntion to join a string // Error definitions
template <typename T>
std::string join(const T& v, std::string delim = ",") {
std::ostringstream s;
for (const auto& i : v) {
if (&i != &v[0]) {
s << delim;
}
s << i;
}
return s.str();
}
// Based generally on https://rmf.io/cxx11/almost-static-if struct Error : public std::runtime_error {
namespace detail { int exit_code;
/// Simple empty scoped class Error(std::string parent, std::string name, int exit_code=255) : runtime_error(parent + ": " + name), exit_code(exit_code) {}
enum class enabler {}; };
}
/// An instance to use in EnableIf struct CallForHelp : public Error {
constexpr detail::enabler dummy = {}; CallForHelp() : Error("CallForHelp","", 0) {}
};
struct BadNameString : public Error {
BadNameString(std::string name) : Error("BadNameString", name, 1) {}
};
struct ParseError : public Error {
ParseError(std::string name) : Error("ParseError", name, 2) {}
};
struct OptionAlreadyAdded : public Error {
OptionAlreadyAdded(std::string name) : Error("OptionAlreadyAdded", name, 3) {}
};
struct OptionNotFound : public Error {
OptionNotFound(std::string name) : Error("OptionNotFound", name, 4) {}
};
struct RequiredError : public Error {
RequiredError(std::string name) : Error("RequiredError", name, 5) {}
};
struct PositionalError : public Error {
PositionalError(std::string name) : Error("PositionalError", name, 6) {}
};
struct HorribleError : public Error {
HorribleError(std::string name) : Error("HorribleError", "(You should never see this error) " + name, 7) {}
};
struct IncorrectConstruction : public Error {
IncorrectConstruction(std::string name) : Error("IncorrectConstruction", name, 8) {}
};
struct EmptyError : public Error {
EmptyError(std::string name) : Error("EmptyError", name, 9) {}
};
// Type tools
//
// Copied from C++14 // Copied from C++14
#if __cplusplus < 201402L #if __cplusplus < 201402L
template< bool B, class T = void > template< bool B, class T = void >
@ -81,6 +99,27 @@ struct is_vector<std::vector<T, A> > {
static bool const value = true; static bool const value = true;
}; };
namespace detail {
// Based generally on https://rmf.io/cxx11/almost-static-if
/// Simple empty scoped class
enum class enabler {};
/// An instance to use in EnableIf
constexpr enabler dummy = {};
/// Simple function to join a string
template <typename T>
std::string join(const T& v, std::string delim = ",") {
std::ostringstream s;
for (const auto& i : v) {
if (&i != &v[0]) {
s << delim;
}
s << i;
}
return s.str();
}
struct Combiner { struct Combiner {
int num; int num;
bool positional; bool positional;
@ -138,50 +177,6 @@ bool _NonexistentPath(std::string filename) {
return stat(filename.c_str(), &buffer) != 0; return stat(filename.c_str(), &buffer) != 0;
} }
struct Error : public std::runtime_error {
int exit_code;
Error(std::string parent, std::string name, int exit_code=255) : runtime_error(parent + ": " + name), exit_code(exit_code) {}
};
struct CallForHelp : public Error {
CallForHelp() : Error("CallForHelp","", 0) {}
};
struct BadNameString : public Error {
BadNameString(std::string name) : Error("BadNameString", name, 1) {}
};
struct ParseError : public Error {
ParseError(std::string name) : Error("ParseError", name, 2) {}
};
struct OptionAlreadyAdded : public Error {
OptionAlreadyAdded(std::string name) : Error("OptionAlreadyAdded", name, 3) {}
};
struct OptionNotFound : public Error {
OptionNotFound(std::string name) : Error("OptionNotFound", name, 4) {}
};
struct RequiredError : public Error {
RequiredError(std::string name) : Error("RequiredError", name, 5) {}
};
struct PositionalError : public Error {
PositionalError(std::string name) : Error("PositionalError", name, 6) {}
};
struct HorribleError : public Error {
HorribleError(std::string name) : Error("HorribleError", "(You should never see this error) " + name, 7) {}
};
struct IncorrectConstruction : public Error {
IncorrectConstruction(std::string name) : Error("IncorrectConstruction", name, 8) {}
};
struct EmptyError : public Error {
EmptyError(std::string name) : Error("EmptyError", name, 9) {}
};
template<typename T> template<typename T>
bool valid_first_char(T c) { bool valid_first_char(T c) {
@ -272,36 +267,72 @@ inline std::tuple<std::vector<std::string>,std::vector<std::string>> get_names(c
} }
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>>(short_names, long_names);
} }
// Currently just throws an error if name is incorrect. May clean later.
inline void cleanup_names(const std::vector<std::string> &input) {
for(const std::string &name : input) { template<typename T, enable_if_t<std::is_integral<T>::value, detail::enabler> = detail::dummy>
if(name.compare(0, 2, "--") == 0) bool lexical_cast(std::string input, T& output) {
//output = output.substring(2); try{
throw BadNameString(name); output = (T) std::stoll(input);
else if(name.compare(0, 1, std::string("-")) == 0) return true;
throw BadNameString(name); } catch (std::invalid_argument) {
//output = output.substring(1); return false;
} catch (std::out_of_range) {
return false;
}
}
template<typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T& output) {
try{
output = (T) std::stold(input);
return true;
} catch (std::invalid_argument) {
return false;
} catch (std::out_of_range) {
return false;
}
}
// String and similar
template<typename T,
enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T& output) {
if(output.size() == input.size())
output.resize(input.size());
for(size_t i=0; i<input.size(); i++)
output[i] = input[i];
return true;
}
// String and similar
template<typename T,
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value
, detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T& output) {
output = input;
return true;
} }
} }
const Combiner NOTHING {0, false,false,false, {}};
const Combiner REQUIRED {1, false,true, false, {}}; // Defines for common Combiners (don't use combiners directly)
const Combiner DEFAULT {1, false,false,true, {}};
const Combiner POSITIONAL {1, true, false,false, {}};
const Combiner ARGS {-1, false,false,false, {}}; const detail::Combiner NOTHING {0, false,false,false, {}};
const Combiner VALIDATORS {1, 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, {}};
// Warning about using these validators: // Warning about using these validators:
// The files could be added/deleted after the validation. This is not common, // 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 // but if this is a possibility, check the file you open afterwards
const Combiner ExistingFile {1, false, false, false, {_ExistingFile}}; const detail::Combiner ExistingFile {1, false, false, false, {detail::_ExistingFile}};
const Combiner ExistingDirectory {1, false, false, false, {_ExistingDirectory}}; const detail::Combiner ExistingDirectory {1, false, false, false, {detail::_ExistingDirectory}};
const Combiner NonexistentPath {1, false, false, false, {_NonexistentPath}}; const detail::Combiner NonexistentPath {1, false, false, false, {detail::_NonexistentPath}};
typedef std::vector<std::vector<std::string>> results_t; typedef std::vector<std::vector<std::string>> results_t;
typedef std::function<bool(results_t)> callback_t; typedef std::function<bool(results_t)> callback_t;
@ -314,7 +345,7 @@ protected:
// Config // Config
std::vector<std::string> snames; std::vector<std::string> snames;
std::vector<std::string> lnames; std::vector<std::string> lnames;
Combiner opts; detail::Combiner opts;
std::string discription; std::string discription;
callback_t callback; callback_t callback;
@ -323,9 +354,9 @@ protected:
public: public:
Option(std::string name, std::string discription = "", 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){ opts(opts), discription(discription), callback(callback){
std::tie(snames, lnames) = get_names(split_names(name)); std::tie(snames, lnames) = detail::get_names(detail::split_names(name));
} }
void clear() { void clear() {
@ -378,7 +409,7 @@ public:
name_list.push_back("-"+sname); name_list.push_back("-"+sname);
for(const std::string& lname : lnames) for(const std::string& lname : lnames)
name_list.push_back("--"+lname); name_list.push_back("--"+lname);
return join(name_list); return detail::join(name_list);
} }
bool check_name(std::string name) const { bool check_name(std::string name) const {
@ -399,7 +430,6 @@ public:
void add_result(int r, std::string s) { void add_result(int r, std::string s) {
logit("Adding result: " + s);
results.at(r).push_back(s); results.at(r).push_back(s);
} }
int get_new() { int get_new() {
@ -420,7 +450,7 @@ public:
for(const auto& item : results) { for(const auto& item : results) {
if(&item!=&results[0]) if(&item!=&results[0])
val+="],["; val+="],[";
val += join(item); val += detail::join(item);
} }
val += "]"; val += "]";
return val; return val;
@ -446,57 +476,9 @@ public:
}; };
template<typename T, enable_if_t<std::is_integral<T>::value, detail::enabler> = dummy>
bool lexical_cast(std::string input, T& output) {
logit("Int lexical cast " + input);
try{
output = (T) std::stoll(input);
return true;
} catch (std::invalid_argument) {
return false;
} catch (std::out_of_range) {
return false;
}
}
template<typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = dummy>
bool lexical_cast(std::string input, T& output) {
logit("Floating lexical cast " + input);
try{
output = (T) std::stold(input);
return true;
} catch (std::invalid_argument) {
return false;
} catch (std::out_of_range) {
return false;
}
}
// String and similar
template<typename T,
enable_if_t<is_vector<T>::value, detail::enabler> = dummy>
bool lexical_cast(std::string input, T& output) {
logit("vector lexical cast: " + input);
if(output.size() == input.size())
output.resize(input.size());
for(size_t i=0; i<input.size(); i++)
output[i] = input[i];
return true;
}
// String and similar
template<typename T,
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value
, detail::enabler> = dummy>
bool lexical_cast(std::string input, T& output) {
logit("Direct lexical cast: " + input);
output = input;
return true;
}
enum class Classifer {NONE, POSITIONAL_MARK, SHORT, LONG, SUBCOMMAND}; enum class Classifer {NONE, POSITIONAL_MARK, SHORT, LONG, SUBCOMMAND};
class App; class App;
// Prototype return value test // Prototype return value test
@ -514,18 +496,16 @@ public:
/// explicit * notation. /// explicit * notation.
T& operator *() const { T& operator *() const {
if(*value) { if(*value) {
//std::cout << "Succ" << std::endl;
return **value; return **value;
} }
else { else {
//std::cout << "Throwing!!!" << std::endl;
throw EmptyError(name); throw EmptyError(name);
} }
} }
}; };
/// Creates a command line program, with very few defaults. /// Creates a command line program, with very few defaults.
/** To use, create a new Program() instance with argc, argv, and a help discription. The templated /** To use, create a new Program() instance with argc, argv, and a help description. The templated
* add_option methods make it easy to prepare options. Remember to call `.start` before starting your * add_option methods make it easy to prepare options. Remember to call `.start` before starting your
* program, so that the options can be evaluated and the help option doesn't accidentally run your program. */ * program, so that the options can be evaluated and the help option doesn't accidentally run your program. */
class App { class App {
@ -568,7 +548,6 @@ public:
App* add_subcommand(std::string name, std::string discription="") { App* add_subcommand(std::string name, std::string discription="") {
subcommands.emplace_back(new App(discription)); subcommands.emplace_back(new App(discription));
subcommands.back()->name = name; subcommands.back()->name = name;
logit(subcommands.back()->name);
return subcommands.back().get(); return subcommands.back().get();
} }
@ -592,7 +571,7 @@ public:
std::string name, ///< The name, long,short std::string name, ///< The name, long,short
callback_t callback, ///< The callback callback_t callback, ///< The callback
std::string discription="", ///< Discription string std::string discription="", ///< Discription string
Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
) { ) {
Option myopt{name, discription, opts, callback}; Option myopt{name, discription, opts, callback};
if(std::find(std::begin(options), std::end(options), myopt) == std::end(options)) if(std::find(std::begin(options), std::end(options), myopt) == std::end(options))
@ -604,12 +583,12 @@ public:
} }
/// Add option for string /// Add option for string
template<typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = dummy> template<typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
Option* add_option( Option* add_option(
std::string name, ///< The name, long,short std::string name, ///< The name, long,short
T &variable, ///< The variable to set T &variable, ///< The variable to set
std::string discription="", ///< Discription string std::string discription="", ///< Discription string
Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
) { ) {
@ -622,7 +601,7 @@ public:
if(res[0].size()!=1) { if(res[0].size()!=1) {
return false; return false;
} }
return lexical_cast(res[0][0], variable); return detail::lexical_cast(res[0][0], variable);
}; };
return add_option(name, fun, discription, opts); return add_option(name, fun, discription, opts);
@ -634,7 +613,7 @@ public:
std::string name, ///< The name, long,short std::string name, ///< The name, long,short
std::vector<T> &variable, ///< The variable to set std::vector<T> &variable, ///< The variable to set
std::string discription="", ///< Discription string std::string discription="", ///< Discription string
Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
) { ) {
if(opts.num==0) if(opts.num==0)
@ -645,7 +624,7 @@ public:
for(const auto &a : res) for(const auto &a : res)
for(const auto &b : a) { for(const auto &b : a) {
variable.emplace_back(); variable.emplace_back();
retval &= lexical_cast(b, variable.back()); retval &= detail::lexical_cast(b, variable.back());
} }
return variable.size() > 0 && retval; return variable.size() > 0 && retval;
}; };
@ -667,7 +646,7 @@ public:
} }
/// Add option for flag /// Add option for flag
template<typename T, enable_if_t<std::is_integral<T>::value, detail::enabler> = dummy> template<typename T, enable_if_t<std::is_integral<T>::value, detail::enabler> = detail::dummy>
Option* add_flag( Option* add_flag(
std::string name, ///< The name, short,long std::string name, ///< The name, short,long
T &count, ///< A varaible holding the count T &count, ///< A varaible holding the count
@ -690,7 +669,7 @@ public:
T &member, ///< The selected member of the set T &member, ///< The selected member of the set
std::set<T> options, ///< The set of posibilities std::set<T> options, ///< The set of posibilities
std::string discription="", ///< Discription string std::string discription="", ///< Discription string
Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
) { ) {
if(opts.num!=1) if(opts.num!=1)
@ -703,7 +682,7 @@ public:
if(res[0].size()!=1) { if(res[0].size()!=1) {
return false; return false;
} }
bool retval = lexical_cast(res[0][0], member); bool retval = detail::lexical_cast(res[0][0], member);
if(!retval) if(!retval)
return false; return false;
return std::find(std::begin(options), std::end(options), member) != std::end(options); return std::find(std::begin(options), std::end(options), member) != std::end(options);
@ -719,11 +698,11 @@ public:
/// Prototype for new output style /// Prototype for new output style
template<typename T = std::string, template<typename T = std::string,
enable_if_t<!is_vector<T>::value, detail::enabler> = dummy> enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
Value<T> make_option( Value<T> make_option(
std::string name, ///< The name, short,long std::string name, ///< The name, short,long
std::string discription="", std::string discription="",
Combiner opts=VALIDATORS detail::Combiner opts=VALIDATORS
) { ) {
if(opts.num!=1) if(opts.num!=1)
@ -740,7 +719,7 @@ public:
return false; return false;
} }
ptr->reset(new T()); // resets the internal ptr ptr->reset(new T()); // resets the internal ptr
return lexical_cast(res[0][0], **ptr); return detail::lexical_cast(res[0][0], **ptr);
}; };
add_option(name, fun, discription, opts); add_option(name, fun, discription, opts);
return out; return out;
@ -748,12 +727,12 @@ public:
/// Prototype for new output style with default /// Prototype for new output style with default
template<typename T, template<typename T,
enable_if_t<!is_vector<T>::value, detail::enabler> = dummy> enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
Value<T> make_option( Value<T> make_option(
std::string name, ///< The name, short,long std::string name, ///< The name, short,long
const T& default_value, const T& default_value,
std::string discription="", std::string discription="",
Combiner opts=VALIDATORS detail::Combiner opts=VALIDATORS
) { ) {
if(opts.num!=1) if(opts.num!=1)
@ -771,7 +750,7 @@ public:
return false; return false;
} }
ptr->reset(new T()); // resets the internal ptr ptr->reset(new T()); // resets the internal ptr
return lexical_cast(res[0][0], **ptr); return detail::lexical_cast(res[0][0], **ptr);
}; };
add_option(name, fun, discription, opts); add_option(name, fun, discription, opts);
return out; return out;
@ -779,11 +758,11 @@ public:
/// Prototype for new output style, vector /// Prototype for new output style, vector
template<typename T, template<typename T,
enable_if_t<is_vector<T>::value, detail::enabler> = dummy> enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
Value<T> make_option( Value<T> make_option(
std::string name, ///< The name, short,long std::string name, ///< The name, short,long
std::string discription="", std::string discription="",
Combiner opts=VALIDATORS detail::Combiner opts=VALIDATORS
) { ) {
if(opts.num==0) if(opts.num==0)
@ -798,7 +777,7 @@ public:
for(const auto &a : res) for(const auto &a : res)
for(const auto &b : a) { for(const auto &b : a) {
(*ptr)->emplace_back(); (*ptr)->emplace_back();
retval &= lexical_cast(b, (*ptr)->back()); retval &= detail::lexical_cast(b, (*ptr)->back());
} }
return (*ptr)->size() > 0 && retval; return (*ptr)->size() > 0 && retval;
}; };
@ -832,7 +811,7 @@ public:
std::string name, ///< The name, short,long std::string name, ///< The name, short,long
std::set<T> options, ///< The set of posibilities std::set<T> options, ///< The set of posibilities
std::string discription="", ///< Discription string std::string discription="", ///< Discription string
Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS())
) { ) {
Value<T> out(name); Value<T> out(name);
@ -849,7 +828,7 @@ public:
return false; return false;
} }
ptr->reset(new T()); ptr->reset(new T());
bool retval = lexical_cast(res[0][0], **ptr); bool retval = detail::lexical_cast(res[0][0], **ptr);
if(!retval) if(!retval)
return false; return false;
return std::find(std::begin(options), std::end(options), **ptr) != std::end(options); return std::find(std::begin(options), std::end(options), **ptr) != std::end(options);
@ -876,7 +855,6 @@ public:
while(args.size()>0) { while(args.size()>0) {
logit("Parse: ["+join(args)+"]");
Classifer classifer = positional_only ? Classifer::NONE : _recognize(args.back()); Classifer classifer = positional_only ? Classifer::NONE : _recognize(args.back());
switch(classifer) { switch(classifer) {
@ -894,7 +872,6 @@ public:
_parse_short(args); _parse_short(args);
break; break;
case Classifer::NONE: case Classifer::NONE:
logit("Positional: "+args.back());
positionals.push_back(args.back()); positionals.push_back(args.back());
args.pop_back(); args.pop_back();
} }
@ -920,7 +897,7 @@ public:
} }
if(positionals.size()>0) if(positionals.size()>0)
throw PositionalError("[" + join(positionals) + "]"); throw PositionalError("[" + detail::join(positionals) + "]");
} }
void _parse_subcommand(std::vector<std::string> &args) { void _parse_subcommand(std::vector<std::string> &args) {
@ -940,12 +917,10 @@ public:
std::string name; std::string name;
std::string rest; std::string rest;
if(!split_short(current, name, rest)) if(!detail::split_short(current, name, rest))
throw HorribleError("Short"); throw HorribleError("Short");
args.pop_back(); args.pop_back();
logit("Working on short: " + name + " (" + rest + ")");
auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_sname(name);}); auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_sname(name);});
if(op == std::end(options)) { if(op == std::end(options)) {
@ -975,7 +950,6 @@ public:
} else while(num>0 && args.size() > 0) { } else while(num>0 && args.size() > 0) {
num--; num--;
std::string current = args.back(); std::string current = args.back();
logit("Adding: "+current);
args.pop_back(); args.pop_back();
op->add_result(vnum,current); op->add_result(vnum,current);
} }
@ -995,9 +969,9 @@ public:
if(com->name == current) if(com->name == current)
return Classifer::SUBCOMMAND; return Classifer::SUBCOMMAND;
} }
if(split_long(current, dummy1, dummy2)) if(detail::split_long(current, dummy1, dummy2))
return Classifer::LONG; return Classifer::LONG;
if(split_short(current, dummy1, dummy2)) if(detail::split_short(current, dummy1, dummy2))
return Classifer::SHORT; return Classifer::SHORT;
return Classifer::NONE; return Classifer::NONE;
} }
@ -1007,13 +981,10 @@ public:
std::string name; std::string name;
std::string value; std::string value;
if(!split_long(current, name, value)) if(!detail::split_long(current, name, value))
throw HorribleError("Long"); throw HorribleError("Long");
args.pop_back(); args.pop_back();
logit("Working on long: " + name + " (" + value + ")");
auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_lname(name);}); auto op = std::find_if(std::begin(options), std::end(options), [name](const Option &v){return v.check_lname(name);});
if(op == std::end(options)) { if(op == std::end(options)) {
@ -1067,9 +1038,7 @@ public:
/// Counts the number of times the given option was passed. /// Counts the number of times the given option was passed.
int count(std::string name) const { int count(std::string name) const {
for(const Option &opt : options) { for(const Option &opt : options) {
logit("Computing: " + opt.get_name());
if(opt.check_name(name)) { if(opt.check_name(name)) {
logit("Counting: " + opt.get_name() + std::to_string(opt.count()));
return opt.count(); return opt.count();
} }
} }

View File

@ -222,7 +222,7 @@ TEST_F(TApp, Reset) {
TEST_F(TApp, FileNotExists) { TEST_F(TApp, FileNotExists) {
std::string myfile{"TestNonFileNotUsed.txt"}; std::string myfile{"TestNonFileNotUsed.txt"};
EXPECT_TRUE(CLI::_NonexistentPath(myfile)); EXPECT_TRUE(CLI::detail::_NonexistentPath(myfile));
std::string filename; std::string filename;
app.add_option("file", filename, "", CLI::NonexistentPath); app.add_option("file", filename, "", CLI::NonexistentPath);
@ -239,12 +239,12 @@ TEST_F(TApp, FileNotExists) {
EXPECT_THROW(run(), CLI::ParseError); EXPECT_THROW(run(), CLI::ParseError);
std::remove(myfile.c_str()); std::remove(myfile.c_str());
EXPECT_FALSE(CLI::_ExistingFile(myfile)); EXPECT_FALSE(CLI::detail::_ExistingFile(myfile));
} }
TEST_F(TApp, FileExists) { TEST_F(TApp, FileExists) {
std::string myfile{"TestNonFileNotUsed.txt"}; std::string myfile{"TestNonFileNotUsed.txt"};
EXPECT_FALSE(CLI::_ExistingFile(myfile)); EXPECT_FALSE(CLI::detail::_ExistingFile(myfile));
std::string filename = "Failed"; std::string filename = "Failed";
app.add_option("file", filename, "", CLI::ExistingFile); app.add_option("file", filename, "", CLI::ExistingFile);
@ -261,7 +261,7 @@ TEST_F(TApp, FileExists) {
EXPECT_EQ(myfile, filename); EXPECT_EQ(myfile, filename);
std::remove(myfile.c_str()); std::remove(myfile.c_str());
EXPECT_FALSE(CLI::_ExistingFile(myfile)); EXPECT_FALSE(CLI::detail::_ExistingFile(myfile));
} }
TEST_F(TApp, InSet) { TEST_F(TApp, InSet) {

View File

@ -7,79 +7,79 @@
TEST(Validators, FileExists) { TEST(Validators, FileExists) {
std::string myfile{"TestFileNotUsed.txt"}; std::string myfile{"TestFileNotUsed.txt"};
EXPECT_FALSE(CLI::_ExistingFile(myfile)); EXPECT_FALSE(CLI::detail::_ExistingFile(myfile));
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_TRUE(CLI::_ExistingFile(myfile)); EXPECT_TRUE(CLI::detail::_ExistingFile(myfile));
std::remove(myfile.c_str()); std::remove(myfile.c_str());
EXPECT_FALSE(CLI::_ExistingFile(myfile)); EXPECT_FALSE(CLI::detail::_ExistingFile(myfile));
} }
TEST(Validators, FileNotExists) { TEST(Validators, FileNotExists) {
std::string myfile{"TestFileNotUsed.txt"}; std::string myfile{"TestFileNotUsed.txt"};
EXPECT_TRUE(CLI::_NonexistentPath(myfile)); EXPECT_TRUE(CLI::detail::_NonexistentPath(myfile));
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_FALSE(CLI::_NonexistentPath(myfile)); EXPECT_FALSE(CLI::detail::_NonexistentPath(myfile));
std::remove(myfile.c_str()); std::remove(myfile.c_str());
EXPECT_TRUE(CLI::_NonexistentPath(myfile)); EXPECT_TRUE(CLI::detail::_NonexistentPath(myfile));
} }
TEST(Split, StringList) { TEST(Split, StringList) {
std::vector<std::string> results {"a", "long", "--lone", "-q"}; std::vector<std::string> results {"a", "long", "--lone", "-q"};
EXPECT_EQ(results, CLI::split_names("a,long,--lone,-q")); EXPECT_EQ(results, CLI::detail::split_names("a,long,--lone,-q"));
EXPECT_EQ(std::vector<std::string>({"one"}), CLI::split_names("one")); EXPECT_EQ(std::vector<std::string>({"one"}), CLI::detail::split_names("one"));
} }
TEST(RegEx, Shorts) { TEST(RegEx, Shorts) {
std::string name, value; std::string name, value;
EXPECT_TRUE(CLI::split_short("-a", name, value)); EXPECT_TRUE(CLI::detail::split_short("-a", name, value));
EXPECT_EQ("a", name); EXPECT_EQ("a", name);
EXPECT_EQ("", value); EXPECT_EQ("", value);
EXPECT_TRUE(CLI::split_short("-B", name, value)); EXPECT_TRUE(CLI::detail::split_short("-B", name, value));
EXPECT_EQ("B", name); EXPECT_EQ("B", name);
EXPECT_EQ("", value); EXPECT_EQ("", value);
EXPECT_TRUE(CLI::split_short("-cc", name, value)); EXPECT_TRUE(CLI::detail::split_short("-cc", name, value));
EXPECT_EQ("c", name); EXPECT_EQ("c", name);
EXPECT_EQ("c", value); EXPECT_EQ("c", value);
EXPECT_TRUE(CLI::split_short("-simple", name, value)); EXPECT_TRUE(CLI::detail::split_short("-simple", name, value));
EXPECT_EQ("s", name); EXPECT_EQ("s", name);
EXPECT_EQ("imple", value); EXPECT_EQ("imple", value);
EXPECT_FALSE(CLI::split_short("--a", name, value)); EXPECT_FALSE(CLI::detail::split_short("--a", name, value));
EXPECT_FALSE(CLI::split_short("--thing", name, value)); EXPECT_FALSE(CLI::detail::split_short("--thing", name, value));
EXPECT_FALSE(CLI::split_short("--", name, value)); EXPECT_FALSE(CLI::detail::split_short("--", name, value));
EXPECT_FALSE(CLI::split_short("something", name, value)); EXPECT_FALSE(CLI::detail::split_short("something", name, value));
EXPECT_FALSE(CLI::split_short("s", name, value)); EXPECT_FALSE(CLI::detail::split_short("s", name, value));
} }
TEST(RegEx, Longs) { TEST(RegEx, Longs) {
std::string name, value; std::string name, value;
EXPECT_TRUE(CLI::split_long("--a", name, value)); EXPECT_TRUE(CLI::detail::split_long("--a", name, value));
EXPECT_EQ("a", name); EXPECT_EQ("a", name);
EXPECT_EQ("", value); EXPECT_EQ("", value);
EXPECT_TRUE(CLI::split_long("--thing", name, value)); EXPECT_TRUE(CLI::detail::split_long("--thing", name, value));
EXPECT_EQ("thing", name); EXPECT_EQ("thing", name);
EXPECT_EQ("", value); EXPECT_EQ("", value);
EXPECT_TRUE(CLI::split_long("--some=thing", name, value)); EXPECT_TRUE(CLI::detail::split_long("--some=thing", name, value));
EXPECT_EQ("some", name); EXPECT_EQ("some", name);
EXPECT_EQ("thing", value); EXPECT_EQ("thing", value);
EXPECT_FALSE(CLI::split_long("-a", name, value)); EXPECT_FALSE(CLI::detail::split_long("-a", name, value));
EXPECT_FALSE(CLI::split_long("-things", name, value)); EXPECT_FALSE(CLI::detail::split_long("-things", name, value));
EXPECT_FALSE(CLI::split_long("Q", name, value)); EXPECT_FALSE(CLI::detail::split_long("Q", name, value));
EXPECT_FALSE(CLI::split_long("--", name, value)); EXPECT_FALSE(CLI::detail::split_long("--", name, value));
} }
@ -88,16 +88,16 @@ TEST(Regex, SplittingNew) {
std::vector<std::string> shorts; std::vector<std::string> shorts;
std::vector<std::string> longs; std::vector<std::string> longs;
EXPECT_NO_THROW(std::tie(shorts, longs) = CLI::get_names({"--long", "s", "-q", "also-long"})); 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>({"long", "also-long"}), longs);
EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts); EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts);
EXPECT_NO_THROW(std::tie(shorts, longs) = CLI::get_names({"--long", "", "s", "-q", "", "also-long"})); 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>({"long", "also-long"}), longs);
EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts); EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts);
EXPECT_THROW(std::tie(shorts, longs) = CLI::get_names({"-"}), CLI::BadNameString); EXPECT_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"-"}), CLI::BadNameString);
EXPECT_THROW(std::tie(shorts, longs) = CLI::get_names({"--"}), CLI::BadNameString); EXPECT_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"--"}), CLI::BadNameString);
EXPECT_THROW(std::tie(shorts, longs) = CLI::get_names({"-hi"}), CLI::BadNameString); EXPECT_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"-hi"}), CLI::BadNameString);
} }