16a957f6fSShawn McCarney /**
26a957f6fSShawn McCarney  * Copyright © 2024 IBM Corporation
36a957f6fSShawn McCarney  *
46a957f6fSShawn McCarney  * Licensed under the Apache License, Version 2.0 (the "License");
56a957f6fSShawn McCarney  * you may not use this file except in compliance with the License.
66a957f6fSShawn McCarney  * You may obtain a copy of the License at
76a957f6fSShawn McCarney  *
86a957f6fSShawn McCarney  *     http://www.apache.org/licenses/LICENSE-2.0
96a957f6fSShawn McCarney  *
106a957f6fSShawn McCarney  * Unless required by applicable law or agreed to in writing, software
116a957f6fSShawn McCarney  * distributed under the License is distributed on an "AS IS" BASIS,
126a957f6fSShawn McCarney  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136a957f6fSShawn McCarney  * See the License for the specific language governing permissions and
146a957f6fSShawn McCarney  * limitations under the License.
156a957f6fSShawn McCarney  */
166a957f6fSShawn McCarney #pragma once
176a957f6fSShawn McCarney 
186a957f6fSShawn McCarney #include "rail.hpp"
196a957f6fSShawn McCarney 
206a957f6fSShawn McCarney #include <nlohmann/json.hpp>
216a957f6fSShawn McCarney 
226a957f6fSShawn McCarney #include <cstdint>
236a957f6fSShawn McCarney #include <filesystem>
24*906cc3f3SShawn McCarney #include <memory>
256a957f6fSShawn McCarney #include <stdexcept>
266a957f6fSShawn McCarney #include <string>
276a957f6fSShawn McCarney #include <vector>
286a957f6fSShawn McCarney 
296a957f6fSShawn McCarney namespace phosphor::power::sequencer::config_file_parser
306a957f6fSShawn McCarney {
316a957f6fSShawn McCarney 
326a957f6fSShawn McCarney /**
336a957f6fSShawn McCarney  * Parses the specified JSON configuration file.
346a957f6fSShawn McCarney  *
356a957f6fSShawn McCarney  * Returns the corresponding C++ Rail objects.
366a957f6fSShawn McCarney  *
376a957f6fSShawn McCarney  * Throws a ConfigFileParserError if an error occurs.
386a957f6fSShawn McCarney  *
396a957f6fSShawn McCarney  * @param pathName configuration file path name
406a957f6fSShawn McCarney  * @return vector of Rail objects
416a957f6fSShawn McCarney  */
426a957f6fSShawn McCarney std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName);
436a957f6fSShawn McCarney 
446a957f6fSShawn McCarney /*
456a957f6fSShawn McCarney  * Internal implementation details for parse()
466a957f6fSShawn McCarney  */
476a957f6fSShawn McCarney namespace internal
486a957f6fSShawn McCarney {
496a957f6fSShawn McCarney 
506a957f6fSShawn McCarney /**
516a957f6fSShawn McCarney  * Returns the specified property of the specified JSON element.
526a957f6fSShawn McCarney  *
536a957f6fSShawn McCarney  * Throws an invalid_argument exception if the property does not exist.
546a957f6fSShawn McCarney  *
556a957f6fSShawn McCarney  * @param element JSON element
566a957f6fSShawn McCarney  * @param property property name
576a957f6fSShawn McCarney  */
586a957f6fSShawn McCarney #pragma GCC diagnostic push
596a957f6fSShawn McCarney #if __GNUC__ == 13
606a957f6fSShawn McCarney #pragma GCC diagnostic ignored "-Wdangling-reference"
616a957f6fSShawn McCarney #endif
626a957f6fSShawn McCarney inline const nlohmann::json& getRequiredProperty(const nlohmann::json& element,
636a957f6fSShawn McCarney                                                  const std::string& property)
646a957f6fSShawn McCarney {
656a957f6fSShawn McCarney     auto it = element.find(property);
666a957f6fSShawn McCarney     if (it == element.end())
676a957f6fSShawn McCarney     {
686a957f6fSShawn McCarney         throw std::invalid_argument{"Required property missing: " + property};
696a957f6fSShawn McCarney     }
706a957f6fSShawn McCarney     return *it;
716a957f6fSShawn McCarney }
726a957f6fSShawn McCarney #pragma GCC diagnostic pop
736a957f6fSShawn McCarney 
746a957f6fSShawn McCarney /**
756a957f6fSShawn McCarney  * Parses a JSON element containing a boolean.
766a957f6fSShawn McCarney  *
776a957f6fSShawn McCarney  * Returns the corresponding C++ boolean value.
786a957f6fSShawn McCarney  *
796a957f6fSShawn McCarney  * Throws an exception if parsing fails.
806a957f6fSShawn McCarney  *
816a957f6fSShawn McCarney  * @param element JSON element
826a957f6fSShawn McCarney  * @return boolean value
836a957f6fSShawn McCarney  */
846a957f6fSShawn McCarney inline bool parseBoolean(const nlohmann::json& element)
856a957f6fSShawn McCarney {
866a957f6fSShawn McCarney     // Verify element contains a boolean
876a957f6fSShawn McCarney     if (!element.is_boolean())
886a957f6fSShawn McCarney     {
896a957f6fSShawn McCarney         throw std::invalid_argument{"Element is not a boolean"};
906a957f6fSShawn McCarney     }
916a957f6fSShawn McCarney     return element.get<bool>();
926a957f6fSShawn McCarney }
936a957f6fSShawn McCarney 
946a957f6fSShawn McCarney /**
956a957f6fSShawn McCarney  * Parses a JSON element containing a GPIO.
966a957f6fSShawn McCarney  *
976a957f6fSShawn McCarney  * Returns the corresponding C++ GPIO object.
986a957f6fSShawn McCarney  *
996a957f6fSShawn McCarney  * Throws an exception if parsing fails.
1006a957f6fSShawn McCarney  *
1016a957f6fSShawn McCarney  * @param element JSON element
1026a957f6fSShawn McCarney  * @return GPIO object
1036a957f6fSShawn McCarney  */
1046a957f6fSShawn McCarney GPIO parseGPIO(const nlohmann::json& element);
1056a957f6fSShawn McCarney 
1066a957f6fSShawn McCarney /**
1076a957f6fSShawn McCarney  * Parses a JSON element containing a rail.
1086a957f6fSShawn McCarney  *
1096a957f6fSShawn McCarney  * Returns the corresponding C++ Rail object.
1106a957f6fSShawn McCarney  *
1116a957f6fSShawn McCarney  * Throws an exception if parsing fails.
1126a957f6fSShawn McCarney  *
1136a957f6fSShawn McCarney  * @param element JSON element
1146a957f6fSShawn McCarney  * @return Rail object
1156a957f6fSShawn McCarney  */
1166a957f6fSShawn McCarney std::unique_ptr<Rail> parseRail(const nlohmann::json& element);
1176a957f6fSShawn McCarney 
1186a957f6fSShawn McCarney /**
1196a957f6fSShawn McCarney  * Parses a JSON element containing an array of rails.
1206a957f6fSShawn McCarney  *
1216a957f6fSShawn McCarney  * Returns the corresponding C++ Rail objects.
1226a957f6fSShawn McCarney  *
1236a957f6fSShawn McCarney  * Throws an exception if parsing fails.
1246a957f6fSShawn McCarney  *
1256a957f6fSShawn McCarney  * @param element JSON element
1266a957f6fSShawn McCarney  * @return vector of Rail objects
1276a957f6fSShawn McCarney  */
1286a957f6fSShawn McCarney std::vector<std::unique_ptr<Rail>>
1296a957f6fSShawn McCarney     parseRailArray(const nlohmann::json& element);
1306a957f6fSShawn McCarney 
1316a957f6fSShawn McCarney /**
1326a957f6fSShawn McCarney  * Parses the JSON root element of the entire configuration file.
1336a957f6fSShawn McCarney  *
1346a957f6fSShawn McCarney  * Returns the corresponding C++ Rail objects.
1356a957f6fSShawn McCarney  *
1366a957f6fSShawn McCarney  * Throws an exception if parsing fails.
1376a957f6fSShawn McCarney  *
1386a957f6fSShawn McCarney  * @param element JSON element
1396a957f6fSShawn McCarney  * @return vector of Rail objects
1406a957f6fSShawn McCarney  */
1416a957f6fSShawn McCarney std::vector<std::unique_ptr<Rail>> parseRoot(const nlohmann::json& element);
1426a957f6fSShawn McCarney 
1436a957f6fSShawn McCarney /**
1446a957f6fSShawn McCarney  * Parses a JSON element containing a string.
1456a957f6fSShawn McCarney  *
1466a957f6fSShawn McCarney  * Returns the corresponding C++ string.
1476a957f6fSShawn McCarney  *
1486a957f6fSShawn McCarney  * Throws an exception if parsing fails.
1496a957f6fSShawn McCarney  *
1506a957f6fSShawn McCarney  * @param element JSON element
1516a957f6fSShawn McCarney  * @param isEmptyValid indicates whether an empty string value is valid
1526a957f6fSShawn McCarney  * @return string value
1536a957f6fSShawn McCarney  */
1546a957f6fSShawn McCarney inline std::string parseString(const nlohmann::json& element,
1556a957f6fSShawn McCarney                                bool isEmptyValid = false)
1566a957f6fSShawn McCarney {
1576a957f6fSShawn McCarney     if (!element.is_string())
1586a957f6fSShawn McCarney     {
1596a957f6fSShawn McCarney         throw std::invalid_argument{"Element is not a string"};
1606a957f6fSShawn McCarney     }
1616a957f6fSShawn McCarney     std::string value = element.get<std::string>();
1626a957f6fSShawn McCarney     if (value.empty() && !isEmptyValid)
1636a957f6fSShawn McCarney     {
1646a957f6fSShawn McCarney         throw std::invalid_argument{"Element contains an empty string"};
1656a957f6fSShawn McCarney     }
1666a957f6fSShawn McCarney     return value;
1676a957f6fSShawn McCarney }
1686a957f6fSShawn McCarney 
1696a957f6fSShawn McCarney /**
1706a957f6fSShawn McCarney  * Parses a JSON element containing an 8-bit unsigned integer.
1716a957f6fSShawn McCarney  *
1726a957f6fSShawn McCarney  * Returns the corresponding C++ uint8_t value.
1736a957f6fSShawn McCarney  *
1746a957f6fSShawn McCarney  * Throws an exception if parsing fails.
1756a957f6fSShawn McCarney  *
1766a957f6fSShawn McCarney  * @param element JSON element
1776a957f6fSShawn McCarney  * @return uint8_t value
1786a957f6fSShawn McCarney  */
1796a957f6fSShawn McCarney inline uint8_t parseUint8(const nlohmann::json& element)
1806a957f6fSShawn McCarney {
1816a957f6fSShawn McCarney     // Verify element contains an integer
1826a957f6fSShawn McCarney     if (!element.is_number_integer())
1836a957f6fSShawn McCarney     {
1846a957f6fSShawn McCarney         throw std::invalid_argument{"Element is not an integer"};
1856a957f6fSShawn McCarney     }
1866a957f6fSShawn McCarney     int value = element.get<int>();
1876a957f6fSShawn McCarney     if ((value < 0) || (value > UINT8_MAX))
1886a957f6fSShawn McCarney     {
1896a957f6fSShawn McCarney         throw std::invalid_argument{"Element is not an 8-bit unsigned integer"};
1906a957f6fSShawn McCarney     }
1916a957f6fSShawn McCarney     return static_cast<uint8_t>(value);
1926a957f6fSShawn McCarney }
1936a957f6fSShawn McCarney 
1946a957f6fSShawn McCarney /**
1956a957f6fSShawn McCarney  * Parses a JSON element containing an unsigned integer.
1966a957f6fSShawn McCarney  *
1976a957f6fSShawn McCarney  * Returns the corresponding C++ unsigned int value.
1986a957f6fSShawn McCarney  *
1996a957f6fSShawn McCarney  * Throws an exception if parsing fails.
2006a957f6fSShawn McCarney  *
2016a957f6fSShawn McCarney  * @param element JSON element
2026a957f6fSShawn McCarney  * @return unsigned int value
2036a957f6fSShawn McCarney  */
2046a957f6fSShawn McCarney inline unsigned int parseUnsignedInteger(const nlohmann::json& element)
2056a957f6fSShawn McCarney {
2066a957f6fSShawn McCarney     // Verify element contains an unsigned integer
2076a957f6fSShawn McCarney     if (!element.is_number_unsigned())
2086a957f6fSShawn McCarney     {
2096a957f6fSShawn McCarney         throw std::invalid_argument{"Element is not an unsigned integer"};
2106a957f6fSShawn McCarney     }
2116a957f6fSShawn McCarney     return element.get<unsigned int>();
2126a957f6fSShawn McCarney }
2136a957f6fSShawn McCarney 
2146a957f6fSShawn McCarney /**
2156a957f6fSShawn McCarney  * Verifies that the specified JSON element is a JSON array.
2166a957f6fSShawn McCarney  *
2176a957f6fSShawn McCarney  * Throws an invalid_argument exception if the element is not an array.
2186a957f6fSShawn McCarney  *
2196a957f6fSShawn McCarney  * @param element JSON element
2206a957f6fSShawn McCarney  */
2216a957f6fSShawn McCarney inline void verifyIsArray(const nlohmann::json& element)
2226a957f6fSShawn McCarney {
2236a957f6fSShawn McCarney     if (!element.is_array())
2246a957f6fSShawn McCarney     {
2256a957f6fSShawn McCarney         throw std::invalid_argument{"Element is not an array"};
2266a957f6fSShawn McCarney     }
2276a957f6fSShawn McCarney }
2286a957f6fSShawn McCarney 
2296a957f6fSShawn McCarney /**
2306a957f6fSShawn McCarney  * Verifies that the specified JSON element is a JSON object.
2316a957f6fSShawn McCarney  *
2326a957f6fSShawn McCarney  * Throws an invalid_argument exception if the element is not an object.
2336a957f6fSShawn McCarney  *
2346a957f6fSShawn McCarney  * @param element JSON element
2356a957f6fSShawn McCarney  */
2366a957f6fSShawn McCarney inline void verifyIsObject(const nlohmann::json& element)
2376a957f6fSShawn McCarney {
2386a957f6fSShawn McCarney     if (!element.is_object())
2396a957f6fSShawn McCarney     {
2406a957f6fSShawn McCarney         throw std::invalid_argument{"Element is not an object"};
2416a957f6fSShawn McCarney     }
2426a957f6fSShawn McCarney }
2436a957f6fSShawn McCarney 
2446a957f6fSShawn McCarney /**
2456a957f6fSShawn McCarney  * Verifies that the specified JSON element contains the expected number of
2466a957f6fSShawn McCarney  * properties.
2476a957f6fSShawn McCarney  *
2486a957f6fSShawn McCarney  * Throws an invalid_argument exception if the element contains a different
2496a957f6fSShawn McCarney  * number of properties.  This indicates the element contains an invalid
2506a957f6fSShawn McCarney  * property.
2516a957f6fSShawn McCarney  *
2526a957f6fSShawn McCarney  * @param element JSON element
2536a957f6fSShawn McCarney  * @param expectedCount expected number of properties in element
2546a957f6fSShawn McCarney  */
2556a957f6fSShawn McCarney inline void verifyPropertyCount(const nlohmann::json& element,
2566a957f6fSShawn McCarney                                 unsigned int expectedCount)
2576a957f6fSShawn McCarney {
2586a957f6fSShawn McCarney     if (element.size() != expectedCount)
2596a957f6fSShawn McCarney     {
2606a957f6fSShawn McCarney         throw std::invalid_argument{"Element contains an invalid property"};
2616a957f6fSShawn McCarney     }
2626a957f6fSShawn McCarney }
2636a957f6fSShawn McCarney 
2646a957f6fSShawn McCarney } // namespace internal
2656a957f6fSShawn McCarney 
2666a957f6fSShawn McCarney } // namespace phosphor::power::sequencer::config_file_parser
267