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 
17 #include "config_file_parser.hpp"
18 
19 #include "config_file_parser_error.hpp"
20 
21 #include <exception>
22 #include <fstream>
23 #include <optional>
24 
25 using json = nlohmann::json;
26 
27 namespace phosphor::power::sequencer::config_file_parser
28 {
29 
30 std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName)
31 {
32     try
33     {
34         // Use standard JSON parser to create tree of JSON elements
35         std::ifstream file{pathName};
36         json rootElement = json::parse(file);
37 
38         // Parse tree of JSON elements and return corresponding C++ objects
39         return internal::parseRoot(rootElement);
40     }
41     catch (const std::exception& e)
42     {
43         throw ConfigFileParserError{pathName, e.what()};
44     }
45 }
46 
47 namespace internal
48 {
49 
50 GPIO parseGPIO(const json& element)
51 {
52     verifyIsObject(element);
53     unsigned int propertyCount{0};
54 
55     // Required line property
56     const json& lineElement = getRequiredProperty(element, "line");
57     unsigned int line = parseUnsignedInteger(lineElement);
58     ++propertyCount;
59 
60     // Optional active_low property
61     bool activeLow{false};
62     auto activeLowIt = element.find("active_low");
63     if (activeLowIt != element.end())
64     {
65         activeLow = parseBoolean(*activeLowIt);
66         ++propertyCount;
67     }
68 
69     // Verify no invalid properties exist
70     verifyPropertyCount(element, propertyCount);
71 
72     return GPIO(line, activeLow);
73 }
74 
75 std::unique_ptr<Rail> parseRail(const json& element)
76 {
77     verifyIsObject(element);
78     unsigned int propertyCount{0};
79 
80     // Required name property
81     const json& nameElement = getRequiredProperty(element, "name");
82     std::string name = parseString(nameElement);
83     ++propertyCount;
84 
85     // Optional presence property
86     std::optional<std::string> presence{};
87     auto presenceIt = element.find("presence");
88     if (presenceIt != element.end())
89     {
90         presence = parseString(*presenceIt);
91         ++propertyCount;
92     }
93 
94     // Optional page property
95     std::optional<uint8_t> page{};
96     auto pageIt = element.find("page");
97     if (pageIt != element.end())
98     {
99         page = parseUint8(*pageIt);
100         ++propertyCount;
101     }
102 
103     // Optional is_power_supply_rail property
104     bool isPowerSupplyRail{false};
105     auto isPowerSupplyRailIt = element.find("is_power_supply_rail");
106     if (isPowerSupplyRailIt != element.end())
107     {
108         isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt);
109         ++propertyCount;
110     }
111 
112     // Optional check_status_vout property
113     bool checkStatusVout{false};
114     auto checkStatusVoutIt = element.find("check_status_vout");
115     if (checkStatusVoutIt != element.end())
116     {
117         checkStatusVout = parseBoolean(*checkStatusVoutIt);
118         ++propertyCount;
119     }
120 
121     // Optional compare_voltage_to_limits property
122     bool compareVoltageToLimits{false};
123     auto compareVoltageToLimitsIt = element.find("compare_voltage_to_limits");
124     if (compareVoltageToLimitsIt != element.end())
125     {
126         compareVoltageToLimits = parseBoolean(*compareVoltageToLimitsIt);
127         ++propertyCount;
128     }
129 
130     // Optional gpio property
131     std::optional<GPIO> gpio{};
132     auto gpioIt = element.find("gpio");
133     if (gpioIt != element.end())
134     {
135         gpio = parseGPIO(*gpioIt);
136         ++propertyCount;
137     }
138 
139     // If check_status_vout or compare_voltage_to_limits property is true,
140     // the page property is required; verify page was specified
141     if ((checkStatusVout || compareVoltageToLimits) && !page.has_value())
142     {
143         throw std::invalid_argument{"Required property missing: page"};
144     }
145 
146     // Verify no invalid properties exist
147     verifyPropertyCount(element, propertyCount);
148 
149     return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
150                                   checkStatusVout, compareVoltageToLimits,
151                                   gpio);
152 }
153 
154 std::vector<std::unique_ptr<Rail>> parseRailArray(const json& element)
155 {
156     verifyIsArray(element);
157     std::vector<std::unique_ptr<Rail>> rails;
158     for (auto& railElement : element)
159     {
160         rails.emplace_back(parseRail(railElement));
161     }
162     return rails;
163 }
164 
165 std::vector<std::unique_ptr<Rail>> parseRoot(const json& element)
166 {
167     verifyIsObject(element);
168     unsigned int propertyCount{0};
169 
170     // Required rails property
171     const json& railsElement = getRequiredProperty(element, "rails");
172     std::vector<std::unique_ptr<Rail>> rails = parseRailArray(railsElement);
173     ++propertyCount;
174 
175     // Verify no invalid properties exist
176     verifyPropertyCount(element, propertyCount);
177 
178     return rails;
179 }
180 
181 } // namespace internal
182 
183 } // namespace phosphor::power::sequencer::config_file_parser
184