MCPcopy Create free account
hub / github.com/CLIUtils/CLI11 / AsNumberWithUnit

Class AsNumberWithUnit

include/CLI/ExtraValidators.hpp:429–556  ·  view source on GitHub ↗

Multiply a number by a factor using given mapping. Can be used to write transforms for SIZE or DURATION inputs. Example: With mapping = `{"b"->1, "kb"->1024, "mb"->1024*1024}` one can recognize inputs like "100", "12kb", "100 MB", that will be automatically transformed to 100, 14448, 104857600. Output number type matches the type in the provided mapping. Therefore, if it is required to interpret

Source from the content-addressed store, hash-verified

427/// Therefore, if it is required to interpret real inputs like "0.42 s",
428/// the mapping should be of a type <string, float> or <string, double>.
429class AsNumberWithUnit : public Validator {
430 public:
431 /// Adjust AsNumberWithUnit behavior.
432 /// CASE_SENSITIVE/CASE_INSENSITIVE controls how units are matched.
433 /// UNIT_OPTIONAL/UNIT_REQUIRED throws ValidationError
434 /// if UNIT_REQUIRED is set and unit literal is not found.
435 enum Options : std::uint8_t {
436 CASE_SENSITIVE = 0,
437 CASE_INSENSITIVE = 1,
438 UNIT_OPTIONAL = 0,
439 UNIT_REQUIRED = 2,
440 DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL
441 };
442
443 template <typename Number>
444 explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
445 Options opts = DEFAULT,
446 const std::string &unit_name = "UNIT") {
447 description(generate_description<Number>(unit_name, opts));
448 validate_mapping(mapping, opts);
449
450 // transform function
451 func_ = [mapping, opts](std::string &input) -> std::string {
452 Number num{};
453
454 detail::rtrim(input);
455 if(input.empty()) {
456 throw ValidationError("Input is empty");
457 }
458
459 // Find split position between number and prefix
460 auto unit_begin = input.end();
461 while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
462 --unit_begin;
463 }
464
465 std::string unit{unit_begin, input.end()};
466 input.resize(static_cast<std::size_t>(std::distance(input.begin(), unit_begin)));
467 detail::trim(input);
468
469 if(opts & UNIT_REQUIRED && unit.empty()) {
470 throw ValidationError("Missing mandatory unit");
471 }
472 if(opts & CASE_INSENSITIVE) {
473 unit = detail::to_lower(unit);
474 }
475 if(unit.empty()) {
476 using CLI::detail::lexical_cast;
477 if(!lexical_cast(input, num)) {
478 throw ValidationError(std::string("Value ") + input + " could not be converted to " +
479 detail::type_name<Number>());
480 }
481 // No need to modify input if no unit passed
482 return {};
483 }
484
485 // find corresponding factor
486 auto it = mapping.find(unit);

Callers 1

Calls

no outgoing calls

Tested by

no test coverage detected