CLI11
C++11 Command Line Interface Parser
Validators.hpp
1 #pragma once
2 
3 // Distributed under the 3-Clause BSD License. See accompanying
4 // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
5 
6 #include "CLI/StringTools.hpp"
7 #include "CLI/TypeTools.hpp"
8 
9 #include <cmath>
10 #include <functional>
11 #include <iostream>
12 #include <map>
13 #include <memory>
14 #include <string>
15 
16 // C standard library
17 // Only needed for existence checking
18 // Could be swapped for filesystem in C++17
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 
22 namespace CLI {
23 
24 class Option;
25 
27 
34 
36 class Validator {
37  protected:
39  std::function<std::string()> desc_function_{[]() { return std::string{}; }};
40 
43  std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
45  std::string name_;
47  bool active_{true};
49  bool non_modifying_{false};
50 
51  public:
52  Validator() = default;
54  explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {}
55  // Construct Validator from basic information
56  Validator(std::function<std::string(std::string &)> op, std::string validator_desc, std::string validator_name = "")
57  : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)),
58  name_(std::move(validator_name)) {}
60  Validator &operation(std::function<std::string(std::string &)> op) {
61  func_ = std::move(op);
62  return *this;
63  }
66  std::string operator()(std::string &str) const {
67  std::string retstring;
68  if(active_) {
69  if(non_modifying_) {
70  std::string value = str;
71  retstring = func_(value);
72  } else {
73  retstring = func_(str);
74  }
75  }
76  return retstring;
77  };
78 
81  std::string operator()(const std::string &str) const {
82  std::string value = str;
83  return (active_) ? func_(value) : std::string{};
84  };
85 
87  Validator &description(std::string validator_desc) {
88  desc_function_ = [validator_desc]() { return validator_desc; };
89  return *this;
90  }
92  std::string get_description() const {
93  if(active_) {
94  return desc_function_();
95  }
96  return std::string{};
97  }
99  Validator &name(std::string validator_name) {
100  name_ = std::move(validator_name);
101  return *this;
102  }
104  const std::string &get_name() const { return name_; }
106  Validator &active(bool active_val = true) {
107  active_ = active_val;
108  return *this;
109  }
110 
112  Validator &non_modifying(bool no_modify = true) {
113  non_modifying_ = no_modify;
114  return *this;
115  }
116 
118  bool get_active() const { return active_; }
119 
121  bool get_modifying() const { return !non_modifying_; }
122 
125  Validator operator&(const Validator &other) const {
126  Validator newval;
127 
128  newval._merge_description(*this, other, " AND ");
129 
130  // Give references (will make a copy in lambda function)
131  const std::function<std::string(std::string & filename)> &f1 = func_;
132  const std::function<std::string(std::string & filename)> &f2 = other.func_;
133 
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 + ")";
139  else
140  return s1 + s2;
141  };
142 
143  newval.active_ = (active_ & other.active_);
144  return newval;
145  }
146 
149  Validator operator|(const Validator &other) const {
150  Validator newval;
151 
152  newval._merge_description(*this, other, " OR ");
153 
154  // Give references (will make a copy in lambda function)
155  const std::function<std::string(std::string &)> &f1 = func_;
156  const std::function<std::string(std::string &)> &f2 = other.func_;
157 
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();
163  else
164  return std::string("(") + s1 + ") OR (" + s2 + ")";
165  };
166  newval.active_ = (active_ & other.active_);
167  return newval;
168  }
169 
172  Validator newval;
173  const std::function<std::string()> &dfunc1 = desc_function_;
174  newval.desc_function_ = [dfunc1]() {
175  auto str = dfunc1();
176  return (!str.empty()) ? std::string("NOT ") + str : std::string{};
177  };
178  // Give references (will make a copy in lambda function)
179  const std::function<std::string(std::string & res)> &f1 = func_;
180 
181  newval.func_ = [f1, dfunc1](std::string &test) -> std::string {
182  std::string s1 = f1(test);
183  if(s1.empty()) {
184  return std::string("check ") + dfunc1() + " succeeded improperly";
185  } else
186  return std::string{};
187  };
188  newval.active_ = active_;
189  return newval;
190  }
191 
192  private:
193  void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger) {
194 
195  const std::function<std::string()> &dfunc1 = val1.desc_function_;
196  const std::function<std::string()> &dfunc2 = val2.desc_function_;
197 
198  desc_function_ = [=]() {
199  std::string f1 = dfunc1();
200  std::string f2 = dfunc2();
201  if((f1.empty()) || (f2.empty())) {
202  return f1 + f2;
203  }
204  return std::string("(") + f1 + ")" + merger + "(" + f2 + ")";
205  };
206  }
207 };
208 
210 class CustomValidator : public Validator {
211  public:
212 };
213 // The implementation of the built in validators is using the Validator class;
214 // the user is only expected to use the const (static) versions (since there's no setup).
215 // Therefore, this is in detail.
216 namespace detail {
217 
220  public:
221  ExistingFileValidator() : Validator("FILE") {
222  func_ = [](std::string &filename) {
223  struct stat buffer;
224  bool exist = stat(filename.c_str(), &buffer) == 0;
225  bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
226  if(!exist) {
227  return "File does not exist: " + filename;
228  } else if(is_dir) {
229  return "File is actually a directory: " + filename;
230  }
231  return std::string();
232  };
233  }
234 };
235 
238  public:
240  func_ = [](std::string &filename) {
241  struct stat buffer;
242  bool exist = stat(filename.c_str(), &buffer) == 0;
243  bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
244  if(!exist) {
245  return "Directory does not exist: " + filename;
246  } else if(!is_dir) {
247  return "Directory is actually a file: " + filename;
248  }
249  return std::string();
250  };
251  }
252 };
253 
256  public:
257  ExistingPathValidator() : Validator("PATH(existing)") {
258  func_ = [](std::string &filename) {
259  struct stat buffer;
260  bool const exist = stat(filename.c_str(), &buffer) == 0;
261  if(!exist) {
262  return "Path does not exist: " + filename;
263  }
264  return std::string();
265  };
266  }
267 };
268 
271  public:
272  NonexistentPathValidator() : Validator("PATH(non-existing)") {
273  func_ = [](std::string &filename) {
274  struct stat buffer;
275  bool exist = stat(filename.c_str(), &buffer) == 0;
276  if(exist) {
277  return "Path already exists: " + filename;
278  }
279  return std::string();
280  };
281  }
282 };
283 
285 class IPV4Validator : public Validator {
286  public:
287  IPV4Validator() : Validator("IPV4") {
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;
292  }
293  int num;
294  bool retval = true;
295  for(const auto &var : result) {
296  retval &= detail::lexical_cast(var, num);
297  if(!retval) {
298  return "Failed parsing number " + var;
299  }
300  if(num < 0 || num > 255) {
301  return "Each IP number must be between 0 and 255 " + var;
302  }
303  }
304  return std::string();
305  };
306  }
307 };
308 
310 class PositiveNumber : public Validator {
311  public:
312  PositiveNumber() : Validator("POSITIVE") {
313  func_ = [](std::string &number_str) {
314  int number;
315  if(!detail::lexical_cast(number_str, number)) {
316  return "Failed parsing number " + number_str;
317  }
318  if(number < 0) {
319  return "Number less then 0 " + number_str;
320  }
321  return std::string();
322  };
323  }
324 };
325 
327 class Number : public Validator {
328  public:
329  Number() : Validator("NUMBER") {
330  func_ = [](std::string &number_str) {
331  double number;
332  if(!detail::lexical_cast(number_str, number)) {
333  return "Failed parsing as a number " + number_str;
334  }
335  return std::string();
336  };
337  }
338 };
339 
340 } // namespace detail
341 
342 // Static is not needed here, because global const implies static.
343 
345 const detail::ExistingFileValidator ExistingFile;
346 
348 const detail::ExistingDirectoryValidator ExistingDirectory;
349 
351 const detail::ExistingPathValidator ExistingPath;
352 
354 const detail::NonexistentPathValidator NonexistentPath;
355 
357 const detail::IPV4Validator ValidIPV4;
358 
360 const detail::PositiveNumber PositiveNumber;
361 
363 const detail::Number Number;
364 
366 class Range : public Validator {
367  public:
372  template <typename T> Range(T min, T max) {
373  std::stringstream out;
374  out << detail::type_name<T>() << " in [" << min << " - " << max << "]";
375  description(out.str());
376 
377  func_ = [min, max](std::string &input) {
378  T val;
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);
382 
383  return std::string();
384  };
385  }
386 
388  template <typename T> explicit Range(T max) : Range(static_cast<T>(0), max) {}
389 };
390 
392 class Bound : public Validator {
393  public:
398  template <typename T> Bound(T min, T max) {
399  std::stringstream out;
400  out << detail::type_name<T>() << " bounded to [" << min << " - " << max << "]";
401  description(out.str());
402 
403  func_ = [min, max](std::string &input) {
404  T val;
405  bool converted = detail::lexical_cast(input, val);
406  if(!converted) {
407  return "Value " + input + " could not be converted";
408  }
409  if(val < min)
410  input = detail::as_string(min);
411  else if(val > max)
412  input = detail::as_string(max);
413 
414  return std::string();
415  };
416  }
417 
419  template <typename T> explicit Bound(T max) : Bound(static_cast<T>(0), max) {}
420 };
421 
422 namespace detail {
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) {
426  return *value;
427 }
428 
429 template <
430  typename T,
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) {
433  return value;
434 }
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; // the type of the object pair
439  std::string out(1, '{');
440  out.append(detail::join(detail::smart_deref(set),
441  [](const iteration_type_t &v) { return detail::pair_adaptor<element_t>::first(v); },
442  ","));
443  out.push_back('}');
444  return out;
445 }
446 
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; // the type of the object pair
451  std::string out(1, '{');
452  out.append(detail::join(detail::smart_deref(map),
453  [key_only](const iteration_type_t &v) {
454  auto res = detail::as_string(detail::pair_adaptor<element_t>::first(v));
455  if(!key_only) {
456  res += "->" + detail::as_string(detail::pair_adaptor<element_t>::second(v));
457  }
458  return res;
459  },
460  ","));
461  out.push_back('}');
462  return out;
463 }
464 
465 template <typename> struct sfinae_true : std::true_type {};
468 template <typename T, typename V>
469 static auto test_find(int) -> sfinae_true<decltype(std::declval<T>().find(std::declval<V>()))>;
470 template <typename, typename V> static auto test_find(long) -> std::false_type;
471 
472 template <typename T, typename V> struct has_find : decltype(test_find<T, V>(0)) {};
473 
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) {
480  return (detail::pair_adaptor<element_t>::first(v) == val);
481  });
482  return {(it != std::end(setref)), it};
483 }
484 
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};
491 }
492 
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;
498  // do the potentially faster first search
499  auto res = search(set, val);
500  if((res.first) || (!(filter_function))) {
501  return res;
502  }
503  // if we haven't found it do the longer linear search with all the element translations
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);
508  return (a == val);
509  });
510  return {(it != std::end(setref)), it};
511 }
512 
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) {
516  a *= b;
517  return true;
518  }
519  T c = a * b;
520  if(c / a != b) {
521  return false;
522  }
523  a = c;
524  return true;
525 }
526 
528 template <typename T>
529 typename std::enable_if<std::is_floating_point<T>::value, bool>::type checked_multiply(T &a, T b) {
530  T c = a * b;
531  if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {
532  return false;
533  }
534  a = c;
535  return true;
536 }
537 
538 } // namespace detail
540 class IsMember : public Validator {
541  public:
542  using filter_fn_t = std::function<std::string(std::string)>;
543 
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)...) {}
548 
550  template <typename T> explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}
551 
554  template <typename T, typename F> explicit IsMember(T set, F filter_function) {
555 
556  // Get the type of the contained item - requires a container have ::value_type
557  // if the type does not have first_type and second_type, these are both value_type
558  using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
559  using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
560 
561  using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
562  // (const char * to std::string)
563 
564  // Make a local copy of the filter function, using a std::function if not one already
565  std::function<local_item_t(local_item_t)> filter_fn = filter_function;
566 
567  // This is the type name for help, it will take the current version of the set contents
568  desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); };
569 
570  // This is the function that validates
571  // It stores a copy of the set pointer-like, so shared_ptr will stay alive
572  func_ = [set, filter_fn](std::string &input) {
573  local_item_t b;
574  if(!detail::lexical_cast(input, b)) {
575  throw ValidationError(input); // name is added later
576  }
577  if(filter_fn) {
578  b = filter_fn(b);
579  }
580  auto res = detail::search(set, b, filter_fn);
581  if(res.first) {
582  // Make sure the version in the input string is identical to the one in the set
583  if(filter_fn) {
584  input = detail::as_string(detail::pair_adaptor<element_t>::first(*(res.second)));
585  }
586 
587  // Return empty error string (success)
588  return std::string{};
589  }
590 
591  // If you reach this point, the result was not found
592  std::string out(" not in ");
593  out += detail::generate_set(detail::smart_deref(set));
594  return out;
595  };
596  }
597 
599  template <typename T, typename... Args>
600  IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
601  : IsMember(std::forward<T>(set),
602  [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
603  other...) {}
604 };
605 
607 template <typename T> using TransformPairs = std::vector<std::pair<std::string, T>>;
608 
610 class Transformer : public Validator {
611  public:
612  using filter_fn_t = std::function<std::string(std::string)>;
613 
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)...) {}
618 
620  template <typename T> explicit Transformer(T &&mapping) : Transformer(std::forward<T>(mapping), nullptr) {}
621 
624  template <typename T, typename F> explicit Transformer(T mapping, F filter_function) {
625 
626  static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
627  "mapping must produce value pairs");
628  // Get the type of the contained item - requires a container have ::value_type
629  // if the type does not have first_type and second_type, these are both value_type
630  using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
631  using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
632  using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
633  // (const char * to std::string)
634 
635  // Make a local copy of the filter function, using a std::function if not one already
636  std::function<local_item_t(local_item_t)> filter_fn = filter_function;
637 
638  // This is the type name for help, it will take the current version of the set contents
639  desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); };
640 
641  func_ = [mapping, filter_fn](std::string &input) {
642  local_item_t b;
643  if(!detail::lexical_cast(input, b)) {
644  return std::string();
645  // there is no possible way we can match anything in the mapping if we can't convert so just return
646  }
647  if(filter_fn) {
648  b = filter_fn(b);
649  }
650  auto res = detail::search(mapping, b, filter_fn);
651  if(res.first) {
652  input = detail::as_string(detail::pair_adaptor<element_t>::second(*res.second));
653  }
654  return std::string{};
655  };
656  }
657 
659  template <typename T, typename... Args>
660  Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
661  : Transformer(std::forward<T>(mapping),
662  [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
663  other...) {}
664 };
665 
668  public:
669  using filter_fn_t = std::function<std::string(std::string)>;
670 
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)...) {}
675 
677  template <typename T> explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {}
678 
681  template <typename T, typename F> explicit CheckedTransformer(T mapping, F filter_function) {
682 
683  static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
684  "mapping must produce value pairs");
685  // Get the type of the contained item - requires a container have ::value_type
686  // if the type does not have first_type and second_type, these are both value_type
687  using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
688  using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
689  using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
690  // (const char * to std::string)
691  using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair //
692  // the type of the object pair
693 
694  // Make a local copy of the filter function, using a std::function if not one already
695  std::function<local_item_t(local_item_t)> filter_fn = filter_function;
696 
697  auto tfunc = [mapping]() {
698  std::string out("value in ");
699  out += detail::generate_map(detail::smart_deref(mapping)) + " OR {";
700  out += detail::join(
701  detail::smart_deref(mapping),
702  [](const iteration_type_t &v) { return detail::as_string(detail::pair_adaptor<element_t>::second(v)); },
703  ",");
704  out.push_back('}');
705  return out;
706  };
707 
708  desc_function_ = tfunc;
709 
710  func_ = [mapping, tfunc, filter_fn](std::string &input) {
711  local_item_t b;
712  bool converted = detail::lexical_cast(input, b);
713  if(converted) {
714  if(filter_fn) {
715  b = filter_fn(b);
716  }
717  auto res = detail::search(mapping, b, filter_fn);
718  if(res.first) {
719  input = detail::as_string(detail::pair_adaptor<element_t>::second(*res.second));
720  return std::string{};
721  }
722  }
723  for(const auto &v : detail::smart_deref(mapping)) {
724  auto output_string = detail::as_string(detail::pair_adaptor<element_t>::second(v));
725  if(output_string == input) {
726  return std::string();
727  }
728  }
729 
730  return "Check " + input + " " + tfunc() + " FAILED";
731  };
732  }
733 
735  template <typename T, typename... Args>
736  CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
737  : CheckedTransformer(std::forward<T>(mapping),
738  [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
739  other...) {}
740 };
741 
743 inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
744 
746 inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }
747 
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));
752  return item;
753 }
754 
766 class AsNumberWithUnit : public Validator {
767  public:
772  enum Options {
773  CASE_SENSITIVE = 0,
774  CASE_INSENSITIVE = 1,
775  UNIT_OPTIONAL = 0,
776  UNIT_REQUIRED = 2,
777  DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL
778  };
779 
780  template <typename Number>
781  explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
782  Options opts = DEFAULT,
783  const std::string &unit_name = "UNIT") {
784  description(generate_description<Number>(unit_name, opts));
785  validate_mapping(mapping, opts);
786 
787  // transform function
788  func_ = [mapping, opts](std::string &input) -> std::string {
789  Number num;
790 
791  detail::rtrim(input);
792  if(input.empty()) {
793  throw ValidationError("Input is empty");
794  }
795 
796  // Find split position between number and prefix
797  auto unit_begin = input.end();
798  while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
799  --unit_begin;
800  }
801 
802  std::string unit{unit_begin, input.end()};
803  input.resize(static_cast<size_t>(std::distance(input.begin(), unit_begin)));
804  detail::trim(input);
805 
806  if(opts & UNIT_REQUIRED && unit.empty()) {
807  throw ValidationError("Missing mandatory unit");
808  }
809  if(opts & CASE_INSENSITIVE) {
810  unit = detail::to_lower(unit);
811  }
812 
813  bool converted = detail::lexical_cast(input, num);
814  if(!converted) {
815  throw ValidationError("Value " + input + " could not be converted to " + detail::type_name<Number>());
816  }
817 
818  if(unit.empty()) {
819  // No need to modify input if no unit passed
820  return {};
821  }
822 
823  // find corresponding factor
824  auto it = mapping.find(unit);
825  if(it == mapping.end()) {
826  throw ValidationError(unit +
827  " unit not recognized. "
828  "Allowed values: " +
829  detail::generate_map(mapping, true));
830  }
831 
832  // perform safe multiplication
833  bool ok = detail::checked_multiply(num, it->second);
834  if(!ok) {
835  throw ValidationError(detail::as_string(num) + " multiplied by " + unit +
836  " factor would cause number overflow. Use smaller value.");
837  }
838  input = detail::as_string(num);
839 
840  return {};
841  };
842  }
843 
844  private:
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.");
851  }
852  if(!detail::isalpha(kv.first)) {
853  throw ValidationError("Unit must contain only letters.");
854  }
855  }
856 
857  // make all units lowercase if CASE_INSENSITIVE
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);
864  }
865  lower_mapping[detail::to_lower(kv.first)] = kv.second;
866  }
867  mapping = std::move(lower_mapping);
868  }
869  }
870 
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) {
876  out << name;
877  } else {
878  out << '[' << name << ']';
879  }
880  return out.str();
881  }
882 };
883 
896  public:
897  using result_t = uint64_t;
898 
906  explicit AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) {
907  if(kb_is_1000) {
908  description("SIZE [b, kb(=1000b), kib(=1024b), ...]");
909  } else {
910  description("SIZE [b, kb(=1024b), ...]");
911  }
912  }
913 
914  private:
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;
920  result_t k = 1;
921  result_t ki = 1;
922  m["b"] = 1;
923  for(std::string p : {"k", "m", "g", "t", "p", "e"}) {
924  k *= k_factor;
925  ki *= ki_factor;
926  m[p] = k;
927  m[p + "b"] = k;
928  m[p + "i"] = ki;
929  m[p + "ib"] = ki;
930  }
931  return m;
932  }
933 
935  static std::map<std::string, result_t> get_mapping(bool kb_is_1000) {
936  if(kb_is_1000) {
937  static auto m = init_mapping(true);
938  return m;
939  } else {
940  static auto m = init_mapping(false);
941  return m;
942  }
943  }
944 };
945 
946 namespace detail {
951 inline std::pair<std::string, std::string> split_program_name(std::string commandline) {
952  // try to determine the programName
953  std::pair<std::string, std::string> vals;
954  trim(commandline);
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) {
959  // if we have reached the end and haven't found a valid file just assume the first argument is the
960  // program name
961  esp = commandline.find_first_of(' ', 1);
962  break;
963  }
964  }
965  vals.first = commandline.substr(0, esp);
966  rtrim(vals.first);
967  // strip the program name
968  vals.second = (esp != std::string::npos) ? commandline.substr(esp + 1) : std::string{};
969  ltrim(vals.second);
970  return vals;
971 }
972 
973 } // namespace detail
975 
976 } // namespace CLI
const std::string & get_name() const
Get the name of the Validator.
Definition: Validators.hpp:104
Definition: App.hpp:29
CheckedTransformer(T mapping)
direct map of std::string to std::string
Definition: Validators.hpp:677
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
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:736
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
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:660
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
Transformer(std::initializer_list< std::pair< std::string, std::string >> values, Args &&... args)
This allows in-place construction.
Definition: Validators.hpp:616
Validator & active(bool active_val=true)
Specify whether the Validator is active or not.
Definition: Validators.hpp:106
Transformer(T &&mapping)
direct map of std::string to std::string
Definition: Validators.hpp:620
Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost ...
Definition: TypeTools.hpp:91
CheckedTransformer(T mapping, F filter_function)
Definition: Validators.hpp:681
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
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
CheckedTransformer(std::initializer_list< std::pair< std::string, std::string >> values, Args &&... args)
This allows in-place construction.
Definition: Validators.hpp:673
AsSizeValue(bool kb_is_1000)
Definition: Validators.hpp:906
Transformer(T mapping, F filter_function)
Definition: Validators.hpp:624
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
Translate named items to other or a value set.
Definition: Validators.hpp:610
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
translate named items to other or a value set
Definition: Validators.hpp:667
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
Validator operator &(const Validator &other) const
Definition: Validators.hpp:125
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