mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 12:13:52 +00:00
Add ignore underscore (#185)
* add ignore_underscore test cases and options to app * add ignore_underscore for add_sets and some more tests for the sets and subcommands * add some documentation lines and some failing tests * update readme with ignore_underscore option * remove failing tests from known issue * remove empty line for code coverage
This commit is contained in:
parent
c3d8d4a2d0
commit
a83109002c
@ -197,6 +197,10 @@ app.add_set(option_name,
|
||||
|
||||
app.add_set_ignore_case(... // String only
|
||||
|
||||
app.add_set_ignore_underscore(... // String only
|
||||
|
||||
app.add_set_ignore_case_underscore(... // String only
|
||||
|
||||
App* subcom = app.add_subcommand(name, description);
|
||||
```
|
||||
|
||||
@ -227,6 +231,7 @@ Before parsing, you can set the following options:
|
||||
- `->envname(name)`: Gets the value from the environment if present and not passed on the command line.
|
||||
- `->group(name)`: The help group to put the option in. No effect for positional options. Defaults to `"Options"`. `""` will not show up in the help print (hidden).
|
||||
- `->ignore_case()`: Ignore the case on the command line (also works on subcommands, does not affect arguments).
|
||||
- `->ignore_underscore()`: Ignore any underscores in the options names (also works on subcommands, does not affect arguments). For example "option_one" will match with optionone. This does not apply to short form options since they only have one character
|
||||
- `->multi_option_policy(CLI::MultiOptionPolicy::Throw)`: Set the multi-option policy. Shortcuts available: `->take_last()`, `->take_first()`, and `->join()`. This will only affect options expecting 1 argument or bool flags (which always default to take last).
|
||||
- `->check(CLI::ExistingFile)`: Requires that the file exists if given.
|
||||
- `->check(CLI::ExistingDirectory)`: Requires that the directory exists.
|
||||
@ -279,6 +284,8 @@ Multiple subcommands are allowed, to allow [`Click`][click] like series of comma
|
||||
There are several options that are supported on the main app and subcommands. These are:
|
||||
|
||||
- `.ignore_case()`: Ignore the case of this subcommand. Inherited by added subcommands, so is usually used on the main `App`.
|
||||
- `.ignore_underscore()`: Ignore any underscores in the subcommand name. Inherited by added subcommands, so is usually used on the main `App`.
|
||||
|
||||
- `.fallthrough()`: Allow extra unmatched options and positionals to "fall through" and be matched on a parent command. Subcommands always are allowed to fall through.
|
||||
- `.require_subcommand()`: Require 1 or more subcommands.
|
||||
- `.require_subcommand(N)`: Require `N` subcommands if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default 0 or more.
|
||||
@ -340,7 +347,7 @@ arguments, use `.config_to_str(default_also=false, prefix="", write_description=
|
||||
|
||||
Many of the defaults for subcommands and even options are inherited from their creators. The inherited default values for subcommands are `allow_extras`, `prefix_command`, `ignore_case`, `fallthrough`, `group`, `footer`, and maximum number of required subcommands. The help flag existence, name, and description are inherited, as well.
|
||||
|
||||
Options have defaults for `group`, `required`, `multi_option_policy`, and `ignore_case`. To set these defaults, you should set the `option_defaults()` object, for example:
|
||||
Options have defaults for `group`, `required`, `multi_option_policy`, `ignore_underscore`, and `ignore_case`. To set these defaults, you should set the `option_defaults()` object, for example:
|
||||
|
||||
```cpp
|
||||
app.option_defaults()->required();
|
||||
|
@ -139,6 +139,9 @@ class App {
|
||||
/// If true, the program name is not case sensitive INHERITABLE
|
||||
bool ignore_case_{false};
|
||||
|
||||
/// If true, the program should ignore underscores INHERITABLE
|
||||
bool ignore_underscore_{false};
|
||||
|
||||
/// Allow subcommand fallthrough, so that parent commands can collect commands after subcommand. INHERITABLE
|
||||
bool fallthrough_{false};
|
||||
|
||||
@ -195,6 +198,7 @@ class App {
|
||||
allow_config_extras_ = parent_->allow_config_extras_;
|
||||
prefix_command_ = parent_->prefix_command_;
|
||||
ignore_case_ = parent_->ignore_case_;
|
||||
ignore_underscore_ = parent_->ignore_underscore_;
|
||||
fallthrough_ = parent_->fallthrough_;
|
||||
group_ = parent_->group_;
|
||||
footer_ = parent_->footer_;
|
||||
@ -265,6 +269,18 @@ class App {
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Ignore underscore. Subcommand inherit value.
|
||||
App *ignore_underscore(bool value = true) {
|
||||
ignore_underscore_ = value;
|
||||
if(parent_ != nullptr) {
|
||||
for(const auto &subc : parent_->subcommands_) {
|
||||
if(subc.get() != this && (this->check_name(subc->name_) || subc->check_name(this->name_)))
|
||||
throw OptionAlreadyAdded(subc->name_);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Set the help formatter
|
||||
App *formatter(std::shared_ptr<FormatterBase> fmt) {
|
||||
formatter_ = fmt;
|
||||
@ -510,7 +526,7 @@ class App {
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Add set of options (No default, temp refernce, such as an inline set)
|
||||
/// Add set of options (No default, temp reference, such as an inline set)
|
||||
template <typename T>
|
||||
Option *add_set(std::string name,
|
||||
T &member, ///< The selected member of the set
|
||||
@ -532,7 +548,7 @@ class App {
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options (No default, non-temp refernce, such as an existing set)
|
||||
/// Add set of options (No default, non-temp reference, such as an existing set)
|
||||
template <typename T>
|
||||
Option *add_set(std::string name,
|
||||
T &member, ///< The selected member of the set
|
||||
@ -558,7 +574,7 @@ class App {
|
||||
template <typename T>
|
||||
Option *add_set(std::string name,
|
||||
T &member, ///< The selected member of the set
|
||||
const std::set<T> &&options, ///< The set of posibilities
|
||||
const std::set<T> &&options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
@ -582,11 +598,11 @@ class App {
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options (with default, L value refernce, such as an existing set)
|
||||
/// Add set of options (with default, L value reference, such as an existing set)
|
||||
template <typename T>
|
||||
Option *add_set(std::string name,
|
||||
T &member, ///< The selected member of the set
|
||||
const std::set<T> &options, ///< The set of posibilities
|
||||
const std::set<T> &options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
@ -668,7 +684,7 @@ class App {
|
||||
/// Add set of options, string only, ignore case (default, R value)
|
||||
Option *add_set_ignore_case(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &&options, ///< The set of posibilities
|
||||
const std::set<std::string> &&options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
@ -699,7 +715,7 @@ class App {
|
||||
/// Add set of options, string only, ignore case (default, L value)
|
||||
Option *add_set_ignore_case(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of posibilities
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
@ -727,6 +743,242 @@ class App {
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore (no default, R value)
|
||||
Option *add_set_ignore_underscore(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &&options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
member = detail::remove_underscore(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::remove_underscore(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(name, fun, description, false);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore (no default, L value)
|
||||
Option *add_set_ignore_underscore(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
member = detail::remove_underscore(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::remove_underscore(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(name, fun, description, false);
|
||||
opt->type_name_fn([&options]() {
|
||||
return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
|
||||
});
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore (default, R value)
|
||||
Option *add_set_ignore_underscore(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &&options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
member = detail::remove_underscore(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::remove_underscore(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(name, fun, description, defaulted);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
if(defaulted) {
|
||||
opt->default_str(member);
|
||||
}
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore (default, L value)
|
||||
Option *add_set_ignore_underscore(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
member = detail::remove_underscore(res[0]);
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::remove_underscore(val) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(name, fun, description, defaulted);
|
||||
opt->type_name_fn([&options]() {
|
||||
return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
|
||||
});
|
||||
if(defaulted) {
|
||||
opt->default_str(member);
|
||||
}
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore and case(no default, R value)
|
||||
Option *add_set_ignore_case_underscore(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &&options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(detail::remove_underscore(res[0]));
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(detail::remove_underscore(val)) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(name, fun, description, false);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore and case(no default, L value)
|
||||
Option *add_set_ignore_case_underscore(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(detail::remove_underscore(res[0]));
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(detail::remove_underscore(val)) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(name, fun, description, false);
|
||||
opt->type_name_fn([&options]() {
|
||||
return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
|
||||
});
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore and case (default, R value)
|
||||
Option *add_set_ignore_case_underscore(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &&options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(detail::remove_underscore(res[0]));
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(detail::remove_underscore(val)) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(name, fun, description, defaulted);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->type_name(typeval);
|
||||
if(defaulted) {
|
||||
opt->default_str(member);
|
||||
}
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore underscore and case (default, L value)
|
||||
Option *add_set_ignore_case_underscore(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(name, ',').at(0);
|
||||
CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
|
||||
member = detail::to_lower(detail::remove_underscore(res[0]));
|
||||
auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
|
||||
return detail::to_lower(detail::remove_underscore(val)) == member;
|
||||
});
|
||||
if(iter == std::end(options))
|
||||
throw ConversionError(member, simple_name);
|
||||
else {
|
||||
member = *iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Option *opt = add_option(name, fun, description, defaulted);
|
||||
opt->type_name_fn([&options]() {
|
||||
return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
|
||||
});
|
||||
if(defaulted) {
|
||||
opt->default_str(member);
|
||||
}
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add a complex number
|
||||
template <typename T>
|
||||
Option *add_complex(std::string name,
|
||||
@ -1122,6 +1374,9 @@ class App {
|
||||
/// Check the status of ignore_case
|
||||
bool get_ignore_case() const { return ignore_case_; }
|
||||
|
||||
/// Check the status of ignore_underscore
|
||||
bool get_ignore_underscore() const { return ignore_underscore_; }
|
||||
|
||||
/// Check the status of fallthrough
|
||||
bool get_fallthrough() const { return fallthrough_; }
|
||||
|
||||
@ -1170,9 +1425,13 @@ class App {
|
||||
/// Get the name of the current app
|
||||
std::string get_name() const { return name_; }
|
||||
|
||||
/// Check the name, case insensitive if set
|
||||
/// Check the name, case insensitive and underscore insensitive if set
|
||||
bool check_name(std::string name_to_check) const {
|
||||
std::string local_name = name_;
|
||||
if(ignore_underscore_) {
|
||||
local_name = detail::remove_underscore(name_);
|
||||
name_to_check = detail::remove_underscore(name_to_check);
|
||||
}
|
||||
if(ignore_case_) {
|
||||
local_name = detail::to_lower(name_);
|
||||
name_to_check = detail::to_lower(name_to_check);
|
||||
|
@ -45,6 +45,9 @@ template <typename CRTP> class OptionBase {
|
||||
/// Ignore the case when matching (option, not value)
|
||||
bool ignore_case_{false};
|
||||
|
||||
/// Ignore underscores when matching (option, not value)
|
||||
bool ignore_underscore_{false};
|
||||
|
||||
/// Allow this option to be given in a configuration file
|
||||
bool configurable_{true};
|
||||
|
||||
@ -56,6 +59,7 @@ template <typename CRTP> class OptionBase {
|
||||
other->group(group_);
|
||||
other->required(required_);
|
||||
other->ignore_case(ignore_case_);
|
||||
other->ignore_underscore(ignore_underscore_);
|
||||
other->configurable(configurable_);
|
||||
other->multi_option_policy(multi_option_policy_);
|
||||
}
|
||||
@ -90,6 +94,9 @@ template <typename CRTP> class OptionBase {
|
||||
/// The status of ignore case
|
||||
bool get_ignore_case() const { return ignore_case_; }
|
||||
|
||||
/// The status of ignore_underscore
|
||||
bool get_ignore_underscore() const { return ignore_underscore_; }
|
||||
|
||||
/// The status of configurable
|
||||
bool get_configurable() const { return configurable_; }
|
||||
|
||||
@ -145,6 +152,12 @@ class OptionDefaults : public OptionBase<OptionDefaults> {
|
||||
ignore_case_ = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Ignore underscores in the option name
|
||||
OptionDefaults *ignore_underscore(bool value = true) {
|
||||
ignore_underscore_ = value;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
class Option : public OptionBase<Option> {
|
||||
@ -411,6 +424,20 @@ class Option : public OptionBase<Option> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Ignore underscores in the option names
|
||||
///
|
||||
/// The template hides the fact that we don't have the definition of App yet.
|
||||
/// You are never expected to add an argument to the template here.
|
||||
template <typename T = App> Option *ignore_underscore(bool value = true) {
|
||||
ignore_underscore_ = value;
|
||||
auto *parent = dynamic_cast<T *>(parent_);
|
||||
for(const Option_p &opt : parent->options_)
|
||||
if(opt.get() != this && *opt == *this)
|
||||
throw OptionAlreadyAdded(opt->get_name(true, true));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Take the last argument if given multiple times (or another policy)
|
||||
Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
|
||||
|
||||
@ -602,7 +629,7 @@ class Option : public OptionBase<Option> {
|
||||
for(const std::string &lname : lnames_)
|
||||
if(other.check_lname(lname))
|
||||
return true;
|
||||
// We need to do the inverse, just in case we are ignore_case
|
||||
// We need to do the inverse, just in case we are ignore_case or ignore underscore
|
||||
for(const std::string &sname : other.snames_)
|
||||
if(check_sname(sname))
|
||||
return true;
|
||||
@ -625,13 +652,17 @@ class Option : public OptionBase<Option> {
|
||||
local_pname = detail::to_lower(local_pname);
|
||||
name = detail::to_lower(name);
|
||||
}
|
||||
if(ignore_underscore_) {
|
||||
local_pname = detail::remove_underscore(local_pname);
|
||||
name = detail::remove_underscore(name);
|
||||
}
|
||||
return name == local_pname;
|
||||
}
|
||||
}
|
||||
|
||||
/// Requires "-" to be removed from string
|
||||
bool check_sname(std::string name) const {
|
||||
if(ignore_case_) {
|
||||
if(ignore_case_) { // there can be no extra underscores in check_sname
|
||||
name = detail::to_lower(name);
|
||||
return std::find_if(std::begin(snames_), std::end(snames_), [&name](std::string local_sname) {
|
||||
return detail::to_lower(local_sname) == name;
|
||||
@ -643,10 +674,23 @@ class Option : public OptionBase<Option> {
|
||||
/// Requires "--" to be removed from string
|
||||
bool check_lname(std::string name) const {
|
||||
if(ignore_case_) {
|
||||
if(ignore_underscore_) {
|
||||
name = detail::to_lower(detail::remove_underscore(name));
|
||||
return std::find_if(std::begin(lnames_), std::end(lnames_), [&name](std::string local_sname) {
|
||||
return detail::to_lower(detail::remove_underscore(local_sname)) == name;
|
||||
}) != std::end(lnames_);
|
||||
} else {
|
||||
name = detail::to_lower(name);
|
||||
return std::find_if(std::begin(lnames_), std::end(lnames_), [&name](std::string local_sname) {
|
||||
return detail::to_lower(local_sname) == name;
|
||||
}) != std::end(lnames_);
|
||||
}
|
||||
|
||||
} else if(ignore_underscore_) {
|
||||
name = detail::remove_underscore(name);
|
||||
return std::find_if(std::begin(lnames_), std::end(lnames_), [&name](std::string local_sname) {
|
||||
return detail::remove_underscore(local_sname) == name;
|
||||
}) != std::end(lnames_);
|
||||
} else
|
||||
return std::find(std::begin(lnames_), std::end(lnames_), name) != std::end(lnames_);
|
||||
}
|
||||
|
@ -142,6 +142,12 @@ inline std::string to_lower(std::string str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
/// remove underscores from a string
|
||||
inline std::string remove_underscore(std::string str) {
|
||||
str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str));
|
||||
return str;
|
||||
}
|
||||
|
||||
/// Split a string '"one two" "three"' into 'one two', 'three'
|
||||
inline std::vector<std::string> split_up(std::string str) {
|
||||
|
||||
|
@ -994,6 +994,115 @@ TEST_F(TApp, InSetIgnoreCase) {
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
/*
|
||||
TEST_F(TApp, InSetIgnoreCaseLValue) {
|
||||
|
||||
std::set<std::string> options{"one", "Two", "THREE"};
|
||||
std::string choice;
|
||||
app.add_set_ignore_case("-q,--quick", choice, options);
|
||||
|
||||
args = {"--quick", "One"};
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
run();
|
||||
EXPECT_EQ("Two", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // Keeps caps from set
|
||||
|
||||
options.clear();
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // this will now fail since options was cleared
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
args = {"--quick=one", "--quick=two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCasePointer) {
|
||||
|
||||
std::set<std::string> *options = new std::set<std::string>{"one", "Two", "THREE"};
|
||||
std::string choice;
|
||||
app.add_set_ignore_case("-q,--quick", choice, *options);
|
||||
|
||||
args = {"--quick", "One"};
|
||||
run();
|
||||
EXPECT_EQ("one", choice);
|
||||
|
||||
args = {"--quick", "two"};
|
||||
run();
|
||||
EXPECT_EQ("Two", choice); // Keeps caps from set
|
||||
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // Keeps caps from set
|
||||
|
||||
delete options;
|
||||
args = {"--quick", "ThrEE"};
|
||||
run();
|
||||
EXPECT_EQ("THREE", choice); // this could cause a seg fault
|
||||
|
||||
args = {"--quick", "four"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
args = {"--quick=one", "--quick=two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
*/
|
||||
TEST_F(TApp, InSetIgnoreUnderscore) {
|
||||
|
||||
std::string choice;
|
||||
app.add_set_ignore_underscore("-q,--quick", choice, {"option_one", "option_two", "optionthree"});
|
||||
|
||||
args = {"--quick", "option_one"};
|
||||
run();
|
||||
EXPECT_EQ("option_one", choice);
|
||||
|
||||
args = {"--quick", "optiontwo"};
|
||||
run();
|
||||
EXPECT_EQ("option_two", choice); // Keeps underscore from set
|
||||
|
||||
args = {"--quick", "_option_thr_ee"};
|
||||
run();
|
||||
EXPECT_EQ("optionthree", choice); // no underscore
|
||||
|
||||
args = {"--quick", "Option4"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
args = {"--quick=option_one", "--quick=option_two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCaseUnderscore) {
|
||||
|
||||
std::string choice;
|
||||
app.add_set_ignore_case_underscore("-q,--quick", choice, {"Option_One", "option_two", "OptionThree"});
|
||||
|
||||
args = {"--quick", "option_one"};
|
||||
run();
|
||||
EXPECT_EQ("Option_One", choice);
|
||||
|
||||
args = {"--quick", "OptionTwo"};
|
||||
run();
|
||||
EXPECT_EQ("option_two", choice); // Keeps underscore and case from set
|
||||
|
||||
args = {"--quick", "_OPTION_thr_ee"};
|
||||
run();
|
||||
EXPECT_EQ("OptionThree", choice); // no underscore
|
||||
|
||||
args = {"--quick", "Option4"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
args = {"--quick=option_one", "--quick=option_two"};
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, VectorFixedString) {
|
||||
std::vector<std::string> strvec;
|
||||
std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
|
||||
|
@ -92,6 +92,17 @@ TEST_F(TApp, MultipleSubcomMatchingWithCaseFirst) {
|
||||
EXPECT_THROW(app.add_subcommand("fIrst"), CLI::OptionAlreadyAdded);
|
||||
}
|
||||
|
||||
TEST_F(TApp, MultipleSubcomMatchingWithUnderscore) {
|
||||
app.add_subcommand("first_option")->ignore_underscore();
|
||||
EXPECT_THROW(app.add_subcommand("firstoption"), CLI::OptionAlreadyAdded);
|
||||
}
|
||||
|
||||
TEST_F(TApp, MultipleSubcomMatchingWithUnderscoreFirst) {
|
||||
app.ignore_underscore();
|
||||
app.add_subcommand("first_option");
|
||||
EXPECT_THROW(app.add_subcommand("firstoption"), CLI::OptionAlreadyAdded);
|
||||
}
|
||||
|
||||
TEST_F(TApp, MultipleSubcomMatchingWithCaseInplace) {
|
||||
app.add_subcommand("first");
|
||||
auto first = app.add_subcommand("fIrst");
|
||||
@ -106,6 +117,20 @@ TEST_F(TApp, MultipleSubcomMatchingWithCaseInplace2) {
|
||||
EXPECT_THROW(first->ignore_case(), CLI::OptionAlreadyAdded);
|
||||
}
|
||||
|
||||
TEST_F(TApp, MultipleSubcomMatchingWithUnderscoreInplace) {
|
||||
app.add_subcommand("first_option");
|
||||
auto first = app.add_subcommand("firstoption");
|
||||
|
||||
EXPECT_THROW(first->ignore_underscore(), CLI::OptionAlreadyAdded);
|
||||
}
|
||||
|
||||
TEST_F(TApp, MultipleSubcomMatchingWithUnderscoreInplace2) {
|
||||
auto first = app.add_subcommand("firstoption");
|
||||
app.add_subcommand("first_option");
|
||||
|
||||
EXPECT_THROW(first->ignore_underscore(), CLI::OptionAlreadyAdded);
|
||||
}
|
||||
|
||||
TEST_F(TApp, MultipleSubcomNoMatchingInplace2) {
|
||||
auto first = app.add_subcommand("first");
|
||||
auto second = app.add_subcommand("second");
|
||||
@ -114,6 +139,14 @@ TEST_F(TApp, MultipleSubcomNoMatchingInplace2) {
|
||||
EXPECT_NO_THROW(second->ignore_case());
|
||||
}
|
||||
|
||||
TEST_F(TApp, MultipleSubcomNoMatchingInplaceUnderscore2) {
|
||||
auto first = app.add_subcommand("first_option");
|
||||
auto second = app.add_subcommand("second_option");
|
||||
|
||||
EXPECT_NO_THROW(first->ignore_underscore());
|
||||
EXPECT_NO_THROW(second->ignore_underscore());
|
||||
}
|
||||
|
||||
TEST_F(TApp, IncorrectConstructionFlagPositional1) { EXPECT_THROW(app.add_flag("cat"), CLI::IncorrectConstruction); }
|
||||
|
||||
TEST_F(TApp, IncorrectConstructionFlagPositional2) {
|
||||
@ -262,6 +295,68 @@ TEST_F(TApp, CheckNameNoCase) {
|
||||
EXPECT_TRUE(pos2->check_name("pos2"));
|
||||
}
|
||||
|
||||
TEST_F(TApp, CheckNameNoUnderscore) {
|
||||
auto long1 = app.add_flag("--longoption1")->ignore_underscore();
|
||||
auto long2 = app.add_flag("--long_option2")->ignore_underscore();
|
||||
|
||||
int x, y;
|
||||
auto pos1 = app.add_option("pos_option_1", x)->ignore_underscore();
|
||||
auto pos2 = app.add_option("posoption2", y)->ignore_underscore();
|
||||
|
||||
EXPECT_TRUE(long1->check_name("--long_option1"));
|
||||
EXPECT_TRUE(long1->check_name("--longoption_1"));
|
||||
EXPECT_TRUE(long1->check_name("--longoption1"));
|
||||
EXPECT_TRUE(long1->check_name("--long__opt_ion__1"));
|
||||
EXPECT_TRUE(long1->check_name("--__l_o_n_g_o_p_t_i_o_n_1"));
|
||||
|
||||
EXPECT_TRUE(long2->check_name("--long_option2"));
|
||||
EXPECT_TRUE(long2->check_name("--longoption2"));
|
||||
EXPECT_TRUE(long2->check_name("--longoption_2"));
|
||||
EXPECT_TRUE(long2->check_name("--long__opt_ion__2"));
|
||||
EXPECT_TRUE(long2->check_name("--__l_o_n_go_p_t_i_o_n_2__"));
|
||||
|
||||
EXPECT_TRUE(pos1->check_name("pos_option1"));
|
||||
EXPECT_TRUE(pos1->check_name("pos_option_1"));
|
||||
EXPECT_TRUE(pos1->check_name("pos_o_p_t_i_on_1"));
|
||||
EXPECT_TRUE(pos1->check_name("posoption1"));
|
||||
|
||||
EXPECT_TRUE(pos2->check_name("pos_option2"));
|
||||
EXPECT_TRUE(pos2->check_name("pos_option_2"));
|
||||
EXPECT_TRUE(pos2->check_name("pos_o_p_t_i_on_2"));
|
||||
EXPECT_TRUE(pos2->check_name("posoption2"));
|
||||
}
|
||||
|
||||
TEST_F(TApp, CheckNameNoCaseNoUnderscore) {
|
||||
auto long1 = app.add_flag("--LongoptioN1")->ignore_underscore()->ignore_case();
|
||||
auto long2 = app.add_flag("--long_Option2")->ignore_case()->ignore_underscore();
|
||||
|
||||
int x, y;
|
||||
auto pos1 = app.add_option("pos_Option_1", x)->ignore_underscore()->ignore_case();
|
||||
auto pos2 = app.add_option("posOption2", y)->ignore_case()->ignore_underscore();
|
||||
|
||||
EXPECT_TRUE(long1->check_name("--Long_Option1"));
|
||||
EXPECT_TRUE(long1->check_name("--lONgoption_1"));
|
||||
EXPECT_TRUE(long1->check_name("--LongOption1"));
|
||||
EXPECT_TRUE(long1->check_name("--long__Opt_ion__1"));
|
||||
EXPECT_TRUE(long1->check_name("--__l_o_N_g_o_P_t_i_O_n_1"));
|
||||
|
||||
EXPECT_TRUE(long2->check_name("--long_Option2"));
|
||||
EXPECT_TRUE(long2->check_name("--LongOption2"));
|
||||
EXPECT_TRUE(long2->check_name("--longOPTION_2"));
|
||||
EXPECT_TRUE(long2->check_name("--long__OPT_ion__2"));
|
||||
EXPECT_TRUE(long2->check_name("--__l_o_n_GO_p_t_i_o_n_2__"));
|
||||
|
||||
EXPECT_TRUE(pos1->check_name("POS_Option1"));
|
||||
EXPECT_TRUE(pos1->check_name("pos_option_1"));
|
||||
EXPECT_TRUE(pos1->check_name("pos_o_p_t_i_on_1"));
|
||||
EXPECT_TRUE(pos1->check_name("posoption1"));
|
||||
|
||||
EXPECT_TRUE(pos2->check_name("pos_option2"));
|
||||
EXPECT_TRUE(pos2->check_name("pos_OPTION_2"));
|
||||
EXPECT_TRUE(pos2->check_name("poS_o_p_T_I_on_2"));
|
||||
EXPECT_TRUE(pos2->check_name("PosOption2"));
|
||||
}
|
||||
|
||||
TEST_F(TApp, PreSpaces) {
|
||||
int x;
|
||||
auto myapp = app.add_option(" -a, --long, other", x);
|
||||
@ -301,6 +396,12 @@ TEST_F(TApp, OptionFromDefaults) {
|
||||
auto opt3 = app.add_option("--simple3", x);
|
||||
EXPECT_TRUE(opt3->get_required());
|
||||
EXPECT_TRUE(opt3->get_ignore_case());
|
||||
|
||||
app.option_defaults()->required()->ignore_underscore();
|
||||
|
||||
auto opt4 = app.add_option("--simple4", x);
|
||||
EXPECT_TRUE(opt4->get_required());
|
||||
EXPECT_TRUE(opt4->get_ignore_underscore());
|
||||
}
|
||||
|
||||
TEST_F(TApp, OptionFromDefaultsSubcommands) {
|
||||
@ -308,6 +409,7 @@ TEST_F(TApp, OptionFromDefaultsSubcommands) {
|
||||
EXPECT_FALSE(app.option_defaults()->get_required());
|
||||
EXPECT_EQ(app.option_defaults()->get_multi_option_policy(), CLI::MultiOptionPolicy::Throw);
|
||||
EXPECT_FALSE(app.option_defaults()->get_ignore_case());
|
||||
EXPECT_FALSE(app.option_defaults()->get_ignore_underscore());
|
||||
EXPECT_TRUE(app.option_defaults()->get_configurable());
|
||||
EXPECT_EQ(app.option_defaults()->get_group(), "Options");
|
||||
|
||||
@ -315,6 +417,7 @@ TEST_F(TApp, OptionFromDefaultsSubcommands) {
|
||||
->required()
|
||||
->multi_option_policy(CLI::MultiOptionPolicy::TakeLast)
|
||||
->ignore_case()
|
||||
->ignore_underscore()
|
||||
->configurable(false)
|
||||
->group("Something");
|
||||
|
||||
@ -323,6 +426,7 @@ TEST_F(TApp, OptionFromDefaultsSubcommands) {
|
||||
EXPECT_TRUE(app2->option_defaults()->get_required());
|
||||
EXPECT_EQ(app2->option_defaults()->get_multi_option_policy(), CLI::MultiOptionPolicy::TakeLast);
|
||||
EXPECT_TRUE(app2->option_defaults()->get_ignore_case());
|
||||
EXPECT_TRUE(app2->option_defaults()->get_ignore_underscore());
|
||||
EXPECT_FALSE(app2->option_defaults()->get_configurable());
|
||||
EXPECT_EQ(app2->option_defaults()->get_group(), "Something");
|
||||
}
|
||||
@ -352,6 +456,7 @@ TEST_F(TApp, SubcommandDefaults) {
|
||||
EXPECT_FALSE(app.get_allow_extras());
|
||||
EXPECT_FALSE(app.get_prefix_command());
|
||||
EXPECT_FALSE(app.get_ignore_case());
|
||||
EXPECT_FALSE(app.get_ignore_underscore());
|
||||
EXPECT_FALSE(app.get_fallthrough());
|
||||
EXPECT_EQ(app.get_footer(), "");
|
||||
EXPECT_EQ(app.get_group(), "Subcommands");
|
||||
@ -361,6 +466,7 @@ TEST_F(TApp, SubcommandDefaults) {
|
||||
app.allow_extras();
|
||||
app.prefix_command();
|
||||
app.ignore_case();
|
||||
app.ignore_underscore();
|
||||
app.fallthrough();
|
||||
app.footer("footy");
|
||||
app.group("Stuff");
|
||||
@ -372,6 +478,7 @@ TEST_F(TApp, SubcommandDefaults) {
|
||||
EXPECT_TRUE(app2->get_allow_extras());
|
||||
EXPECT_TRUE(app2->get_prefix_command());
|
||||
EXPECT_TRUE(app2->get_ignore_case());
|
||||
EXPECT_TRUE(app2->get_ignore_underscore());
|
||||
EXPECT_TRUE(app2->get_fallthrough());
|
||||
EXPECT_EQ(app2->get_footer(), "footy");
|
||||
EXPECT_EQ(app2->get_group(), "Stuff");
|
||||
|
@ -530,6 +530,43 @@ TEST_F(TApp, SubcomInheritCaseCheck) {
|
||||
EXPECT_EQ(sub2, app.get_subcommands().at(0));
|
||||
}
|
||||
|
||||
TEST_F(SubcommandProgram, UnderscoreCheck) {
|
||||
args = {"start_"};
|
||||
EXPECT_THROW(run(), CLI::ExtrasError);
|
||||
|
||||
args = {"start"};
|
||||
run();
|
||||
|
||||
start->ignore_underscore();
|
||||
run();
|
||||
|
||||
args = {"_start_"};
|
||||
run();
|
||||
}
|
||||
|
||||
TEST_F(TApp, SubcomInheritUnderscoreCheck) {
|
||||
app.ignore_underscore();
|
||||
auto sub1 = app.add_subcommand("sub_option1");
|
||||
auto sub2 = app.add_subcommand("sub_option2");
|
||||
|
||||
run();
|
||||
EXPECT_EQ((size_t)0, app.get_subcommands().size());
|
||||
EXPECT_EQ((size_t)2, app.get_subcommands({}).size());
|
||||
EXPECT_EQ((size_t)1, app.get_subcommands([](const CLI::App *s) { return s->get_name() == "sub_option1"; }).size());
|
||||
|
||||
args = {"suboption1"};
|
||||
run();
|
||||
EXPECT_EQ(sub1, app.get_subcommands().at(0));
|
||||
EXPECT_EQ((size_t)1, app.get_subcommands().size());
|
||||
|
||||
app.clear();
|
||||
EXPECT_EQ((size_t)0, app.get_subcommands().size());
|
||||
|
||||
args = {"_suboption2"};
|
||||
run();
|
||||
EXPECT_EQ(sub2, app.get_subcommands().at(0));
|
||||
}
|
||||
|
||||
TEST_F(SubcommandProgram, HelpOrder) {
|
||||
|
||||
args = {"-h"};
|
||||
|
Loading…
x
Reference in New Issue
Block a user