1 /**
2  * Copyright © 2021 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 "modifier.hpp"
18 
19 #include "json/manager.hpp"
20 
21 #include <fmt/format.h>
22 
23 #include <phosphor-logging/log.hpp>
24 
25 using namespace phosphor::logging;
26 
27 namespace phosphor::fan::control::json
28 {
29 
30 /**
31  * @brief Variant visitor to return a value of the template type specified.
32  */
33 template <typename T>
34 struct ToTypeVisitor
35 {
36     template <typename U>
37     T operator()(const U& t) const
38     {
39         if constexpr (std::is_arithmetic_v<U> && std::is_arithmetic_v<T>)
40         {
41             return static_cast<T>(t);
42         }
43         throw std::invalid_argument(
44             "Non arithmetic type used in ToTypeVisitor");
45     }
46 };
47 
48 /**
49  * @brief Implements the minus operator to subtract two values.
50  *
51  * With strings values, A - B removes all occurrences of B in A.
52  * Throws if the type is a bool.
53  */
54 struct MinusOperator : public Modifier::BaseOperator
55 {
56     PropertyVariantType operator()(double val) override
57     {
58         return val - std::visit(ToTypeVisitor<double>(), arg);
59     }
60 
61     PropertyVariantType operator()(int32_t val) override
62     {
63         return val - std::visit(ToTypeVisitor<int32_t>(), arg);
64     }
65 
66     PropertyVariantType operator()(int64_t val) override
67     {
68         return val - std::visit(ToTypeVisitor<int64_t>(), arg);
69     }
70 
71     PropertyVariantType operator()(const std::string& val) override
72     {
73         // Remove all occurrences of arg from val.
74         auto value = val;
75         auto toRemove = std::get<std::string>(arg);
76         size_t pos;
77         while ((pos = value.find(toRemove)) != std::string::npos)
78         {
79             value.erase(pos, toRemove.size());
80         }
81 
82         return value;
83     }
84 
85     PropertyVariantType operator()(bool val) override
86     {
87         throw std::runtime_error{
88             "Bool not allowed as a 'minus' modifier value"};
89     }
90 
91     MinusOperator(PropertyVariantType& arg) : arg(arg)
92     {}
93 
94     PropertyVariantType arg;
95 };
96 
97 Modifier::Modifier(const json& jsonObj)
98 {
99     setValue(jsonObj);
100     setOperator(jsonObj);
101 }
102 
103 void Modifier::setValue(const json& jsonObj)
104 {
105     if (!jsonObj.contains("value"))
106     {
107         log<level::ERR>(
108             fmt::format("Modifier entry in JSON missing 'value': {}",
109                         jsonObj.dump())
110                 .c_str());
111         throw std::invalid_argument("Invalid modifier JSON");
112     }
113 
114     const auto& object = jsonObj.at("value");
115     if (auto boolPtr = object.get_ptr<const bool*>())
116     {
117         _value = *boolPtr;
118     }
119     else if (auto intPtr = object.get_ptr<const int64_t*>())
120     {
121         _value = *intPtr;
122     }
123     else if (auto doublePtr = object.get_ptr<const double*>())
124     {
125         _value = *doublePtr;
126     }
127     else if (auto stringPtr = object.get_ptr<const std::string*>())
128     {
129         _value = *stringPtr;
130     }
131     else
132     {
133         log<level::ERR>(
134             fmt::format(
135                 "Invalid JSON type for value property in modifer json: {}",
136                 jsonObj.dump())
137                 .c_str());
138         throw std::invalid_argument("Invalid modifier JSON");
139     }
140 }
141 
142 void Modifier::setOperator(const json& jsonObj)
143 {
144     if (!jsonObj.contains("operator"))
145     {
146         log<level::ERR>(
147             fmt::format("Modifier entry in JSON missing 'operator': {}",
148                         jsonObj.dump())
149                 .c_str());
150         throw std::invalid_argument("Invalid modifier JSON");
151     }
152 
153     auto op = jsonObj["operator"].get<std::string>();
154 
155     if (op == "minus")
156     {
157         _operator = std::make_unique<MinusOperator>(_value);
158     }
159     else
160     {
161         log<level::ERR>(fmt::format("Invalid operator in the modifier JSON: {}",
162                                     jsonObj.dump())
163                             .c_str());
164         throw std::invalid_argument("Invalid operator in the modifier JSON");
165     }
166 }
167 
168 PropertyVariantType Modifier::doOp(const PropertyVariantType& val)
169 {
170     return std::visit(*_operator, val);
171 }
172 
173 } // namespace phosphor::fan::control::json
174