xref: /openbmc/phosphor-power/json_parser_utils.cpp (revision f1845c0621324fc8435a8e29377f0b1306e636b7)
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