| 2959 | |
| 2960 | template <typename Number> |
| 2961 | explicit AsNumberWithUnit(std::map<std::string, Number> mapping, Options opts = DEFAULT, |
| 2962 | const std::string &unit_name = "UNIT") { |
| 2963 | description(generate_description<Number>(unit_name, opts)); |
| 2964 | validate_mapping(mapping, opts); |
| 2965 | |
| 2966 | // transform function |
| 2967 | func_ = [mapping, opts](std::string &input) -> std::string { |
| 2968 | Number num; |
| 2969 | |
| 2970 | detail::rtrim(input); |
| 2971 | if (input.empty()) { |
| 2972 | throw ValidationError("Input is empty"); |
| 2973 | } |
| 2974 | |
| 2975 | // Find split position between number and prefix |
| 2976 | auto unit_begin = input.end(); |
| 2977 | while (unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) { |
| 2978 | --unit_begin; |
| 2979 | } |
| 2980 | |
| 2981 | std::string unit{unit_begin, input.end()}; |
| 2982 | input.resize(static_cast<std::size_t>(std::distance(input.begin(), unit_begin))); |
| 2983 | detail::trim(input); |
| 2984 | |
| 2985 | if (opts & UNIT_REQUIRED && unit.empty()) { |
| 2986 | throw ValidationError("Missing mandatory unit"); |
| 2987 | } |
| 2988 | if (opts & CASE_INSENSITIVE) { |
| 2989 | unit = detail::to_lower(unit); |
| 2990 | } |
| 2991 | |
| 2992 | bool converted = detail::lexical_cast(input, num); |
| 2993 | if (!converted) { |
| 2994 | throw ValidationError(std::string("Value ") + input + " could not be converted to " + |
| 2995 | detail::type_name<Number>()); |
| 2996 | } |
| 2997 | |
| 2998 | if (unit.empty()) { |
| 2999 | // No need to modify input if no unit passed |
| 3000 | return {}; |
| 3001 | } |
| 3002 | |
| 3003 | // find corresponding factor |
| 3004 | auto it = mapping.find(unit); |
| 3005 | if (it == mapping.end()) { |
| 3006 | throw ValidationError(unit + |
| 3007 | " unit not recognized. " |
| 3008 | "Allowed values: " + |
| 3009 | detail::generate_map(mapping, true)); |
| 3010 | } |
| 3011 | |
| 3012 | // perform safe multiplication |
| 3013 | bool ok = detail::checked_multiply(num, it->second); |
| 3014 | if (!ok) { |
| 3015 | throw ValidationError(detail::to_string(num) + " multiplied by " + unit + |
| 3016 | " factor would cause number overflow. Use smaller value."); |
| 3017 | } |
| 3018 | input = detail::to_string(num); |
nothing calls this directly
no test coverage detected