File indexing completed on 2024-09-28 07:02:18
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009
0010 #include <algorithm>
0011 #include <functional>
0012 #include <memory>
0013 #include <set>
0014 #include <string>
0015 #include <tuple>
0016 #include <utility>
0017 #include <vector>
0018
0019
0020 #include "Error.hpp"
0021 #include "Macros.hpp"
0022 #include "Split.hpp"
0023 #include "StringTools.hpp"
0024 #include "Validators.hpp"
0025
0026 namespace CLI {
0027
0028
0029 using results_t = std::vector<std::string>;
0030
0031 using callback_t = std::function<bool(const results_t &)>;
0032
0033 class Option;
0034 class App;
0035
0036 using Option_p = std::unique_ptr<Option>;
0037
0038 enum class MultiOptionPolicy : char {
0039 Throw,
0040 TakeLast,
0041 TakeFirst,
0042 Join,
0043 TakeAll
0044 };
0045
0046
0047
0048 template <typename CRTP> class OptionBase {
0049 friend App;
0050
0051 protected:
0052
0053 std::string group_ = std::string("Options");
0054
0055
0056 bool required_{false};
0057
0058
0059 bool ignore_case_{false};
0060
0061
0062 bool ignore_underscore_{false};
0063
0064
0065 bool configurable_{true};
0066
0067
0068 bool disable_flag_override_{false};
0069
0070
0071 char delimiter_{'\0'};
0072
0073
0074 bool always_capture_default_{false};
0075
0076
0077 MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};
0078
0079
0080 template <typename T> void copy_to(T *other) const {
0081 other->group(group_);
0082 other->required(required_);
0083 other->ignore_case(ignore_case_);
0084 other->ignore_underscore(ignore_underscore_);
0085 other->configurable(configurable_);
0086 other->disable_flag_override(disable_flag_override_);
0087 other->delimiter(delimiter_);
0088 other->always_capture_default(always_capture_default_);
0089 other->multi_option_policy(multi_option_policy_);
0090 }
0091
0092 public:
0093
0094
0095
0096 CRTP *group(const std::string &name) {
0097 group_ = name;
0098 return static_cast<CRTP *>(this);
0099 }
0100
0101
0102 CRTP *required(bool value = true) {
0103 required_ = value;
0104 return static_cast<CRTP *>(this);
0105 }
0106
0107
0108 CRTP *mandatory(bool value = true) { return required(value); }
0109
0110 CRTP *always_capture_default(bool value = true) {
0111 always_capture_default_ = value;
0112 return static_cast<CRTP *>(this);
0113 }
0114
0115
0116
0117
0118 const std::string &get_group() const { return group_; }
0119
0120
0121 bool get_required() const { return required_; }
0122
0123
0124 bool get_ignore_case() const { return ignore_case_; }
0125
0126
0127 bool get_ignore_underscore() const { return ignore_underscore_; }
0128
0129
0130 bool get_configurable() const { return configurable_; }
0131
0132
0133 bool get_disable_flag_override() const { return disable_flag_override_; }
0134
0135
0136 char get_delimiter() const { return delimiter_; }
0137
0138
0139 bool get_always_capture_default() const { return always_capture_default_; }
0140
0141
0142 MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; }
0143
0144
0145
0146
0147 CRTP *take_last() {
0148 auto self = static_cast<CRTP *>(this);
0149 self->multi_option_policy(MultiOptionPolicy::TakeLast);
0150 return self;
0151 }
0152
0153
0154 CRTP *take_first() {
0155 auto self = static_cast<CRTP *>(this);
0156 self->multi_option_policy(MultiOptionPolicy::TakeFirst);
0157 return self;
0158 }
0159
0160
0161 CRTP *take_all() {
0162 auto self = static_cast<CRTP *>(this);
0163 self->multi_option_policy(MultiOptionPolicy::TakeAll);
0164 return self;
0165 }
0166
0167
0168 CRTP *join() {
0169 auto self = static_cast<CRTP *>(this);
0170 self->multi_option_policy(MultiOptionPolicy::Join);
0171 return self;
0172 }
0173
0174
0175 CRTP *join(char delim) {
0176 auto self = static_cast<CRTP *>(this);
0177 self->delimiter_ = delim;
0178 self->multi_option_policy(MultiOptionPolicy::Join);
0179 return self;
0180 }
0181
0182
0183 CRTP *configurable(bool value = true) {
0184 configurable_ = value;
0185 return static_cast<CRTP *>(this);
0186 }
0187
0188
0189 CRTP *delimiter(char value = '\0') {
0190 delimiter_ = value;
0191 return static_cast<CRTP *>(this);
0192 }
0193 };
0194
0195
0196
0197 class OptionDefaults : public OptionBase<OptionDefaults> {
0198 public:
0199 OptionDefaults() = default;
0200
0201
0202
0203
0204 OptionDefaults *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
0205 multi_option_policy_ = value;
0206 return this;
0207 }
0208
0209
0210 OptionDefaults *ignore_case(bool value = true) {
0211 ignore_case_ = value;
0212 return this;
0213 }
0214
0215
0216 OptionDefaults *ignore_underscore(bool value = true) {
0217 ignore_underscore_ = value;
0218 return this;
0219 }
0220
0221
0222 OptionDefaults *disable_flag_override(bool value = true) {
0223 disable_flag_override_ = value;
0224 return this;
0225 }
0226
0227
0228 OptionDefaults *delimiter(char value = '\0') {
0229 delimiter_ = value;
0230 return this;
0231 }
0232 };
0233
0234 class Option : public OptionBase<Option> {
0235 friend App;
0236
0237 protected:
0238
0239
0240
0241
0242 std::vector<std::string> snames_{};
0243
0244
0245 std::vector<std::string> lnames_{};
0246
0247
0248
0249 std::vector<std::pair<std::string, std::string>> default_flag_values_{};
0250
0251
0252 std::vector<std::string> fnames_{};
0253
0254
0255 std::string pname_{};
0256
0257
0258 std::string envname_{};
0259
0260
0261
0262
0263
0264
0265 std::string description_{};
0266
0267
0268 std::string default_str_{};
0269
0270
0271 std::string option_text_{};
0272
0273
0274
0275
0276 std::function<std::string()> type_name_{[]() { return std::string(); }};
0277
0278
0279 std::function<std::string()> default_function_{};
0280
0281
0282
0283
0284
0285
0286
0287 int type_size_max_{1};
0288
0289 int type_size_min_{1};
0290
0291
0292 int expected_min_{1};
0293
0294 int expected_max_{1};
0295
0296
0297 std::vector<Validator> validators_{};
0298
0299
0300 std::set<Option *> needs_{};
0301
0302
0303 std::set<Option *> excludes_{};
0304
0305
0306
0307
0308
0309
0310 App *parent_{nullptr};
0311
0312
0313 callback_t callback_{};
0314
0315
0316
0317
0318
0319
0320 results_t results_{};
0321
0322 results_t proc_results_{};
0323
0324 enum class option_state : char {
0325 parsing = 0,
0326 validated = 2,
0327 reduced = 4,
0328 callback_run = 6,
0329 };
0330
0331 option_state current_option_state_{option_state::parsing};
0332
0333 bool allow_extra_args_{false};
0334
0335 bool flag_like_{false};
0336
0337 bool run_callback_for_default_{false};
0338
0339 bool inject_separator_{false};
0340
0341
0342
0343 Option(std::string option_name, std::string option_description, callback_t callback, App *parent)
0344 : description_(std::move(option_description)), parent_(parent), callback_(std::move(callback)) {
0345 std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(option_name));
0346 }
0347
0348 public:
0349
0350
0351
0352 Option(const Option &) = delete;
0353 Option &operator=(const Option &) = delete;
0354
0355
0356 std::size_t count() const { return results_.size(); }
0357
0358
0359 bool empty() const { return results_.empty(); }
0360
0361
0362 explicit operator bool() const { return !empty(); }
0363
0364
0365 void clear() {
0366 results_.clear();
0367 current_option_state_ = option_state::parsing;
0368 }
0369
0370
0371
0372
0373
0374
0375 Option *expected(int value) {
0376 if(value < 0) {
0377 expected_min_ = -value;
0378 if(expected_max_ < expected_min_) {
0379 expected_max_ = expected_min_;
0380 }
0381 allow_extra_args_ = true;
0382 flag_like_ = false;
0383 } else if(value == detail::expected_max_vector_size) {
0384 expected_min_ = 1;
0385 expected_max_ = detail::expected_max_vector_size;
0386 allow_extra_args_ = true;
0387 flag_like_ = false;
0388 } else {
0389 expected_min_ = value;
0390 expected_max_ = value;
0391 flag_like_ = (expected_min_ == 0);
0392 }
0393 return this;
0394 }
0395
0396
0397 Option *expected(int value_min, int value_max) {
0398 if(value_min < 0) {
0399 value_min = -value_min;
0400 }
0401
0402 if(value_max < 0) {
0403 value_max = detail::expected_max_vector_size;
0404 }
0405 if(value_max < value_min) {
0406 expected_min_ = value_max;
0407 expected_max_ = value_min;
0408 } else {
0409 expected_max_ = value_max;
0410 expected_min_ = value_min;
0411 }
0412
0413 return this;
0414 }
0415
0416
0417 Option *allow_extra_args(bool value = true) {
0418 allow_extra_args_ = value;
0419 return this;
0420 }
0421
0422 bool get_allow_extra_args() const { return allow_extra_args_; }
0423
0424
0425
0426 Option *run_callback_for_default(bool value = true) {
0427 run_callback_for_default_ = value;
0428 return this;
0429 }
0430
0431 bool get_run_callback_for_default() const { return run_callback_for_default_; }
0432
0433
0434 Option *check(Validator validator, const std::string &validator_name = "") {
0435 validator.non_modifying();
0436 validators_.push_back(std::move(validator));
0437 if(!validator_name.empty())
0438 validators_.back().name(validator_name);
0439 return this;
0440 }
0441
0442
0443 Option *check(std::function<std::string(const std::string &)> Validator,
0444 std::string Validator_description = "",
0445 std::string Validator_name = "") {
0446 validators_.emplace_back(Validator, std::move(Validator_description), std::move(Validator_name));
0447 validators_.back().non_modifying();
0448 return this;
0449 }
0450
0451
0452 Option *transform(Validator Validator, const std::string &Validator_name = "") {
0453 validators_.insert(validators_.begin(), std::move(Validator));
0454 if(!Validator_name.empty())
0455 validators_.front().name(Validator_name);
0456 return this;
0457 }
0458
0459
0460 Option *transform(const std::function<std::string(std::string)> &func,
0461 std::string transform_description = "",
0462 std::string transform_name = "") {
0463 validators_.insert(validators_.begin(),
0464 Validator(
0465 [func](std::string &val) {
0466 val = func(val);
0467 return std::string{};
0468 },
0469 std::move(transform_description),
0470 std::move(transform_name)));
0471
0472 return this;
0473 }
0474
0475
0476 Option *each(const std::function<void(std::string)> &func) {
0477 validators_.emplace_back(
0478 [func](std::string &inout) {
0479 func(inout);
0480 return std::string{};
0481 },
0482 std::string{});
0483 return this;
0484 }
0485
0486 Validator *get_validator(const std::string &Validator_name = "") {
0487 for(auto &Validator : validators_) {
0488 if(Validator_name == Validator.get_name()) {
0489 return &Validator;
0490 }
0491 }
0492 if((Validator_name.empty()) && (!validators_.empty())) {
0493 return &(validators_.front());
0494 }
0495 throw OptionNotFound(std::string{"Validator "} + Validator_name + " Not Found");
0496 }
0497
0498
0499 Validator *get_validator(int index) {
0500
0501 if(index >= 0 && index < static_cast<int>(validators_.size())) {
0502 return &(validators_[static_cast<decltype(validators_)::size_type>(index)]);
0503 }
0504 throw OptionNotFound("Validator index is not valid");
0505 }
0506
0507
0508 Option *needs(Option *opt) {
0509 if(opt != this) {
0510 needs_.insert(opt);
0511 }
0512 return this;
0513 }
0514
0515
0516 template <typename T = App> Option *needs(std::string opt_name) {
0517 auto opt = static_cast<T *>(parent_)->get_option_no_throw(opt_name);
0518 if(opt == nullptr) {
0519 throw IncorrectConstruction::MissingOption(opt_name);
0520 }
0521 return needs(opt);
0522 }
0523
0524
0525 template <typename A, typename B, typename... ARG> Option *needs(A opt, B opt1, ARG... args) {
0526 needs(opt);
0527 return needs(opt1, args...);
0528 }
0529
0530
0531 bool remove_needs(Option *opt) {
0532 auto iterator = std::find(std::begin(needs_), std::end(needs_), opt);
0533
0534 if(iterator == std::end(needs_)) {
0535 return false;
0536 }
0537 needs_.erase(iterator);
0538 return true;
0539 }
0540
0541
0542 Option *excludes(Option *opt) {
0543 if(opt == this) {
0544 throw(IncorrectConstruction("and option cannot exclude itself"));
0545 }
0546 excludes_.insert(opt);
0547
0548
0549 opt->excludes_.insert(this);
0550
0551
0552
0553
0554 return this;
0555 }
0556
0557
0558 template <typename T = App> Option *excludes(std::string opt_name) {
0559 auto opt = static_cast<T *>(parent_)->get_option_no_throw(opt_name);
0560 if(opt == nullptr) {
0561 throw IncorrectConstruction::MissingOption(opt_name);
0562 }
0563 return excludes(opt);
0564 }
0565
0566
0567 template <typename A, typename B, typename... ARG> Option *excludes(A opt, B opt1, ARG... args) {
0568 excludes(opt);
0569 return excludes(opt1, args...);
0570 }
0571
0572
0573 bool remove_excludes(Option *opt) {
0574 auto iterator = std::find(std::begin(excludes_), std::end(excludes_), opt);
0575
0576 if(iterator == std::end(excludes_)) {
0577 return false;
0578 }
0579 excludes_.erase(iterator);
0580 return true;
0581 }
0582
0583
0584 Option *envname(std::string name) {
0585 envname_ = std::move(name);
0586 return this;
0587 }
0588
0589
0590
0591
0592
0593 template <typename T = App> Option *ignore_case(bool value = true) {
0594 if(!ignore_case_ && value) {
0595 ignore_case_ = value;
0596 auto *parent = static_cast<T *>(parent_);
0597 for(const Option_p &opt : parent->options_) {
0598 if(opt.get() == this) {
0599 continue;
0600 }
0601 auto &omatch = opt->matching_name(*this);
0602 if(!omatch.empty()) {
0603 ignore_case_ = false;
0604 throw OptionAlreadyAdded("adding ignore case caused a name conflict with " + omatch);
0605 }
0606 }
0607 } else {
0608 ignore_case_ = value;
0609 }
0610 return this;
0611 }
0612
0613
0614
0615
0616
0617 template <typename T = App> Option *ignore_underscore(bool value = true) {
0618
0619 if(!ignore_underscore_ && value) {
0620 ignore_underscore_ = value;
0621 auto *parent = static_cast<T *>(parent_);
0622 for(const Option_p &opt : parent->options_) {
0623 if(opt.get() == this) {
0624 continue;
0625 }
0626 auto &omatch = opt->matching_name(*this);
0627 if(!omatch.empty()) {
0628 ignore_underscore_ = false;
0629 throw OptionAlreadyAdded("adding ignore underscore caused a name conflict with " + omatch);
0630 }
0631 }
0632 } else {
0633 ignore_underscore_ = value;
0634 }
0635 return this;
0636 }
0637
0638
0639 Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
0640 if(value != multi_option_policy_) {
0641 if(multi_option_policy_ == MultiOptionPolicy::Throw && expected_max_ == detail::expected_max_vector_size &&
0642 expected_min_ > 1) {
0643
0644 expected_max_ = expected_min_;
0645 }
0646 multi_option_policy_ = value;
0647 current_option_state_ = option_state::parsing;
0648 }
0649 return this;
0650 }
0651
0652
0653 Option *disable_flag_override(bool value = true) {
0654 disable_flag_override_ = value;
0655 return this;
0656 }
0657
0658
0659
0660
0661
0662 int get_type_size() const { return type_size_min_; }
0663
0664
0665 int get_type_size_min() const { return type_size_min_; }
0666
0667 int get_type_size_max() const { return type_size_max_; }
0668
0669
0670 int get_inject_separator() const { return inject_separator_; }
0671
0672
0673 std::string get_envname() const { return envname_; }
0674
0675
0676 std::set<Option *> get_needs() const { return needs_; }
0677
0678
0679 std::set<Option *> get_excludes() const { return excludes_; }
0680
0681
0682 std::string get_default_str() const { return default_str_; }
0683
0684
0685 callback_t get_callback() const { return callback_; }
0686
0687
0688 const std::vector<std::string> &get_lnames() const { return lnames_; }
0689
0690
0691 const std::vector<std::string> &get_snames() const { return snames_; }
0692
0693
0694 const std::vector<std::string> &get_fnames() const { return fnames_; }
0695
0696 const std::string &get_single_name() const {
0697 if(!lnames_.empty()) {
0698 return lnames_[0];
0699 }
0700 if(!pname_.empty()) {
0701 return pname_;
0702 }
0703 if(!snames_.empty()) {
0704 return snames_[0];
0705 }
0706 return envname_;
0707 }
0708
0709 int get_expected() const { return expected_min_; }
0710
0711
0712 int get_expected_min() const { return expected_min_; }
0713
0714 int get_expected_max() const { return expected_max_; }
0715
0716
0717 int get_items_expected_min() const { return type_size_min_ * expected_min_; }
0718
0719
0720 int get_items_expected_max() const {
0721 int t = type_size_max_;
0722 return detail::checked_multiply(t, expected_max_) ? t : detail::expected_max_vector_size;
0723 }
0724
0725 int get_items_expected() const { return get_items_expected_min(); }
0726
0727
0728 bool get_positional() const { return pname_.length() > 0; }
0729
0730
0731 bool nonpositional() const { return (snames_.size() + lnames_.size()) > 0; }
0732
0733
0734 bool has_description() const { return description_.length() > 0; }
0735
0736
0737 const std::string &get_description() const { return description_; }
0738
0739
0740 Option *description(std::string option_description) {
0741 description_ = std::move(option_description);
0742 return this;
0743 }
0744
0745 Option *option_text(std::string text) {
0746 option_text_ = std::move(text);
0747 return this;
0748 }
0749
0750 const std::string &get_option_text() const { return option_text_; }
0751
0752
0753
0754
0755
0756
0757
0758
0759
0760 std::string get_name(bool positional = false,
0761 bool all_options = false
0762 ) const {
0763 if(get_group().empty())
0764 return {};
0765
0766 if(all_options) {
0767
0768 std::vector<std::string> name_list;
0769
0770
0771 if((positional && (!pname_.empty())) || (snames_.empty() && lnames_.empty())) {
0772 name_list.push_back(pname_);
0773 }
0774 if((get_items_expected() == 0) && (!fnames_.empty())) {
0775 for(const std::string &sname : snames_) {
0776 name_list.push_back("-" + sname);
0777 if(check_fname(sname)) {
0778 name_list.back() += "{" + get_flag_value(sname, "") + "}";
0779 }
0780 }
0781
0782 for(const std::string &lname : lnames_) {
0783 name_list.push_back("--" + lname);
0784 if(check_fname(lname)) {
0785 name_list.back() += "{" + get_flag_value(lname, "") + "}";
0786 }
0787 }
0788 } else {
0789 for(const std::string &sname : snames_)
0790 name_list.push_back("-" + sname);
0791
0792 for(const std::string &lname : lnames_)
0793 name_list.push_back("--" + lname);
0794 }
0795
0796 return detail::join(name_list);
0797 }
0798
0799
0800 if(positional)
0801 return pname_;
0802
0803
0804 if(!lnames_.empty())
0805 return std::string(2, '-') + lnames_[0];
0806
0807
0808 if(!snames_.empty())
0809 return std::string(1, '-') + snames_[0];
0810
0811
0812 return pname_;
0813 }
0814
0815
0816
0817
0818
0819
0820 void run_callback() {
0821
0822 if(current_option_state_ == option_state::parsing) {
0823 _validate_results(results_);
0824 current_option_state_ = option_state::validated;
0825 }
0826
0827 if(current_option_state_ < option_state::reduced) {
0828 _reduce_results(proc_results_, results_);
0829 current_option_state_ = option_state::reduced;
0830 }
0831 if(current_option_state_ >= option_state::reduced) {
0832 current_option_state_ = option_state::callback_run;
0833 if(!(callback_)) {
0834 return;
0835 }
0836 const results_t &send_results = proc_results_.empty() ? results_ : proc_results_;
0837 bool local_result = callback_(send_results);
0838
0839 if(!local_result)
0840 throw ConversionError(get_name(), results_);
0841 }
0842 }
0843
0844
0845 const std::string &matching_name(const Option &other) const {
0846 static const std::string estring;
0847 for(const std::string &sname : snames_)
0848 if(other.check_sname(sname))
0849 return sname;
0850 for(const std::string &lname : lnames_)
0851 if(other.check_lname(lname))
0852 return lname;
0853
0854 if(ignore_case_ ||
0855 ignore_underscore_) {
0856 for(const std::string &sname : other.snames_)
0857 if(check_sname(sname))
0858 return sname;
0859 for(const std::string &lname : other.lnames_)
0860 if(check_lname(lname))
0861 return lname;
0862 }
0863 return estring;
0864 }
0865
0866 bool operator==(const Option &other) const { return !matching_name(other).empty(); }
0867
0868
0869 bool check_name(const std::string &name) const {
0870
0871 if(name.length() > 2 && name[0] == '-' && name[1] == '-')
0872 return check_lname(name.substr(2));
0873 if(name.length() > 1 && name.front() == '-')
0874 return check_sname(name.substr(1));
0875 if(!pname_.empty()) {
0876 std::string local_pname = pname_;
0877 std::string local_name = name;
0878 if(ignore_underscore_) {
0879 local_pname = detail::remove_underscore(local_pname);
0880 local_name = detail::remove_underscore(local_name);
0881 }
0882 if(ignore_case_) {
0883 local_pname = detail::to_lower(local_pname);
0884 local_name = detail::to_lower(local_name);
0885 }
0886 if(local_name == local_pname) {
0887 return true;
0888 }
0889 }
0890
0891 if(!envname_.empty()) {
0892
0893 return (name == envname_);
0894 }
0895 return false;
0896 }
0897
0898
0899 bool check_sname(std::string name) const {
0900 return (detail::find_member(std::move(name), snames_, ignore_case_) >= 0);
0901 }
0902
0903
0904 bool check_lname(std::string name) const {
0905 return (detail::find_member(std::move(name), lnames_, ignore_case_, ignore_underscore_) >= 0);
0906 }
0907
0908
0909 bool check_fname(std::string name) const {
0910 if(fnames_.empty()) {
0911 return false;
0912 }
0913 return (detail::find_member(std::move(name), fnames_, ignore_case_, ignore_underscore_) >= 0);
0914 }
0915
0916
0917
0918 std::string get_flag_value(const std::string &name, std::string input_value) const {
0919 static const std::string trueString{"true"};
0920 static const std::string falseString{"false"};
0921 static const std::string emptyString{"{}"};
0922
0923 if(disable_flag_override_) {
0924 if(!((input_value.empty()) || (input_value == emptyString))) {
0925 auto default_ind = detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);
0926 if(default_ind >= 0) {
0927
0928 if(default_flag_values_[static_cast<std::size_t>(default_ind)].second != input_value) {
0929 throw(ArgumentMismatch::FlagOverride(name));
0930 }
0931 } else {
0932 if(input_value != trueString) {
0933 throw(ArgumentMismatch::FlagOverride(name));
0934 }
0935 }
0936 }
0937 }
0938 auto ind = detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);
0939 if((input_value.empty()) || (input_value == emptyString)) {
0940 if(flag_like_) {
0941 return (ind < 0) ? trueString : default_flag_values_[static_cast<std::size_t>(ind)].second;
0942 } else {
0943 return (ind < 0) ? default_str_ : default_flag_values_[static_cast<std::size_t>(ind)].second;
0944 }
0945 }
0946 if(ind < 0) {
0947 return input_value;
0948 }
0949 if(default_flag_values_[static_cast<std::size_t>(ind)].second == falseString) {
0950 try {
0951 auto val = detail::to_flag_value(input_value);
0952 return (val == 1) ? falseString : (val == (-1) ? trueString : std::to_string(-val));
0953 } catch(const std::invalid_argument &) {
0954 return input_value;
0955 }
0956 } else {
0957 return input_value;
0958 }
0959 }
0960
0961
0962 Option *add_result(std::string s) {
0963 _add_result(std::move(s), results_);
0964 current_option_state_ = option_state::parsing;
0965 return this;
0966 }
0967
0968
0969 Option *add_result(std::string s, int &results_added) {
0970 results_added = _add_result(std::move(s), results_);
0971 current_option_state_ = option_state::parsing;
0972 return this;
0973 }
0974
0975
0976 Option *add_result(std::vector<std::string> s) {
0977 for(auto &str : s) {
0978 _add_result(std::move(str), results_);
0979 }
0980 current_option_state_ = option_state::parsing;
0981 return this;
0982 }
0983
0984
0985 const results_t &results() const { return results_; }
0986
0987
0988 results_t reduced_results() const {
0989 results_t res = proc_results_.empty() ? results_ : proc_results_;
0990 if(current_option_state_ < option_state::reduced) {
0991 if(current_option_state_ == option_state::parsing) {
0992 res = results_;
0993 _validate_results(res);
0994 }
0995 if(!res.empty()) {
0996 results_t extra;
0997 _reduce_results(extra, res);
0998 if(!extra.empty()) {
0999 res = std::move(extra);
1000 }
1001 }
1002 }
1003 return res;
1004 }
1005
1006
1007 template <typename T> void results(T &output) const {
1008 bool retval;
1009 if(current_option_state_ >= option_state::reduced || (results_.size() == 1 && validators_.empty())) {
1010 const results_t &res = (proc_results_.empty()) ? results_ : proc_results_;
1011 retval = detail::lexical_conversion<T, T>(res, output);
1012 } else {
1013 results_t res;
1014 if(results_.empty()) {
1015 if(!default_str_.empty()) {
1016
1017 _add_result(std::string(default_str_), res);
1018 _validate_results(res);
1019 results_t extra;
1020 _reduce_results(extra, res);
1021 if(!extra.empty()) {
1022 res = std::move(extra);
1023 }
1024 } else {
1025 res.emplace_back();
1026 }
1027 } else {
1028 res = reduced_results();
1029 }
1030 retval = detail::lexical_conversion<T, T>(res, output);
1031 }
1032 if(!retval) {
1033 throw ConversionError(get_name(), results_);
1034 }
1035 }
1036
1037
1038 template <typename T> T as() const {
1039 T output;
1040 results(output);
1041 return output;
1042 }
1043
1044
1045 bool get_callback_run() const { return (current_option_state_ == option_state::callback_run); }
1046
1047
1048
1049
1050
1051
1052 Option *type_name_fn(std::function<std::string()> typefun) {
1053 type_name_ = std::move(typefun);
1054 return this;
1055 }
1056
1057
1058 Option *type_name(std::string typeval) {
1059 type_name_fn([typeval]() { return typeval; });
1060 return this;
1061 }
1062
1063
1064 Option *type_size(int option_type_size) {
1065 if(option_type_size < 0) {
1066
1067 type_size_max_ = -option_type_size;
1068 type_size_min_ = -option_type_size;
1069 expected_max_ = detail::expected_max_vector_size;
1070 } else {
1071 type_size_max_ = option_type_size;
1072 if(type_size_max_ < detail::expected_max_vector_size) {
1073 type_size_min_ = option_type_size;
1074 } else {
1075 inject_separator_ = true;
1076 }
1077 if(type_size_max_ == 0)
1078 required_ = false;
1079 }
1080 return this;
1081 }
1082
1083 Option *type_size(int option_type_size_min, int option_type_size_max) {
1084 if(option_type_size_min < 0 || option_type_size_max < 0) {
1085
1086 expected_max_ = detail::expected_max_vector_size;
1087 option_type_size_min = (std::abs)(option_type_size_min);
1088 option_type_size_max = (std::abs)(option_type_size_max);
1089 }
1090
1091 if(option_type_size_min > option_type_size_max) {
1092 type_size_max_ = option_type_size_min;
1093 type_size_min_ = option_type_size_max;
1094 } else {
1095 type_size_min_ = option_type_size_min;
1096 type_size_max_ = option_type_size_max;
1097 }
1098 if(type_size_max_ == 0) {
1099 required_ = false;
1100 }
1101 if(type_size_max_ >= detail::expected_max_vector_size) {
1102 inject_separator_ = true;
1103 }
1104 return this;
1105 }
1106
1107
1108 void inject_separator(bool value = true) { inject_separator_ = value; }
1109
1110
1111 Option *default_function(const std::function<std::string()> &func) {
1112 default_function_ = func;
1113 return this;
1114 }
1115
1116
1117 Option *capture_default_str() {
1118 if(default_function_) {
1119 default_str_ = default_function_();
1120 }
1121 return this;
1122 }
1123
1124
1125 Option *default_str(std::string val) {
1126 default_str_ = std::move(val);
1127 return this;
1128 }
1129
1130
1131
1132 template <typename X> Option *default_val(const X &val) {
1133 std::string val_str = detail::to_string(val);
1134 auto old_option_state = current_option_state_;
1135 results_t old_results{std::move(results_)};
1136 results_.clear();
1137 try {
1138 add_result(val_str);
1139 if(run_callback_for_default_) {
1140 run_callback();
1141 current_option_state_ = option_state::parsing;
1142 } else {
1143 _validate_results(results_);
1144 current_option_state_ = old_option_state;
1145 }
1146 } catch(const CLI::Error &) {
1147
1148 results_ = std::move(old_results);
1149 current_option_state_ = old_option_state;
1150 throw;
1151 }
1152 results_ = std::move(old_results);
1153 default_str_ = std::move(val_str);
1154 return this;
1155 }
1156
1157
1158 std::string get_type_name() const {
1159 std::string full_type_name = type_name_();
1160 if(!validators_.empty()) {
1161 for(auto &Validator : validators_) {
1162 std::string vtype = Validator.get_description();
1163 if(!vtype.empty()) {
1164 full_type_name += ":" + vtype;
1165 }
1166 }
1167 }
1168 return full_type_name;
1169 }
1170
1171 private:
1172
1173 void _validate_results(results_t &res) const {
1174
1175 if(!validators_.empty()) {
1176 if(type_size_max_ > 1) {
1177 int index = 0;
1178 if(get_items_expected_max() < static_cast<int>(res.size()) &&
1179 multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {
1180
1181 index = get_items_expected_max() - static_cast<int>(res.size());
1182 }
1183
1184 for(std::string &result : res) {
1185 if(detail::is_separator(result) && type_size_max_ != type_size_min_ && index >= 0) {
1186 index = 0;
1187 continue;
1188 }
1189 auto err_msg = _validate(result, (index >= 0) ? (index % type_size_max_) : index);
1190 if(!err_msg.empty())
1191 throw ValidationError(get_name(), err_msg);
1192 ++index;
1193 }
1194 } else {
1195 int index = 0;
1196 if(expected_max_ < static_cast<int>(res.size()) &&
1197 multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {
1198
1199 index = expected_max_ - static_cast<int>(res.size());
1200 }
1201 for(std::string &result : res) {
1202 auto err_msg = _validate(result, index);
1203 ++index;
1204 if(!err_msg.empty())
1205 throw ValidationError(get_name(), err_msg);
1206 }
1207 }
1208 }
1209 }
1210
1211
1212
1213
1214 void _reduce_results(results_t &res, const results_t &original) const {
1215
1216
1217
1218
1219 res.clear();
1220
1221 switch(multi_option_policy_) {
1222 case MultiOptionPolicy::TakeAll:
1223 break;
1224 case MultiOptionPolicy::TakeLast: {
1225
1226 std::size_t trim_size = std::min<std::size_t>(
1227 static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
1228 if(original.size() != trim_size) {
1229 res.assign(original.end() - static_cast<results_t::difference_type>(trim_size), original.end());
1230 }
1231 } break;
1232 case MultiOptionPolicy::TakeFirst: {
1233 std::size_t trim_size = std::min<std::size_t>(
1234 static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
1235 if(original.size() != trim_size) {
1236 res.assign(original.begin(), original.begin() + static_cast<results_t::difference_type>(trim_size));
1237 }
1238 } break;
1239 case MultiOptionPolicy::Join:
1240 if(results_.size() > 1) {
1241 res.push_back(detail::join(original, std::string(1, (delimiter_ == '\0') ? '\n' : delimiter_)));
1242 }
1243 break;
1244 case MultiOptionPolicy::Throw:
1245 default: {
1246 auto num_min = static_cast<std::size_t>(get_items_expected_min());
1247 auto num_max = static_cast<std::size_t>(get_items_expected_max());
1248 if(num_min == 0) {
1249 num_min = 1;
1250 }
1251 if(num_max == 0) {
1252 num_max = 1;
1253 }
1254 if(original.size() < num_min) {
1255 throw ArgumentMismatch::AtLeast(get_name(), static_cast<int>(num_min), original.size());
1256 }
1257 if(original.size() > num_max) {
1258 throw ArgumentMismatch::AtMost(get_name(), static_cast<int>(num_max), original.size());
1259 }
1260 break;
1261 }
1262 }
1263 }
1264
1265
1266 std::string _validate(std::string &result, int index) const {
1267 std::string err_msg;
1268 if(result.empty() && expected_min_ == 0) {
1269
1270 return err_msg;
1271 }
1272 for(const auto &vali : validators_) {
1273 auto v = vali.get_application_index();
1274 if(v == -1 || v == index) {
1275 try {
1276 err_msg = vali(result);
1277 } catch(const ValidationError &err) {
1278 err_msg = err.what();
1279 }
1280 if(!err_msg.empty())
1281 break;
1282 }
1283 }
1284
1285 return err_msg;
1286 }
1287
1288
1289 int _add_result(std::string &&result, std::vector<std::string> &res) const {
1290 int result_count = 0;
1291 if(allow_extra_args_ && !result.empty() && result.front() == '[' &&
1292 result.back() == ']') {
1293 result.pop_back();
1294
1295 for(auto &var : CLI::detail::split(result.substr(1), ',')) {
1296 if(!var.empty()) {
1297 result_count += _add_result(std::move(var), res);
1298 }
1299 }
1300 return result_count;
1301 }
1302 if(delimiter_ == '\0') {
1303 res.push_back(std::move(result));
1304 ++result_count;
1305 } else {
1306 if((result.find_first_of(delimiter_) != std::string::npos)) {
1307 for(const auto &var : CLI::detail::split(result, delimiter_)) {
1308 if(!var.empty()) {
1309 res.push_back(var);
1310 ++result_count;
1311 }
1312 }
1313 } else {
1314 res.push_back(std::move(result));
1315 ++result_count;
1316 }
1317 }
1318 return result_count;
1319 }
1320 };
1321
1322
1323 }