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