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 check_status_vout property
104     bool checkStatusVout{false};
105     auto checkStatusVoutIt = element.find("check_status_vout");
106     if (checkStatusVoutIt != element.end())
107     {
108         checkStatusVout = parseBoolean(*checkStatusVoutIt);
109         ++propertyCount;
110     }
111 
112     // Optional compare_voltage_to_limits property
113     bool compareVoltageToLimits{false};
114     auto compareVoltageToLimitsIt = element.find("compare_voltage_to_limits");
115     if (compareVoltageToLimitsIt != element.end())
116     {
117         compareVoltageToLimits = parseBoolean(*compareVoltageToLimitsIt);
118         ++propertyCount;
119     }
120 
121     // Optional gpio property
122     std::optional<GPIO> gpio{};
123     auto gpioIt = element.find("gpio");
124     if (gpioIt != element.end())
125     {
126         gpio = parseGPIO(*gpioIt);
127         ++propertyCount;
128     }
129 
130     // If check_status_vout or compare_voltage_to_limits property is true,
131     // the page property is required; verify page was specified
132     if ((checkStatusVout || compareVoltageToLimits) && !page.has_value())
133     {
134         throw std::invalid_argument{"Required property missing: page"};
135     }
136 
137     // Verify no invalid properties exist
138     verifyPropertyCount(element, propertyCount);
139 
140     return std::make_unique<Rail>(name, presence, page, checkStatusVout,
141                                   compareVoltageToLimits, gpio);
142 }
143 
144 std::vector<std::unique_ptr<Rail>> parseRailArray(const json& element)
145 {
146     verifyIsArray(element);
147     std::vector<std::unique_ptr<Rail>> rails;
148     for (auto& railElement : element)
149     {
150         rails.emplace_back(parseRail(railElement));
151     }
152     return rails;
153 }
154 
155 std::vector<std::unique_ptr<Rail>> parseRoot(const json& element)
156 {
157     verifyIsObject(element);
158     unsigned int propertyCount{0};
159 
160     // Required rails property
161     const json& railsElement = getRequiredProperty(element, "rails");
162     std::vector<std::unique_ptr<Rail>> rails = parseRailArray(railsElement);
163     ++propertyCount;
164 
165     // Verify no invalid properties exist
166     verifyPropertyCount(element, propertyCount);
167 
168     return rails;
169 }
170 
171 } // namespace internal
172 
173 } // namespace phosphor::power::sequencer::config_file_parser
174