138f85004SShawn McCarney /** 238f85004SShawn McCarney * Copyright © 2025 IBM Corporation 338f85004SShawn McCarney * 438f85004SShawn McCarney * Licensed under the Apache License, Version 2.0 (the "License"); 538f85004SShawn McCarney * you may not use this file except in compliance with the License. 638f85004SShawn McCarney * You may obtain a copy of the License at 738f85004SShawn McCarney * 838f85004SShawn McCarney * http://www.apache.org/licenses/LICENSE-2.0 938f85004SShawn McCarney * 1038f85004SShawn McCarney * Unless required by applicable law or agreed to in writing, software 1138f85004SShawn McCarney * distributed under the License is distributed on an "AS IS" BASIS, 1238f85004SShawn McCarney * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1338f85004SShawn McCarney * See the License for the specific language governing permissions and 1438f85004SShawn McCarney * limitations under the License. 1538f85004SShawn McCarney */ 1638f85004SShawn McCarney 1738f85004SShawn McCarney #include "json_parser_utils.hpp" 1838f85004SShawn McCarney 19*f1845c06SShawn McCarney #include <charconv> 20*f1845c06SShawn McCarney #include <regex> 21*f1845c06SShawn McCarney 2238f85004SShawn McCarney namespace phosphor::power::json_parser_utils 2338f85004SShawn McCarney { 2438f85004SShawn McCarney 25*f1845c06SShawn McCarney const std::map<std::string, std::string> NO_VARIABLES{}; 26*f1845c06SShawn McCarney 27*f1845c06SShawn McCarney static std::regex VARIABLE_REGEX{R"(\$\{([A-Za-z0-9_]+)\})"}; 28*f1845c06SShawn McCarney 29*f1845c06SShawn McCarney uint8_t parseBitPosition(const nlohmann::json& element, 30*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 31*f1845c06SShawn McCarney { 32*f1845c06SShawn McCarney int value = parseInteger(element, variables); 33*f1845c06SShawn McCarney if ((value < 0) || (value > 7)) 34*f1845c06SShawn McCarney { 35*f1845c06SShawn McCarney throw std::invalid_argument{"Element is not a bit position"}; 36*f1845c06SShawn McCarney } 37*f1845c06SShawn McCarney return static_cast<uint8_t>(value); 38*f1845c06SShawn McCarney } 39*f1845c06SShawn McCarney 40*f1845c06SShawn McCarney uint8_t parseBitValue(const nlohmann::json& element, 41*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 42*f1845c06SShawn McCarney { 43*f1845c06SShawn McCarney int value = parseInteger(element, variables); 44*f1845c06SShawn McCarney if ((value < 0) || (value > 1)) 45*f1845c06SShawn McCarney { 46*f1845c06SShawn McCarney throw std::invalid_argument{"Element is not a bit value"}; 47*f1845c06SShawn McCarney } 48*f1845c06SShawn McCarney return static_cast<uint8_t>(value); 49*f1845c06SShawn McCarney } 50*f1845c06SShawn McCarney 51*f1845c06SShawn McCarney bool parseBoolean(const nlohmann::json& element, 52*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 53*f1845c06SShawn McCarney { 54*f1845c06SShawn McCarney if (element.is_boolean()) 55*f1845c06SShawn McCarney { 56*f1845c06SShawn McCarney return element.get<bool>(); 57*f1845c06SShawn McCarney } 58*f1845c06SShawn McCarney 59*f1845c06SShawn McCarney if (element.is_string() && !variables.empty()) 60*f1845c06SShawn McCarney { 61*f1845c06SShawn McCarney std::string value = parseString(element, true, variables); 62*f1845c06SShawn McCarney if (value == "true") 63*f1845c06SShawn McCarney { 64*f1845c06SShawn McCarney return true; 65*f1845c06SShawn McCarney } 66*f1845c06SShawn McCarney else if (value == "false") 67*f1845c06SShawn McCarney { 68*f1845c06SShawn McCarney return false; 69*f1845c06SShawn McCarney } 70*f1845c06SShawn McCarney } 71*f1845c06SShawn McCarney 72*f1845c06SShawn McCarney throw std::invalid_argument{"Element is not a boolean"}; 73*f1845c06SShawn McCarney } 74*f1845c06SShawn McCarney 75*f1845c06SShawn McCarney double parseDouble(const nlohmann::json& element, 76*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 77*f1845c06SShawn McCarney { 78*f1845c06SShawn McCarney if (element.is_number()) 79*f1845c06SShawn McCarney { 80*f1845c06SShawn McCarney return element.get<double>(); 81*f1845c06SShawn McCarney } 82*f1845c06SShawn McCarney 83*f1845c06SShawn McCarney if (element.is_string() && !variables.empty()) 84*f1845c06SShawn McCarney { 85*f1845c06SShawn McCarney std::string strValue = parseString(element, true, variables); 86*f1845c06SShawn McCarney const char* first = strValue.data(); 87*f1845c06SShawn McCarney const char* last = strValue.data() + strValue.size(); 88*f1845c06SShawn McCarney double value; 89*f1845c06SShawn McCarney auto [ptr, ec] = std::from_chars(first, last, value); 90*f1845c06SShawn McCarney if ((ptr == last) && (ec == std::errc())) 91*f1845c06SShawn McCarney { 92*f1845c06SShawn McCarney return value; 93*f1845c06SShawn McCarney } 94*f1845c06SShawn McCarney } 95*f1845c06SShawn McCarney 96*f1845c06SShawn McCarney throw std::invalid_argument{"Element is not a double"}; 97*f1845c06SShawn McCarney } 98*f1845c06SShawn McCarney 99*f1845c06SShawn McCarney uint8_t parseHexByte(const nlohmann::json& element, 100*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 101*f1845c06SShawn McCarney { 102*f1845c06SShawn McCarney std::string value = parseString(element, true, variables); 103*f1845c06SShawn McCarney bool isHex = (value.compare(0, 2, "0x") == 0) && (value.size() > 2) && 104*f1845c06SShawn McCarney (value.size() < 5) && 105*f1845c06SShawn McCarney (value.find_first_not_of("0123456789abcdefABCDEF", 2) == 106*f1845c06SShawn McCarney std::string::npos); 107*f1845c06SShawn McCarney if (!isHex) 108*f1845c06SShawn McCarney { 109*f1845c06SShawn McCarney throw std::invalid_argument{"Element is not hexadecimal string"}; 110*f1845c06SShawn McCarney } 111*f1845c06SShawn McCarney return static_cast<uint8_t>(std::stoul(value, nullptr, 0)); 112*f1845c06SShawn McCarney } 113*f1845c06SShawn McCarney 114*f1845c06SShawn McCarney std::vector<uint8_t> parseHexByteArray( 115*f1845c06SShawn McCarney const nlohmann::json& element, 116*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 11738f85004SShawn McCarney { 11838f85004SShawn McCarney verifyIsArray(element); 11938f85004SShawn McCarney std::vector<uint8_t> values; 12038f85004SShawn McCarney for (auto& valueElement : element) 12138f85004SShawn McCarney { 122*f1845c06SShawn McCarney values.emplace_back(parseHexByte(valueElement, variables)); 12338f85004SShawn McCarney } 12438f85004SShawn McCarney return values; 12538f85004SShawn McCarney } 12638f85004SShawn McCarney 127*f1845c06SShawn McCarney int8_t parseInt8(const nlohmann::json& element, 128*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 129*f1845c06SShawn McCarney { 130*f1845c06SShawn McCarney int value = parseInteger(element, variables); 131*f1845c06SShawn McCarney if ((value < INT8_MIN) || (value > INT8_MAX)) 132*f1845c06SShawn McCarney { 133*f1845c06SShawn McCarney throw std::invalid_argument{"Element is not an 8-bit signed integer"}; 134*f1845c06SShawn McCarney } 135*f1845c06SShawn McCarney return static_cast<int8_t>(value); 136*f1845c06SShawn McCarney } 137*f1845c06SShawn McCarney 138*f1845c06SShawn McCarney int parseInteger(const nlohmann::json& element, 139*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 140*f1845c06SShawn McCarney { 141*f1845c06SShawn McCarney if (element.is_number_integer()) 142*f1845c06SShawn McCarney { 143*f1845c06SShawn McCarney return element.get<int>(); 144*f1845c06SShawn McCarney } 145*f1845c06SShawn McCarney 146*f1845c06SShawn McCarney if (element.is_string() && !variables.empty()) 147*f1845c06SShawn McCarney { 148*f1845c06SShawn McCarney std::string strValue = parseString(element, true, variables); 149*f1845c06SShawn McCarney const char* first = strValue.data(); 150*f1845c06SShawn McCarney const char* last = strValue.data() + strValue.size(); 151*f1845c06SShawn McCarney int value; 152*f1845c06SShawn McCarney auto [ptr, ec] = std::from_chars(first, last, value); 153*f1845c06SShawn McCarney if ((ptr == last) && (ec == std::errc())) 154*f1845c06SShawn McCarney { 155*f1845c06SShawn McCarney return value; 156*f1845c06SShawn McCarney } 157*f1845c06SShawn McCarney } 158*f1845c06SShawn McCarney 159*f1845c06SShawn McCarney throw std::invalid_argument{"Element is not an integer"}; 160*f1845c06SShawn McCarney } 161*f1845c06SShawn McCarney 162*f1845c06SShawn McCarney std::string parseString(const nlohmann::json& element, bool isEmptyValid, 163*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 164*f1845c06SShawn McCarney { 165*f1845c06SShawn McCarney if (!element.is_string()) 166*f1845c06SShawn McCarney { 167*f1845c06SShawn McCarney throw std::invalid_argument{"Element is not a string"}; 168*f1845c06SShawn McCarney } 169*f1845c06SShawn McCarney std::string value = element.get<std::string>(); 170*f1845c06SShawn McCarney internal::expandVariables(value, variables); 171*f1845c06SShawn McCarney if (value.empty() && !isEmptyValid) 172*f1845c06SShawn McCarney { 173*f1845c06SShawn McCarney throw std::invalid_argument{"Element contains an empty string"}; 174*f1845c06SShawn McCarney } 175*f1845c06SShawn McCarney return value; 176*f1845c06SShawn McCarney } 177*f1845c06SShawn McCarney 178*f1845c06SShawn McCarney uint8_t parseUint8(const nlohmann::json& element, 179*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 180*f1845c06SShawn McCarney { 181*f1845c06SShawn McCarney int value = parseInteger(element, variables); 182*f1845c06SShawn McCarney if ((value < 0) || (value > UINT8_MAX)) 183*f1845c06SShawn McCarney { 184*f1845c06SShawn McCarney throw std::invalid_argument{"Element is not an 8-bit unsigned integer"}; 185*f1845c06SShawn McCarney } 186*f1845c06SShawn McCarney return static_cast<uint8_t>(value); 187*f1845c06SShawn McCarney } 188*f1845c06SShawn McCarney 189*f1845c06SShawn McCarney unsigned int parseUnsignedInteger( 190*f1845c06SShawn McCarney const nlohmann::json& element, 191*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 192*f1845c06SShawn McCarney { 193*f1845c06SShawn McCarney int value = parseInteger(element, variables); 194*f1845c06SShawn McCarney if (value < 0) 195*f1845c06SShawn McCarney { 196*f1845c06SShawn McCarney throw std::invalid_argument{"Element is not an unsigned integer"}; 197*f1845c06SShawn McCarney } 198*f1845c06SShawn McCarney return static_cast<unsigned int>(value); 199*f1845c06SShawn McCarney } 200*f1845c06SShawn McCarney 201*f1845c06SShawn McCarney namespace internal 202*f1845c06SShawn McCarney { 203*f1845c06SShawn McCarney 204*f1845c06SShawn McCarney void expandVariables(std::string& value, 205*f1845c06SShawn McCarney const std::map<std::string, std::string>& variables) 206*f1845c06SShawn McCarney { 207*f1845c06SShawn McCarney if (variables.empty()) 208*f1845c06SShawn McCarney { 209*f1845c06SShawn McCarney return; 210*f1845c06SShawn McCarney } 211*f1845c06SShawn McCarney 212*f1845c06SShawn McCarney std::smatch results; 213*f1845c06SShawn McCarney while (std::regex_search(value, results, VARIABLE_REGEX)) 214*f1845c06SShawn McCarney { 215*f1845c06SShawn McCarney if (results.size() != 2) 216*f1845c06SShawn McCarney { 217*f1845c06SShawn McCarney throw std::runtime_error{ 218*f1845c06SShawn McCarney "Unexpected regular expression match result while parsing string"}; 219*f1845c06SShawn McCarney } 220*f1845c06SShawn McCarney const std::string& variable = results[1]; 221*f1845c06SShawn McCarney auto it = variables.find(variable); 222*f1845c06SShawn McCarney if (it == variables.end()) 223*f1845c06SShawn McCarney { 224*f1845c06SShawn McCarney throw std::invalid_argument{"Undefined variable: " + variable}; 225*f1845c06SShawn McCarney } 226*f1845c06SShawn McCarney value.replace(results.position(0), results.length(0), it->second); 227*f1845c06SShawn McCarney } 228*f1845c06SShawn McCarney } 229*f1845c06SShawn McCarney 230*f1845c06SShawn McCarney } // namespace internal 231*f1845c06SShawn McCarney 23238f85004SShawn McCarney } // namespace phosphor::power::json_parser_utils 233