/** * Copyright © 2025 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "json_parser_utils.hpp" #include #include namespace phosphor::power::json_parser_utils { const std::map NO_VARIABLES{}; static std::regex VARIABLE_REGEX{R"(\$\{([A-Za-z0-9_]+)\})"}; uint8_t parseBitPosition(const nlohmann::json& element, const std::map& variables) { int value = parseInteger(element, variables); if ((value < 0) || (value > 7)) { throw std::invalid_argument{"Element is not a bit position"}; } return static_cast(value); } uint8_t parseBitValue(const nlohmann::json& element, const std::map& variables) { int value = parseInteger(element, variables); if ((value < 0) || (value > 1)) { throw std::invalid_argument{"Element is not a bit value"}; } return static_cast(value); } bool parseBoolean(const nlohmann::json& element, const std::map& variables) { if (element.is_boolean()) { return element.get(); } if (element.is_string() && !variables.empty()) { std::string value = parseString(element, true, variables); if (value == "true") { return true; } else if (value == "false") { return false; } } throw std::invalid_argument{"Element is not a boolean"}; } double parseDouble(const nlohmann::json& element, const std::map& variables) { if (element.is_number()) { return element.get(); } if (element.is_string() && !variables.empty()) { std::string strValue = parseString(element, true, variables); const char* first = strValue.data(); const char* last = strValue.data() + strValue.size(); double value; auto [ptr, ec] = std::from_chars(first, last, value); if ((ptr == last) && (ec == std::errc())) { return value; } } throw std::invalid_argument{"Element is not a double"}; } uint8_t parseHexByte(const nlohmann::json& element, const std::map& variables) { std::string value = parseString(element, true, variables); bool isHex = (value.compare(0, 2, "0x") == 0) && (value.size() > 2) && (value.size() < 5) && (value.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos); if (!isHex) { throw std::invalid_argument{"Element is not hexadecimal string"}; } return static_cast(std::stoul(value, nullptr, 0)); } std::vector parseHexByteArray( const nlohmann::json& element, const std::map& variables) { verifyIsArray(element); std::vector values; for (auto& valueElement : element) { values.emplace_back(parseHexByte(valueElement, variables)); } return values; } int8_t parseInt8(const nlohmann::json& element, const std::map& variables) { int value = parseInteger(element, variables); if ((value < INT8_MIN) || (value > INT8_MAX)) { throw std::invalid_argument{"Element is not an 8-bit signed integer"}; } return static_cast(value); } int parseInteger(const nlohmann::json& element, const std::map& variables) { if (element.is_number_integer()) { return element.get(); } if (element.is_string() && !variables.empty()) { std::string strValue = parseString(element, true, variables); const char* first = strValue.data(); const char* last = strValue.data() + strValue.size(); int value; auto [ptr, ec] = std::from_chars(first, last, value); if ((ptr == last) && (ec == std::errc())) { return value; } } throw std::invalid_argument{"Element is not an integer"}; } std::string parseString(const nlohmann::json& element, bool isEmptyValid, const std::map& variables) { if (!element.is_string()) { throw std::invalid_argument{"Element is not a string"}; } std::string value = element.get(); internal::expandVariables(value, variables); if (value.empty() && !isEmptyValid) { throw std::invalid_argument{"Element contains an empty string"}; } return value; } uint8_t parseUint8(const nlohmann::json& element, const std::map& variables) { int value = parseInteger(element, variables); if ((value < 0) || (value > UINT8_MAX)) { throw std::invalid_argument{"Element is not an 8-bit unsigned integer"}; } return static_cast(value); } uint16_t parseUint16(const nlohmann::json& element, const std::map& variables) { int value = parseInteger(element, variables); if ((value < 0) || (value > UINT16_MAX)) { throw std::invalid_argument{"Element is not a 16-bit unsigned integer"}; } return static_cast(value); } unsigned int parseUnsignedInteger( const nlohmann::json& element, const std::map& variables) { int value = parseInteger(element, variables); if (value < 0) { throw std::invalid_argument{"Element is not an unsigned integer"}; } return static_cast(value); } namespace internal { void expandVariables(std::string& value, const std::map& variables) { if (variables.empty()) { return; } std::smatch results; while (std::regex_search(value, results, VARIABLE_REGEX)) { if (results.size() != 2) { throw std::runtime_error{ "Unexpected regular expression match result while parsing string"}; } const std::string& variable = results[1]; auto it = variables.find(variable); if (it == variables.end()) { throw std::invalid_argument{"Undefined variable: " + variable}; } value.replace(results.position(0), results.length(0), it->second); } } } // namespace internal } // namespace phosphor::power::json_parser_utils