blob: 77d96504313bb515ae1fa14ac9ead592b304c9b5 [file] [log] [blame]
Shawn McCarney6a957f62024-01-10 16:15:19 -06001/**
2 * Copyright © 2024 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#pragma once
17
18#include "rail.hpp"
19
20#include <nlohmann/json.hpp>
21
22#include <cstdint>
23#include <filesystem>
Shawn McCarney906cc3f2024-02-01 13:33:06 -060024#include <memory>
Shawn McCarney6a957f62024-01-10 16:15:19 -060025#include <stdexcept>
26#include <string>
27#include <vector>
28
29namespace phosphor::power::sequencer::config_file_parser
30{
31
32/**
Shawn McCarneye9144ab2024-05-22 12:34:18 -050033 * Standard JSON configuration file directory on the BMC.
34 */
35extern const std::filesystem::path standardConfigFileDirectory;
36
37/**
38 * Finds the JSON configuration file for the current system based on the
39 * specified compatible system types.
40 *
41 * This is required when a single BMC firmware image supports multiple system
42 * types and some system types require different configuration files.
43 *
44 * The compatible system types must be ordered from most to least specific.
45 * Example:
46 * - com.acme.Hardware.Chassis.Model.MegaServer4CPU
47 * - com.acme.Hardware.Chassis.Model.MegaServer
48 * - com.acme.Hardware.Chassis.Model.Server
49 *
50 * Throws an exception if an error occurs.
51 *
52 * @param compatibleSystemTypes compatible system types for the current system
53 * ordered from most to least specific
54 * @param configFileDir directory containing configuration files
55 * @return path to the JSON configuration file, or an empty path if none was
56 * found
57 */
58std::filesystem::path find(
59 const std::vector<std::string>& compatibleSystemTypes,
60 const std::filesystem::path& configFileDir = standardConfigFileDirectory);
61
62/**
Shawn McCarney6a957f62024-01-10 16:15:19 -060063 * Parses the specified JSON configuration file.
64 *
65 * Returns the corresponding C++ Rail objects.
66 *
67 * Throws a ConfigFileParserError if an error occurs.
68 *
69 * @param pathName configuration file path name
70 * @return vector of Rail objects
71 */
72std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName);
73
74/*
75 * Internal implementation details for parse()
76 */
77namespace internal
78{
79
80/**
81 * Returns the specified property of the specified JSON element.
82 *
83 * Throws an invalid_argument exception if the property does not exist.
84 *
85 * @param element JSON element
86 * @param property property name
87 */
88#pragma GCC diagnostic push
89#if __GNUC__ == 13
90#pragma GCC diagnostic ignored "-Wdangling-reference"
91#endif
92inline const nlohmann::json& getRequiredProperty(const nlohmann::json& element,
93 const std::string& property)
94{
95 auto it = element.find(property);
96 if (it == element.end())
97 {
98 throw std::invalid_argument{"Required property missing: " + property};
99 }
100 return *it;
101}
102#pragma GCC diagnostic pop
103
104/**
105 * Parses a JSON element containing a boolean.
106 *
107 * Returns the corresponding C++ boolean value.
108 *
109 * Throws an exception if parsing fails.
110 *
111 * @param element JSON element
112 * @return boolean value
113 */
114inline bool parseBoolean(const nlohmann::json& element)
115{
116 // Verify element contains a boolean
117 if (!element.is_boolean())
118 {
119 throw std::invalid_argument{"Element is not a boolean"};
120 }
121 return element.get<bool>();
122}
123
124/**
125 * Parses a JSON element containing a GPIO.
126 *
127 * Returns the corresponding C++ GPIO object.
128 *
129 * Throws an exception if parsing fails.
130 *
131 * @param element JSON element
132 * @return GPIO object
133 */
134GPIO parseGPIO(const nlohmann::json& element);
135
136/**
137 * Parses a JSON element containing a rail.
138 *
139 * Returns the corresponding C++ Rail object.
140 *
141 * Throws an exception if parsing fails.
142 *
143 * @param element JSON element
144 * @return Rail object
145 */
146std::unique_ptr<Rail> parseRail(const nlohmann::json& element);
147
148/**
149 * Parses a JSON element containing an array of rails.
150 *
151 * Returns the corresponding C++ Rail objects.
152 *
153 * Throws an exception if parsing fails.
154 *
155 * @param element JSON element
156 * @return vector of Rail objects
157 */
158std::vector<std::unique_ptr<Rail>>
159 parseRailArray(const nlohmann::json& element);
160
161/**
162 * Parses the JSON root element of the entire configuration file.
163 *
164 * Returns the corresponding C++ Rail objects.
165 *
166 * Throws an exception if parsing fails.
167 *
168 * @param element JSON element
169 * @return vector of Rail objects
170 */
171std::vector<std::unique_ptr<Rail>> parseRoot(const nlohmann::json& element);
172
173/**
174 * Parses a JSON element containing a string.
175 *
176 * Returns the corresponding C++ string.
177 *
178 * Throws an exception if parsing fails.
179 *
180 * @param element JSON element
181 * @param isEmptyValid indicates whether an empty string value is valid
182 * @return string value
183 */
184inline std::string parseString(const nlohmann::json& element,
185 bool isEmptyValid = false)
186{
187 if (!element.is_string())
188 {
189 throw std::invalid_argument{"Element is not a string"};
190 }
191 std::string value = element.get<std::string>();
192 if (value.empty() && !isEmptyValid)
193 {
194 throw std::invalid_argument{"Element contains an empty string"};
195 }
196 return value;
197}
198
199/**
200 * Parses a JSON element containing an 8-bit unsigned integer.
201 *
202 * Returns the corresponding C++ uint8_t value.
203 *
204 * Throws an exception if parsing fails.
205 *
206 * @param element JSON element
207 * @return uint8_t value
208 */
209inline uint8_t parseUint8(const nlohmann::json& element)
210{
211 // Verify element contains an integer
212 if (!element.is_number_integer())
213 {
214 throw std::invalid_argument{"Element is not an integer"};
215 }
216 int value = element.get<int>();
217 if ((value < 0) || (value > UINT8_MAX))
218 {
219 throw std::invalid_argument{"Element is not an 8-bit unsigned integer"};
220 }
221 return static_cast<uint8_t>(value);
222}
223
224/**
225 * Parses a JSON element containing an unsigned integer.
226 *
227 * Returns the corresponding C++ unsigned int value.
228 *
229 * Throws an exception if parsing fails.
230 *
231 * @param element JSON element
232 * @return unsigned int value
233 */
234inline unsigned int parseUnsignedInteger(const nlohmann::json& element)
235{
236 // Verify element contains an unsigned integer
237 if (!element.is_number_unsigned())
238 {
239 throw std::invalid_argument{"Element is not an unsigned integer"};
240 }
241 return element.get<unsigned int>();
242}
243
244/**
245 * Verifies that the specified JSON element is a JSON array.
246 *
247 * Throws an invalid_argument exception if the element is not an array.
248 *
249 * @param element JSON element
250 */
251inline void verifyIsArray(const nlohmann::json& element)
252{
253 if (!element.is_array())
254 {
255 throw std::invalid_argument{"Element is not an array"};
256 }
257}
258
259/**
260 * Verifies that the specified JSON element is a JSON object.
261 *
262 * Throws an invalid_argument exception if the element is not an object.
263 *
264 * @param element JSON element
265 */
266inline void verifyIsObject(const nlohmann::json& element)
267{
268 if (!element.is_object())
269 {
270 throw std::invalid_argument{"Element is not an object"};
271 }
272}
273
274/**
275 * Verifies that the specified JSON element contains the expected number of
276 * properties.
277 *
278 * Throws an invalid_argument exception if the element contains a different
279 * number of properties. This indicates the element contains an invalid
280 * property.
281 *
282 * @param element JSON element
283 * @param expectedCount expected number of properties in element
284 */
285inline void verifyPropertyCount(const nlohmann::json& element,
286 unsigned int expectedCount)
287{
288 if (element.size() != expectedCount)
289 {
290 throw std::invalid_argument{"Element contains an invalid property"};
291 }
292}
293
294} // namespace internal
295
296} // namespace phosphor::power::sequencer::config_file_parser