mirror of
https://github.com/CLIUtils/CLI11.git
synced 2026-01-19 04:52:08 +00:00
Extras configuration (#1270)
Allow more control over how extras are interpreted. Specifically allowing some assumptions to be made about arguments to unrecognized options. With `CLI::ExtrasMode::AssumeMultipleArguments` all positional arguments following an unrecognized option will be considered extra as well even if there are open positional arguments. With `CLI::ExtrasMode::AssumeSingleArgument` a positional argument following an unrecognized option will also be considered unrecognized and go into the `remaining()` bin. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -66,7 +66,20 @@ CLI11_INLINE std::string simple(const App *app, const Error &e);
|
||||
CLI11_INLINE std::string help(const App *app, const Error &e);
|
||||
} // namespace FailureMessage
|
||||
|
||||
/// enumeration of modes of how to deal with command line extras
|
||||
enum class ExtrasMode : std::uint8_t {
|
||||
Error = 0,
|
||||
ErrorImmediately,
|
||||
Ignore,
|
||||
AssumeSingleArgument,
|
||||
AssumeMultipleArguments,
|
||||
Capture
|
||||
};
|
||||
|
||||
/// enumeration of modes of how to deal with extras in config files
|
||||
enum class ConfigExtrasMode : std::uint8_t { Error = 0, Ignore, IgnoreAll, Capture };
|
||||
|
||||
/// @brief enumeration of modes of how to deal with extras in config files
|
||||
enum class config_extras_mode : std::uint8_t { error = 0, ignore, ignore_all, capture };
|
||||
|
||||
/// @brief enumeration of prefix command modes, separator requires that the first extra argument be a "--", other
|
||||
@@ -116,11 +129,11 @@ class App {
|
||||
std::string description_{};
|
||||
|
||||
/// If true, allow extra arguments (ie, don't throw an error). INHERITABLE
|
||||
bool allow_extras_{false};
|
||||
ExtrasMode allow_extras_{ExtrasMode::Error};
|
||||
|
||||
/// If ignore, allow extra arguments in the ini file (ie, don't throw an error). INHERITABLE
|
||||
/// if error, error on an extra argument, and if capture feed it to the app
|
||||
config_extras_mode allow_config_extras_{config_extras_mode::ignore};
|
||||
ConfigExtrasMode allow_config_extras_{ConfigExtrasMode::Ignore};
|
||||
|
||||
/// If true, cease processing on an unrecognized option (implies allow_extras) INHERITABLE
|
||||
PrefixCommandMode prefix_command_{PrefixCommandMode::Off};
|
||||
@@ -388,6 +401,12 @@ class App {
|
||||
|
||||
/// Remove the error when extras are left over on the command line.
|
||||
App *allow_extras(bool allow = true) {
|
||||
allow_extras_ = allow ? ExtrasMode::Capture : ExtrasMode::Error;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Remove the error when extras are left over on the command line.
|
||||
App *allow_extras(ExtrasMode allow) {
|
||||
allow_extras_ = allow;
|
||||
return this;
|
||||
}
|
||||
@@ -461,16 +480,22 @@ class App {
|
||||
/// ignore extras in config files
|
||||
App *allow_config_extras(bool allow = true) {
|
||||
if(allow) {
|
||||
allow_config_extras_ = config_extras_mode::capture;
|
||||
allow_extras_ = true;
|
||||
allow_config_extras_ = ConfigExtrasMode::Capture;
|
||||
allow_extras_ = ExtrasMode::Capture;
|
||||
} else {
|
||||
allow_config_extras_ = config_extras_mode::error;
|
||||
allow_config_extras_ = ConfigExtrasMode::Error;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// ignore extras in config files
|
||||
App *allow_config_extras(config_extras_mode mode) {
|
||||
allow_config_extras_ = static_cast<ConfigExtrasMode>(mode);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// ignore extras in config files
|
||||
App *allow_config_extras(ConfigExtrasMode mode) {
|
||||
allow_config_extras_ = mode;
|
||||
return this;
|
||||
}
|
||||
@@ -1179,7 +1204,10 @@ class App {
|
||||
CLI11_NODISCARD PrefixCommandMode get_prefix_command_mode() const { return prefix_command_; }
|
||||
|
||||
/// Get the status of allow extras
|
||||
CLI11_NODISCARD bool get_allow_extras() const { return allow_extras_; }
|
||||
CLI11_NODISCARD bool get_allow_extras() const { return allow_extras_ > ExtrasMode::Ignore; }
|
||||
|
||||
/// Get the mode of allow_extras
|
||||
CLI11_NODISCARD ExtrasMode get_allow_extras_mode() const { return allow_extras_; }
|
||||
|
||||
/// Get the status of required
|
||||
CLI11_NODISCARD bool get_required() const { return required_; }
|
||||
@@ -1210,7 +1238,9 @@ class App {
|
||||
CLI11_NODISCARD bool get_validate_optional_arguments() const { return validate_optional_arguments_; }
|
||||
|
||||
/// Get the status of allow extras
|
||||
CLI11_NODISCARD config_extras_mode get_allow_config_extras() const { return allow_config_extras_; }
|
||||
CLI11_NODISCARD config_extras_mode get_allow_config_extras() const {
|
||||
return static_cast<config_extras_mode>(allow_config_extras_);
|
||||
}
|
||||
|
||||
/// Get a pointer to the help flag.
|
||||
Option *get_help_ptr() { return help_ptr_; }
|
||||
|
||||
@@ -986,7 +986,7 @@ CLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::remaining(bool recurs
|
||||
}
|
||||
// Get from a subcommand that may allow extras
|
||||
if(recurse) {
|
||||
if(!allow_extras_) {
|
||||
if(allow_extras_ == ExtrasMode::Error || allow_extras_ == ExtrasMode::Ignore) {
|
||||
for(const auto &sub : subcommands_) {
|
||||
if(sub->name_.empty() && !sub->missing_.empty()) {
|
||||
for(const std::pair<detail::Classifier, std::string> &miss : sub->missing_) {
|
||||
@@ -1461,13 +1461,13 @@ CLI11_INLINE void App::_process() {
|
||||
}
|
||||
|
||||
CLI11_INLINE void App::_process_extras() {
|
||||
if(!allow_extras_ && prefix_command_ == PrefixCommandMode::Off) {
|
||||
if(allow_extras_ == ExtrasMode::Error && prefix_command_ == PrefixCommandMode::Off) {
|
||||
std::size_t num_left_over = remaining_size();
|
||||
if(num_left_over > 0) {
|
||||
throw ExtrasError(name_, remaining(false));
|
||||
}
|
||||
}
|
||||
if(!allow_extras_ && prefix_command_ == PrefixCommandMode::SeparatorOnly) {
|
||||
if(allow_extras_ == ExtrasMode::Error && prefix_command_ == PrefixCommandMode::SeparatorOnly) {
|
||||
std::size_t num_left_over = remaining_size();
|
||||
if(num_left_over > 0) {
|
||||
if(remaining(false).front() != "--") {
|
||||
@@ -1555,7 +1555,7 @@ CLI11_INLINE void App::_parse_stream(std::istream &input) {
|
||||
|
||||
CLI11_INLINE void App::_parse_config(const std::vector<ConfigItem> &args) {
|
||||
for(const ConfigItem &item : args) {
|
||||
if(!_parse_single_config(item) && allow_config_extras_ == config_extras_mode::error)
|
||||
if(!_parse_single_config(item) && allow_config_extras_ == ConfigExtrasMode::Error)
|
||||
throw ConfigError::Extras(item.fullname());
|
||||
}
|
||||
}
|
||||
@@ -1730,7 +1730,8 @@ CLI11_INLINE bool App::_parse_single(std::vector<std::string> &args, bool &posit
|
||||
args.pop_back();
|
||||
positional_only = true;
|
||||
if(get_prefix_command()) {
|
||||
_move_to_missing(classifier, "--");
|
||||
// don't care about extras mode here
|
||||
missing_.emplace_back(classifier, "--");
|
||||
while(!args.empty()) {
|
||||
missing_.emplace_back(detail::Classifier::NONE, args.back());
|
||||
args.pop_back();
|
||||
@@ -2153,6 +2154,16 @@ App::_parse_arg(std::vector<std::string> &args, detail::Classifier current_type,
|
||||
missing_.emplace_back(detail::Classifier::NONE, args.back());
|
||||
args.pop_back();
|
||||
}
|
||||
} else if(allow_extras_ == ExtrasMode::AssumeSingleArgument) {
|
||||
if(!args.empty() && _recognize(args.back(), false) == detail::Classifier::NONE) {
|
||||
_move_to_missing(detail::Classifier::NONE, args.back());
|
||||
args.pop_back();
|
||||
}
|
||||
} else if(allow_extras_ == ExtrasMode::AssumeMultipleArguments) {
|
||||
while(!args.empty() && _recognize(args.back(), false) == detail::Classifier::NONE) {
|
||||
_move_to_missing(detail::Classifier::NONE, args.back());
|
||||
args.pop_back();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -2342,20 +2353,31 @@ CLI11_NODISCARD CLI11_INLINE const std::string &App::_compare_subcommand_names(c
|
||||
return estring;
|
||||
}
|
||||
|
||||
inline bool capture_extras(ExtrasMode mode) {
|
||||
return mode == ExtrasMode::Capture || mode == ExtrasMode::AssumeSingleArgument ||
|
||||
mode == ExtrasMode::AssumeMultipleArguments;
|
||||
}
|
||||
CLI11_INLINE void App::_move_to_missing(detail::Classifier val_type, const std::string &val) {
|
||||
if(allow_extras_ || subcommands_.empty() || get_prefix_command()) {
|
||||
missing_.emplace_back(val_type, val);
|
||||
if(allow_extras_ == ExtrasMode::ErrorImmediately) {
|
||||
throw ExtrasError(name_, std::vector<std::string>{val});
|
||||
}
|
||||
if(capture_extras(allow_extras_) || subcommands_.empty() || get_prefix_command()) {
|
||||
if(allow_extras_ != ExtrasMode::Ignore) {
|
||||
missing_.emplace_back(val_type, val);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// allow extra arguments to be placed in an option group if it is allowed there
|
||||
for(auto &subc : subcommands_) {
|
||||
if(subc->name_.empty() && subc->allow_extras_) {
|
||||
if(subc->name_.empty() && capture_extras(subc->allow_extras_)) {
|
||||
subc->missing_.emplace_back(val_type, val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if we haven't found any place to put them yet put them in missing
|
||||
missing_.emplace_back(val_type, val);
|
||||
if(allow_extras_ != ExtrasMode::Ignore) {
|
||||
// if we haven't found any place to put them yet put them in missing
|
||||
missing_.emplace_back(val_type, val);
|
||||
}
|
||||
}
|
||||
|
||||
CLI11_INLINE void App::_move_option(Option *opt, App *app) {
|
||||
|
||||
Reference in New Issue
Block a user