File indexing completed on 2026-05-08 08:37:05
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008 #if (defined(CLI11_ENABLE_EXTRA_VALIDATORS) && CLI11_ENABLE_EXTRA_VALIDATORS == 1) || \
0009 (!defined(CLI11_DISABLE_EXTRA_VALIDATORS) || CLI11_DISABLE_EXTRA_VALIDATORS == 0)
0010
0011
0012 #include "Error.hpp"
0013 #include "Macros.hpp"
0014 #include "StringTools.hpp"
0015 #include "Validators.hpp"
0016
0017
0018 #include <cmath>
0019 #include <cstdint>
0020 #include <functional>
0021 #include <iostream>
0022 #include <limits>
0023 #include <map>
0024 #include <memory>
0025 #include <string>
0026 #include <utility>
0027 #include <vector>
0028
0029
0030 namespace CLI {
0031
0032
0033
0034
0035 namespace detail {
0036
0037
0038 class IPV4Validator : public Validator {
0039 public:
0040 IPV4Validator();
0041 };
0042
0043 }
0044
0045
0046 template <typename DesiredType> class TypeValidator : public Validator {
0047 public:
0048 explicit TypeValidator(const std::string &validator_name)
0049 : Validator(validator_name, [](std::string &input_string) {
0050 using CLI::detail::lexical_cast;
0051 auto val = DesiredType();
0052 if(!lexical_cast(input_string, val)) {
0053 return std::string("Failed parsing ") + input_string + " as a " + detail::type_name<DesiredType>();
0054 }
0055 return std::string{};
0056 }) {}
0057 TypeValidator() : TypeValidator(detail::type_name<DesiredType>()) {}
0058 };
0059
0060
0061 const TypeValidator<double> Number("NUMBER");
0062
0063
0064 class Bound : public Validator {
0065 public:
0066
0067
0068
0069
0070 template <typename T> Bound(T min_val, T max_val) {
0071 std::stringstream out;
0072 out << detail::type_name<T>() << " bounded to [" << min_val << " - " << max_val << "]";
0073 description(out.str());
0074
0075 func_ = [min_val, max_val](std::string &input) {
0076 using CLI::detail::lexical_cast;
0077 T val;
0078 bool converted = lexical_cast(input, val);
0079 if(!converted) {
0080 return std::string("Value ") + input + " could not be converted";
0081 }
0082 if(val < min_val)
0083 input = detail::to_string(min_val);
0084 else if(val > max_val)
0085 input = detail::to_string(max_val);
0086
0087 return std::string{};
0088 };
0089 }
0090
0091
0092 template <typename T> explicit Bound(T max_val) : Bound(static_cast<T>(0), max_val) {}
0093 };
0094
0095
0096
0097
0098 const detail::IPV4Validator ValidIPV4;
0099
0100 namespace detail {
0101 template <typename T,
0102 enable_if_t<is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
0103 auto smart_deref(T value) -> decltype(*value) {
0104 return *value;
0105 }
0106
0107 template <
0108 typename T,
0109 enable_if_t<!is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
0110 typename std::remove_reference<T>::type &smart_deref(T &value) {
0111
0112 return value;
0113 }
0114
0115 template <typename T> std::string generate_set(const T &set) {
0116 using element_t = typename detail::element_type<T>::type;
0117 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;
0118 std::string out(1, '{');
0119 out.append(detail::join(
0120 detail::smart_deref(set),
0121 [](const iteration_type_t &v) { return detail::pair_adaptor<element_t>::first(v); },
0122 ","));
0123 out.push_back('}');
0124 return out;
0125 }
0126
0127
0128 template <typename T> std::string generate_map(const T &map, bool key_only = false) {
0129 using element_t = typename detail::element_type<T>::type;
0130 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;
0131 std::string out(1, '{');
0132 out.append(detail::join(
0133 detail::smart_deref(map),
0134 [key_only](const iteration_type_t &v) {
0135 std::string res{detail::to_string(detail::pair_adaptor<element_t>::first(v))};
0136
0137 if(!key_only) {
0138 res.append("->");
0139 res += detail::to_string(detail::pair_adaptor<element_t>::second(v));
0140 }
0141 return res;
0142 },
0143 ","));
0144 out.push_back('}');
0145 return out;
0146 }
0147
0148 template <typename C, typename V> struct has_find {
0149 template <typename CC, typename VV>
0150 static auto test(int) -> decltype(std::declval<CC>().find(std::declval<VV>()), std::true_type());
0151 template <typename, typename> static auto test(...) -> decltype(std::false_type());
0152
0153 static const auto value = decltype(test<C, V>(0))::value;
0154 using type = std::integral_constant<bool, value>;
0155 };
0156
0157
0158 template <typename T, typename V, enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>
0159 auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0160 using element_t = typename detail::element_type<T>::type;
0161 auto &setref = detail::smart_deref(set);
0162 auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) {
0163 return (detail::pair_adaptor<element_t>::first(v) == val);
0164 });
0165 return {(it != std::end(setref)), it};
0166 }
0167
0168
0169 template <typename T, typename V, enable_if_t<has_find<T, V>::value, detail::enabler> = detail::dummy>
0170 auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0171 auto &setref = detail::smart_deref(set);
0172 auto it = setref.find(val);
0173 return {(it != std::end(setref)), it};
0174 }
0175
0176
0177 template <typename T, typename V>
0178 auto search(const T &set, const V &val, const std::function<V(V)> &filter_function)
0179 -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
0180 using element_t = typename detail::element_type<T>::type;
0181
0182 auto res = search(set, val);
0183 if((res.first) || (!(filter_function))) {
0184 return res;
0185 }
0186
0187 auto &setref = detail::smart_deref(set);
0188 auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) {
0189 V a{detail::pair_adaptor<element_t>::first(v)};
0190 a = filter_function(a);
0191 return (a == val);
0192 });
0193 return {(it != std::end(setref)), it};
0194 }
0195
0196 }
0197
0198 class IsMember : public Validator {
0199 public:
0200 using filter_fn_t = std::function<std::string(std::string)>;
0201
0202
0203 template <typename T, typename... Args>
0204 IsMember(std::initializer_list<T> values, Args &&...args)
0205 : IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
0206
0207
0208 template <typename T> explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}
0209
0210
0211
0212 template <typename T, typename F> explicit IsMember(T set, F filter_function) {
0213
0214
0215
0216 using element_t = typename detail::element_type<T>::type;
0217 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0218
0219 using local_item_t = typename IsMemberType<item_t>::type;
0220
0221
0222
0223 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0224
0225
0226 desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); };
0227
0228
0229
0230 func_ = [set, filter_fn](std::string &input) {
0231 using CLI::detail::lexical_cast;
0232 local_item_t b;
0233 if(!lexical_cast(input, b)) {
0234 throw ValidationError(input);
0235 }
0236 if(filter_fn) {
0237 b = filter_fn(b);
0238 }
0239 auto res = detail::search(set, b, filter_fn);
0240 if(res.first) {
0241
0242 if(filter_fn) {
0243 input = detail::value_string(detail::pair_adaptor<element_t>::first(*(res.second)));
0244 }
0245
0246
0247 return std::string{};
0248 }
0249
0250
0251 return input + " not in " + detail::generate_set(detail::smart_deref(set));
0252 };
0253 }
0254
0255
0256 template <typename T, typename... Args>
0257 IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
0258 : IsMember(
0259 std::forward<T>(set),
0260 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0261 other...) {}
0262 };
0263
0264
0265 template <typename T> using TransformPairs = std::vector<std::pair<std::string, T>>;
0266
0267
0268 class Transformer : public Validator {
0269 public:
0270 using filter_fn_t = std::function<std::string(std::string)>;
0271
0272
0273 template <typename... Args>
0274 Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
0275 : Transformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
0276
0277
0278 template <typename T> explicit Transformer(T &&mapping) : Transformer(std::forward<T>(mapping), nullptr) {}
0279
0280
0281
0282 template <typename T, typename F> explicit Transformer(T mapping, F filter_function) {
0283
0284 static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
0285 "mapping must produce value pairs");
0286
0287
0288 using element_t = typename detail::element_type<T>::type;
0289 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0290 using local_item_t = typename IsMemberType<item_t>::type;
0291
0292
0293
0294 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0295
0296
0297 desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); };
0298
0299 func_ = [mapping, filter_fn](std::string &input) {
0300 using CLI::detail::lexical_cast;
0301 local_item_t b;
0302 if(!lexical_cast(input, b)) {
0303 return std::string();
0304
0305 }
0306 if(filter_fn) {
0307 b = filter_fn(b);
0308 }
0309 auto res = detail::search(mapping, b, filter_fn);
0310 if(res.first) {
0311 input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
0312 }
0313 return std::string{};
0314 };
0315 }
0316
0317
0318 template <typename T, typename... Args>
0319 Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
0320 : Transformer(
0321 std::forward<T>(mapping),
0322 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0323 other...) {}
0324 };
0325
0326
0327 class CheckedTransformer : public Validator {
0328 public:
0329 using filter_fn_t = std::function<std::string(std::string)>;
0330
0331
0332 template <typename... Args>
0333 CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
0334 : CheckedTransformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
0335
0336
0337 template <typename T> explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {}
0338
0339
0340
0341 template <typename T, typename F> explicit CheckedTransformer(T mapping, F filter_function) {
0342
0343 static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
0344 "mapping must produce value pairs");
0345
0346
0347 using element_t = typename detail::element_type<T>::type;
0348 using item_t = typename detail::pair_adaptor<element_t>::first_type;
0349 using local_item_t = typename IsMemberType<item_t>::type;
0350
0351 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type;
0352
0353
0354 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
0355
0356 auto tfunc = [mapping]() {
0357 std::string out("value in ");
0358 out += detail::generate_map(detail::smart_deref(mapping)) + " OR {";
0359 out += detail::join(
0360 detail::smart_deref(mapping),
0361 [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor<element_t>::second(v)); },
0362 ",");
0363 out.push_back('}');
0364 return out;
0365 };
0366
0367 desc_function_ = tfunc;
0368
0369 func_ = [mapping, tfunc, filter_fn](std::string &input) {
0370 using CLI::detail::lexical_cast;
0371 local_item_t b;
0372 bool converted = lexical_cast(input, b);
0373 if(converted) {
0374 if(filter_fn) {
0375 b = filter_fn(b);
0376 }
0377 auto res = detail::search(mapping, b, filter_fn);
0378 if(res.first) {
0379 input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
0380 return std::string{};
0381 }
0382 }
0383 for(const auto &v : detail::smart_deref(mapping)) {
0384 auto output_string = detail::value_string(detail::pair_adaptor<element_t>::second(v));
0385 if(output_string == input) {
0386 return std::string();
0387 }
0388 }
0389
0390 return "Check " + input + " " + tfunc() + " FAILED";
0391 };
0392 }
0393
0394
0395 template <typename T, typename... Args>
0396 CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
0397 : CheckedTransformer(
0398 std::forward<T>(mapping),
0399 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
0400 other...) {}
0401 };
0402
0403
0404 inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
0405
0406
0407 inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }
0408
0409
0410 inline std::string ignore_space(std::string item) {
0411 item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item));
0412 item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item));
0413 return item;
0414 }
0415
0416
0417
0418
0419
0420
0421
0422
0423
0424
0425
0426
0427 class AsNumberWithUnit : public Validator {
0428 public:
0429
0430
0431
0432
0433 enum Options : std::uint8_t {
0434 CASE_SENSITIVE = 0,
0435 CASE_INSENSITIVE = 1,
0436 UNIT_OPTIONAL = 0,
0437 UNIT_REQUIRED = 2,
0438 DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL
0439 };
0440
0441 template <typename Number>
0442 explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
0443 Options opts = DEFAULT,
0444 const std::string &unit_name = "UNIT") {
0445 description(generate_description<Number>(unit_name, opts));
0446 validate_mapping(mapping, opts);
0447
0448
0449 func_ = [mapping, opts](std::string &input) -> std::string {
0450 Number num{};
0451
0452 detail::rtrim(input);
0453 if(input.empty()) {
0454 throw ValidationError("Input is empty");
0455 }
0456
0457
0458 auto unit_begin = input.end();
0459 while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
0460 --unit_begin;
0461 }
0462
0463 std::string unit{unit_begin, input.end()};
0464 input.resize(static_cast<std::size_t>(std::distance(input.begin(), unit_begin)));
0465 detail::trim(input);
0466
0467 if(opts & UNIT_REQUIRED && unit.empty()) {
0468 throw ValidationError("Missing mandatory unit");
0469 }
0470 if(opts & CASE_INSENSITIVE) {
0471 unit = detail::to_lower(unit);
0472 }
0473 if(unit.empty()) {
0474 using CLI::detail::lexical_cast;
0475 if(!lexical_cast(input, num)) {
0476 throw ValidationError(std::string("Value ") + input + " could not be converted to " +
0477 detail::type_name<Number>());
0478 }
0479
0480 return {};
0481 }
0482
0483
0484 auto it = mapping.find(unit);
0485 if(it == mapping.end()) {
0486 throw ValidationError(unit +
0487 " unit not recognized. "
0488 "Allowed values: " +
0489 detail::generate_map(mapping, true));
0490 }
0491
0492 if(!input.empty()) {
0493 using CLI::detail::lexical_cast;
0494 bool converted = lexical_cast(input, num);
0495 if(!converted) {
0496 throw ValidationError(std::string("Value ") + input + " could not be converted to " +
0497 detail::type_name<Number>());
0498 }
0499
0500 bool ok = detail::checked_multiply(num, it->second);
0501 if(!ok) {
0502 throw ValidationError(detail::to_string(num) + " multiplied by " + unit +
0503 " factor would cause number overflow. Use smaller value.");
0504 }
0505 } else {
0506 num = static_cast<Number>(it->second);
0507 }
0508
0509 input = detail::to_string(num);
0510
0511 return {};
0512 };
0513 }
0514
0515 private:
0516
0517
0518 template <typename Number> static void validate_mapping(std::map<std::string, Number> &mapping, Options opts) {
0519 for(auto &kv : mapping) {
0520 if(kv.first.empty()) {
0521 throw ValidationError("Unit must not be empty.");
0522 }
0523 if(!detail::isalpha(kv.first)) {
0524 throw ValidationError("Unit must contain only letters.");
0525 }
0526 }
0527
0528
0529 if(opts & CASE_INSENSITIVE) {
0530 std::map<std::string, Number> lower_mapping;
0531 for(auto &kv : mapping) {
0532 auto s = detail::to_lower(kv.first);
0533 if(lower_mapping.count(s)) {
0534 throw ValidationError(std::string("Several matching lowercase unit representations are found: ") +
0535 s);
0536 }
0537 lower_mapping[detail::to_lower(kv.first)] = kv.second;
0538 }
0539 mapping = std::move(lower_mapping);
0540 }
0541 }
0542
0543
0544 template <typename Number> static std::string generate_description(const std::string &name, Options opts) {
0545 std::stringstream out;
0546 out << detail::type_name<Number>() << ' ';
0547 if(opts & UNIT_REQUIRED) {
0548 out << name;
0549 } else {
0550 out << '[' << name << ']';
0551 }
0552 return out.str();
0553 }
0554 };
0555
0556 inline AsNumberWithUnit::Options operator|(const AsNumberWithUnit::Options &a, const AsNumberWithUnit::Options &b) {
0557 return static_cast<AsNumberWithUnit::Options>(static_cast<int>(a) | static_cast<int>(b));
0558 }
0559
0560
0561
0562
0563
0564
0565
0566
0567
0568
0569
0570
0571 class AsSizeValue : public AsNumberWithUnit {
0572 public:
0573 using result_t = std::uint64_t;
0574
0575
0576
0577
0578
0579
0580
0581
0582 explicit AsSizeValue(bool kb_is_1000);
0583
0584 private:
0585
0586 static std::map<std::string, result_t> init_mapping(bool kb_is_1000);
0587
0588
0589 static std::map<std::string, result_t> get_mapping(bool kb_is_1000);
0590 };
0591
0592 #if defined(CLI11_ENABLE_EXTRA_VALIDATORS) && CLI11_ENABLE_EXTRA_VALIDATORS != 0
0593
0594 #if CLI11_HAS_FILESYSTEM
0595 namespace detail {
0596 enum class Permission : std::uint8_t { none = 0, read = 1, write = 2, exec = 4 };
0597 class PermissionValidator : public Validator {
0598 public:
0599 explicit PermissionValidator(Permission permission);
0600 };
0601 }
0602
0603
0604 const detail::PermissionValidator ReadPermissions(detail::Permission::read);
0605
0606
0607 const detail::PermissionValidator WritePermissions(detail::Permission::write);
0608
0609
0610 const detail::PermissionValidator ExecPermissions(detail::Permission::exec);
0611 #endif
0612
0613 #endif
0614
0615 }
0616
0617 #ifndef CLI11_COMPILE
0618 #include "impl/ExtraValidators_inl.hpp" // IWYU pragma: export
0619 #endif
0620
0621 #endif