6 #include "CLI/StringTools.hpp" 7 #include "CLI/TypeTools.hpp" 20 #include <sys/types.h> 43 std::function<std::string(std::string &)>
func_{[](std::string &) {
return std::string{}; }};
56 Validator(std::function<std::string(std::string &)> op, std::string validator_desc, std::string validator_name =
"")
58 name_(std::move(validator_name)) {}
61 func_ = std::move(op);
67 std::string retstring;
70 std::string value = str;
71 retstring =
func_(value);
73 retstring =
func_(str);
82 std::string value = str;
100 name_ = std::move(validator_name);
128 newval._merge_description(*
this, other,
" AND ");
131 const std::function<std::string(std::string & filename)> &f1 =
func_;
132 const std::function<std::string(std::string & filename)> &f2 = other.
func_;
134 newval.
func_ = [f1, f2](std::string &input) {
135 std::string s1 = f1(input);
136 std::string s2 = f2(input);
137 if(!s1.empty() && !s2.empty())
138 return std::string(
"(") + s1 +
") AND (" + s2 +
")";
152 newval._merge_description(*
this, other,
" OR ");
155 const std::function<std::string(std::string &)> &f1 =
func_;
156 const std::function<std::string(std::string &)> &f2 = other.
func_;
158 newval.
func_ = [f1, f2](std::string &input) {
159 std::string s1 = f1(input);
160 std::string s2 = f2(input);
161 if(s1.empty() || s2.empty())
162 return std::string();
164 return std::string(
"(") + s1 +
") OR (" + s2 +
")";
176 return (!str.empty()) ? std::string(
"NOT ") + str : std::string{};
179 const std::function<std::string(std::string & res)> &f1 =
func_;
181 newval.
func_ = [f1, dfunc1](std::string &test) -> std::string {
182 std::string s1 = f1(test);
184 return std::string(
"check ") + dfunc1() +
" succeeded improperly";
186 return std::string{};
193 void _merge_description(
const Validator &val1,
const Validator &val2,
const std::string &merger) {
199 std::string f1 = dfunc1();
200 std::string f2 = dfunc2();
201 if((f1.empty()) || (f2.empty())) {
204 return std::string(
"(") + f1 +
")" + merger +
"(" + f2 +
")";
222 func_ = [](std::string &filename) {
224 bool exist = stat(filename.c_str(), &buffer) == 0;
225 bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
227 return "File does not exist: " + filename;
229 return "File is actually a directory: " + filename;
231 return std::string();
240 func_ = [](std::string &filename) {
242 bool exist = stat(filename.c_str(), &buffer) == 0;
243 bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
245 return "Directory does not exist: " + filename;
247 return "Directory is actually a file: " + filename;
249 return std::string();
258 func_ = [](std::string &filename) {
260 bool const exist = stat(filename.c_str(), &buffer) == 0;
262 return "Path does not exist: " + filename;
264 return std::string();
273 func_ = [](std::string &filename) {
275 bool exist = stat(filename.c_str(), &buffer) == 0;
277 return "Path already exists: " + filename;
279 return std::string();
288 func_ = [](std::string &ip_addr) {
289 auto result = CLI::detail::split(ip_addr,
'.');
290 if(result.size() != 4) {
291 return "Invalid IPV4 address must have four parts " + ip_addr;
295 for(
const auto &var : result) {
296 retval &= detail::lexical_cast(var, num);
298 return "Failed parsing number " + var;
300 if(num < 0 || num > 255) {
301 return "Each IP number must be between 0 and 255 " + var;
304 return std::string();
313 func_ = [](std::string &number_str) {
315 if(!detail::lexical_cast(number_str, number)) {
316 return "Failed parsing number " + number_str;
319 return "Number less then 0 " + number_str;
321 return std::string();
330 func_ = [](std::string &number_str) {
332 if(!detail::lexical_cast(number_str, number)) {
333 return "Failed parsing as a number " + number_str;
335 return std::string();
372 template <
typename T>
Range(T min, T max) {
373 std::stringstream out;
374 out << detail::type_name<T>() <<
" in [" << min <<
" - " << max <<
"]";
377 func_ = [min, max](std::string &input) {
379 bool converted = detail::lexical_cast(input, val);
380 if((!converted) || (val < min || val > max))
381 return "Value " + input +
" not in range " + std::to_string(min) +
" to " + std::to_string(max);
383 return std::string();
388 template <
typename T>
explicit Range(T max) :
Range(static_cast<T>(0), max) {}
398 template <
typename T>
Bound(T min, T max) {
399 std::stringstream out;
400 out << detail::type_name<T>() <<
" bounded to [" << min <<
" - " << max <<
"]";
403 func_ = [min, max](std::string &input) {
405 bool converted = detail::lexical_cast(input, val);
407 return "Value " + input +
" could not be converted";
410 input = detail::as_string(min);
412 input = detail::as_string(max);
414 return std::string();
419 template <
typename T>
explicit Bound(T max) :
Bound(static_cast<T>(0), max) {}
423 template <
typename T,
424 enable_if_t<is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
425 auto smart_deref(T value) -> decltype(*value) {
431 enable_if_t<!is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
432 typename std::remove_reference<T>::type &smart_deref(T &value) {
436 template <
typename T> std::string generate_set(
const T &set) {
437 using element_t =
typename detail::element_type<T>::type;
438 using iteration_type_t =
typename detail::pair_adaptor<element_t>::value_type;
439 std::string out(1,
'{');
440 out.append(detail::join(detail::smart_deref(set),
448 template <
typename T> std::string generate_map(
const T &map,
bool key_only =
false) {
449 using element_t =
typename detail::element_type<T>::type;
450 using iteration_type_t =
typename detail::pair_adaptor<element_t>::value_type;
451 std::string out(1,
'{');
452 out.append(detail::join(detail::smart_deref(map),
453 [key_only](
const iteration_type_t &v) {
468 template <
typename T,
typename V>
470 template <
typename,
typename V>
static auto test_find(
long) -> std::false_type;
472 template <
typename T,
typename V>
struct has_find : decltype(test_find<T, V>(0)) {};
475 template <typename T, typename V, enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>
476 auto search(
const T &set,
const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
477 using element_t =
typename detail::element_type<T>::type;
478 auto &setref = detail::smart_deref(set);
479 auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) {
482 return {(it != std::end(setref)), it};
486 template <typename T, typename V, enable_if_t<has_find<T, V>::value, detail::enabler> = detail::dummy>
487 auto search(
const T &set,
const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
488 auto &setref = detail::smart_deref(set);
489 auto it = setref.find(val);
490 return {(it != std::end(setref)), it};
494 template <
typename T,
typename V>
495 auto search(
const T &set,
const V &val,
const std::function<V(V)> &filter_function)
496 -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
497 using element_t =
typename detail::element_type<T>::type;
499 auto res = search(set, val);
500 if((res.first) || (!(filter_function))) {
504 auto &setref = detail::smart_deref(set);
505 auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) {
507 a = filter_function(a);
510 return {(it != std::end(setref)), it};
514 template <
typename T>
typename std::enable_if<std::is_integral<T>::value,
bool>::type checked_multiply(T &a, T b) {
515 if(a == 0 || b == 0) {
528 template <
typename T>
529 typename std::enable_if<std::is_floating_point<T>::value,
bool>::type checked_multiply(T &a, T b) {
531 if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {
542 using filter_fn_t = std::function<std::string(std::string)>;
545 template <
typename T,
typename... Args>
546 explicit IsMember(std::initializer_list<T> values, Args &&... args)
547 :
IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
550 template <
typename T>
explicit IsMember(T &&set) :
IsMember(std::forward<T>(set), nullptr) {}
554 template <
typename T,
typename F>
explicit IsMember(T set, F filter_function) {
558 using element_t =
typename detail::element_type<T>::type;
559 using item_t =
typename detail::pair_adaptor<element_t>::first_type;
561 using local_item_t =
typename IsMemberType<item_t>::type;
565 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
568 desc_function_ = [set]() {
return detail::generate_set(detail::smart_deref(set)); };
572 func_ = [set, filter_fn](std::string &input) {
574 if(!detail::lexical_cast(input, b)) {
580 auto res = detail::search(set, b, filter_fn);
588 return std::string{};
592 std::string out(
" not in ");
593 out += detail::generate_set(detail::smart_deref(set));
599 template <
typename T,
typename... Args>
600 IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
602 [filter_fn_1, filter_fn_2](std::string a) {
return filter_fn_2(filter_fn_1(a)); },
607 template <
typename T>
using TransformPairs = std::vector<std::pair<std::string, T>>;
612 using filter_fn_t = std::function<std::string(std::string)>;
615 template <
typename... Args>
616 explicit Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
617 :
Transformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
624 template <
typename T,
typename F>
explicit Transformer(T mapping, F filter_function) {
627 "mapping must produce value pairs");
630 using element_t =
typename detail::element_type<T>::type;
631 using item_t =
typename detail::pair_adaptor<element_t>::first_type;
632 using local_item_t =
typename IsMemberType<item_t>::type;
636 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
639 desc_function_ = [mapping]() {
return detail::generate_map(detail::smart_deref(mapping)); };
641 func_ = [mapping, filter_fn](std::string &input) {
643 if(!detail::lexical_cast(input, b)) {
644 return std::string();
650 auto res = detail::search(mapping, b, filter_fn);
654 return std::string{};
659 template <
typename T,
typename... Args>
660 Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
662 [filter_fn_1, filter_fn_2](std::string a) {
return filter_fn_2(filter_fn_1(a)); },
669 using filter_fn_t = std::function<std::string(std::string)>;
672 template <
typename... Args>
673 explicit CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
674 :
CheckedTransformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
684 "mapping must produce value pairs");
687 using element_t =
typename detail::element_type<T>::type;
688 using item_t =
typename detail::pair_adaptor<element_t>::first_type;
689 using local_item_t =
typename IsMemberType<item_t>::type;
691 using iteration_type_t =
typename detail::pair_adaptor<element_t>::value_type;
695 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
697 auto tfunc = [mapping]() {
698 std::string out(
"value in ");
699 out += detail::generate_map(detail::smart_deref(mapping)) +
" OR {";
701 detail::smart_deref(mapping),
710 func_ = [mapping, tfunc, filter_fn](std::string &input) {
712 bool converted = detail::lexical_cast(input, b);
717 auto res = detail::search(mapping, b, filter_fn);
720 return std::string{};
723 for(
const auto &v : detail::smart_deref(mapping)) {
725 if(output_string == input) {
726 return std::string();
730 return "Check " + input +
" " + tfunc() +
" FAILED";
735 template <
typename T,
typename... Args>
738 [filter_fn_1, filter_fn_2](std::string a) {
return filter_fn_2(filter_fn_1(a)); },
743 inline std::string ignore_case(std::string item) {
return detail::to_lower(item); }
746 inline std::string ignore_underscore(std::string item) {
return detail::remove_underscore(item); }
749 inline std::string ignore_space(std::string item) {
750 item.erase(std::remove(std::begin(item), std::end(item),
' '), std::end(item));
751 item.erase(std::remove(std::begin(item), std::end(item),
'\t'), std::end(item));
774 CASE_INSENSITIVE = 1,
777 DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL
780 template <
typename Number>
783 const std::string &unit_name =
"UNIT") {
784 description(generate_description<Number>(unit_name, opts));
785 validate_mapping(mapping, opts);
788 func_ = [mapping, opts](std::string &input) -> std::string {
791 detail::rtrim(input);
797 auto unit_begin = input.end();
798 while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
802 std::string unit{unit_begin, input.end()};
803 input.resize(static_cast<size_t>(std::distance(input.begin(), unit_begin)));
806 if(opts & UNIT_REQUIRED && unit.empty()) {
807 throw ValidationError(
"Missing mandatory unit");
809 if(opts & CASE_INSENSITIVE) {
810 unit = detail::to_lower(unit);
813 bool converted = detail::lexical_cast(input, num);
815 throw ValidationError(
"Value " + input +
" could not be converted to " + detail::type_name<Number>());
824 auto it = mapping.find(unit);
825 if(it == mapping.end()) {
826 throw ValidationError(unit +
827 " unit not recognized. " 829 detail::generate_map(mapping,
true));
833 bool ok = detail::checked_multiply(num, it->second);
835 throw ValidationError(detail::as_string(num) +
" multiplied by " + unit +
836 " factor would cause number overflow. Use smaller value.");
838 input = detail::as_string(num);
847 template <
typename Number>
static void validate_mapping(std::map<std::string, Number> &mapping,
Options opts) {
848 for(
auto &kv : mapping) {
849 if(kv.first.empty()) {
850 throw ValidationError(
"Unit must not be empty.");
852 if(!detail::isalpha(kv.first)) {
853 throw ValidationError(
"Unit must contain only letters.");
858 if(opts & CASE_INSENSITIVE) {
859 std::map<std::string, Number> lower_mapping;
860 for(
auto &kv : mapping) {
861 auto s = detail::to_lower(kv.first);
862 if(lower_mapping.count(s)) {
863 throw ValidationError(
"Several matching lowercase unit representations are found: " + s);
865 lower_mapping[detail::to_lower(kv.first)] = kv.second;
867 mapping = std::move(lower_mapping);
872 template <
typename Number>
static std::string generate_description(
const std::string &
name,
Options opts) {
873 std::stringstream out;
874 out << detail::type_name<Number>() <<
' ';
875 if(opts & UNIT_REQUIRED) {
878 out <<
'[' <<
name <<
']';
897 using result_t = uint64_t;
908 description(
"SIZE [b, kb(=1000b), kib(=1024b), ...]");
916 static std::map<std::string, result_t> init_mapping(
bool kb_is_1000) {
917 std::map<std::string, result_t> m;
918 result_t k_factor = kb_is_1000 ? 1000 : 1024;
919 result_t ki_factor = 1024;
923 for(std::string p : {
"k",
"m",
"g",
"t",
"p",
"e"}) {
935 static std::map<std::string, result_t> get_mapping(
bool kb_is_1000) {
937 static auto m = init_mapping(
true);
940 static auto m = init_mapping(
false);
951 inline std::pair<std::string, std::string> split_program_name(std::string commandline) {
953 std::pair<std::string, std::string> vals;
955 auto esp = commandline.find_first_of(
' ', 1);
956 while(!ExistingFile(commandline.substr(0, esp)).empty()) {
957 esp = commandline.find_first_of(
' ', esp + 1);
958 if(esp == std::string::npos) {
961 esp = commandline.find_first_of(
' ', 1);
965 vals.first = commandline.substr(0, esp);
968 vals.second = (esp != std::string::npos) ? commandline.substr(esp + 1) : std::string{};
const std::string & get_name() const
Get the name of the Validator.
Definition: Validators.hpp:104
Definition: Validators.hpp:465
Bound(T max)
Range of one value is 0 to value.
Definition: Validators.hpp:419
Check for an non-existing path.
Definition: Validators.hpp:270
Validator & operation(std::function< std::string(std::string &)> op)
Set the Validator operation function.
Definition: Validators.hpp:60
IsMember(T &&set)
This checks to see if an item is in a set (empty function)
Definition: Validators.hpp:550
std::string operator()(const std::string &str) const
Definition: Validators.hpp:81
bool non_modifying_
specify that a validator should not modify the input
Definition: Validators.hpp:49
Class wrapping some of the accessors of Validator.
Definition: Validators.hpp:210
Produce a range (factory). Min and max are inclusive.
Definition: Validators.hpp:366
Validate the given string is a legal ipv4 address.
Definition: Validators.hpp:285
Some validators that are provided.
Definition: Validators.hpp:36
Validator & active(bool active_val=true)
Specify whether the Validator is active or not.
Definition: Validators.hpp:106
Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost ...
Definition: TypeTools.hpp:91
Bound(T min, T max)
Definition: Validators.hpp:398
Verify items are in a set.
Definition: Validators.hpp:540
Range(T min, T max)
Definition: Validators.hpp:372
Check for an existing path.
Definition: Validators.hpp:255
Check for an existing file (returns error message if check fails)
Definition: Validators.hpp:219
Validator operator&(const Validator &other) const
Definition: Validators.hpp:125
Validator operator!() const
Create a validator that fails when a given validator succeeds.
Definition: Validators.hpp:171
Check for an existing directory (returns error message if check fails)
Definition: Validators.hpp:237
IsMember(std::initializer_list< T > values, Args &&... args)
This allows in-place construction using an initializer list.
Definition: Validators.hpp:546
std::string get_description() const
Generate type description information for the Validator.
Definition: Validators.hpp:92
Validate the argument is a number and greater than or equal to 0.
Definition: Validators.hpp:327
AsSizeValue(bool kb_is_1000)
Definition: Validators.hpp:906
Range(T max)
Range of one value is 0 to value.
Definition: Validators.hpp:388
bool active_
Enable for Validator to allow it to be disabled if need be.
Definition: Validators.hpp:47
Definition: Validators.hpp:895
Validator & description(std::string validator_desc)
Specify the type string.
Definition: Validators.hpp:87
std::function< std::string()> desc_function_
This is the description function, if empty the description_ will be used.
Definition: Validators.hpp:39
Validate the argument is a number and greater than or equal to 0.
Definition: Validators.hpp:310
Validator & name(std::string validator_name)
Specify the type string.
Definition: Validators.hpp:99
std::string name_
The name for search purposes of the Validator.
Definition: Validators.hpp:45
bool get_active() const
Get a boolean if the validator is active.
Definition: Validators.hpp:118
static auto first(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the first value (really just the underlying value)
Definition: TypeTools.hpp:97
Definition: Validators.hpp:472
Options
Definition: Validators.hpp:772
Validator(std::string validator_desc)
Construct a Validator with just the description string.
Definition: Validators.hpp:54
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:121
Validator operator|(const Validator &other) const
Definition: Validators.hpp:149
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:600
Validator & non_modifying(bool no_modify=true)
Specify whether the Validator can be modifying or not.
Definition: Validators.hpp:112
Produce a bounded range (factory). Min and max are inclusive.
Definition: Validators.hpp:392
std::function< std::string(std::string &)> func_
Definition: Validators.hpp:43
std::string operator()(std::string &str) const
Definition: Validators.hpp:66
IsMember(T set, F filter_function)
Definition: Validators.hpp:554
static auto second(Q &&pair_value) -> decltype(std::forward< Q >(pair_value))
Get the second value (really just the underlying value)
Definition: TypeTools.hpp:101
Definition: Validators.hpp:766
Thrown when validation of results fails.
Definition: Error.hpp:198