1 /**
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>
24 #include <stdexcept>
25 #include <string>
26 #include <vector>
27 
28 namespace phosphor::power::sequencer::config_file_parser
29 {
30 
31 /**
32  * Parses the specified JSON configuration file.
33  *
34  * Returns the corresponding C++ Rail objects.
35  *
36  * Throws a ConfigFileParserError if an error occurs.
37  *
38  * @param pathName configuration file path name
39  * @return vector of Rail objects
40  */
41 std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName);
42 
43 /*
44  * Internal implementation details for parse()
45  */
46 namespace internal
47 {
48 
49 /**
50  * Returns the specified property of the specified JSON element.
51  *
52  * Throws an invalid_argument exception if the property does not exist.
53  *
54  * @param element JSON element
55  * @param property property name
56  */
57 #pragma GCC diagnostic push
58 #if __GNUC__ == 13
59 #pragma GCC diagnostic ignored "-Wdangling-reference"
60 #endif
61 inline const nlohmann::json& getRequiredProperty(const nlohmann::json& element,
62                                                  const std::string& property)
63 {
64     auto it = element.find(property);
65     if (it == element.end())
66     {
67         throw std::invalid_argument{"Required property missing: " + property};
68     }
69     return *it;
70 }
71 #pragma GCC diagnostic pop
72 
73 /**
74  * Parses a JSON element containing a boolean.
75  *
76  * Returns the corresponding C++ boolean value.
77  *
78  * Throws an exception if parsing fails.
79  *
80  * @param element JSON element
81  * @return boolean value
82  */
83 inline bool parseBoolean(const nlohmann::json& element)
84 {
85     // Verify element contains a boolean
86     if (!element.is_boolean())
87     {
88         throw std::invalid_argument{"Element is not a boolean"};
89     }
90     return element.get<bool>();
91 }
92 
93 /**
94  * Parses a JSON element containing a GPIO.
95  *
96  * Returns the corresponding C++ GPIO object.
97  *
98  * Throws an exception if parsing fails.
99  *
100  * @param element JSON element
101  * @return GPIO object
102  */
103 GPIO parseGPIO(const nlohmann::json& element);
104 
105 /**
106  * Parses a JSON element containing a rail.
107  *
108  * Returns the corresponding C++ Rail object.
109  *
110  * Throws an exception if parsing fails.
111  *
112  * @param element JSON element
113  * @return Rail object
114  */
115 std::unique_ptr<Rail> parseRail(const nlohmann::json& element);
116 
117 /**
118  * Parses a JSON element containing an array of rails.
119  *
120  * Returns the corresponding C++ Rail objects.
121  *
122  * Throws an exception if parsing fails.
123  *
124  * @param element JSON element
125  * @return vector of Rail objects
126  */
127 std::vector<std::unique_ptr<Rail>>
128     parseRailArray(const nlohmann::json& element);
129 
130 /**
131  * Parses the JSON root element of the entire configuration file.
132  *
133  * Returns the corresponding C++ Rail objects.
134  *
135  * Throws an exception if parsing fails.
136  *
137  * @param element JSON element
138  * @return vector of Rail objects
139  */
140 std::vector<std::unique_ptr<Rail>> parseRoot(const nlohmann::json& element);
141 
142 /**
143  * Parses a JSON element containing a string.
144  *
145  * Returns the corresponding C++ string.
146  *
147  * Throws an exception if parsing fails.
148  *
149  * @param element JSON element
150  * @param isEmptyValid indicates whether an empty string value is valid
151  * @return string value
152  */
153 inline std::string parseString(const nlohmann::json& element,
154                                bool isEmptyValid = false)
155 {
156     if (!element.is_string())
157     {
158         throw std::invalid_argument{"Element is not a string"};
159     }
160     std::string value = element.get<std::string>();
161     if (value.empty() && !isEmptyValid)
162     {
163         throw std::invalid_argument{"Element contains an empty string"};
164     }
165     return value;
166 }
167 
168 /**
169  * Parses a JSON element containing an 8-bit unsigned integer.
170  *
171  * Returns the corresponding C++ uint8_t value.
172  *
173  * Throws an exception if parsing fails.
174  *
175  * @param element JSON element
176  * @return uint8_t value
177  */
178 inline uint8_t parseUint8(const nlohmann::json& element)
179 {
180     // Verify element contains an integer
181     if (!element.is_number_integer())
182     {
183         throw std::invalid_argument{"Element is not an integer"};
184     }
185     int value = element.get<int>();
186     if ((value < 0) || (value > UINT8_MAX))
187     {
188         throw std::invalid_argument{"Element is not an 8-bit unsigned integer"};
189     }
190     return static_cast<uint8_t>(value);
191 }
192 
193 /**
194  * Parses a JSON element containing an unsigned integer.
195  *
196  * Returns the corresponding C++ unsigned int value.
197  *
198  * Throws an exception if parsing fails.
199  *
200  * @param element JSON element
201  * @return unsigned int value
202  */
203 inline unsigned int parseUnsignedInteger(const nlohmann::json& element)
204 {
205     // Verify element contains an unsigned integer
206     if (!element.is_number_unsigned())
207     {
208         throw std::invalid_argument{"Element is not an unsigned integer"};
209     }
210     return element.get<unsigned int>();
211 }
212 
213 /**
214  * Verifies that the specified JSON element is a JSON array.
215  *
216  * Throws an invalid_argument exception if the element is not an array.
217  *
218  * @param element JSON element
219  */
220 inline void verifyIsArray(const nlohmann::json& element)
221 {
222     if (!element.is_array())
223     {
224         throw std::invalid_argument{"Element is not an array"};
225     }
226 }
227 
228 /**
229  * Verifies that the specified JSON element is a JSON object.
230  *
231  * Throws an invalid_argument exception if the element is not an object.
232  *
233  * @param element JSON element
234  */
235 inline void verifyIsObject(const nlohmann::json& element)
236 {
237     if (!element.is_object())
238     {
239         throw std::invalid_argument{"Element is not an object"};
240     }
241 }
242 
243 /**
244  * Verifies that the specified JSON element contains the expected number of
245  * properties.
246  *
247  * Throws an invalid_argument exception if the element contains a different
248  * number of properties.  This indicates the element contains an invalid
249  * property.
250  *
251  * @param element JSON element
252  * @param expectedCount expected number of properties in element
253  */
254 inline void verifyPropertyCount(const nlohmann::json& element,
255                                 unsigned int expectedCount)
256 {
257     if (element.size() != expectedCount)
258     {
259         throw std::invalid_argument{"Element contains an invalid property"};
260     }
261 }
262 
263 } // namespace internal
264 
265 } // namespace phosphor::power::sequencer::config_file_parser
266