File indexing completed on 2024-09-28 07:02:19
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include "Macros.hpp"
0010 #include "StringTools.hpp"
0011 #include "TypeTools.hpp"
0012
0013
0014 #include <cmath>
0015 #include <cstdint>
0016 #include <functional>
0017 #include <iostream>
0018 #include <limits>
0019 #include <map>
0020 #include <memory>
0021 #include <string>
0022 #include <utility>
0023 #include <vector>
0024
0025
0026
0027
0028
0029
0030 #if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM
0031 #if __has_include(<filesystem>)
0032
0033 #if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
0034 #define CLI11_HAS_FILESYSTEM 0
0035 #else
0036 #include <filesystem>
0037 #if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703
0038 #if defined _GLIBCXX_RELEASE && _GLIBCXX_RELEASE >= 9
0039 #define CLI11_HAS_FILESYSTEM 1
0040 #elif defined(__GLIBCXX__)
0041
0042 #define CLI11_HAS_FILESYSTEM 0
0043 #else
0044 #define CLI11_HAS_FILESYSTEM 1
0045 #endif
0046 #else
0047 #define CLI11_HAS_FILESYSTEM 0
0048 #endif
0049 #endif
0050 #endif
0051 #endif
0052
0053 #if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
0054 #include <filesystem> // NOLINT(build/include)
0055 #else
0056 #include <sys/stat.h>
0057 #include <sys/types.h>
0058 #endif
0059
0060
0061
0062 namespace CLI {
0063
0064
0065 class Option;
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077 class Validator {
0078 protected:
0079
0080 std::function<std::string()> desc_function_{[]() { return std::string{}; }};
0081
0082
0083
0084 std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
0085
0086 std::string name_{};
0087
0088 int application_index_ = -1;
0089
0090 bool active_{true};
0091
0092 bool non_modifying_{false};
0093
0094 public:
0095 Validator() = default;
0096
0097 explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {}
0098
0099 Validator(std::function<std::string(std::string &)> op, std::string validator_desc, std::string validator_name = "")
0100 : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)),
0101 name_(std::move(validator_name)) {}
0102
0103 Validator &operation(std::function<std::string(std::string &)> op) {
0104 func_ = std::move(op);
0105 return *this;
0106 }
0107
0108
0109 std::string operator()(std::string &str) const {
0110 std::string retstring;
0111 if(active_) {
0112 if(non_modifying_) {
0113 std::string value = str;
0114 retstring = func_(value);
0115 } else {
0116 retstring = func_(str);
0117 }
0118 }
0119 return retstring;
0120 }
0121
0122
0123
0124 std::string operator()(const std::string &str) const {
0125 std::string value = str;
0126 return (active_) ? func_(value) : std::string{};
0127 }
0128
0129
0130 Validator &description(std::string validator_desc) {
0131 desc_function_ = [validator_desc]() { return validator_desc; };
0132 return *this;
0133 }
0134
0135 Validator description(std::string validator_desc) const {
0136 Validator newval(*this);
0137 newval.desc_function_ = [validator_desc]() { return validator_desc; };
0138 return newval;
0139 }
0140
0141 std::string get_description() const {
0142 if(active_) {
0143 return desc_function_();
0144 }
0145 return std::string{};
0146 }
0147
0148 Validator &name(std::string validator_name) {
0149 name_ = std::move(validator_name);
0150 return *this;
0151 }
0152
0153 Validator name(std::string validator_name) const {
0154 Validator newval(*this);
0155 newval.name_ = std::move(validator_name);
0156 return newval;
0157 }
0158
0159 const std::string &get_name() const { return name_; }
0160
0161 Validator &active(bool active_val = true) {
0162 active_ = active_val;
0163 return *this;
0164 }
0165
0166 Validator active(bool active_val = true) const {
0167 Validator newval(*this);
0168 newval.active_ = active_val;
0169 return newval;
0170 }
0171
0172
0173 Validator &non_modifying(bool no_modify = true) {
0174 non_modifying_ = no_modify;
0175 return *this;
0176 }
0177
0178 Validator &application_index(int app_index) {
0179 application_index_ = app_index;
0180 return *this;
0181 }
0182
0183 Validator application_index(int app_index) const {
0184 Validator newval(*this);
0185 newval.application_index_ = app_index;
0186 return newval;
0187 }
0188
0189 int get_application_index() const { return application_index_; }
0190
0191 bool get_active() const { return active_; }
0192
0193
0194 bool get_modifying() const { return !non_modifying_; }
0195
0196
0197
0198 Validator operator&(const Validator &other) const {
0199 Validator newval;
0200
0201 newval._merge_description(*this, other, " AND ");
0202
0203
0204 const std::function<std::string(std::string & filename)> &f1 = func_;
0205 const std::function<std::string(std::string & filename)> &f2 = other.func_;
0206
0207 newval.func_ = [f1, f2](std::string &input) {
0208 std::string s1 = f1(input);
0209 std::string s2 = f2(input);
0210 if(!s1.empty() && !s2.empty())
0211 return std::string("(") + s1 + ") AND (" + s2 + ")";
0212 else
0213 return s1 + s2;
0214 };
0215
0216 newval.active_ = (active_ & other.active_);
0217 newval.application_index_ = application_index_;
0218 return newval;
0219 }
0220
0221
0222
0223 Validator operator|(const Validator &other) const {
0224 Validator newval;
0225
0226 newval._merge_description(*this, other, " OR ");
0227
0228
0229 const std::function<std::string(std::string &)> &f1 = func_;
0230 const std::function<std::string(std::string &)> &f2 = other.func_;
0231
0232 newval.func_ = [f1, f2](std::string &input) {
0233 std::string s1 = f1(input);
0234 std::string s2 = f2(input);
0235 if(s1.empty() || s2.empty())
0236 return std::string();
0237
0238 return std::string("(") + s1 + ") OR (" + s2 + ")";
0239 };
0240 newval.active_ = (active_ & other.active_);
0241 newval.application_index_ = application_index_;
0242 return newval;
0243 }
0244
0245
0246 Validator operator!() const {
0247 Validator newval;
0248 const std::function<std::string()> &dfunc1 = desc_function_;
0249 newval.desc_function_ = [dfunc1]() {
0250 auto str = dfunc1();
0251 return (!str.empty()) ? std::string("NOT ") + str : std::string{};
0252 };
0253
0254 const std::function<std::string(std::string & res)> &f1 = func_;
0255
0256 newval.func_ = [f1, dfunc1](std::string &test) -> std::string {
0257 std::string s1 = f1(test);
0258 if(s1.empty()) {
0259 return std::string("check ") + dfunc1() + " succeeded improperly";
0260 }
0261 return std::string{};
0262 };
0263 newval.active_ = active_;
0264 newval.application_index_ = application_index_;
0265 return newval;
0266 }
0267
0268 private:
0269 void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger) {
0270
0271 const std::function<std::string()> &dfunc1 = val1.desc_function_;
0272 const std::function<std::string()> &dfunc2 = val2.desc_function_;
0273
0274 desc_function_ = [=]() {
0275 std::string f1 = dfunc1();
0276 std::string f2 = dfunc2();
0277 if((f1.empty()) || (f2.empty())) {
0278 return f1 + f2;
0279 }
0280 return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')';
0281 };
0282 }
0283 };
0284
0285
0286 class CustomValidator : public Validator {
0287 public:
0288 };
0289
0290
0291
0292 namespace detail {
0293
0294
0295 enum class path_type { nonexistent, file, directory };
0296
0297 #if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
0298
0299 inline path_type check_path(const char *file) noexcept {
0300 std::error_code ec;
0301 auto stat = std::filesystem::status(file, ec);
0302 if(ec) {
0303 return path_type::nonexistent;
0304 }
0305 switch(stat.type()) {
0306 case std::filesystem::file_type::none:
0307 case std::filesystem::file_type::not_found:
0308 return path_type::nonexistent;
0309 case std::filesystem::file_type::directory:
0310 return path_type::directory;
0311 case std::filesystem::file_type::symlink:
0312 case std::filesystem::file_type::block:
0313 case std::filesystem::file_type::character:
0314 case std::filesystem::file_type::fifo:
0315 case std::filesystem::file_type::socket:
0316 case std::filesystem::file_type::regular:
0317 case std::filesystem::file_type::unknown:
0318 default:
0319 return path_type::file;
0320 }
0321 }
0322 #else
0323
0324 inline path_type check_path(const char *file) noexcept {
0325 #if defined(_MSC_VER)
0326 struct __stat64 buffer;
0327 if(_stat64(file, &buffer) == 0) {
0328 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
0329 }
0330 #else
0331 struct stat buffer;
0332 if(stat(file, &buffer) == 0) {
0333 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
0334 }
0335 #endif
0336 return path_type::nonexistent;
0337 }
0338 #endif
0339
0340 class ExistingFileValidator : public Validator {
0341 public:
0342 ExistingFileValidator() : Validator("FILE") {
0343 func_ = [](std::string &filename) {
0344 auto path_result = check_path(filename.c_str());
0345 if(path_result == path_type::nonexistent) {
0346 return "File does not exist: " + filename;
0347 }
0348 if(path_result == path_type::directory) {
0349 return "File is actually a directory: " + filename;
0350 }
0351 return std::string();
0352 };
0353 }
0354 };
0355
0356
0357 class ExistingDirectoryValidator : public Validator {
0358 public:
0359 ExistingDirectoryValidator() : Validator("DIR") {
0360 func_ = [](std::string &filename) {
0361 auto path_result = check_path(filename.c_str());
0362 if(path_result == path_type::nonexistent) {
0363 return "Directory does not exist: " + filename;
0364 }
0365 if(path_result == path_type::file) {
0366 return "Directory is actually a file: " + filename;
0367 }
0368 return std::string();
0369 };
0370 }
0371 };
0372
0373
0374 class ExistingPathValidator : public Validator {
0375 public:
0376 ExistingPathValidator() : Validator("PATH(existing)") {
0377 func_ = [](std::string &filename) {
0378 auto path_result = check_path(filename.c_str());
0379 if(path_result == path_type::nonexistent) {
0380 return "Path does not exist: " + filename;
0381 }
0382 return std::string();
0383 };
0384 }
0385 };
0386
0387
0388 class NonexistentPathValidator : public Validator {
0389 public:
0390 NonexistentPathValidator() : Validator("PATH(non-existing)") {
0391 func_ = [](std::string &filename) {
0392 auto path_result = check_path(filename.c_str());
0393 if(path_result != path_type::nonexistent) {
0394 return "Path already exists: " + filename;
0395 }
0396 return std::string();
0397 };
0398 }
0399 };
0400
0401
0402 class IPV4Validator : public Validator {
0403 public:
0404 IPV4Validator() : Validator("IPV4") {
0405 func_ = [](std::string &ip_addr) {
0406 auto result = CLI::detail::split(ip_addr, '.');
0407 if(result.size() != 4) {
0408 return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')';
0409 }
0410 int num;
0411 for(const auto &var : result) {
0412 bool retval = detail::lexical_cast(var, num);
0413 if(!retval) {
0414 return std::string("Failed parsing number (") + var + ')';
0415 }
0416 if(num < 0 || num > 255) {
0417 return std::string("Each IP number must be between 0 and 255 ") + var;
0418 }
0419 }
0420 return std::string();
0421 };
0422 }
0423 };
0424
0425 }
0426
0427
0428
0429
0430 const detail::ExistingFileValidator ExistingFile;
0431
0432
0433 const detail::ExistingDirectoryValidator ExistingDirectory;
0434
0435
0436 const detail::ExistingPathValidator ExistingPath;
0437
0438
0439 const detail::NonexistentPathValidator NonexistentPath;
0440
0441
0442 const detail::IPV4Validator ValidIPV4;
0443
0444
0445 template <typename DesiredType> class TypeValidator : public Validator {
0446 public:
0447 explicit TypeValidator(const std::string &validator_name) : Validator(validator_name) {
0448 func_ = [](std::string &input_string) {
0449 auto val = DesiredType();
0450 if(!detail::lexical_cast(input_string, val)) {
0451 return std::string("Failed parsing ") + input_string + " as a " + detail::type_name<DesiredType>();
0452 }
0453 return std::string();
0454 };
0455 }
0456 TypeValidator() : TypeValidator(detail::type_name<DesiredType>()) {}
0457 };
0458
0459
0460 const TypeValidator<double> Number("NUMBER");
0461
0462
0463 class Range : public Validator {
0464 public:
0465
0466
0467
0468
0469 template <typename T>
0470 Range(T min, T max, const std::string &validator_name = std::string{}) : Validator(validator_name) {
0471 if(validator_name.empty()) {
0472 std::stringstream out;
0473 out << detail::type_name<T>() << " in [" << min << " - " << max << "]";
0474 description(out.str());
0475 }
0476
0477 func_ = [min, max](std::string &input) {
0478 T val;
0479 bool converted = detail::lexical_cast(input, val);
0480 if((!converted) || (val < min || val > max))
0481 return std::string("Value ") + input + " not in range " + std::to_string(min) + " to " +
0482 std::to_string(max);
0483
0484 return std::string();
0485 };
0486 }
0487
0488
0489 template <typename T>
0490 explicit Range(T max, const std::string &validator_name = std::string{})
0491 : Range(static_cast<T>(0), max, validator_name) {}
0492 };
0493
0494
0495 const Range NonNegativeNumber(std::numeric_limits<double>::max(), "NONNEGATIVE");
0496
0497
0498 const Range PositiveNumber(std::numeric_limits<double>::min(), std::numeric_limits<double>::max(), "POSITIVE");
0499
0500
0501 class Bound : public Validator {
0502 public:
0503
0504
0505
0506
0507 template <typename T> Bound(T min, T max) {
0508 std::stringstream out;
0509 out << detail::type_name<T>() << " bounded to [" << min << " - " << max << "]";
0510 description(out.str());
0511
0512 func_ = [min, max](std::string &input) {
0513 T val;
0514 bool converted = detail::lexical_cast(input, val);
0515 if(!converted) {
0516 return std::string("Value ") + input + " could not be converted";
0517 }
0518 if(val < min)
0519 input = detail::to_string(min);
0520 else if(val > max)
0521 input = detail::to_string(max);
0522
0523 return std::string{};
0524 };
0525 }
0526
0527
0528 template <typename T> explicit Bound(T max) : Bound(static_cast<T>(0), max) {}
0529 };
0530
0531 namespace detail {
0532 template <typename T,
0533 enable_if_t<is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
0534 auto smart_deref(T value) -> decltype(*value) {
0535 return *value;
0536 }
0537
0538 template <
0539 typename T,
0540 enable_if_t<!is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
0541 typename std::remove_reference<T>::type &smart_deref(T &value) {
0542 return value;
0543 }
0544
0545 template <typename T> std::string generate_set(const T &set) {
0546 using element_t = typename detail::element_type<T>::type;
0547 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;
0548 std::string out(1, '{');
0549 out.append(detail::join(
0550 detail::smart_deref(set),
0551 [](const iteration_type_t &v) { return detail::pair_adaptor<element_t>::first(v); },
0552 ","));
0553 out.push_back('}');
0554 return out;
0555 }
0556
0557
0558 template <typename T> std::string generate_map(const T &map, bool key_only = false) {
0559 using element_t = typename detail::element_type<T>::type;
0560 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;
0561 std::string out(1, '{');
0562 out.append(detail::join(
0563 detail::smart_deref(map),
0564 [key_only](const iteration_type_t &v) {
0565 std::string res{detail::to_string(detail::pair_adaptor<element_t>::first(v))};
0566
0567 if(!key_only) {
0568 res.append("->");
0569 res += detail::to_string(detail::pair_adaptor<element_t>::second(v));
0570 }
0571 return res;
0572 },
0573 ","));
0574 out.push_back('}');
0575 return out;
0576 }
0577
0578 template <typename C, typename V> struct has_find {
0579 template <typename CC, typename VV>
0580 static auto test(int) -> decltype(std::declval<CC>().find(std::declval<VV>()), std::true_type());
0581 template <typename, typename> static auto test(...) -> decltype(std::false_type());
0582
0583 static const auto value = decltype(test<C, V>(0))::value;
0584 using type = std::integral_constant<bool, value>;
0585 };
0586
0587
0588 template <typename T, typename V, enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>
0589 auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0590 using element_t = typename detail::element_type<T>::type;
0591 auto &setref = detail::smart_deref(set);
0592 auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) {
0593 return (detail::pair_adaptor<element_t>::first(v) == val);
0594 });
0595 return {(it != std::end(setref)), it};
0596 }
0597
0598
0599 template <typename T, typename V, enable_if_t<has_find<T, V>::value, detail::enabler> = detail::dummy>
0600 auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0601 auto &setref = detail::smart_deref(set);
0602 auto it = setref.find(val);
0603 return {(it != std::end(setref)), it};
0604 }
0605
0606
0607 template <typename T, typename V>
0608 auto search(const T &set, const V &val, const std::function<V(V)> &filter_function)
0609 -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0610 using element_t = typename detail::element_type<T>::type;
0611
0612 auto res = search(set, val);
0613 if((res.first) || (!(filter_function))) {
0614 return res;
0615 }
0616
0617 auto &setref = detail::smart_deref(set);
0618 auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) {
0619 V a{detail::pair_adaptor<element_t>::first(v)};
0620 a = filter_function(a);
0621 return (a == val);
0622 });
0623 return {(it != std::end(setref)), it};
0624 }
0625
0626
0627
0628
0629
0630 template <typename T>
0631 inline typename std::enable_if<std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
0632 if((a > 0) == (b > 0)) {
0633 return ((std::numeric_limits<T>::max)() / (std::abs)(a) < (std::abs)(b));
0634 } else {
0635 return ((std::numeric_limits<T>::min)() / (std::abs)(a) > -(std::abs)(b));
0636 }
0637 }
0638
0639 template <typename T>
0640 inline typename std::enable_if<!std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
0641 return ((std::numeric_limits<T>::max)() / a < b);
0642 }
0643
0644
0645 template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) {
0646 if(a == 0 || b == 0 || a == 1 || b == 1) {
0647 a *= b;
0648 return true;
0649 }
0650 if(a == (std::numeric_limits<T>::min)() || b == (std::numeric_limits<T>::min)()) {
0651 return false;
0652 }
0653 if(overflowCheck(a, b)) {
0654 return false;
0655 }
0656 a *= b;
0657 return true;
0658 }
0659
0660
0661 template <typename T>
0662 typename std::enable_if<std::is_floating_point<T>::value, bool>::type checked_multiply(T &a, T b) {
0663 T c = a * b;
0664 if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {
0665 return false;
0666 }
0667 a = c;
0668 return true;
0669 }
0670
0671 }
0672
0673 class IsMember : public Validator {
0674 public:
0675 using filter_fn_t = std::function<std::string(std::string)>;
0676
0677
0678 template <typename T, typename... Args>
0679 IsMember(std::initializer_list<T> values, Args &&... args)
0680 : IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
0681
0682
0683 template <typename T> explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}
0684
0685
0686
0687 template <typename T, typename F> explicit IsMember(T set, F filter_function) {
0688
0689
0690
0691 using element_t = typename detail::element_type<T>::type;
0692 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0693
0694 using local_item_t = typename IsMemberType<item_t>::type;
0695
0696
0697
0698 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0699
0700
0701 desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); };
0702
0703
0704
0705 func_ = [set, filter_fn](std::string &input) {
0706 local_item_t b;
0707 if(!detail::lexical_cast(input, b)) {
0708 throw ValidationError(input);
0709 }
0710 if(filter_fn) {
0711 b = filter_fn(b);
0712 }
0713 auto res = detail::search(set, b, filter_fn);
0714 if(res.first) {
0715
0716 if(filter_fn) {
0717 input = detail::value_string(detail::pair_adaptor<element_t>::first(*(res.second)));
0718 }
0719
0720
0721 return std::string{};
0722 }
0723
0724
0725 return input + " not in " + detail::generate_set(detail::smart_deref(set));
0726 };
0727 }
0728
0729
0730 template <typename T, typename... Args>
0731 IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
0732 : IsMember(
0733 std::forward<T>(set),
0734 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0735 other...) {}
0736 };
0737
0738
0739 template <typename T> using TransformPairs = std::vector<std::pair<std::string, T>>;
0740
0741
0742 class Transformer : public Validator {
0743 public:
0744 using filter_fn_t = std::function<std::string(std::string)>;
0745
0746
0747 template <typename... Args>
0748 Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
0749 : Transformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
0750
0751
0752 template <typename T> explicit Transformer(T &&mapping) : Transformer(std::forward<T>(mapping), nullptr) {}
0753
0754
0755
0756 template <typename T, typename F> explicit Transformer(T mapping, F filter_function) {
0757
0758 static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
0759 "mapping must produce value pairs");
0760
0761
0762 using element_t = typename detail::element_type<T>::type;
0763 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0764 using local_item_t = typename IsMemberType<item_t>::type;
0765
0766
0767
0768 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0769
0770
0771 desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); };
0772
0773 func_ = [mapping, filter_fn](std::string &input) {
0774 local_item_t b;
0775 if(!detail::lexical_cast(input, b)) {
0776 return std::string();
0777
0778 }
0779 if(filter_fn) {
0780 b = filter_fn(b);
0781 }
0782 auto res = detail::search(mapping, b, filter_fn);
0783 if(res.first) {
0784 input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
0785 }
0786 return std::string{};
0787 };
0788 }
0789
0790
0791 template <typename T, typename... Args>
0792 Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
0793 : Transformer(
0794 std::forward<T>(mapping),
0795 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0796 other...) {}
0797 };
0798
0799
0800 class CheckedTransformer : public Validator {
0801 public:
0802 using filter_fn_t = std::function<std::string(std::string)>;
0803
0804
0805 template <typename... Args>
0806 CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
0807 : CheckedTransformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
0808
0809
0810 template <typename T> explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {}
0811
0812
0813
0814 template <typename T, typename F> explicit CheckedTransformer(T mapping, F filter_function) {
0815
0816 static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
0817 "mapping must produce value pairs");
0818
0819
0820 using element_t = typename detail::element_type<T>::type;
0821 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0822 using local_item_t = typename IsMemberType<item_t>::type;
0823
0824 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;
0825
0826
0827 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0828
0829 auto tfunc = [mapping]() {
0830 std::string out("value in ");
0831 out += detail::generate_map(detail::smart_deref(mapping)) + " OR {";
0832 out += detail::join(
0833 detail::smart_deref(mapping),
0834 [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor<element_t>::second(v)); },
0835 ",");
0836 out.push_back('}');
0837 return out;
0838 };
0839
0840 desc_function_ = tfunc;
0841
0842 func_ = [mapping, tfunc, filter_fn](std::string &input) {
0843 local_item_t b;
0844 bool converted = detail::lexical_cast(input, b);
0845 if(converted) {
0846 if(filter_fn) {
0847 b = filter_fn(b);
0848 }
0849 auto res = detail::search(mapping, b, filter_fn);
0850 if(res.first) {
0851 input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
0852 return std::string{};
0853 }
0854 }
0855 for(const auto &v : detail::smart_deref(mapping)) {
0856 auto output_string = detail::value_string(detail::pair_adaptor<element_t>::second(v));
0857 if(output_string == input) {
0858 return std::string();
0859 }
0860 }
0861
0862 return "Check " + input + " " + tfunc() + " FAILED";
0863 };
0864 }
0865
0866
0867 template <typename T, typename... Args>
0868 CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
0869 : CheckedTransformer(
0870 std::forward<T>(mapping),
0871 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0872 other...) {}
0873 };
0874
0875
0876 inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
0877
0878
0879 inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }
0880
0881
0882 inline std::string ignore_space(std::string item) {
0883 item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item));
0884 item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item));
0885 return item;
0886 }
0887
0888
0889
0890
0891
0892
0893
0894
0895
0896
0897
0898
0899 class AsNumberWithUnit : public Validator {
0900 public:
0901
0902
0903
0904
0905 enum Options {
0906 CASE_SENSITIVE = 0,
0907 CASE_INSENSITIVE = 1,
0908 UNIT_OPTIONAL = 0,
0909 UNIT_REQUIRED = 2,
0910 DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL
0911 };
0912
0913 template <typename Number>
0914 explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
0915 Options opts = DEFAULT,
0916 const std::string &unit_name = "UNIT") {
0917 description(generate_description<Number>(unit_name, opts));
0918 validate_mapping(mapping, opts);
0919
0920
0921 func_ = [mapping, opts](std::string &input) -> std::string {
0922 Number num;
0923
0924 detail::rtrim(input);
0925 if(input.empty()) {
0926 throw ValidationError("Input is empty");
0927 }
0928
0929
0930 auto unit_begin = input.end();
0931 while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
0932 --unit_begin;
0933 }
0934
0935 std::string unit{unit_begin, input.end()};
0936 input.resize(static_cast<std::size_t>(std::distance(input.begin(), unit_begin)));
0937 detail::trim(input);
0938
0939 if(opts & UNIT_REQUIRED && unit.empty()) {
0940 throw ValidationError("Missing mandatory unit");
0941 }
0942 if(opts & CASE_INSENSITIVE) {
0943 unit = detail::to_lower(unit);
0944 }
0945 if(unit.empty()) {
0946 if(!detail::lexical_cast(input, num)) {
0947 throw ValidationError(std::string("Value ") + input + " could not be converted to " +
0948 detail::type_name<Number>());
0949 }
0950
0951 return {};
0952 }
0953
0954
0955 auto it = mapping.find(unit);
0956 if(it == mapping.end()) {
0957 throw ValidationError(unit +
0958 " unit not recognized. "
0959 "Allowed values: " +
0960 detail::generate_map(mapping, true));
0961 }
0962
0963 if(!input.empty()) {
0964 bool converted = detail::lexical_cast(input, num);
0965 if(!converted) {
0966 throw ValidationError(std::string("Value ") + input + " could not be converted to " +
0967 detail::type_name<Number>());
0968 }
0969
0970 bool ok = detail::checked_multiply(num, it->second);
0971 if(!ok) {
0972 throw ValidationError(detail::to_string(num) + " multiplied by " + unit +
0973 " factor would cause number overflow. Use smaller value.");
0974 }
0975 } else {
0976 num = static_cast<Number>(it->second);
0977 }
0978
0979 input = detail::to_string(num);
0980
0981 return {};
0982 };
0983 }
0984
0985 private:
0986
0987
0988 template <typename Number> static void validate_mapping(std::map<std::string, Number> &mapping, Options opts) {
0989 for(auto &kv : mapping) {
0990 if(kv.first.empty()) {
0991 throw ValidationError("Unit must not be empty.");
0992 }
0993 if(!detail::isalpha(kv.first)) {
0994 throw ValidationError("Unit must contain only letters.");
0995 }
0996 }
0997
0998
0999 if(opts & CASE_INSENSITIVE) {
1000 std::map<std::string, Number> lower_mapping;
1001 for(auto &kv : mapping) {
1002 auto s = detail::to_lower(kv.first);
1003 if(lower_mapping.count(s)) {
1004 throw ValidationError(std::string("Several matching lowercase unit representations are found: ") +
1005 s);
1006 }
1007 lower_mapping[detail::to_lower(kv.first)] = kv.second;
1008 }
1009 mapping = std::move(lower_mapping);
1010 }
1011 }
1012
1013
1014 template <typename Number> static std::string generate_description(const std::string &name, Options opts) {
1015 std::stringstream out;
1016 out << detail::type_name<Number>() << ' ';
1017 if(opts & UNIT_REQUIRED) {
1018 out << name;
1019 } else {
1020 out << '[' << name << ']';
1021 }
1022 return out.str();
1023 }
1024 };
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037 class AsSizeValue : public AsNumberWithUnit {
1038 public:
1039 using result_t = std::uint64_t;
1040
1041
1042
1043
1044
1045
1046
1047
1048 explicit AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) {
1049 if(kb_is_1000) {
1050 description("SIZE [b, kb(=1000b), kib(=1024b), ...]");
1051 } else {
1052 description("SIZE [b, kb(=1024b), ...]");
1053 }
1054 }
1055
1056 private:
1057
1058 static std::map<std::string, result_t> init_mapping(bool kb_is_1000) {
1059 std::map<std::string, result_t> m;
1060 result_t k_factor = kb_is_1000 ? 1000 : 1024;
1061 result_t ki_factor = 1024;
1062 result_t k = 1;
1063 result_t ki = 1;
1064 m["b"] = 1;
1065 for(std::string p : {"k", "m", "g", "t", "p", "e"}) {
1066 k *= k_factor;
1067 ki *= ki_factor;
1068 m[p] = k;
1069 m[p + "b"] = k;
1070 m[p + "i"] = ki;
1071 m[p + "ib"] = ki;
1072 }
1073 return m;
1074 }
1075
1076
1077 static std::map<std::string, result_t> get_mapping(bool kb_is_1000) {
1078 if(kb_is_1000) {
1079 static auto m = init_mapping(true);
1080 return m;
1081 } else {
1082 static auto m = init_mapping(false);
1083 return m;
1084 }
1085 }
1086 };
1087
1088 namespace detail {
1089
1090
1091
1092
1093 inline std::pair<std::string, std::string> split_program_name(std::string commandline) {
1094
1095 std::pair<std::string, std::string> vals;
1096 trim(commandline);
1097 auto esp = commandline.find_first_of(' ', 1);
1098 while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) {
1099 esp = commandline.find_first_of(' ', esp + 1);
1100 if(esp == std::string::npos) {
1101
1102
1103 if(commandline[0] == '"' || commandline[0] == '\'' || commandline[0] == '`') {
1104 bool embeddedQuote = false;
1105 auto keyChar = commandline[0];
1106 auto end = commandline.find_first_of(keyChar, 1);
1107 while((end != std::string::npos) && (commandline[end - 1] == '\\')) {
1108 end = commandline.find_first_of(keyChar, end + 1);
1109 embeddedQuote = true;
1110 }
1111 if(end != std::string::npos) {
1112 vals.first = commandline.substr(1, end - 1);
1113 esp = end + 1;
1114 if(embeddedQuote) {
1115 vals.first = find_and_replace(vals.first, std::string("\\") + keyChar, std::string(1, keyChar));
1116 embeddedQuote = false;
1117 }
1118 } else {
1119 esp = commandline.find_first_of(' ', 1);
1120 }
1121 } else {
1122 esp = commandline.find_first_of(' ', 1);
1123 }
1124
1125 break;
1126 }
1127 }
1128 if(vals.first.empty()) {
1129 vals.first = commandline.substr(0, esp);
1130 rtrim(vals.first);
1131 }
1132
1133
1134 vals.second = (esp != std::string::npos) ? commandline.substr(esp + 1) : std::string{};
1135 ltrim(vals.second);
1136 return vals;
1137 }
1138
1139 }
1140
1141
1142
1143 }