xref: /openbmc/phosphor-power/json_parser_utils.cpp (revision 8873f428276818761348b4091574334870ae51a7)
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 
19f1845c06SShawn McCarney #include <charconv>
20f1845c06SShawn McCarney #include <regex>
21f1845c06SShawn McCarney 
2238f85004SShawn McCarney namespace phosphor::power::json_parser_utils
2338f85004SShawn McCarney {
2438f85004SShawn McCarney 
25f1845c06SShawn McCarney const std::map<std::string, std::string> NO_VARIABLES{};
26f1845c06SShawn McCarney 
27f1845c06SShawn McCarney static std::regex VARIABLE_REGEX{R"(\$\{([A-Za-z0-9_]+)\})"};
28f1845c06SShawn McCarney 
parseBitPosition(const nlohmann::json & element,const std::map<std::string,std::string> & variables)29f1845c06SShawn McCarney uint8_t parseBitPosition(const nlohmann::json& element,
30f1845c06SShawn McCarney                          const std::map<std::string, std::string>& variables)
31f1845c06SShawn McCarney {
32f1845c06SShawn McCarney     int value = parseInteger(element, variables);
33f1845c06SShawn McCarney     if ((value < 0) || (value > 7))
34f1845c06SShawn McCarney     {
35f1845c06SShawn McCarney         throw std::invalid_argument{"Element is not a bit position"};
36f1845c06SShawn McCarney     }
37f1845c06SShawn McCarney     return static_cast<uint8_t>(value);
38f1845c06SShawn McCarney }
39f1845c06SShawn McCarney 
parseBitValue(const nlohmann::json & element,const std::map<std::string,std::string> & variables)40f1845c06SShawn McCarney uint8_t parseBitValue(const nlohmann::json& element,
41f1845c06SShawn McCarney                       const std::map<std::string, std::string>& variables)
42f1845c06SShawn McCarney {
43f1845c06SShawn McCarney     int value = parseInteger(element, variables);
44f1845c06SShawn McCarney     if ((value < 0) || (value > 1))
45f1845c06SShawn McCarney     {
46f1845c06SShawn McCarney         throw std::invalid_argument{"Element is not a bit value"};
47f1845c06SShawn McCarney     }
48f1845c06SShawn McCarney     return static_cast<uint8_t>(value);
49f1845c06SShawn McCarney }
50f1845c06SShawn McCarney 
parseBoolean(const nlohmann::json & element,const std::map<std::string,std::string> & variables)51f1845c06SShawn McCarney bool parseBoolean(const nlohmann::json& element,
52f1845c06SShawn McCarney                   const std::map<std::string, std::string>& variables)
53f1845c06SShawn McCarney {
54f1845c06SShawn McCarney     if (element.is_boolean())
55f1845c06SShawn McCarney     {
56f1845c06SShawn McCarney         return element.get<bool>();
57f1845c06SShawn McCarney     }
58f1845c06SShawn McCarney 
59f1845c06SShawn McCarney     if (element.is_string() && !variables.empty())
60f1845c06SShawn McCarney     {
61f1845c06SShawn McCarney         std::string value = parseString(element, true, variables);
62f1845c06SShawn McCarney         if (value == "true")
63f1845c06SShawn McCarney         {
64f1845c06SShawn McCarney             return true;
65f1845c06SShawn McCarney         }
66f1845c06SShawn McCarney         else if (value == "false")
67f1845c06SShawn McCarney         {
68f1845c06SShawn McCarney             return false;
69f1845c06SShawn McCarney         }
70f1845c06SShawn McCarney     }
71f1845c06SShawn McCarney 
72f1845c06SShawn McCarney     throw std::invalid_argument{"Element is not a boolean"};
73f1845c06SShawn McCarney }
74f1845c06SShawn McCarney 
parseDouble(const nlohmann::json & element,const std::map<std::string,std::string> & variables)75f1845c06SShawn McCarney double parseDouble(const nlohmann::json& element,
76f1845c06SShawn McCarney                    const std::map<std::string, std::string>& variables)
77f1845c06SShawn McCarney {
78f1845c06SShawn McCarney     if (element.is_number())
79f1845c06SShawn McCarney     {
80f1845c06SShawn McCarney         return element.get<double>();
81f1845c06SShawn McCarney     }
82f1845c06SShawn McCarney 
83f1845c06SShawn McCarney     if (element.is_string() && !variables.empty())
84f1845c06SShawn McCarney     {
85f1845c06SShawn McCarney         std::string strValue = parseString(element, true, variables);
86f1845c06SShawn McCarney         const char* first = strValue.data();
87f1845c06SShawn McCarney         const char* last = strValue.data() + strValue.size();
88f1845c06SShawn McCarney         double value;
89f1845c06SShawn McCarney         auto [ptr, ec] = std::from_chars(first, last, value);
90f1845c06SShawn McCarney         if ((ptr == last) && (ec == std::errc()))
91f1845c06SShawn McCarney         {
92f1845c06SShawn McCarney             return value;
93f1845c06SShawn McCarney         }
94f1845c06SShawn McCarney     }
95f1845c06SShawn McCarney 
96f1845c06SShawn McCarney     throw std::invalid_argument{"Element is not a double"};
97f1845c06SShawn McCarney }
98f1845c06SShawn McCarney 
parseHexByte(const nlohmann::json & element,const std::map<std::string,std::string> & variables)99f1845c06SShawn McCarney uint8_t parseHexByte(const nlohmann::json& element,
100f1845c06SShawn McCarney                      const std::map<std::string, std::string>& variables)
101f1845c06SShawn McCarney {
102f1845c06SShawn McCarney     std::string value = parseString(element, true, variables);
103f1845c06SShawn McCarney     bool isHex = (value.compare(0, 2, "0x") == 0) && (value.size() > 2) &&
104f1845c06SShawn McCarney                  (value.size() < 5) &&
105f1845c06SShawn McCarney                  (value.find_first_not_of("0123456789abcdefABCDEF", 2) ==
106f1845c06SShawn McCarney                   std::string::npos);
107f1845c06SShawn McCarney     if (!isHex)
108f1845c06SShawn McCarney     {
109f1845c06SShawn McCarney         throw std::invalid_argument{"Element is not hexadecimal string"};
110f1845c06SShawn McCarney     }
111f1845c06SShawn McCarney     return static_cast<uint8_t>(std::stoul(value, nullptr, 0));
112f1845c06SShawn McCarney }
113f1845c06SShawn McCarney 
parseHexByteArray(const nlohmann::json & element,const std::map<std::string,std::string> & variables)114f1845c06SShawn McCarney std::vector<uint8_t> parseHexByteArray(
115f1845c06SShawn McCarney     const nlohmann::json& element,
116f1845c06SShawn 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     {
122f1845c06SShawn McCarney         values.emplace_back(parseHexByte(valueElement, variables));
12338f85004SShawn McCarney     }
12438f85004SShawn McCarney     return values;
12538f85004SShawn McCarney }
12638f85004SShawn McCarney 
parseInt8(const nlohmann::json & element,const std::map<std::string,std::string> & variables)127f1845c06SShawn McCarney int8_t parseInt8(const nlohmann::json& element,
128f1845c06SShawn McCarney                  const std::map<std::string, std::string>& variables)
129f1845c06SShawn McCarney {
130f1845c06SShawn McCarney     int value = parseInteger(element, variables);
131f1845c06SShawn McCarney     if ((value < INT8_MIN) || (value > INT8_MAX))
132f1845c06SShawn McCarney     {
133f1845c06SShawn McCarney         throw std::invalid_argument{"Element is not an 8-bit signed integer"};
134f1845c06SShawn McCarney     }
135f1845c06SShawn McCarney     return static_cast<int8_t>(value);
136f1845c06SShawn McCarney }
137f1845c06SShawn McCarney 
parseInteger(const nlohmann::json & element,const std::map<std::string,std::string> & variables)138f1845c06SShawn McCarney int parseInteger(const nlohmann::json& element,
139f1845c06SShawn McCarney                  const std::map<std::string, std::string>& variables)
140f1845c06SShawn McCarney {
141f1845c06SShawn McCarney     if (element.is_number_integer())
142f1845c06SShawn McCarney     {
143f1845c06SShawn McCarney         return element.get<int>();
144f1845c06SShawn McCarney     }
145f1845c06SShawn McCarney 
146f1845c06SShawn McCarney     if (element.is_string() && !variables.empty())
147f1845c06SShawn McCarney     {
148f1845c06SShawn McCarney         std::string strValue = parseString(element, true, variables);
149f1845c06SShawn McCarney         const char* first = strValue.data();
150f1845c06SShawn McCarney         const char* last = strValue.data() + strValue.size();
151f1845c06SShawn McCarney         int value;
152f1845c06SShawn McCarney         auto [ptr, ec] = std::from_chars(first, last, value);
153f1845c06SShawn McCarney         if ((ptr == last) && (ec == std::errc()))
154f1845c06SShawn McCarney         {
155f1845c06SShawn McCarney             return value;
156f1845c06SShawn McCarney         }
157f1845c06SShawn McCarney     }
158f1845c06SShawn McCarney 
159f1845c06SShawn McCarney     throw std::invalid_argument{"Element is not an integer"};
160f1845c06SShawn McCarney }
161f1845c06SShawn McCarney 
parseString(const nlohmann::json & element,bool isEmptyValid,const std::map<std::string,std::string> & variables)162f1845c06SShawn McCarney std::string parseString(const nlohmann::json& element, bool isEmptyValid,
163f1845c06SShawn McCarney                         const std::map<std::string, std::string>& variables)
164f1845c06SShawn McCarney {
165f1845c06SShawn McCarney     if (!element.is_string())
166f1845c06SShawn McCarney     {
167f1845c06SShawn McCarney         throw std::invalid_argument{"Element is not a string"};
168f1845c06SShawn McCarney     }
169f1845c06SShawn McCarney     std::string value = element.get<std::string>();
170f1845c06SShawn McCarney     internal::expandVariables(value, variables);
171f1845c06SShawn McCarney     if (value.empty() && !isEmptyValid)
172f1845c06SShawn McCarney     {
173f1845c06SShawn McCarney         throw std::invalid_argument{"Element contains an empty string"};
174f1845c06SShawn McCarney     }
175f1845c06SShawn McCarney     return value;
176f1845c06SShawn McCarney }
177f1845c06SShawn McCarney 
parseUint8(const nlohmann::json & element,const std::map<std::string,std::string> & variables)178f1845c06SShawn McCarney uint8_t parseUint8(const nlohmann::json& element,
179f1845c06SShawn McCarney                    const std::map<std::string, std::string>& variables)
180f1845c06SShawn McCarney {
181f1845c06SShawn McCarney     int value = parseInteger(element, variables);
182f1845c06SShawn McCarney     if ((value < 0) || (value > UINT8_MAX))
183f1845c06SShawn McCarney     {
184f1845c06SShawn McCarney         throw std::invalid_argument{"Element is not an 8-bit unsigned integer"};
185f1845c06SShawn McCarney     }
186f1845c06SShawn McCarney     return static_cast<uint8_t>(value);
187f1845c06SShawn McCarney }
188f1845c06SShawn McCarney 
parseUint16(const nlohmann::json & element,const std::map<std::string,std::string> & variables)189*8873f428SShawn McCarney uint16_t parseUint16(const nlohmann::json& element,
190*8873f428SShawn McCarney                      const std::map<std::string, std::string>& variables)
191*8873f428SShawn McCarney {
192*8873f428SShawn McCarney     int value = parseInteger(element, variables);
193*8873f428SShawn McCarney     if ((value < 0) || (value > UINT16_MAX))
194*8873f428SShawn McCarney     {
195*8873f428SShawn McCarney         throw std::invalid_argument{"Element is not a 16-bit unsigned integer"};
196*8873f428SShawn McCarney     }
197*8873f428SShawn McCarney     return static_cast<uint16_t>(value);
198*8873f428SShawn McCarney }
199*8873f428SShawn McCarney 
parseUnsignedInteger(const nlohmann::json & element,const std::map<std::string,std::string> & variables)200f1845c06SShawn McCarney unsigned int parseUnsignedInteger(
201f1845c06SShawn McCarney     const nlohmann::json& element,
202f1845c06SShawn McCarney     const std::map<std::string, std::string>& variables)
203f1845c06SShawn McCarney {
204f1845c06SShawn McCarney     int value = parseInteger(element, variables);
205f1845c06SShawn McCarney     if (value < 0)
206f1845c06SShawn McCarney     {
207f1845c06SShawn McCarney         throw std::invalid_argument{"Element is not an unsigned integer"};
208f1845c06SShawn McCarney     }
209f1845c06SShawn McCarney     return static_cast<unsigned int>(value);
210f1845c06SShawn McCarney }
211f1845c06SShawn McCarney 
212f1845c06SShawn McCarney namespace internal
213f1845c06SShawn McCarney {
214f1845c06SShawn McCarney 
expandVariables(std::string & value,const std::map<std::string,std::string> & variables)215f1845c06SShawn McCarney void expandVariables(std::string& value,
216f1845c06SShawn McCarney                      const std::map<std::string, std::string>& variables)
217f1845c06SShawn McCarney {
218f1845c06SShawn McCarney     if (variables.empty())
219f1845c06SShawn McCarney     {
220f1845c06SShawn McCarney         return;
221f1845c06SShawn McCarney     }
222f1845c06SShawn McCarney 
223f1845c06SShawn McCarney     std::smatch results;
224f1845c06SShawn McCarney     while (std::regex_search(value, results, VARIABLE_REGEX))
225f1845c06SShawn McCarney     {
226f1845c06SShawn McCarney         if (results.size() != 2)
227f1845c06SShawn McCarney         {
228f1845c06SShawn McCarney             throw std::runtime_error{
229f1845c06SShawn McCarney                 "Unexpected regular expression match result while parsing string"};
230f1845c06SShawn McCarney         }
231f1845c06SShawn McCarney         const std::string& variable = results[1];
232f1845c06SShawn McCarney         auto it = variables.find(variable);
233f1845c06SShawn McCarney         if (it == variables.end())
234f1845c06SShawn McCarney         {
235f1845c06SShawn McCarney             throw std::invalid_argument{"Undefined variable: " + variable};
236f1845c06SShawn McCarney         }
237f1845c06SShawn McCarney         value.replace(results.position(0), results.length(0), it->second);
238f1845c06SShawn McCarney     }
239f1845c06SShawn McCarney }
240f1845c06SShawn McCarney 
241f1845c06SShawn McCarney } // namespace internal
242f1845c06SShawn McCarney 
24338f85004SShawn McCarney } // namespace phosphor::power::json_parser_utils
244