File indexing completed on 2025-01-18 09:54:43
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009
0010 #include <CLI/Option.hpp>
0011
0012
0013 #include <algorithm>
0014 #include <string>
0015 #include <utility>
0016 #include <vector>
0017
0018
0019 namespace CLI {
0020
0021
0022 template <typename CRTP> template <typename T> void OptionBase<CRTP>::copy_to(T *other) const {
0023 other->group(group_);
0024 other->required(required_);
0025 other->ignore_case(ignore_case_);
0026 other->ignore_underscore(ignore_underscore_);
0027 other->configurable(configurable_);
0028 other->disable_flag_override(disable_flag_override_);
0029 other->delimiter(delimiter_);
0030 other->always_capture_default(always_capture_default_);
0031 other->multi_option_policy(multi_option_policy_);
0032 }
0033
0034 CLI11_INLINE Option *Option::expected(int value) {
0035 if(value < 0) {
0036 expected_min_ = -value;
0037 if(expected_max_ < expected_min_) {
0038 expected_max_ = expected_min_;
0039 }
0040 allow_extra_args_ = true;
0041 flag_like_ = false;
0042 } else if(value == detail::expected_max_vector_size) {
0043 expected_min_ = 1;
0044 expected_max_ = detail::expected_max_vector_size;
0045 allow_extra_args_ = true;
0046 flag_like_ = false;
0047 } else {
0048 expected_min_ = value;
0049 expected_max_ = value;
0050 flag_like_ = (expected_min_ == 0);
0051 }
0052 return this;
0053 }
0054
0055 CLI11_INLINE Option *Option::expected(int value_min, int value_max) {
0056 if(value_min < 0) {
0057 value_min = -value_min;
0058 }
0059
0060 if(value_max < 0) {
0061 value_max = detail::expected_max_vector_size;
0062 }
0063 if(value_max < value_min) {
0064 expected_min_ = value_max;
0065 expected_max_ = value_min;
0066 } else {
0067 expected_max_ = value_max;
0068 expected_min_ = value_min;
0069 }
0070
0071 return this;
0072 }
0073
0074 CLI11_INLINE Option *Option::check(Validator validator, const std::string &validator_name) {
0075 validator.non_modifying();
0076 validators_.push_back(std::move(validator));
0077 if(!validator_name.empty())
0078 validators_.back().name(validator_name);
0079 return this;
0080 }
0081
0082 CLI11_INLINE Option *Option::check(std::function<std::string(const std::string &)> Validator,
0083 std::string Validator_description,
0084 std::string Validator_name) {
0085 validators_.emplace_back(Validator, std::move(Validator_description), std::move(Validator_name));
0086 validators_.back().non_modifying();
0087 return this;
0088 }
0089
0090 CLI11_INLINE Option *Option::transform(Validator Validator, const std::string &Validator_name) {
0091 validators_.insert(validators_.begin(), std::move(Validator));
0092 if(!Validator_name.empty())
0093 validators_.front().name(Validator_name);
0094 return this;
0095 }
0096
0097 CLI11_INLINE Option *Option::transform(const std::function<std::string(std::string)> &func,
0098 std::string transform_description,
0099 std::string transform_name) {
0100 validators_.insert(validators_.begin(),
0101 Validator(
0102 [func](std::string &val) {
0103 val = func(val);
0104 return std::string{};
0105 },
0106 std::move(transform_description),
0107 std::move(transform_name)));
0108
0109 return this;
0110 }
0111
0112 CLI11_INLINE Option *Option::each(const std::function<void(std::string)> &func) {
0113 validators_.emplace_back(
0114 [func](std::string &inout) {
0115 func(inout);
0116 return std::string{};
0117 },
0118 std::string{});
0119 return this;
0120 }
0121
0122 CLI11_INLINE Validator *Option::get_validator(const std::string &Validator_name) {
0123 for(auto &Validator : validators_) {
0124 if(Validator_name == Validator.get_name()) {
0125 return &Validator;
0126 }
0127 }
0128 if((Validator_name.empty()) && (!validators_.empty())) {
0129 return &(validators_.front());
0130 }
0131 throw OptionNotFound(std::string{"Validator "} + Validator_name + " Not Found");
0132 }
0133
0134 CLI11_INLINE Validator *Option::get_validator(int index) {
0135
0136 if(index >= 0 && index < static_cast<int>(validators_.size())) {
0137 return &(validators_[static_cast<decltype(validators_)::size_type>(index)]);
0138 }
0139 throw OptionNotFound("Validator index is not valid");
0140 }
0141
0142 CLI11_INLINE bool Option::remove_needs(Option *opt) {
0143 auto iterator = std::find(std::begin(needs_), std::end(needs_), opt);
0144
0145 if(iterator == std::end(needs_)) {
0146 return false;
0147 }
0148 needs_.erase(iterator);
0149 return true;
0150 }
0151
0152 CLI11_INLINE Option *Option::excludes(Option *opt) {
0153 if(opt == this) {
0154 throw(IncorrectConstruction("and option cannot exclude itself"));
0155 }
0156 excludes_.insert(opt);
0157
0158
0159 opt->excludes_.insert(this);
0160
0161
0162
0163
0164 return this;
0165 }
0166
0167 CLI11_INLINE bool Option::remove_excludes(Option *opt) {
0168 auto iterator = std::find(std::begin(excludes_), std::end(excludes_), opt);
0169
0170 if(iterator == std::end(excludes_)) {
0171 return false;
0172 }
0173 excludes_.erase(iterator);
0174 return true;
0175 }
0176
0177 template <typename T> Option *Option::ignore_case(bool value) {
0178 if(!ignore_case_ && value) {
0179 ignore_case_ = value;
0180 auto *parent = static_cast<T *>(parent_);
0181 for(const Option_p &opt : parent->options_) {
0182 if(opt.get() == this) {
0183 continue;
0184 }
0185 const auto &omatch = opt->matching_name(*this);
0186 if(!omatch.empty()) {
0187 ignore_case_ = false;
0188 throw OptionAlreadyAdded("adding ignore case caused a name conflict with " + omatch);
0189 }
0190 }
0191 } else {
0192 ignore_case_ = value;
0193 }
0194 return this;
0195 }
0196
0197 template <typename T> Option *Option::ignore_underscore(bool value) {
0198
0199 if(!ignore_underscore_ && value) {
0200 ignore_underscore_ = value;
0201 auto *parent = static_cast<T *>(parent_);
0202 for(const Option_p &opt : parent->options_) {
0203 if(opt.get() == this) {
0204 continue;
0205 }
0206 const auto &omatch = opt->matching_name(*this);
0207 if(!omatch.empty()) {
0208 ignore_underscore_ = false;
0209 throw OptionAlreadyAdded("adding ignore underscore caused a name conflict with " + omatch);
0210 }
0211 }
0212 } else {
0213 ignore_underscore_ = value;
0214 }
0215 return this;
0216 }
0217
0218 CLI11_INLINE Option *Option::multi_option_policy(MultiOptionPolicy value) {
0219 if(value != multi_option_policy_) {
0220 if(multi_option_policy_ == MultiOptionPolicy::Throw && expected_max_ == detail::expected_max_vector_size &&
0221 expected_min_ > 1) {
0222
0223 expected_max_ = expected_min_;
0224 }
0225 multi_option_policy_ = value;
0226 current_option_state_ = option_state::parsing;
0227 }
0228 return this;
0229 }
0230
0231 CLI11_NODISCARD CLI11_INLINE std::string Option::get_name(bool positional, bool all_options) const {
0232 if(get_group().empty())
0233 return {};
0234
0235 if(all_options) {
0236
0237 std::vector<std::string> name_list;
0238
0239
0240 if((positional && (!pname_.empty())) || (snames_.empty() && lnames_.empty())) {
0241 name_list.push_back(pname_);
0242 }
0243 if((get_items_expected() == 0) && (!fnames_.empty())) {
0244 for(const std::string &sname : snames_) {
0245 name_list.push_back("-" + sname);
0246 if(check_fname(sname)) {
0247 name_list.back() += "{" + get_flag_value(sname, "") + "}";
0248 }
0249 }
0250
0251 for(const std::string &lname : lnames_) {
0252 name_list.push_back("--" + lname);
0253 if(check_fname(lname)) {
0254 name_list.back() += "{" + get_flag_value(lname, "") + "}";
0255 }
0256 }
0257 } else {
0258 for(const std::string &sname : snames_)
0259 name_list.push_back("-" + sname);
0260
0261 for(const std::string &lname : lnames_)
0262 name_list.push_back("--" + lname);
0263 }
0264
0265 return detail::join(name_list);
0266 }
0267
0268
0269 if(positional)
0270 return pname_;
0271
0272
0273 if(!lnames_.empty())
0274 return std::string(2, '-') + lnames_[0];
0275
0276
0277 if(!snames_.empty())
0278 return std::string(1, '-') + snames_[0];
0279
0280
0281 return pname_;
0282 }
0283
0284 CLI11_INLINE void Option::run_callback() {
0285 if(force_callback_ && results_.empty()) {
0286 add_result(default_str_);
0287 }
0288 if(current_option_state_ == option_state::parsing) {
0289 _validate_results(results_);
0290 current_option_state_ = option_state::validated;
0291 }
0292
0293 if(current_option_state_ < option_state::reduced) {
0294 _reduce_results(proc_results_, results_);
0295 current_option_state_ = option_state::reduced;
0296 }
0297 if(current_option_state_ >= option_state::reduced) {
0298 current_option_state_ = option_state::callback_run;
0299 if(!(callback_)) {
0300 return;
0301 }
0302 const results_t &send_results = proc_results_.empty() ? results_ : proc_results_;
0303 bool local_result = callback_(send_results);
0304
0305 if(!local_result)
0306 throw ConversionError(get_name(), results_);
0307 }
0308 }
0309
0310 CLI11_NODISCARD CLI11_INLINE const std::string &Option::matching_name(const Option &other) const {
0311 static const std::string estring;
0312 for(const std::string &sname : snames_)
0313 if(other.check_sname(sname))
0314 return sname;
0315 for(const std::string &lname : lnames_)
0316 if(other.check_lname(lname))
0317 return lname;
0318
0319 if(ignore_case_ ||
0320 ignore_underscore_) {
0321 for(const std::string &sname : other.snames_)
0322 if(check_sname(sname))
0323 return sname;
0324 for(const std::string &lname : other.lnames_)
0325 if(check_lname(lname))
0326 return lname;
0327 }
0328 return estring;
0329 }
0330
0331 CLI11_NODISCARD CLI11_INLINE bool Option::check_name(const std::string &name) const {
0332
0333 if(name.length() > 2 && name[0] == '-' && name[1] == '-')
0334 return check_lname(name.substr(2));
0335 if(name.length() > 1 && name.front() == '-')
0336 return check_sname(name.substr(1));
0337 if(!pname_.empty()) {
0338 std::string local_pname = pname_;
0339 std::string local_name = name;
0340 if(ignore_underscore_) {
0341 local_pname = detail::remove_underscore(local_pname);
0342 local_name = detail::remove_underscore(local_name);
0343 }
0344 if(ignore_case_) {
0345 local_pname = detail::to_lower(local_pname);
0346 local_name = detail::to_lower(local_name);
0347 }
0348 if(local_name == local_pname) {
0349 return true;
0350 }
0351 }
0352
0353 if(!envname_.empty()) {
0354
0355 return (name == envname_);
0356 }
0357 return false;
0358 }
0359
0360 CLI11_NODISCARD CLI11_INLINE std::string Option::get_flag_value(const std::string &name,
0361 std::string input_value) const {
0362 static const std::string trueString{"true"};
0363 static const std::string falseString{"false"};
0364 static const std::string emptyString{"{}"};
0365
0366 if(disable_flag_override_) {
0367 if(!((input_value.empty()) || (input_value == emptyString))) {
0368 auto default_ind = detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);
0369 if(default_ind >= 0) {
0370
0371 if(default_flag_values_[static_cast<std::size_t>(default_ind)].second != input_value) {
0372 throw(ArgumentMismatch::FlagOverride(name));
0373 }
0374 } else {
0375 if(input_value != trueString) {
0376 throw(ArgumentMismatch::FlagOverride(name));
0377 }
0378 }
0379 }
0380 }
0381 auto ind = detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);
0382 if((input_value.empty()) || (input_value == emptyString)) {
0383 if(flag_like_) {
0384 return (ind < 0) ? trueString : default_flag_values_[static_cast<std::size_t>(ind)].second;
0385 }
0386 return (ind < 0) ? default_str_ : default_flag_values_[static_cast<std::size_t>(ind)].second;
0387 }
0388 if(ind < 0) {
0389 return input_value;
0390 }
0391 if(default_flag_values_[static_cast<std::size_t>(ind)].second == falseString) {
0392 try {
0393 auto val = detail::to_flag_value(input_value);
0394 return (val == 1) ? falseString : (val == (-1) ? trueString : std::to_string(-val));
0395 } catch(const std::invalid_argument &) {
0396 return input_value;
0397 }
0398 } else {
0399 return input_value;
0400 }
0401 }
0402
0403 CLI11_INLINE Option *Option::add_result(std::string s) {
0404 _add_result(std::move(s), results_);
0405 current_option_state_ = option_state::parsing;
0406 return this;
0407 }
0408
0409 CLI11_INLINE Option *Option::add_result(std::string s, int &results_added) {
0410 results_added = _add_result(std::move(s), results_);
0411 current_option_state_ = option_state::parsing;
0412 return this;
0413 }
0414
0415 CLI11_INLINE Option *Option::add_result(std::vector<std::string> s) {
0416 current_option_state_ = option_state::parsing;
0417 for(auto &str : s) {
0418 _add_result(std::move(str), results_);
0419 }
0420 return this;
0421 }
0422
0423 CLI11_NODISCARD CLI11_INLINE results_t Option::reduced_results() const {
0424 results_t res = proc_results_.empty() ? results_ : proc_results_;
0425 if(current_option_state_ < option_state::reduced) {
0426 if(current_option_state_ == option_state::parsing) {
0427 res = results_;
0428 _validate_results(res);
0429 }
0430 if(!res.empty()) {
0431 results_t extra;
0432 _reduce_results(extra, res);
0433 if(!extra.empty()) {
0434 res = std::move(extra);
0435 }
0436 }
0437 }
0438 return res;
0439 }
0440
0441 CLI11_INLINE Option *Option::type_size(int option_type_size) {
0442 if(option_type_size < 0) {
0443
0444 type_size_max_ = -option_type_size;
0445 type_size_min_ = -option_type_size;
0446 expected_max_ = detail::expected_max_vector_size;
0447 } else {
0448 type_size_max_ = option_type_size;
0449 if(type_size_max_ < detail::expected_max_vector_size) {
0450 type_size_min_ = option_type_size;
0451 } else {
0452 inject_separator_ = true;
0453 }
0454 if(type_size_max_ == 0)
0455 required_ = false;
0456 }
0457 return this;
0458 }
0459
0460 CLI11_INLINE Option *Option::type_size(int option_type_size_min, int option_type_size_max) {
0461 if(option_type_size_min < 0 || option_type_size_max < 0) {
0462
0463 expected_max_ = detail::expected_max_vector_size;
0464 option_type_size_min = (std::abs)(option_type_size_min);
0465 option_type_size_max = (std::abs)(option_type_size_max);
0466 }
0467
0468 if(option_type_size_min > option_type_size_max) {
0469 type_size_max_ = option_type_size_min;
0470 type_size_min_ = option_type_size_max;
0471 } else {
0472 type_size_min_ = option_type_size_min;
0473 type_size_max_ = option_type_size_max;
0474 }
0475 if(type_size_max_ == 0) {
0476 required_ = false;
0477 }
0478 if(type_size_max_ >= detail::expected_max_vector_size) {
0479 inject_separator_ = true;
0480 }
0481 return this;
0482 }
0483
0484 CLI11_NODISCARD CLI11_INLINE std::string Option::get_type_name() const {
0485 std::string full_type_name = type_name_();
0486 if(!validators_.empty()) {
0487 for(const auto &Validator : validators_) {
0488 std::string vtype = Validator.get_description();
0489 if(!vtype.empty()) {
0490 full_type_name += ":" + vtype;
0491 }
0492 }
0493 }
0494 return full_type_name;
0495 }
0496
0497 CLI11_INLINE void Option::_validate_results(results_t &res) const {
0498
0499 if(!validators_.empty()) {
0500 if(type_size_max_ > 1) {
0501 int index = 0;
0502 if(get_items_expected_max() < static_cast<int>(res.size()) &&
0503 multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {
0504
0505 index = get_items_expected_max() - static_cast<int>(res.size());
0506 }
0507
0508 for(std::string &result : res) {
0509 if(detail::is_separator(result) && type_size_max_ != type_size_min_ && index >= 0) {
0510 index = 0;
0511 continue;
0512 }
0513 auto err_msg = _validate(result, (index >= 0) ? (index % type_size_max_) : index);
0514 if(!err_msg.empty())
0515 throw ValidationError(get_name(), err_msg);
0516 ++index;
0517 }
0518 } else {
0519 int index = 0;
0520 if(expected_max_ < static_cast<int>(res.size()) &&
0521 multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {
0522
0523 index = expected_max_ - static_cast<int>(res.size());
0524 }
0525 for(std::string &result : res) {
0526 auto err_msg = _validate(result, index);
0527 ++index;
0528 if(!err_msg.empty())
0529 throw ValidationError(get_name(), err_msg);
0530 }
0531 }
0532 }
0533 }
0534
0535 CLI11_INLINE void Option::_reduce_results(results_t &out, const results_t &original) const {
0536
0537
0538
0539
0540 out.clear();
0541
0542 switch(multi_option_policy_) {
0543 case MultiOptionPolicy::TakeAll:
0544 break;
0545 case MultiOptionPolicy::TakeLast: {
0546
0547 std::size_t trim_size = std::min<std::size_t>(
0548 static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
0549 if(original.size() != trim_size) {
0550 out.assign(original.end() - static_cast<results_t::difference_type>(trim_size), original.end());
0551 }
0552 } break;
0553 case MultiOptionPolicy::TakeFirst: {
0554 std::size_t trim_size = std::min<std::size_t>(
0555 static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
0556 if(original.size() != trim_size) {
0557 out.assign(original.begin(), original.begin() + static_cast<results_t::difference_type>(trim_size));
0558 }
0559 } break;
0560 case MultiOptionPolicy::Join:
0561 if(results_.size() > 1) {
0562 out.push_back(detail::join(original, std::string(1, (delimiter_ == '\0') ? '\n' : delimiter_)));
0563 }
0564 break;
0565 case MultiOptionPolicy::Sum:
0566 out.push_back(detail::sum_string_vector(original));
0567 break;
0568 case MultiOptionPolicy::Throw:
0569 default: {
0570 auto num_min = static_cast<std::size_t>(get_items_expected_min());
0571 auto num_max = static_cast<std::size_t>(get_items_expected_max());
0572 if(num_min == 0) {
0573 num_min = 1;
0574 }
0575 if(num_max == 0) {
0576 num_max = 1;
0577 }
0578 if(original.size() < num_min) {
0579 throw ArgumentMismatch::AtLeast(get_name(), static_cast<int>(num_min), original.size());
0580 }
0581 if(original.size() > num_max) {
0582 throw ArgumentMismatch::AtMost(get_name(), static_cast<int>(num_max), original.size());
0583 }
0584 break;
0585 }
0586 }
0587
0588
0589 if(out.empty()) {
0590 if(original.size() == 1 && original[0] == "{}" && get_items_expected_min() > 0) {
0591 out.push_back("{}");
0592 out.push_back("%%");
0593 }
0594 } else if(out.size() == 1 && out[0] == "{}" && get_items_expected_min() > 0) {
0595 out.push_back("%%");
0596 }
0597 }
0598
0599 CLI11_INLINE std::string Option::_validate(std::string &result, int index) const {
0600 std::string err_msg;
0601 if(result.empty() && expected_min_ == 0) {
0602
0603 return err_msg;
0604 }
0605 for(const auto &vali : validators_) {
0606 auto v = vali.get_application_index();
0607 if(v == -1 || v == index) {
0608 try {
0609 err_msg = vali(result);
0610 } catch(const ValidationError &err) {
0611 err_msg = err.what();
0612 }
0613 if(!err_msg.empty())
0614 break;
0615 }
0616 }
0617
0618 return err_msg;
0619 }
0620
0621 CLI11_INLINE int Option::_add_result(std::string &&result, std::vector<std::string> &res) const {
0622 int result_count = 0;
0623 if(allow_extra_args_ && !result.empty() && result.front() == '[' &&
0624 result.back() == ']') {
0625 result.pop_back();
0626
0627 for(auto &var : CLI::detail::split(result.substr(1), ',')) {
0628 if(!var.empty()) {
0629 result_count += _add_result(std::move(var), res);
0630 }
0631 }
0632 return result_count;
0633 }
0634 if(delimiter_ == '\0') {
0635 res.push_back(std::move(result));
0636 ++result_count;
0637 } else {
0638 if((result.find_first_of(delimiter_) != std::string::npos)) {
0639 for(const auto &var : CLI::detail::split(result, delimiter_)) {
0640 if(!var.empty()) {
0641 res.push_back(var);
0642 ++result_count;
0643 }
0644 }
0645 } else {
0646 res.push_back(std::move(result));
0647 ++result_count;
0648 }
0649 }
0650 return result_count;
0651 }
0652
0653 }