Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:54:43

0001 // Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
0002 // under NSF AWARD 1414736 and by the respective contributors.
0003 // All rights reserved.
0004 //
0005 // SPDX-License-Identifier: BSD-3-Clause
0006 
0007 #pragma once
0008 
0009 // This include is only needed for IDEs to discover symbols
0010 #include <CLI/Option.hpp>
0011 
0012 // [CLI11:public_includes:set]
0013 #include <algorithm>
0014 #include <string>
0015 #include <utility>
0016 #include <vector>
0017 // [CLI11:public_includes:end]
0018 
0019 namespace CLI {
0020 // [CLI11:option_inl_hpp:verbatim]
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     // This is an signed int so that it is not equivalent to a pointer.
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     // Help text should be symmetric - excluding a should exclude b
0159     opt->excludes_.insert(this);
0160 
0161     // Ignoring the insert return value, excluding twice is now allowed.
0162     // (Mostly to allow both directions to be excluded by user, even though the library does it for you.)
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) {  // this bizarre condition is to maintain backwards compatibility
0222                                  // with the previous behavior of expected_ with vectors
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 {};  // Hidden
0234 
0235     if(all_options) {
0236 
0237         std::vector<std::string> name_list;
0238 
0239         /// The all list will never include a positional unless asked or that's the only name.
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     // This returns the positional name no matter what
0269     if(positional)
0270         return pname_;
0271 
0272     // Prefer long name
0273     if(!lnames_.empty())
0274         return std::string(2, '-') + lnames_[0];
0275 
0276     // Or short name if no long name
0277     if(!snames_.empty())
0278         return std::string(1, '-') + snames_[0];
0279 
0280     // If positional is the only name, it's okay to use that
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_) {  // We need to do the inverse, in case we are ignore_case or 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         // this needs to be the original since envname_ shouldn't match on case insensitivity
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     // check for disable flag override_
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                 // We can static cast this to std::size_t because it is more than 0 in this block
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         // this section is included for backwards compatibility
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         // this section is included for backwards compatibility
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     // Run the Validators (can change the string)
0499     if(!validators_.empty()) {
0500         if(type_size_max_ > 1) {  // in this context index refers to the index in the type
0501             int index = 0;
0502             if(get_items_expected_max() < static_cast<int>(res.size()) &&
0503                multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {
0504                 // create a negative index for the earliest ones
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;  // reset index for variable size chunks
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                 // create a negative index for the earliest ones
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     // max num items expected or length of vector, always at least 1
0538     // Only valid for a trimming policy
0539 
0540     out.clear();
0541     // Operation depends on the policy setting
0542     switch(multi_option_policy_) {
0543     case MultiOptionPolicy::TakeAll:
0544         break;
0545     case MultiOptionPolicy::TakeLast: {
0546         // Allow multi-option sizes (including 0)
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     // this check is to allow an empty vector in certain circumstances but not if expected is not zero.
0588     // {} is the indicator for an empty container
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         // an empty with nothing expected is allowed
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() == ']') {  // this is now a vector string likely from the default or user entry
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 // [CLI11:option_inl_hpp:end]
0653 }  // namespace CLI