in fboss/cli/fboss2/CLI11/App.hpp [2087:2210]
void _process_requirements() {
// check excludes
bool excluded{false};
std::string excluder;
for(auto &opt : exclude_options_) {
if(opt->count() > 0) {
excluded = true;
excluder = opt->get_name();
}
}
for(auto &subc : exclude_subcommands_) {
if(subc->count_all() > 0) {
excluded = true;
excluder = subc->get_display_name();
}
}
if(excluded) {
if(count_all() > 0) {
throw ExcludesError(get_display_name(), excluder);
}
// if we are excluded but didn't receive anything, just return
return;
}
// check excludes
bool missing_needed{false};
std::string missing_need;
for(auto &opt : need_options_) {
if(opt->count() == 0) {
missing_needed = true;
missing_need = opt->get_name();
}
}
for(auto &subc : need_subcommands_) {
if(subc->count_all() == 0) {
missing_needed = true;
missing_need = subc->get_display_name();
}
}
if(missing_needed) {
if(count_all() > 0) {
throw RequiresError(get_display_name(), missing_need);
}
// if we missing something but didn't have any options, just return
return;
}
std::size_t used_options = 0;
for(const Option_p &opt : options_) {
if(opt->count() != 0) {
++used_options;
}
// Required but empty
if(opt->get_required() && opt->count() == 0) {
throw RequiredError(opt->get_name());
}
// Requires
for(const Option *opt_req : opt->needs_)
if(opt->count() > 0 && opt_req->count() == 0)
throw RequiresError(opt->get_name(), opt_req->get_name());
// Excludes
for(const Option *opt_ex : opt->excludes_)
if(opt->count() > 0 && opt_ex->count() != 0)
throw ExcludesError(opt->get_name(), opt_ex->get_name());
}
// check for the required number of subcommands
if(require_subcommand_min_ > 0) {
auto selected_subcommands = get_subcommands();
if(require_subcommand_min_ > selected_subcommands.size())
throw RequiredError::Subcommand(require_subcommand_min_);
}
// Max error cannot occur, the extra subcommand will parse as an ExtrasError or a remaining item.
// run this loop to check how many unnamed subcommands were actually used since they are considered options
// from the perspective of an App
for(App_p &sub : subcommands_) {
if(sub->disabled_)
continue;
if(sub->name_.empty() && sub->count_all() > 0) {
++used_options;
}
}
if(require_option_min_ > used_options || (require_option_max_ > 0 && require_option_max_ < used_options)) {
auto option_list = detail::join(options_, [](const Option_p &ptr) { return ptr->get_name(false, true); });
if(option_list.compare(0, 10, "-h,--help,") == 0) {
option_list.erase(0, 10);
}
auto subc_list = get_subcommands([](App *app) { return ((app->get_name().empty()) && (!app->disabled_)); });
if(!subc_list.empty()) {
option_list += "," + detail::join(subc_list, [](const App *app) { return app->get_display_name(); });
}
throw RequiredError::Option(require_option_min_, require_option_max_, used_options, option_list);
}
// now process the requirements for subcommands if needed
for(App_p &sub : subcommands_) {
if(sub->disabled_)
continue;
if(sub->name_.empty() && sub->required_ == false) {
if(sub->count_all() == 0) {
if(require_option_min_ > 0 && require_option_min_ <= used_options) {
continue;
// if we have met the requirement and there is nothing in this option group skip checking
// requirements
}
if(require_option_max_ > 0 && used_options >= require_option_min_) {
continue;
// if we have met the requirement and there is nothing in this option group skip checking
// requirements
}
}
}
if(sub->count() > 0 || sub->name_.empty()) {
sub->_process_requirements();
}
if(sub->required_ && sub->count_all() == 0) {
throw(CLI::RequiredError(sub->get_display_name()));
}
}
}