CLI11 2.2.0
Loading...
Searching...
No Matches
Validators.hpp
Go to the documentation of this file.
1// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
2// under NSF AWARD 1414736 and by the respective contributors.
3// All rights reserved.
4//
5// SPDX-License-Identifier: BSD-3-Clause
6
7#pragma once
8
9#include "Macros.hpp"
10#include "StringTools.hpp"
11#include "TypeTools.hpp"
12
13// [CLI11:public_includes:set]
14#include <cmath>
15#include <cstdint>
16#include <functional>
17#include <iostream>
18#include <limits>
19#include <map>
20#include <memory>
21#include <string>
22#include <utility>
23#include <vector>
24// [CLI11:public_includes:end]
25
26// [CLI11:validators_hpp_filesystem:verbatim]
27
28// C standard library
29// Only needed for existence checking
30#if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM
31#if __has_include(<filesystem>)
32// Filesystem cannot be used if targeting macOS < 10.15
33#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
34#define CLI11_HAS_FILESYSTEM 0
35#elif defined(__wasi__)
36// As of wasi-sdk-14, filesystem is not implemented
37#define CLI11_HAS_FILESYSTEM 0
38#else
39#include <filesystem>
40#if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703
41#if defined _GLIBCXX_RELEASE && _GLIBCXX_RELEASE >= 9
42#define CLI11_HAS_FILESYSTEM 1
43#elif defined(__GLIBCXX__)
44// if we are using gcc and Version <9 default to no filesystem
45#define CLI11_HAS_FILESYSTEM 0
46#else
47#define CLI11_HAS_FILESYSTEM 1
48#endif
49#else
50#define CLI11_HAS_FILESYSTEM 0
51#endif
52#endif
53#endif
54#endif
55
56#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
57#include <filesystem> // NOLINT(build/include)
58#else
59#include <sys/stat.h>
60#include <sys/types.h>
61#endif
62
63// [CLI11:validators_hpp_filesystem:end]
64
65namespace CLI {
66// [CLI11:validators_hpp:verbatim]
67
68class Option;
69
71
78
80class Validator {
81 protected:
83 std::function<std::string()> desc_function_{[]() { return std::string{}; }};
84
87 std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
89 std::string name_{};
93 bool active_{true};
95 bool non_modifying_{false};
96
97 public:
98 Validator() = default;
100 explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {}
102 Validator(std::function<std::string(std::string &)> op, std::string validator_desc, std::string validator_name = "")
103 : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)),
104 name_(std::move(validator_name)) {}
106 Validator &operation(std::function<std::string(std::string &)> op) {
107 func_ = std::move(op);
108 return *this;
109 }
112 std::string operator()(std::string &str) const {
113 std::string retstring;
114 if(active_) {
115 if(non_modifying_) {
116 std::string value = str;
117 retstring = func_(value);
118 } else {
119 retstring = func_(str);
120 }
121 }
122 return retstring;
123 }
124
127 std::string operator()(const std::string &str) const {
128 std::string value = str;
129 return (active_) ? func_(value) : std::string{};
130 }
131
133 Validator &description(std::string validator_desc) {
134 desc_function_ = [validator_desc]() { return validator_desc; };
135 return *this;
136 }
138 Validator description(std::string validator_desc) const {
139 Validator newval(*this);
140 newval.desc_function_ = [validator_desc]() { return validator_desc; };
141 return newval;
142 }
144 std::string get_description() const {
145 if(active_) {
146 return desc_function_();
147 }
148 return std::string{};
149 }
151 Validator &name(std::string validator_name) {
152 name_ = std::move(validator_name);
153 return *this;
154 }
156 Validator name(std::string validator_name) const {
157 Validator newval(*this);
158 newval.name_ = std::move(validator_name);
159 return newval;
160 }
162 const std::string &get_name() const { return name_; }
164 Validator &active(bool active_val = true) {
165 active_ = active_val;
166 return *this;
167 }
169 Validator active(bool active_val = true) const {
170 Validator newval(*this);
171 newval.active_ = active_val;
172 return newval;
173 }
174
176 Validator &non_modifying(bool no_modify = true) {
177 non_modifying_ = no_modify;
178 return *this;
179 }
181 Validator &application_index(int app_index) {
182 application_index_ = app_index;
183 return *this;
184 }
186 Validator application_index(int app_index) const {
187 Validator newval(*this);
188 newval.application_index_ = app_index;
189 return newval;
190 }
194 bool get_active() const { return active_; }
195
197 bool get_modifying() const { return !non_modifying_; }
198
201 Validator operator&(const Validator &other) const {
202 Validator newval;
203
204 newval._merge_description(*this, other, " AND ");
205
206 // Give references (will make a copy in lambda function)
207 const std::function<std::string(std::string & filename)> &f1 = func_;
208 const std::function<std::string(std::string & filename)> &f2 = other.func_;
209
210 newval.func_ = [f1, f2](std::string &input) {
211 std::string s1 = f1(input);
212 std::string s2 = f2(input);
213 if(!s1.empty() && !s2.empty())
214 return std::string("(") + s1 + ") AND (" + s2 + ")";
215 else
216 return s1 + s2;
217 };
218
219 newval.active_ = (active_ & other.active_);
221 return newval;
222 }
223
226 Validator operator|(const Validator &other) const {
227 Validator newval;
228
229 newval._merge_description(*this, other, " OR ");
230
231 // Give references (will make a copy in lambda function)
232 const std::function<std::string(std::string &)> &f1 = func_;
233 const std::function<std::string(std::string &)> &f2 = other.func_;
234
235 newval.func_ = [f1, f2](std::string &input) {
236 std::string s1 = f1(input);
237 std::string s2 = f2(input);
238 if(s1.empty() || s2.empty())
239 return std::string();
240
241 return std::string("(") + s1 + ") OR (" + s2 + ")";
242 };
243 newval.active_ = (active_ & other.active_);
245 return newval;
246 }
247
250 Validator newval;
251 const std::function<std::string()> &dfunc1 = desc_function_;
252 newval.desc_function_ = [dfunc1]() {
253 auto str = dfunc1();
254 return (!str.empty()) ? std::string("NOT ") + str : std::string{};
255 };
256 // Give references (will make a copy in lambda function)
257 const std::function<std::string(std::string & res)> &f1 = func_;
258
259 newval.func_ = [f1, dfunc1](std::string &test) -> std::string {
260 std::string s1 = f1(test);
261 if(s1.empty()) {
262 return std::string("check ") + dfunc1() + " succeeded improperly";
263 }
264 return std::string{};
265 };
266 newval.active_ = active_;
268 return newval;
269 }
270
271 private:
272 void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger) {
273
274 const std::function<std::string()> &dfunc1 = val1.desc_function_;
275 const std::function<std::string()> &dfunc2 = val2.desc_function_;
276
277 desc_function_ = [=]() {
278 std::string f1 = dfunc1();
279 std::string f2 = dfunc2();
280 if((f1.empty()) || (f2.empty())) {
281 return f1 + f2;
282 }
283 return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')';
284 };
285 }
286}; // namespace CLI
287
290 public:
291};
292// The implementation of the built in validators is using the Validator class;
293// the user is only expected to use the const (static) versions (since there's no setup).
294// Therefore, this is in detail.
295namespace detail {
296
299
300#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
302inline path_type check_path(const char *file) noexcept {
303 std::error_code ec;
304 auto stat = std::filesystem::status(file, ec);
305 if(ec) {
307 }
308 switch(stat.type()) {
309 case std::filesystem::file_type::none:
310 case std::filesystem::file_type::not_found:
312 case std::filesystem::file_type::directory:
314 case std::filesystem::file_type::symlink:
315 case std::filesystem::file_type::block:
316 case std::filesystem::file_type::character:
317 case std::filesystem::file_type::fifo:
318 case std::filesystem::file_type::socket:
319 case std::filesystem::file_type::regular:
320 case std::filesystem::file_type::unknown:
321 default:
322 return path_type::file;
323 }
324}
325#else
327inline path_type check_path(const char *file) noexcept {
328#if defined(_MSC_VER)
329 struct __stat64 buffer;
330 if(_stat64(file, &buffer) == 0) {
331 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
332 }
333#else
334 struct stat buffer;
335 if(stat(file, &buffer) == 0) {
336 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
337 }
338#endif
340}
341#endif
344 public:
346 func_ = [](std::string &filename) {
347 auto path_result = check_path(filename.c_str());
348 if(path_result == path_type::nonexistent) {
349 return "File does not exist: " + filename;
350 }
351 if(path_result == path_type::directory) {
352 return "File is actually a directory: " + filename;
353 }
354 return std::string();
355 };
356 }
357};
358
361 public:
363 func_ = [](std::string &filename) {
364 auto path_result = check_path(filename.c_str());
365 if(path_result == path_type::nonexistent) {
366 return "Directory does not exist: " + filename;
367 }
368 if(path_result == path_type::file) {
369 return "Directory is actually a file: " + filename;
370 }
371 return std::string();
372 };
373 }
374};
375
378 public:
379 ExistingPathValidator() : Validator("PATH(existing)") {
380 func_ = [](std::string &filename) {
381 auto path_result = check_path(filename.c_str());
382 if(path_result == path_type::nonexistent) {
383 return "Path does not exist: " + filename;
384 }
385 return std::string();
386 };
387 }
388};
389
392 public:
393 NonexistentPathValidator() : Validator("PATH(non-existing)") {
394 func_ = [](std::string &filename) {
395 auto path_result = check_path(filename.c_str());
396 if(path_result != path_type::nonexistent) {
397 return "Path already exists: " + filename;
398 }
399 return std::string();
400 };
401 }
402};
403
405class IPV4Validator : public Validator {
406 public:
408 func_ = [](std::string &ip_addr) {
409 auto result = CLI::detail::split(ip_addr, '.');
410 if(result.size() != 4) {
411 return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')';
412 }
413 int num;
414 for(const auto &var : result) {
415 bool retval = detail::lexical_cast(var, num);
416 if(!retval) {
417 return std::string("Failed parsing number (") + var + ')';
418 }
419 if(num < 0 || num > 255) {
420 return std::string("Each IP number must be between 0 and 255 ") + var;
421 }
422 }
423 return std::string();
424 };
425 }
426};
427
428} // namespace detail
429
430// Static is not needed here, because global const implies static.
431
434
437
440
443
446
448template <typename DesiredType> class TypeValidator : public Validator {
449 public:
450 explicit TypeValidator(const std::string &validator_name) : Validator(validator_name) {
451 func_ = [](std::string &input_string) {
452 auto val = DesiredType();
453 if(!detail::lexical_cast(input_string, val)) {
454 return std::string("Failed parsing ") + input_string + " as a " + detail::type_name<DesiredType>();
455 }
456 return std::string();
457 };
458 }
459 TypeValidator() : TypeValidator(detail::type_name<DesiredType>()) {}
460};
461
464
468 public:
469 explicit FileOnDefaultPath(std::string default_path, bool enableErrorReturn = true) : Validator("FILE") {
470 func_ = [default_path, enableErrorReturn](std::string &filename) {
471 auto path_result = detail::check_path(filename.c_str());
472 if(path_result == detail::path_type::nonexistent) {
473 std::string test_file_path = default_path;
474 if(default_path.back() != '/' && default_path.back() != '\\') {
475 // Add folder separator
476 test_file_path += '/';
477 }
478 test_file_path.append(filename);
479 path_result = detail::check_path(test_file_path.c_str());
480 if(path_result == detail::path_type::file) {
481 filename = test_file_path;
482 } else {
483 if(enableErrorReturn) {
484 return "File does not exist: " + filename;
485 }
486 }
487 }
488 return std::string{};
489 };
490 }
491};
492
494class Range : public Validator {
495 public:
500 template <typename T>
501 Range(T min_val, T max_val, const std::string &validator_name = std::string{}) : Validator(validator_name) {
502 if(validator_name.empty()) {
503 std::stringstream out;
504 out << detail::type_name<T>() << " in [" << min_val << " - " << max_val << "]";
505 description(out.str());
506 }
507
508 func_ = [min_val, max_val](std::string &input) {
509 T val;
510 bool converted = detail::lexical_cast(input, val);
511 if((!converted) || (val < min_val || val > max_val)) {
512 std::stringstream out;
513 out << "Value " << input << " not in range [";
514 out << min_val << " - " << max_val << "]";
515 return out.str();
516 }
517 return std::string{};
518 };
519 }
520
522 template <typename T>
523 explicit Range(T max_val, const std::string &validator_name = std::string{})
524 : Range(static_cast<T>(0), max_val, validator_name) {}
525};
526
528const Range NonNegativeNumber((std::numeric_limits<double>::max)(), "NONNEGATIVE");
529
531const Range PositiveNumber((std::numeric_limits<double>::min)(), (std::numeric_limits<double>::max)(), "POSITIVE");
532
534class Bound : public Validator {
535 public:
540 template <typename T> Bound(T min_val, T max_val) {
541 std::stringstream out;
542 out << detail::type_name<T>() << " bounded to [" << min_val << " - " << max_val << "]";
543 description(out.str());
544
545 func_ = [min_val, max_val](std::string &input) {
546 T val;
547 bool converted = detail::lexical_cast(input, val);
548 if(!converted) {
549 return std::string("Value ") + input + " could not be converted";
550 }
551 if(val < min_val)
552 input = detail::to_string(min_val);
553 else if(val > max_val)
554 input = detail::to_string(max_val);
555
556 return std::string{};
557 };
558 }
559
561 template <typename T> explicit Bound(T max_val) : Bound(static_cast<T>(0), max_val) {}
562};
563
564namespace detail {
565template <typename T,
566 enable_if_t<is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
567auto smart_deref(T value) -> decltype(*value) {
568 return *value;
569}
570
571template <
572 typename T,
574typename std::remove_reference<T>::type &smart_deref(T &value) {
575 return value;
576}
578template <typename T> std::string generate_set(const T &set) {
579 using element_t = typename detail::element_type<T>::type;
580 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
581 std::string out(1, '{');
582 out.append(detail::join(
583 detail::smart_deref(set),
584 [](const iteration_type_t &v) { return detail::pair_adaptor<element_t>::first(v); },
585 ","));
586 out.push_back('}');
587 return out;
588}
589
591template <typename T> std::string generate_map(const T &map, bool key_only = false) {
592 using element_t = typename detail::element_type<T>::type;
593 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
594 std::string out(1, '{');
595 out.append(detail::join(
596 detail::smart_deref(map),
597 [key_only](const iteration_type_t &v) {
598 std::string res{detail::to_string(detail::pair_adaptor<element_t>::first(v))};
599
600 if(!key_only) {
601 res.append("->");
602 res += detail::to_string(detail::pair_adaptor<element_t>::second(v));
603 }
604 return res;
605 },
606 ","));
607 out.push_back('}');
608 return out;
609}
610
611template <typename C, typename V> struct has_find {
612 template <typename CC, typename VV>
613 static auto test(int) -> decltype(std::declval<CC>().find(std::declval<VV>()), std::true_type());
614 template <typename, typename> static auto test(...) -> decltype(std::false_type());
615
616 static const auto value = decltype(test<C, V>(0))::value;
617 using type = std::integral_constant<bool, value>;
618};
619
621template <typename T, typename V, enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>
622auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
623 using element_t = typename detail::element_type<T>::type;
624 auto &setref = detail::smart_deref(set);
625 auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) {
627 });
628 return {(it != std::end(setref)), it};
629}
630
632template <typename T, typename V, enable_if_t<has_find<T, V>::value, detail::enabler> = detail::dummy>
633auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
634 auto &setref = detail::smart_deref(set);
635 auto it = setref.find(val);
636 return {(it != std::end(setref)), it};
637}
638
640template <typename T, typename V>
641auto search(const T &set, const V &val, const std::function<V(V)> &filter_function)
642 -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
643 using element_t = typename detail::element_type<T>::type;
644 // do the potentially faster first search
645 auto res = search(set, val);
646 if((res.first) || (!(filter_function))) {
647 return res;
648 }
649 // if we haven't found it do the longer linear search with all the element translations
650 auto &setref = detail::smart_deref(set);
651 auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) {
653 a = filter_function(a);
654 return (a == val);
655 });
656 return {(it != std::end(setref)), it};
657}
658
659// the following suggestion was made by Nikita Ofitserov(@himikof)
660// done in templates to prevent compiler warnings on negation of unsigned numbers
661
663template <typename T>
664inline typename std::enable_if<std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
665 if((a > 0) == (b > 0)) {
666 return ((std::numeric_limits<T>::max)() / (std::abs)(a) < (std::abs)(b));
667 } else {
668 return ((std::numeric_limits<T>::min)() / (std::abs)(a) > -(std::abs)(b));
669 }
670}
672template <typename T>
673inline typename std::enable_if<!std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
674 return ((std::numeric_limits<T>::max)() / a < b);
675}
676
678template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) {
679 if(a == 0 || b == 0 || a == 1 || b == 1) {
680 a *= b;
681 return true;
682 }
683 if(a == (std::numeric_limits<T>::min)() || b == (std::numeric_limits<T>::min)()) {
684 return false;
685 }
686 if(overflowCheck(a, b)) {
687 return false;
688 }
689 a *= b;
690 return true;
691}
692
694template <typename T>
695typename std::enable_if<std::is_floating_point<T>::value, bool>::type checked_multiply(T &a, T b) {
696 T c = a * b;
697 if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {
698 return false;
699 }
700 a = c;
701 return true;
702}
703
704} // namespace detail
706class IsMember : public Validator {
707 public:
708 using filter_fn_t = std::function<std::string(std::string)>;
709
711 template <typename T, typename... Args>
712 IsMember(std::initializer_list<T> values, Args &&...args)
713 : IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
714
716 template <typename T> explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}
717
720 template <typename T, typename F> explicit IsMember(T set, F filter_function) {
721
722 // Get the type of the contained item - requires a container have ::value_type
723 // if the type does not have first_type and second_type, these are both value_type
724 using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
725 using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
726
727 using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
728 // (const char * to std::string)
729
730 // Make a local copy of the filter function, using a std::function if not one already
731 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
732
733 // This is the type name for help, it will take the current version of the set contents
734 desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); };
735
736 // This is the function that validates
737 // It stores a copy of the set pointer-like, so shared_ptr will stay alive
738 func_ = [set, filter_fn](std::string &input) {
739 local_item_t b;
740 if(!detail::lexical_cast(input, b)) {
741 throw ValidationError(input); // name is added later
742 }
743 if(filter_fn) {
744 b = filter_fn(b);
745 }
746 auto res = detail::search(set, b, filter_fn);
747 if(res.first) {
748 // Make sure the version in the input string is identical to the one in the set
749 if(filter_fn) {
750 input = detail::value_string(detail::pair_adaptor<element_t>::first(*(res.second)));
751 }
752
753 // Return empty error string (success)
754 return std::string{};
755 }
756
757 // If you reach this point, the result was not found
758 return input + " not in " + detail::generate_set(detail::smart_deref(set));
759 };
760 }
761
763 template <typename T, typename... Args>
764 IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
765 : IsMember(
766 std::forward<T>(set),
767 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
768 other...) {}
769};
770
772template <typename T> using TransformPairs = std::vector<std::pair<std::string, T>>;
773
775class Transformer : public Validator {
776 public:
777 using filter_fn_t = std::function<std::string(std::string)>;
778
780 template <typename... Args>
781 Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
782 : Transformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
783
785 template <typename T> explicit Transformer(T &&mapping) : Transformer(std::forward<T>(mapping), nullptr) {}
786
789 template <typename T, typename F> explicit Transformer(T mapping, F filter_function) {
790
792 "mapping must produce value pairs");
793 // Get the type of the contained item - requires a container have ::value_type
794 // if the type does not have first_type and second_type, these are both value_type
795 using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
796 using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
797 using local_item_t = typename IsMemberType<item_t>::type; // Will convert bad types to good ones
798 // (const char * to std::string)
799
800 // Make a local copy of the filter function, using a std::function if not one already
801 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
802
803 // This is the type name for help, it will take the current version of the set contents
804 desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); };
805
806 func_ = [mapping, filter_fn](std::string &input) {
807 local_item_t b;
808 if(!detail::lexical_cast(input, b)) {
809 return std::string();
810 // there is no possible way we can match anything in the mapping if we can't convert so just return
811 }
812 if(filter_fn) {
813 b = filter_fn(b);
814 }
815 auto res = detail::search(mapping, b, filter_fn);
816 if(res.first) {
817 input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
818 }
819 return std::string{};
820 };
821 }
822
824 template <typename T, typename... Args>
825 Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
826 : Transformer(
827 std::forward<T>(mapping),
828 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
829 other...) {}
830};
831
834 public:
835 using filter_fn_t = std::function<std::string(std::string)>;
836
838 template <typename... Args>
839 CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
840 : CheckedTransformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
841
843 template <typename T> explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {}
844
847 template <typename T, typename F> explicit CheckedTransformer(T mapping, F filter_function) {
848
850 "mapping must produce value pairs");
851 // Get the type of the contained item - requires a container have ::value_type
852 // if the type does not have first_type and second_type, these are both value_type
853 using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
854 using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
855 using local_item_t = typename IsMemberType<item_t>::type; // Will convert bad types to good ones
856 // (const char * to std::string)
857 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
858
859 // Make a local copy of the filter function, using a std::function if not one already
860 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
861
862 auto tfunc = [mapping]() {
863 std::string out("value in ");
864 out += detail::generate_map(detail::smart_deref(mapping)) + " OR {";
865 out += detail::join(
866 detail::smart_deref(mapping),
867 [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor<element_t>::second(v)); },
868 ",");
869 out.push_back('}');
870 return out;
871 };
872
873 desc_function_ = tfunc;
874
875 func_ = [mapping, tfunc, filter_fn](std::string &input) {
876 local_item_t b;
877 bool converted = detail::lexical_cast(input, b);
878 if(converted) {
879 if(filter_fn) {
880 b = filter_fn(b);
881 }
882 auto res = detail::search(mapping, b, filter_fn);
883 if(res.first) {
884 input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
885 return std::string{};
886 }
887 }
888 for(const auto &v : detail::smart_deref(mapping)) {
889 auto output_string = detail::value_string(detail::pair_adaptor<element_t>::second(v));
890 if(output_string == input) {
891 return std::string();
892 }
893 }
894
895 return "Check " + input + " " + tfunc() + " FAILED";
896 };
897 }
898
900 template <typename T, typename... Args>
901 CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
903 std::forward<T>(mapping),
904 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
905 other...) {}
906};
907
909inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
910
912inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }
913
915inline std::string ignore_space(std::string item) {
916 item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item));
917 item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item));
918 return item;
919}
920
933 public:
938 enum Options {
939 CASE_SENSITIVE = 0,
940 CASE_INSENSITIVE = 1,
941 UNIT_OPTIONAL = 0,
942 UNIT_REQUIRED = 2,
943 DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL
944 };
945
946 template <typename Number>
947 explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
948 Options opts = DEFAULT,
949 const std::string &unit_name = "UNIT") {
950 description(generate_description<Number>(unit_name, opts));
951 validate_mapping(mapping, opts);
952
953 // transform function
954 func_ = [mapping, opts](std::string &input) -> std::string {
955 Number num;
956
957 detail::rtrim(input);
958 if(input.empty()) {
959 throw ValidationError("Input is empty");
960 }
961
962 // Find split position between number and prefix
963 auto unit_begin = input.end();
964 while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
965 --unit_begin;
966 }
967
968 std::string unit{unit_begin, input.end()};
969 input.resize(static_cast<std::size_t>(std::distance(input.begin(), unit_begin)));
970 detail::trim(input);
971
972 if(opts & UNIT_REQUIRED && unit.empty()) {
973 throw ValidationError("Missing mandatory unit");
974 }
975 if(opts & CASE_INSENSITIVE) {
976 unit = detail::to_lower(unit);
977 }
978 if(unit.empty()) {
979 if(!detail::lexical_cast(input, num)) {
980 throw ValidationError(std::string("Value ") + input + " could not be converted to " +
981 detail::type_name<Number>());
982 }
983 // No need to modify input if no unit passed
984 return {};
985 }
986
987 // find corresponding factor
988 auto it = mapping.find(unit);
989 if(it == mapping.end()) {
990 throw ValidationError(unit +
991 " unit not recognized. "
992 "Allowed values: " +
993 detail::generate_map(mapping, true));
994 }
995
996 if(!input.empty()) {
997 bool converted = detail::lexical_cast(input, num);
998 if(!converted) {
999 throw ValidationError(std::string("Value ") + input + " could not be converted to " +
1000 detail::type_name<Number>());
1001 }
1002 // perform safe multiplication
1003 bool ok = detail::checked_multiply(num, it->second);
1004 if(!ok) {
1005 throw ValidationError(detail::to_string(num) + " multiplied by " + unit +
1006 " factor would cause number overflow. Use smaller value.");
1007 }
1008 } else {
1009 num = static_cast<Number>(it->second);
1010 }
1011
1012 input = detail::to_string(num);
1013
1014 return {};
1015 };
1016 }
1017
1018 private:
1021 template <typename Number> static void validate_mapping(std::map<std::string, Number> &mapping, Options opts) {
1022 for(auto &kv : mapping) {
1023 if(kv.first.empty()) {
1024 throw ValidationError("Unit must not be empty.");
1025 }
1026 if(!detail::isalpha(kv.first)) {
1027 throw ValidationError("Unit must contain only letters.");
1028 }
1029 }
1030
1031 // make all units lowercase if CASE_INSENSITIVE
1032 if(opts & CASE_INSENSITIVE) {
1033 std::map<std::string, Number> lower_mapping;
1034 for(auto &kv : mapping) {
1035 auto s = detail::to_lower(kv.first);
1036 if(lower_mapping.count(s)) {
1037 throw ValidationError(std::string("Several matching lowercase unit representations are found: ") +
1038 s);
1039 }
1040 lower_mapping[detail::to_lower(kv.first)] = kv.second;
1041 }
1042 mapping = std::move(lower_mapping);
1043 }
1044 }
1045
1047 template <typename Number> static std::string generate_description(const std::string &name, Options opts) {
1048 std::stringstream out;
1049 out << detail::type_name<Number>() << ' ';
1050 if(opts & UNIT_REQUIRED) {
1051 out << name;
1052 } else {
1053 out << '[' << name << ']';
1054 }
1055 return out.str();
1056 }
1057};
1058
1071 public:
1072 using result_t = std::uint64_t;
1073
1081 explicit AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) {
1082 if(kb_is_1000) {
1083 description("SIZE [b, kb(=1000b), kib(=1024b), ...]");
1084 } else {
1085 description("SIZE [b, kb(=1024b), ...]");
1086 }
1087 }
1088
1089 private:
1091 static std::map<std::string, result_t> init_mapping(bool kb_is_1000) {
1092 std::map<std::string, result_t> m;
1093 result_t k_factor = kb_is_1000 ? 1000 : 1024;
1094 result_t ki_factor = 1024;
1095 result_t k = 1;
1096 result_t ki = 1;
1097 m["b"] = 1;
1098 for(std::string p : {"k", "m", "g", "t", "p", "e"}) {
1099 k *= k_factor;
1100 ki *= ki_factor;
1101 m[p] = k;
1102 m[p + "b"] = k;
1103 m[p + "i"] = ki;
1104 m[p + "ib"] = ki;
1105 }
1106 return m;
1107 }
1108
1110 static std::map<std::string, result_t> get_mapping(bool kb_is_1000) {
1111 if(kb_is_1000) {
1112 static auto m = init_mapping(true);
1113 return m;
1114 } else {
1115 static auto m = init_mapping(false);
1116 return m;
1117 }
1118 }
1119};
1120
1121namespace detail {
1126inline std::pair<std::string, std::string> split_program_name(std::string commandline) {
1127 // try to determine the programName
1128 std::pair<std::string, std::string> vals;
1129 trim(commandline);
1130 auto esp = commandline.find_first_of(' ', 1);
1131 while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) {
1132 esp = commandline.find_first_of(' ', esp + 1);
1133 if(esp == std::string::npos) {
1134 // if we have reached the end and haven't found a valid file just assume the first argument is the
1135 // program name
1136 if(commandline[0] == '"' || commandline[0] == '\'' || commandline[0] == '`') {
1137 bool embeddedQuote = false;
1138 auto keyChar = commandline[0];
1139 auto end = commandline.find_first_of(keyChar, 1);
1140 while((end != std::string::npos) && (commandline[end - 1] == '\\')) { // deal with escaped quotes
1141 end = commandline.find_first_of(keyChar, end + 1);
1142 embeddedQuote = true;
1143 }
1144 if(end != std::string::npos) {
1145 vals.first = commandline.substr(1, end - 1);
1146 esp = end + 1;
1147 if(embeddedQuote) {
1148 vals.first = find_and_replace(vals.first, std::string("\\") + keyChar, std::string(1, keyChar));
1149 }
1150 } else {
1151 esp = commandline.find_first_of(' ', 1);
1152 }
1153 } else {
1154 esp = commandline.find_first_of(' ', 1);
1155 }
1156
1157 break;
1158 }
1159 }
1160 if(vals.first.empty()) {
1161 vals.first = commandline.substr(0, esp);
1162 rtrim(vals.first);
1163 }
1164
1165 // strip the program name
1166 vals.second = (esp != std::string::npos) ? commandline.substr(esp + 1) : std::string{};
1167 ltrim(vals.second);
1168 return vals;
1169}
1170
1171} // namespace detail
1173
1174// [CLI11:validators_hpp:end]
1175} // namespace CLI
Definition Validators.hpp:932
Options
Definition Validators.hpp:938
AsNumberWithUnit(std::map< std::string, Number > mapping, Options opts=DEFAULT, const std::string &unit_name="UNIT")
Definition Validators.hpp:947
Definition Validators.hpp:1070
AsSizeValue(bool kb_is_1000)
Definition Validators.hpp:1081
std::uint64_t result_t
Definition Validators.hpp:1072
Produce a bounded range (factory). Min and max are inclusive.
Definition Validators.hpp:534
Bound(T min_val, T max_val)
Definition Validators.hpp:540
Bound(T max_val)
Range of one value is 0 to value.
Definition Validators.hpp:561
translate named items to other or a value set
Definition Validators.hpp:833
CheckedTransformer(T mapping)
direct map of std::string to std::string
Definition Validators.hpp:843
CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
You can pass in as many filter functions as you like, they nest.
Definition Validators.hpp:901
std::function< std::string(std::string)> filter_fn_t
Definition Validators.hpp:835
CheckedTransformer(std::initializer_list< std::pair< std::string, std::string > > values, Args &&...args)
This allows in-place construction.
Definition Validators.hpp:839
CheckedTransformer(T mapping, F filter_function)
Definition Validators.hpp:847
Class wrapping some of the accessors of Validator.
Definition Validators.hpp:289
Definition Validators.hpp:467
FileOnDefaultPath(std::string default_path, bool enableErrorReturn=true)
Definition Validators.hpp:469
Verify items are in a set.
Definition Validators.hpp:706
IsMember(T &&set)
This checks to see if an item is in a set (empty function)
Definition Validators.hpp:716
IsMember(T set, F filter_function)
Definition Validators.hpp:720
IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
You can pass in as many filter functions as you like, they nest (string only currently)
Definition Validators.hpp:764
IsMember(std::initializer_list< T > values, Args &&...args)
This allows in-place construction using an initializer list.
Definition Validators.hpp:712
std::function< std::string(std::string)> filter_fn_t
Definition Validators.hpp:708
Produce a range (factory). Min and max are inclusive.
Definition Validators.hpp:494
Range(T min_val, T max_val, const std::string &validator_name=std::string{})
Definition Validators.hpp:501
Range(T max_val, const std::string &validator_name=std::string{})
Range of one value is 0 to value.
Definition Validators.hpp:523
Translate named items to other or a value set.
Definition Validators.hpp:775
Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
You can pass in as many filter functions as you like, they nest.
Definition Validators.hpp:825
Transformer(std::initializer_list< std::pair< std::string, std::string > > values, Args &&...args)
This allows in-place construction.
Definition Validators.hpp:781
std::function< std::string(std::string)> filter_fn_t
Definition Validators.hpp:777
Transformer(T &&mapping)
direct map of std::string to std::string
Definition Validators.hpp:785
Transformer(T mapping, F filter_function)
Definition Validators.hpp:789
Validate the input as a particular type.
Definition Validators.hpp:448
TypeValidator()
Definition Validators.hpp:459
TypeValidator(const std::string &validator_name)
Definition Validators.hpp:450
Thrown when validation of results fails.
Definition Error.hpp:212
Some validators that are provided.
Definition Validators.hpp:80
bool get_active() const
Get a boolean if the validator is active.
Definition Validators.hpp:194
int application_index_
A Validator will only apply to an indexed value (-1 is all elements)
Definition Validators.hpp:91
Validator & non_modifying(bool no_modify=true)
Specify whether the Validator can be modifying or not.
Definition Validators.hpp:176
Validator & description(std::string validator_desc)
Specify the type string.
Definition Validators.hpp:133
std::string operator()(const std::string &str) const
Definition Validators.hpp:127
Validator operator&(const Validator &other) const
Definition Validators.hpp:201
bool active_
Enable for Validator to allow it to be disabled if need be.
Definition Validators.hpp:93
Validator name(std::string validator_name) const
Specify the type string.
Definition Validators.hpp:156
Validator application_index(int app_index) const
Specify the application index of a validator.
Definition Validators.hpp:186
bool non_modifying_
specify that a validator should not modify the input
Definition Validators.hpp:95
const std::string & get_name() const
Get the name of the Validator.
Definition Validators.hpp:162
Validator(std::string validator_desc)
Construct a Validator with just the description string.
Definition Validators.hpp:100
std::string get_description() const
Generate type description information for the Validator.
Definition Validators.hpp:144
Validator & name(std::string validator_name)
Specify the type string.
Definition Validators.hpp:151
Validator()=default
std::string operator()(std::string &str) const
Definition Validators.hpp:112
Validator active(bool active_val=true) const
Specify whether the Validator is active or not.
Definition Validators.hpp:169
std::function< std::string()> desc_function_
This is the description function, if empty the description_ will be used.
Definition Validators.hpp:83
int get_application_index() const
Get the current value of the application index.
Definition Validators.hpp:192
std::function< std::string(std::string &)> func_
Definition Validators.hpp:87
Validator operator!() const
Create a validator that fails when a given validator succeeds.
Definition Validators.hpp:249
Validator operator|(const Validator &other) const
Definition Validators.hpp:226
Validator & active(bool active_val=true)
Specify whether the Validator is active or not.
Definition Validators.hpp:164
std::string name_
The name for search purposes of the Validator.
Definition Validators.hpp:89
Validator description(std::string validator_desc) const
Specify the type string.
Definition Validators.hpp:138
bool get_modifying() const
Get a boolean if the validator is allowed to modify the input returns true if it can modify the input...
Definition Validators.hpp:197
Validator & application_index(int app_index)
Specify the application index of a validator.
Definition Validators.hpp:181
Validator(std::function< std::string(std::string &)> op, std::string validator_desc, std::string validator_name="")
Construct Validator from basic information.
Definition Validators.hpp:102
Validator & operation(std::function< std::string(std::string &)> op)
Set the Validator operation function.
Definition Validators.hpp:106
Check for an existing directory (returns error message if check fails)
Definition Validators.hpp:360
ExistingDirectoryValidator()
Definition Validators.hpp:362
Check for an existing file (returns error message if check fails)
Definition Validators.hpp:343
ExistingFileValidator()
Definition Validators.hpp:345
Check for an existing path.
Definition Validators.hpp:377
ExistingPathValidator()
Definition Validators.hpp:379
Validate the given string is a legal ipv4 address.
Definition Validators.hpp:405
IPV4Validator()
Definition Validators.hpp:407
Check for an non-existing path.
Definition Validators.hpp:391
NonexistentPathValidator()
Definition Validators.hpp:393
auto smart_deref(T value) -> decltype(*value)
Definition Validators.hpp:567
path_type check_path(const char *file) noexcept
get the type of the path from a file name
Definition Validators.hpp:327
path_type
CLI enumeration of different file types.
Definition Validators.hpp:298
std::string generate_map(const T &map, bool key_only=false)
Generate a string representation of a map.
Definition Validators.hpp:591
std::string & rtrim(std::string &str)
Trim whitespace from right of string.
Definition StringTools.hpp:123
std::enable_if< std::is_signed< T >::value, T >::type overflowCheck(const T &a, const T &b)
Do a check for overflow on signed numbers.
Definition Validators.hpp:664
std::enable_if< std::is_integral< T >::value, bool >::type checked_multiply(T &a, T b)
Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise.
Definition Validators.hpp:678
std::string & trim(std::string &str)
Trim whitespace from string.
Definition StringTools.hpp:138
std::string generate_set(const T &set)
Generate a string representation of a set.
Definition Validators.hpp:578
std::pair< std::string, std::string > split_program_name(std::string commandline)
Definition Validators.hpp:1126
std::string & ltrim(std::string &str)
Trim whitespace from left of string.
Definition StringTools.hpp:109
auto search(const T &set, const V &val) -> std::pair< bool, decltype(std::begin(detail::smart_deref(set)))>
A search function.
Definition Validators.hpp:622
std::string find_and_replace(std::string str, std::string from, std::string to)
Find and replace a substring with another substring.
Definition StringTools.hpp:273
std::vector< std::string > split(const std::string &s, char delim)
Split a string by a delim.
Definition StringTools.hpp:46
bool embeddedQuote
Definition StringTools.hpp:355
char keyChar
Definition StringTools.hpp:356
enabler
Simple empty scoped class.
Definition TypeTools.hpp:31
bool lexical_cast(const std::string &input, T &output)
Integer conversion.
Definition TypeTools.hpp:883
Definition App.hpp:34
std::string ignore_case(std::string item)
Helper function to allow ignore_case to be passed to IsMember or Transform.
Definition Validators.hpp:909
std::string ignore_underscore(std::string item)
Helper function to allow ignore_underscore to be passed to IsMember or Transform.
Definition Validators.hpp:912
const detail::ExistingDirectoryValidator ExistingDirectory
Check for an existing directory (returns error message if check fails)
Definition Validators.hpp:436
typename std::enable_if< B, T >::type enable_if_t
Definition TypeTools.hpp:42
const detail::NonexistentPathValidator NonexistentPath
Check for an non-existing path.
Definition Validators.hpp:442
const detail::IPV4Validator ValidIPV4
Check for an IP4 address.
Definition Validators.hpp:445
const detail::ExistingPathValidator ExistingPath
Check for an existing path.
Definition Validators.hpp:439
const detail::ExistingFileValidator ExistingFile
Check for existing file (returns error message if check fails)
Definition Validators.hpp:433
const Range NonNegativeNumber((std::numeric_limits< double >::max)(), "NONNEGATIVE")
Check for a non negative number.
std::string ignore_space(std::string item)
Helper function to allow checks to ignore spaces to be passed to IsMember or Transform.
Definition Validators.hpp:915
const Range PositiveNumber((std::numeric_limits< double >::min)(),(std::numeric_limits< double >::max)(), "POSITIVE")
Check for a positive valued number (val>0.0), min() her is the smallest positive number.
const TypeValidator< double > Number("NUMBER")
Check for a number.
std::vector< std::pair< std::string, T > > TransformPairs
definition of the default transformation object
Definition Validators.hpp:772
T type
Definition TypeTools.hpp:74
T type
Definition TypeTools.hpp:87
Definition Validators.hpp:611
std::integral_constant< bool, value > type
Definition Validators.hpp:617
static auto test(int) -> decltype(std::declval< CC >().find(std::declval< VV >()), std::true_type())
static auto test(...) -> decltype(std::false_type())
Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost ...
Definition TypeTools.hpp:98
typename T::value_type value_type
Definition TypeTools.hpp:99
typename std::remove_const< value_type >::type first_type
Definition TypeTools.hpp:100