mirror of
https://github.com/CLIUtils/CLI11.git
synced 2026-01-19 04:52:08 +00:00
Option callback priority v2 (#1226)
Extension allowing all possible priority combinations. Add a field callback_priority to OptionBase. --------- Co-authored-by: Philip Top <phlptp@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1303,12 +1303,12 @@ class App {
|
||||
void _process_env();
|
||||
|
||||
/// Process callbacks. Runs on *all* subcommands.
|
||||
void _process_callbacks();
|
||||
void _process_callbacks(CallbackPriority priority);
|
||||
|
||||
/// Run help flag processing if any are found.
|
||||
///
|
||||
/// The flags allow recursive calls to remember if there was a help flag on a parent.
|
||||
void _process_help_flags(bool trigger_help = false, bool trigger_all_help = false) const;
|
||||
void _process_help_flags(CallbackPriority priority, bool trigger_help = false, bool trigger_all_help = false) const;
|
||||
|
||||
/// Verify required options and cross requirements. Subcommands too (only if selected).
|
||||
void _process_requirements();
|
||||
|
||||
@@ -50,6 +50,18 @@ enum class MultiOptionPolicy : char {
|
||||
Reverse, //!< take only the last Expected number of arguments in reverse order
|
||||
};
|
||||
|
||||
/// @brief enumeration for the callback priority
|
||||
enum class CallbackPriority : std::uint8_t {
|
||||
FirstPreHelp = 0,
|
||||
First = 1,
|
||||
PreRequirementsCheckPreHelp = 2,
|
||||
PreRequirementsCheck = 3,
|
||||
NormalPreHelp = 4,
|
||||
Normal = 5,
|
||||
LastPreHelp = 6,
|
||||
Last = 7
|
||||
}; // namespace CLI
|
||||
|
||||
/// This is the CRTP base class for Option and OptionDefaults. It was designed this way
|
||||
/// to share parts of the class; an OptionDefaults can copy to an Option.
|
||||
template <typename CRTP> class OptionBase {
|
||||
@@ -84,6 +96,9 @@ template <typename CRTP> class OptionBase {
|
||||
/// Policy for handling multiple arguments beyond the expected Max
|
||||
MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};
|
||||
|
||||
/// Priority of callback
|
||||
CallbackPriority callback_priority_{CallbackPriority::Normal};
|
||||
|
||||
/// Copy the contents to another similar class (one based on OptionBase)
|
||||
template <typename T> void copy_to(T *other) const;
|
||||
|
||||
@@ -142,6 +157,9 @@ template <typename CRTP> class OptionBase {
|
||||
/// The status of the multi option policy
|
||||
CLI11_NODISCARD MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; }
|
||||
|
||||
/// The priority of callback
|
||||
CLI11_NODISCARD CallbackPriority get_callback_priority() const { return callback_priority_; }
|
||||
|
||||
// Shortcuts for multi option policy
|
||||
|
||||
/// Set the multi option policy to take last
|
||||
@@ -201,6 +219,12 @@ class OptionDefaults : public OptionBase<OptionDefaults> {
|
||||
|
||||
// Methods here need a different implementation if they are Option vs. OptionDefault
|
||||
|
||||
/// Set the callback priority
|
||||
OptionDefaults *callback_priority(CallbackPriority value = CallbackPriority::Normal) {
|
||||
callback_priority_ = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Take the last argument if given multiple times
|
||||
OptionDefaults *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
|
||||
multi_option_policy_ = value;
|
||||
@@ -343,7 +367,6 @@ class Option : public OptionBase<Option> {
|
||||
bool trigger_on_result_{false};
|
||||
/// flag indicating that the option should force the callback regardless if any results present
|
||||
bool force_callback_{false};
|
||||
///@}
|
||||
|
||||
/// Making an option by hand is not defined, it must be made by the App class
|
||||
Option(std::string option_name,
|
||||
@@ -420,6 +443,13 @@ class Option : public OptionBase<Option> {
|
||||
/// Get the current value of run_callback_for_default
|
||||
CLI11_NODISCARD bool get_run_callback_for_default() const { return run_callback_for_default_; }
|
||||
|
||||
/// Set the value of callback priority which controls when the callback function should be called relative to other
|
||||
/// parsing operations the default This is controlled automatically but could be manipulated by the user.
|
||||
Option *callback_priority(CallbackPriority value = CallbackPriority::Normal) {
|
||||
callback_priority_ = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Adds a shared validator
|
||||
Option *check(Validator_p validator);
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@ CLI11_INLINE Option *App::set_help_flag(std::string flag_name, const std::string
|
||||
// Empty name will simply remove the help flag
|
||||
if(!flag_name.empty()) {
|
||||
help_ptr_ = add_flag(flag_name, help_description);
|
||||
help_ptr_->configurable(false);
|
||||
help_ptr_->configurable(false)->callback_priority(CallbackPriority::First);
|
||||
}
|
||||
|
||||
return help_ptr_;
|
||||
@@ -301,7 +301,7 @@ CLI11_INLINE Option *App::set_help_all_flag(std::string help_name, const std::st
|
||||
// Empty name will simply remove the help all flag
|
||||
if(!help_name.empty()) {
|
||||
help_all_ptr_ = add_flag(help_name, help_description);
|
||||
help_all_ptr_->configurable(false);
|
||||
help_all_ptr_->configurable(false)->callback_priority(CallbackPriority::First);
|
||||
}
|
||||
|
||||
return help_all_ptr_;
|
||||
@@ -319,7 +319,7 @@ App::set_version_flag(std::string flag_name, const std::string &versionString, c
|
||||
if(!flag_name.empty()) {
|
||||
version_ptr_ = add_flag_callback(
|
||||
flag_name, [versionString]() { throw(CLI::CallForVersion(versionString, 0)); }, version_help);
|
||||
version_ptr_->configurable(false);
|
||||
version_ptr_->configurable(false)->callback_priority(CallbackPriority::First);
|
||||
}
|
||||
|
||||
return version_ptr_;
|
||||
@@ -336,7 +336,7 @@ App::set_version_flag(std::string flag_name, std::function<std::string()> vfunc,
|
||||
if(!flag_name.empty()) {
|
||||
version_ptr_ =
|
||||
add_flag_callback(flag_name, [vfunc]() { throw(CLI::CallForVersion(vfunc(), 0)); }, version_help);
|
||||
version_ptr_->configurable(false);
|
||||
version_ptr_->configurable(false)->callback_priority(CallbackPriority::First);
|
||||
}
|
||||
|
||||
return version_ptr_;
|
||||
@@ -1239,43 +1239,48 @@ CLI11_INLINE void App::_process_env() {
|
||||
}
|
||||
}
|
||||
|
||||
CLI11_INLINE void App::_process_callbacks() {
|
||||
CLI11_INLINE void App::_process_callbacks(CallbackPriority priority) {
|
||||
|
||||
for(App_p &sub : subcommands_) {
|
||||
// process the priority option_groups first
|
||||
if(sub->get_name().empty() && sub->parse_complete_callback_) {
|
||||
if(sub->count_all() > 0) {
|
||||
sub->_process_callbacks();
|
||||
sub->run_callback();
|
||||
sub->_process_callbacks(priority);
|
||||
if(priority == CallbackPriority::Normal) {
|
||||
// only run the subcommand callback at normal priority
|
||||
sub->run_callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(const Option_p &opt : options_) {
|
||||
if((*opt) && !opt->get_callback_run()) {
|
||||
opt->run_callback();
|
||||
if(opt->get_callback_priority() == priority) {
|
||||
if((*opt) && !opt->get_callback_run()) {
|
||||
opt->run_callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
for(App_p &sub : subcommands_) {
|
||||
if(!sub->parse_complete_callback_) {
|
||||
sub->_process_callbacks();
|
||||
sub->_process_callbacks(priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLI11_INLINE void App::_process_help_flags(bool trigger_help, bool trigger_all_help) const {
|
||||
CLI11_INLINE void App::_process_help_flags(CallbackPriority priority, bool trigger_help, bool trigger_all_help) const {
|
||||
const Option *help_ptr = get_help_ptr();
|
||||
const Option *help_all_ptr = get_help_all_ptr();
|
||||
|
||||
if(help_ptr != nullptr && help_ptr->count() > 0)
|
||||
if(help_ptr != nullptr && help_ptr->count() > 0 && help_ptr->get_callback_priority() == priority)
|
||||
trigger_help = true;
|
||||
if(help_all_ptr != nullptr && help_all_ptr->count() > 0)
|
||||
if(help_all_ptr != nullptr && help_all_ptr->count() > 0 && help_all_ptr->get_callback_priority() == priority)
|
||||
trigger_all_help = true;
|
||||
|
||||
// If there were parsed subcommands, call those. First subcommand wins if there are multiple ones.
|
||||
if(!parsed_subcommands_.empty()) {
|
||||
for(const App *sub : parsed_subcommands_)
|
||||
sub->_process_help_flags(trigger_help, trigger_all_help);
|
||||
sub->_process_help_flags(priority, trigger_help, trigger_all_help);
|
||||
|
||||
// Only the final subcommand should call for help. All help wins over help.
|
||||
} else if(trigger_all_help) {
|
||||
@@ -1416,7 +1421,10 @@ CLI11_INLINE void App::_process_requirements() {
|
||||
CLI11_INLINE void App::_process() {
|
||||
// help takes precedence over other potential errors and config and environment shouldn't be processed if help
|
||||
// throws
|
||||
_process_help_flags();
|
||||
_process_callbacks(CallbackPriority::FirstPreHelp);
|
||||
_process_help_flags(CallbackPriority::First);
|
||||
_process_callbacks(CallbackPriority::First);
|
||||
|
||||
std::exception_ptr config_exception;
|
||||
try {
|
||||
// the config file might generate a FileError but that should not be processed until later in the process
|
||||
@@ -1430,13 +1438,23 @@ CLI11_INLINE void App::_process() {
|
||||
}
|
||||
// callbacks and requirements processing can generate exceptions which should take priority
|
||||
// over the config file error if one exists.
|
||||
_process_callbacks(CallbackPriority::PreRequirementsCheckPreHelp);
|
||||
_process_help_flags(CallbackPriority::PreRequirementsCheck);
|
||||
_process_callbacks(CallbackPriority::PreRequirementsCheck);
|
||||
|
||||
_process_requirements();
|
||||
|
||||
_process_callbacks();
|
||||
_process_callbacks(CallbackPriority::NormalPreHelp);
|
||||
_process_help_flags(CallbackPriority::Normal);
|
||||
_process_callbacks(CallbackPriority::Normal);
|
||||
|
||||
if(config_exception) {
|
||||
std::rethrow_exception(config_exception);
|
||||
}
|
||||
|
||||
_process_callbacks(CallbackPriority::LastPreHelp);
|
||||
_process_help_flags(CallbackPriority::Last);
|
||||
_process_callbacks(CallbackPriority::Last);
|
||||
}
|
||||
|
||||
CLI11_INLINE void App::_process_extras() {
|
||||
@@ -1496,10 +1514,20 @@ CLI11_INLINE void App::_parse(std::vector<std::string> &args) {
|
||||
// Convert missing (pairs) to extras (string only) ready for processing in another app
|
||||
args = remaining_for_passthrough(false);
|
||||
} else if(parse_complete_callback_) {
|
||||
_process_callbacks(CallbackPriority::FirstPreHelp);
|
||||
_process_help_flags(CallbackPriority::First);
|
||||
_process_callbacks(CallbackPriority::First);
|
||||
_process_env();
|
||||
_process_callbacks();
|
||||
_process_help_flags();
|
||||
_process_callbacks(CallbackPriority::PreRequirementsCheckPreHelp);
|
||||
_process_help_flags(CallbackPriority::PreRequirementsCheck);
|
||||
_process_callbacks(CallbackPriority::PreRequirementsCheck);
|
||||
_process_requirements();
|
||||
_process_callbacks(CallbackPriority::NormalPreHelp);
|
||||
_process_help_flags(CallbackPriority::Normal);
|
||||
_process_callbacks(CallbackPriority::Normal);
|
||||
_process_callbacks(CallbackPriority::LastPreHelp);
|
||||
_process_help_flags(CallbackPriority::Last);
|
||||
_process_callbacks(CallbackPriority::Last);
|
||||
run_callback(false, true);
|
||||
}
|
||||
}
|
||||
@@ -1620,8 +1648,15 @@ CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t
|
||||
// check for section close
|
||||
if(item.name == "--") {
|
||||
if(configurable_ && parse_complete_callback_) {
|
||||
_process_callbacks();
|
||||
_process_callbacks(CallbackPriority::FirstPreHelp);
|
||||
_process_callbacks(CallbackPriority::First);
|
||||
_process_callbacks(CallbackPriority::PreRequirementsCheckPreHelp);
|
||||
_process_callbacks(CallbackPriority::PreRequirementsCheck);
|
||||
_process_requirements();
|
||||
_process_callbacks(CallbackPriority::NormalPreHelp);
|
||||
_process_callbacks(CallbackPriority::Normal);
|
||||
_process_callbacks(CallbackPriority::LastPreHelp);
|
||||
_process_callbacks(CallbackPriority::Last);
|
||||
run_callback();
|
||||
}
|
||||
return true;
|
||||
@@ -2081,10 +2116,20 @@ App::_parse_arg(std::vector<std::string> &args, detail::Classifier current_type,
|
||||
_trigger_pre_parse(args.size());
|
||||
// run the parse complete callback since the subcommand processing is now complete
|
||||
if(sub->parse_complete_callback_) {
|
||||
sub->_process_callbacks(CallbackPriority::FirstPreHelp);
|
||||
sub->_process_help_flags(CallbackPriority::First);
|
||||
sub->_process_callbacks(CallbackPriority::First);
|
||||
sub->_process_env();
|
||||
sub->_process_callbacks();
|
||||
sub->_process_help_flags();
|
||||
sub->_process_callbacks(CallbackPriority::PreRequirementsCheckPreHelp);
|
||||
sub->_process_help_flags(CallbackPriority::PreRequirementsCheck);
|
||||
sub->_process_callbacks(CallbackPriority::PreRequirementsCheck);
|
||||
sub->_process_requirements();
|
||||
sub->_process_callbacks(CallbackPriority::NormalPreHelp);
|
||||
sub->_process_help_flags(CallbackPriority::Normal);
|
||||
sub->_process_callbacks(CallbackPriority::Normal);
|
||||
sub->_process_callbacks(CallbackPriority::LastPreHelp);
|
||||
sub->_process_help_flags(CallbackPriority::Last);
|
||||
sub->_process_callbacks(CallbackPriority::Last);
|
||||
sub->run_callback(false, true);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -32,6 +32,7 @@ template <typename CRTP> template <typename T> void OptionBase<CRTP>::copy_to(T
|
||||
other->delimiter(delimiter_);
|
||||
other->always_capture_default(always_capture_default_);
|
||||
other->multi_option_policy(multi_option_policy_);
|
||||
other->callback_priority(callback_priority_);
|
||||
}
|
||||
|
||||
CLI11_INLINE Option *Option::expected(int value) {
|
||||
|
||||
Reference in New Issue
Block a user