1 /**
2  * Copyright © 2020 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 #include "i2c_interface.hpp"
21 #include "pmbus_utils.hpp"
22 
23 #include <exception>
24 #include <fstream>
25 #include <optional>
26 #include <utility>
27 
28 using json = nlohmann::json;
29 
30 namespace phosphor::power::regulators::config_file_parser
31 {
32 
33 std::tuple<std::vector<std::unique_ptr<Rule>>,
34            std::vector<std::unique_ptr<Chassis>>>
35     parse(const std::filesystem::path& pathName)
36 {
37     try
38     {
39         // Use standard JSON parser to create tree of JSON elements
40         std::ifstream file{pathName};
41         json rootElement = json::parse(file);
42 
43         // Parse tree of JSON elements and return corresponding C++ objects
44         return internal::parseRoot(rootElement);
45     }
46     catch (const std::exception& e)
47     {
48         throw ConfigFileParserError{pathName, e.what()};
49     }
50 }
51 
52 namespace internal
53 {
54 
55 std::unique_ptr<Action> parseAction(const json& element)
56 {
57     verifyIsObject(element);
58     unsigned int propertyCount{0};
59 
60     // Optional comments property; value not stored
61     if (element.contains("comments"))
62     {
63         ++propertyCount;
64     }
65 
66     // Required action type property; there must be exactly one specified
67     std::unique_ptr<Action> action{};
68     if (element.contains("and"))
69     {
70         action = parseAnd(element["and"]);
71         ++propertyCount;
72     }
73     else if (element.contains("compare_presence"))
74     {
75         action = parseComparePresence(element["compare_presence"]);
76         ++propertyCount;
77     }
78     else if (element.contains("compare_vpd"))
79     {
80         // TODO: Not implemented yet
81         // action = parseCompareVPD(element["compare_vpd"]);
82         // ++propertyCount;
83     }
84     else if (element.contains("i2c_compare_bit"))
85     {
86         action = parseI2CCompareBit(element["i2c_compare_bit"]);
87         ++propertyCount;
88     }
89     else if (element.contains("i2c_compare_byte"))
90     {
91         action = parseI2CCompareByte(element["i2c_compare_byte"]);
92         ++propertyCount;
93     }
94     else if (element.contains("i2c_compare_bytes"))
95     {
96         action = parseI2CCompareBytes(element["i2c_compare_bytes"]);
97         ++propertyCount;
98     }
99     else if (element.contains("i2c_write_bit"))
100     {
101         action = parseI2CWriteBit(element["i2c_write_bit"]);
102         ++propertyCount;
103     }
104     else if (element.contains("i2c_write_byte"))
105     {
106         action = parseI2CWriteByte(element["i2c_write_byte"]);
107         ++propertyCount;
108     }
109     else if (element.contains("i2c_write_bytes"))
110     {
111         action = parseI2CWriteBytes(element["i2c_write_bytes"]);
112         ++propertyCount;
113     }
114     else if (element.contains("if"))
115     {
116         action = parseIf(element["if"]);
117         ++propertyCount;
118     }
119     else if (element.contains("not"))
120     {
121         action = parseNot(element["not"]);
122         ++propertyCount;
123     }
124     else if (element.contains("or"))
125     {
126         action = parseOr(element["or"]);
127         ++propertyCount;
128     }
129     else if (element.contains("pmbus_read_sensor"))
130     {
131         // TODO: Not implemented yet
132         // action = parsePMBusReadSensor(element["pmbus_read_sensor"]);
133         // ++propertyCount;
134     }
135     else if (element.contains("pmbus_write_vout_command"))
136     {
137         action =
138             parsePMBusWriteVoutCommand(element["pmbus_write_vout_command"]);
139         ++propertyCount;
140     }
141     else if (element.contains("run_rule"))
142     {
143         action = parseRunRule(element["run_rule"]);
144         ++propertyCount;
145     }
146     else if (element.contains("set_device"))
147     {
148         action = parseSetDevice(element["set_device"]);
149         ++propertyCount;
150     }
151     else
152     {
153         throw std::invalid_argument{"Required action type property missing"};
154     }
155 
156     // Verify no invalid properties exist
157     verifyPropertyCount(element, propertyCount);
158 
159     return action;
160 }
161 
162 std::vector<std::unique_ptr<Action>> parseActionArray(const json& element)
163 {
164     verifyIsArray(element);
165     std::vector<std::unique_ptr<Action>> actions;
166     for (auto& actionElement : element)
167     {
168         actions.emplace_back(parseAction(actionElement));
169     }
170     return actions;
171 }
172 
173 std::unique_ptr<AndAction> parseAnd(const json& element)
174 {
175     verifyIsArray(element);
176 
177     // Verify if array size less than 2
178     if (element.size() < 2)
179     {
180         throw std::invalid_argument{"Array must contain two or more actions"};
181     }
182     // Array of two or more actions
183     std::vector<std::unique_ptr<Action>> actions = parseActionArray(element);
184 
185     return std::make_unique<AndAction>(std::move(actions));
186 }
187 
188 std::unique_ptr<Chassis> parseChassis(const json& element)
189 {
190     verifyIsObject(element);
191     unsigned int propertyCount{0};
192 
193     // Optional comments property; value not stored
194     if (element.contains("comments"))
195     {
196         ++propertyCount;
197     }
198 
199     // Required number property
200     const json& numberElement = getRequiredProperty(element, "number");
201     unsigned int number = parseUnsignedInteger(numberElement);
202     if (number < 1)
203     {
204         throw std::invalid_argument{"Invalid chassis number: Must be > 0"};
205     }
206     ++propertyCount;
207 
208     // Optional devices property
209     std::vector<std::unique_ptr<Device>> devices{};
210     auto devicesIt = element.find("devices");
211     if (devicesIt != element.end())
212     {
213         devices = parseDeviceArray(*devicesIt);
214         ++propertyCount;
215     }
216 
217     // Verify no invalid properties exist
218     verifyPropertyCount(element, propertyCount);
219 
220     return std::make_unique<Chassis>(number, std::move(devices));
221 }
222 
223 std::vector<std::unique_ptr<Chassis>> parseChassisArray(const json& element)
224 {
225     verifyIsArray(element);
226     std::vector<std::unique_ptr<Chassis>> chassis;
227     for (auto& chassisElement : element)
228     {
229         chassis.emplace_back(parseChassis(chassisElement));
230     }
231     return chassis;
232 }
233 
234 std::unique_ptr<ComparePresenceAction> parseComparePresence(const json& element)
235 {
236     verifyIsObject(element);
237     unsigned int propertyCount{0};
238 
239     // Required fru property
240     const json& fruElement = getRequiredProperty(element, "fru");
241     std::string fru = parseString(fruElement);
242     ++propertyCount;
243 
244     // Required value property
245     const json& valueElement = getRequiredProperty(element, "value");
246     bool value = parseBoolean(valueElement);
247     ++propertyCount;
248 
249     // Verify no invalid properties exist
250     verifyPropertyCount(element, propertyCount);
251 
252     return std::make_unique<ComparePresenceAction>(fru, value);
253 }
254 
255 std::unique_ptr<Configuration> parseConfiguration(const json& element)
256 {
257     verifyIsObject(element);
258     unsigned int propertyCount{0};
259 
260     // Optional comments property; value not stored
261     if (element.contains("comments"))
262     {
263         ++propertyCount;
264     }
265 
266     // Optional volts property
267     std::optional<double> volts{};
268     auto voltsIt = element.find("volts");
269     if (voltsIt != element.end())
270     {
271         volts = parseDouble(*voltsIt);
272         ++propertyCount;
273     }
274 
275     // Required rule_id or actions property
276     std::vector<std::unique_ptr<Action>> actions{};
277     actions = parseRuleIDOrActionsProperty(element);
278     ++propertyCount;
279 
280     // Verify no invalid properties exist
281     verifyPropertyCount(element, propertyCount);
282 
283     return std::make_unique<Configuration>(volts, std::move(actions));
284 }
285 
286 std::unique_ptr<Device> parseDevice(const json& element)
287 {
288     verifyIsObject(element);
289     unsigned int propertyCount{0};
290 
291     // Optional comments property; value not stored
292     if (element.contains("comments"))
293     {
294         ++propertyCount;
295     }
296 
297     // Required id property
298     const json& idElement = getRequiredProperty(element, "id");
299     std::string id = parseString(idElement);
300     ++propertyCount;
301 
302     // Required is_regulator property
303     const json& isRegulatorElement =
304         getRequiredProperty(element, "is_regulator");
305     bool isRegulator = parseBoolean(isRegulatorElement);
306     ++propertyCount;
307 
308     // Required fru property
309     const json& fruElement = getRequiredProperty(element, "fru");
310     std::string fru = parseString(fruElement);
311     ++propertyCount;
312 
313     // Required i2c_interface property
314     const json& i2cInterfaceElement =
315         getRequiredProperty(element, "i2c_interface");
316     std::unique_ptr<i2c::I2CInterface> i2cInterface =
317         parseI2CInterface(i2cInterfaceElement);
318     ++propertyCount;
319 
320     // Optional presence_detection property
321     std::unique_ptr<PresenceDetection> presenceDetection{};
322     auto presenceDetectionIt = element.find("presence_detection");
323     if (presenceDetectionIt != element.end())
324     {
325         presenceDetection = parsePresenceDetection(*presenceDetectionIt);
326         ++propertyCount;
327     }
328 
329     // Optional configuration property
330     std::unique_ptr<Configuration> configuration{};
331     auto configurationIt = element.find("configuration");
332     if (configurationIt != element.end())
333     {
334         configuration = parseConfiguration(*configurationIt);
335         ++propertyCount;
336     }
337 
338     // Optional rails property
339     std::vector<std::unique_ptr<Rail>> rails{};
340     auto railsIt = element.find("rails");
341     if (railsIt != element.end())
342     {
343         if (!isRegulator)
344         {
345             throw std::invalid_argument{
346                 "Invalid rails property when is_regulator is false"};
347         }
348         rails = parseRailArray(*railsIt);
349         ++propertyCount;
350     }
351 
352     // Verify no invalid properties exist
353     verifyPropertyCount(element, propertyCount);
354 
355     return std::make_unique<Device>(id, isRegulator, fru,
356                                     std::move(i2cInterface),
357                                     std::move(presenceDetection),
358                                     std::move(configuration), std::move(rails));
359 }
360 
361 std::vector<std::unique_ptr<Device>> parseDeviceArray(const json& element)
362 {
363     verifyIsArray(element);
364     std::vector<std::unique_ptr<Device>> devices;
365     for (auto& deviceElement : element)
366     {
367         devices.emplace_back(parseDevice(deviceElement));
368     }
369     return devices;
370 }
371 
372 std::vector<uint8_t> parseHexByteArray(const json& element)
373 {
374     verifyIsArray(element);
375     std::vector<uint8_t> values;
376     for (auto& valueElement : element)
377     {
378         values.emplace_back(parseHexByte(valueElement));
379     }
380     return values;
381 }
382 
383 std::unique_ptr<I2CCompareBitAction> parseI2CCompareBit(const json& element)
384 {
385     verifyIsObject(element);
386     unsigned int propertyCount{0};
387 
388     // Required register property
389     const json& regElement = getRequiredProperty(element, "register");
390     uint8_t reg = parseHexByte(regElement);
391     ++propertyCount;
392 
393     // Required position property
394     const json& positionElement = getRequiredProperty(element, "position");
395     uint8_t position = parseBitPosition(positionElement);
396     ++propertyCount;
397 
398     // Required value property
399     const json& valueElement = getRequiredProperty(element, "value");
400     uint8_t value = parseBitValue(valueElement);
401     ++propertyCount;
402 
403     // Verify no invalid properties exist
404     verifyPropertyCount(element, propertyCount);
405 
406     return std::make_unique<I2CCompareBitAction>(reg, position, value);
407 }
408 
409 std::unique_ptr<I2CCompareByteAction> parseI2CCompareByte(const json& element)
410 {
411     verifyIsObject(element);
412     unsigned int propertyCount{0};
413 
414     // Required register property
415     const json& regElement = getRequiredProperty(element, "register");
416     uint8_t reg = parseHexByte(regElement);
417     ++propertyCount;
418 
419     // Required value property
420     const json& valueElement = getRequiredProperty(element, "value");
421     uint8_t value = parseHexByte(valueElement);
422     ++propertyCount;
423 
424     // Optional mask property
425     uint8_t mask = 0xff;
426     auto maskIt = element.find("mask");
427     if (maskIt != element.end())
428     {
429         mask = parseHexByte(*maskIt);
430         ++propertyCount;
431     }
432 
433     // Verify no invalid properties exist
434     verifyPropertyCount(element, propertyCount);
435 
436     return std::make_unique<I2CCompareByteAction>(reg, value, mask);
437 }
438 
439 std::unique_ptr<I2CCompareBytesAction> parseI2CCompareBytes(const json& element)
440 {
441     verifyIsObject(element);
442     unsigned int propertyCount{0};
443 
444     // Required register property
445     const json& regElement = getRequiredProperty(element, "register");
446     uint8_t reg = parseHexByte(regElement);
447     ++propertyCount;
448 
449     // Required values property
450     const json& valueElement = getRequiredProperty(element, "values");
451     std::vector<uint8_t> values = parseHexByteArray(valueElement);
452     ++propertyCount;
453 
454     // Optional masks property
455     std::vector<uint8_t> masks{};
456     auto masksIt = element.find("masks");
457     if (masksIt != element.end())
458     {
459         masks = parseHexByteArray(*masksIt);
460         ++propertyCount;
461     }
462 
463     // Verify masks array (if specified) was same size as values array
464     if ((!masks.empty()) && (masks.size() != values.size()))
465     {
466         throw std::invalid_argument{"Invalid number of elements in masks"};
467     }
468 
469     // Verify no invalid properties exist
470     verifyPropertyCount(element, propertyCount);
471 
472     if (masks.empty())
473     {
474         return std::make_unique<I2CCompareBytesAction>(reg, values);
475     }
476     return std::make_unique<I2CCompareBytesAction>(reg, values, masks);
477 }
478 
479 std::unique_ptr<i2c::I2CInterface> parseI2CInterface(const json& element)
480 {
481     verifyIsObject(element);
482     unsigned int propertyCount{0};
483 
484     // Required bus property
485     const json& busElement = getRequiredProperty(element, "bus");
486     uint8_t bus = parseUint8(busElement);
487     ++propertyCount;
488 
489     // Required address property
490     const json& addressElement = getRequiredProperty(element, "address");
491     uint8_t address = parseHexByte(addressElement);
492     ++propertyCount;
493 
494     verifyPropertyCount(element, propertyCount);
495     return i2c::create(bus, address, i2c::I2CInterface::InitialState::CLOSED);
496 }
497 
498 std::unique_ptr<I2CWriteBitAction> parseI2CWriteBit(const json& element)
499 {
500     verifyIsObject(element);
501     unsigned int propertyCount{0};
502 
503     // Required register property
504     const json& regElement = getRequiredProperty(element, "register");
505     uint8_t reg = parseHexByte(regElement);
506     ++propertyCount;
507 
508     // Required position property
509     const json& positionElement = getRequiredProperty(element, "position");
510     uint8_t position = parseBitPosition(positionElement);
511     ++propertyCount;
512 
513     // Required value property
514     const json& valueElement = getRequiredProperty(element, "value");
515     uint8_t value = parseBitValue(valueElement);
516     ++propertyCount;
517 
518     // Verify no invalid properties exist
519     verifyPropertyCount(element, propertyCount);
520 
521     return std::make_unique<I2CWriteBitAction>(reg, position, value);
522 }
523 
524 std::unique_ptr<I2CWriteByteAction> parseI2CWriteByte(const json& element)
525 {
526     verifyIsObject(element);
527     unsigned int propertyCount{0};
528 
529     // Required register property
530     const json& regElement = getRequiredProperty(element, "register");
531     uint8_t reg = parseHexByte(regElement);
532     ++propertyCount;
533 
534     // Required value property
535     const json& valueElement = getRequiredProperty(element, "value");
536     uint8_t value = parseHexByte(valueElement);
537     ++propertyCount;
538 
539     // Optional mask property
540     uint8_t mask = 0xff;
541     auto maskIt = element.find("mask");
542     if (maskIt != element.end())
543     {
544         mask = parseHexByte(*maskIt);
545         ++propertyCount;
546     }
547 
548     // Verify no invalid properties exist
549     verifyPropertyCount(element, propertyCount);
550 
551     return std::make_unique<I2CWriteByteAction>(reg, value, mask);
552 }
553 
554 std::unique_ptr<I2CWriteBytesAction> parseI2CWriteBytes(const json& element)
555 {
556     verifyIsObject(element);
557     unsigned int propertyCount{0};
558 
559     // Required register property
560     const json& regElement = getRequiredProperty(element, "register");
561     uint8_t reg = parseHexByte(regElement);
562     ++propertyCount;
563 
564     // Required values property
565     const json& valueElement = getRequiredProperty(element, "values");
566     std::vector<uint8_t> values = parseHexByteArray(valueElement);
567     ++propertyCount;
568 
569     // Optional masks property
570     std::vector<uint8_t> masks{};
571     auto masksIt = element.find("masks");
572     if (masksIt != element.end())
573     {
574         masks = parseHexByteArray(*masksIt);
575         ++propertyCount;
576     }
577 
578     // Verify masks array (if specified) was same size as values array
579     if ((!masks.empty()) && (masks.size() != values.size()))
580     {
581         throw std::invalid_argument{"Invalid number of elements in masks"};
582     }
583 
584     // Verify no invalid properties exist
585     verifyPropertyCount(element, propertyCount);
586 
587     if (masks.empty())
588     {
589         return std::make_unique<I2CWriteBytesAction>(reg, values);
590     }
591     return std::make_unique<I2CWriteBytesAction>(reg, values, masks);
592 }
593 
594 std::unique_ptr<IfAction> parseIf(const json& element)
595 {
596     verifyIsObject(element);
597     unsigned int propertyCount{0};
598 
599     // Required condition property
600     const json& conditionElement = getRequiredProperty(element, "condition");
601     std::unique_ptr<Action> conditionAction = parseAction(conditionElement);
602     ++propertyCount;
603 
604     // Required then property
605     const json& thenElement = getRequiredProperty(element, "then");
606     std::vector<std::unique_ptr<Action>> thenActions =
607         parseActionArray(thenElement);
608     ++propertyCount;
609 
610     // Optional else property
611     std::vector<std::unique_ptr<Action>> elseActions{};
612     auto elseIt = element.find("else");
613     if (elseIt != element.end())
614     {
615         elseActions = parseActionArray(*elseIt);
616         ++propertyCount;
617     }
618 
619     // Verify no invalid properties exist
620     verifyPropertyCount(element, propertyCount);
621 
622     return std::make_unique<IfAction>(std::move(conditionAction),
623                                       std::move(thenActions),
624                                       std::move(elseActions));
625 }
626 
627 std::unique_ptr<NotAction> parseNot(const json& element)
628 {
629     // Required action to execute
630     std::unique_ptr<Action> action = parseAction(element);
631 
632     return std::make_unique<NotAction>(std::move(action));
633 }
634 
635 std::unique_ptr<OrAction> parseOr(const json& element)
636 {
637     verifyIsArray(element);
638 
639     // Verify if array size less than 2
640     if (element.size() < 2)
641     {
642         throw std::invalid_argument{"Array must contain two or more actions"};
643     }
644     // Array of two or more actions
645     std::vector<std::unique_ptr<Action>> actions = parseActionArray(element);
646 
647     return std::make_unique<OrAction>(std::move(actions));
648 }
649 
650 std::unique_ptr<PMBusWriteVoutCommandAction>
651     parsePMBusWriteVoutCommand(const json& element)
652 {
653     verifyIsObject(element);
654     unsigned int propertyCount{0};
655 
656     // Optional volts property
657     std::optional<double> volts{};
658     auto voltsIt = element.find("volts");
659     if (voltsIt != element.end())
660     {
661         volts = parseDouble(*voltsIt);
662         ++propertyCount;
663     }
664 
665     // Required format property
666     const json& formatElement = getRequiredProperty(element, "format");
667     std::string formatString = parseString(formatElement);
668     if (formatString != "linear")
669     {
670         throw std::invalid_argument{"Invalid format value: " + formatString};
671     }
672     pmbus_utils::VoutDataFormat format = pmbus_utils::VoutDataFormat::linear;
673     ++propertyCount;
674 
675     // Optional exponent property
676     std::optional<int8_t> exponent{};
677     auto exponentIt = element.find("exponent");
678     if (exponentIt != element.end())
679     {
680         exponent = parseInt8(*exponentIt);
681         ++propertyCount;
682     }
683 
684     // Optional is_verified property
685     bool isVerified = false;
686     auto isVerifiedIt = element.find("is_verified");
687     if (isVerifiedIt != element.end())
688     {
689         isVerified = parseBoolean(*isVerifiedIt);
690         ++propertyCount;
691     }
692 
693     // Verify no invalid properties exist
694     verifyPropertyCount(element, propertyCount);
695 
696     return std::make_unique<PMBusWriteVoutCommandAction>(volts, format,
697                                                          exponent, isVerified);
698 }
699 
700 std::unique_ptr<PresenceDetection> parsePresenceDetection(const json& element)
701 {
702     verifyIsObject(element);
703     unsigned int propertyCount{0};
704 
705     // Optional comments property; value not stored
706     if (element.contains("comments"))
707     {
708         ++propertyCount;
709     }
710 
711     // Required rule_id or actions property
712     std::vector<std::unique_ptr<Action>> actions{};
713     actions = parseRuleIDOrActionsProperty(element);
714     ++propertyCount;
715 
716     // Verify no invalid properties exist
717     verifyPropertyCount(element, propertyCount);
718 
719     return std::make_unique<PresenceDetection>(std::move(actions));
720 }
721 
722 std::unique_ptr<Rail> parseRail(const json& element)
723 {
724     verifyIsObject(element);
725     unsigned int propertyCount{0};
726 
727     // Optional comments property; value not stored
728     if (element.contains("comments"))
729     {
730         ++propertyCount;
731     }
732 
733     // Required id property
734     const json& idElement = getRequiredProperty(element, "id");
735     std::string id = parseString(idElement);
736     ++propertyCount;
737 
738     // Optional configuration property
739     std::unique_ptr<Configuration> configuration{};
740     auto configurationIt = element.find("configuration");
741     if (configurationIt != element.end())
742     {
743         configuration = parseConfiguration(*configurationIt);
744         ++propertyCount;
745     }
746 
747     // Optional sensor_monitoring property
748     std::unique_ptr<SensorMonitoring> sensorMonitoring{};
749     auto sensorMonitoringIt = element.find("sensor_monitoring");
750     if (sensorMonitoringIt != element.end())
751     {
752         sensorMonitoring = parseSensorMonitoring(*sensorMonitoringIt);
753         ++propertyCount;
754     }
755 
756     // Verify no invalid properties exist
757     verifyPropertyCount(element, propertyCount);
758 
759     return std::make_unique<Rail>(id, std::move(configuration),
760                                   std::move(sensorMonitoring));
761 }
762 
763 std::vector<std::unique_ptr<Rail>> parseRailArray(const json& element)
764 {
765     verifyIsArray(element);
766     std::vector<std::unique_ptr<Rail>> rails;
767     for (auto& railElement : element)
768     {
769         rails.emplace_back(parseRail(railElement));
770     }
771     return rails;
772 }
773 
774 std::tuple<std::vector<std::unique_ptr<Rule>>,
775            std::vector<std::unique_ptr<Chassis>>>
776     parseRoot(const json& element)
777 {
778     verifyIsObject(element);
779     unsigned int propertyCount{0};
780 
781     // Optional comments property; value not stored
782     if (element.contains("comments"))
783     {
784         ++propertyCount;
785     }
786 
787     // Optional rules property
788     std::vector<std::unique_ptr<Rule>> rules{};
789     auto rulesIt = element.find("rules");
790     if (rulesIt != element.end())
791     {
792         rules = parseRuleArray(*rulesIt);
793         ++propertyCount;
794     }
795 
796     // Required chassis property
797     const json& chassisElement = getRequiredProperty(element, "chassis");
798     std::vector<std::unique_ptr<Chassis>> chassis =
799         parseChassisArray(chassisElement);
800     ++propertyCount;
801 
802     // Verify no invalid properties exist
803     verifyPropertyCount(element, propertyCount);
804 
805     return std::make_tuple(std::move(rules), std::move(chassis));
806 }
807 
808 std::unique_ptr<Rule> parseRule(const json& element)
809 {
810     verifyIsObject(element);
811     unsigned int propertyCount{0};
812 
813     // Optional comments property; value not stored
814     if (element.contains("comments"))
815     {
816         ++propertyCount;
817     }
818 
819     // Required id property
820     const json& idElement = getRequiredProperty(element, "id");
821     std::string id = parseString(idElement);
822     ++propertyCount;
823 
824     // Required actions property
825     const json& actionsElement = getRequiredProperty(element, "actions");
826     std::vector<std::unique_ptr<Action>> actions =
827         parseActionArray(actionsElement);
828     ++propertyCount;
829 
830     // Verify no invalid properties exist
831     verifyPropertyCount(element, propertyCount);
832 
833     return std::make_unique<Rule>(id, std::move(actions));
834 }
835 
836 std::vector<std::unique_ptr<Rule>> parseRuleArray(const json& element)
837 {
838     verifyIsArray(element);
839     std::vector<std::unique_ptr<Rule>> rules;
840     for (auto& ruleElement : element)
841     {
842         rules.emplace_back(parseRule(ruleElement));
843     }
844     return rules;
845 }
846 
847 std::vector<std::unique_ptr<Action>>
848     parseRuleIDOrActionsProperty(const json& element)
849 {
850     verifyIsObject(element);
851     // Required rule_id or actions property
852     std::vector<std::unique_ptr<Action>> actions{};
853     auto ruleIDIt = element.find("rule_id");
854     auto actionsIt = element.find("actions");
855     if ((actionsIt == element.end()) && (ruleIDIt != element.end()))
856     {
857         std::string ruleID = parseString(*ruleIDIt);
858         actions.emplace_back(std::make_unique<RunRuleAction>(ruleID));
859     }
860     else if ((actionsIt != element.end()) && (ruleIDIt == element.end()))
861     {
862         actions = parseActionArray(*actionsIt);
863     }
864     else
865     {
866         throw std::invalid_argument{"Invalid property combination: Must "
867                                     "contain either rule_id or actions"};
868     }
869 
870     return actions;
871 }
872 
873 std::unique_ptr<RunRuleAction> parseRunRule(const json& element)
874 {
875     // String ruleID
876     std::string ruleID = parseString(element);
877 
878     return std::make_unique<RunRuleAction>(ruleID);
879 }
880 
881 std::unique_ptr<SensorMonitoring> parseSensorMonitoring(const json& element)
882 {
883     verifyIsObject(element);
884     unsigned int propertyCount{0};
885 
886     // Optional comments property; value not stored
887     if (element.contains("comments"))
888     {
889         ++propertyCount;
890     }
891 
892     // Required rule_id or actions property
893     std::vector<std::unique_ptr<Action>> actions{};
894     actions = parseRuleIDOrActionsProperty(element);
895     ++propertyCount;
896 
897     // Verify no invalid properties exist
898     verifyPropertyCount(element, propertyCount);
899 
900     return std::make_unique<SensorMonitoring>(std::move(actions));
901 }
902 
903 std::unique_ptr<SetDeviceAction> parseSetDevice(const json& element)
904 {
905     // String deviceID
906     std::string deviceID = parseString(element);
907 
908     return std::make_unique<SetDeviceAction>(deviceID);
909 }
910 
911 } // namespace internal
912 
913 } // namespace phosphor::power::regulators::config_file_parser
914