1 #pragma once
2
3 #include <nlohmann/json.hpp>
4 #include <sdbusplus/message/types.hpp>
5
6 #include <cmath>
7 #include <limits>
8
9 namespace utils
10 {
11
12 namespace numeric_literals
13 {
14 constexpr std::string_view NaN = "NaN";
15 constexpr std::string_view infinity = "inf";
16 constexpr std::string_view infinity_negative = "-inf";
17 } // namespace numeric_literals
18
from_json(const nlohmann::json & j,sdbusplus::message::object_path & o)19 inline void from_json(const nlohmann::json& j,
20 sdbusplus::message::object_path& o)
21 {
22 o = j.get<std::string>();
23 }
24
from_json(const nlohmann::json & j,std::vector<sdbusplus::message::object_path> & o)25 inline void from_json(const nlohmann::json& j,
26 std::vector<sdbusplus::message::object_path>& o)
27 {
28 o.clear();
29 for (const nlohmann::json& item : j)
30 {
31 o.emplace_back(item.get<std::string>());
32 }
33 }
34
to_json(nlohmann::json & j,const double & val)35 inline void to_json(nlohmann::json& j, const double& val)
36 {
37 if (std::isnan(val))
38 {
39 j = numeric_literals::NaN;
40 }
41 else if (val == std::numeric_limits<double>::infinity())
42 {
43 j = numeric_literals::infinity;
44 }
45 else if (val == -std::numeric_limits<double>::infinity())
46 {
47 j = numeric_literals::infinity_negative;
48 }
49 else
50 {
51 j = val;
52 }
53 }
54
from_json(const nlohmann::json & j,double & val)55 inline void from_json(const nlohmann::json& j, double& val)
56 {
57 if (j.is_number())
58 {
59 val = j.get<double>();
60 }
61 else
62 {
63 auto str_val = j.get<std::string>();
64 if (str_val == numeric_literals::NaN)
65 {
66 val = std::numeric_limits<double>::quiet_NaN();
67 }
68 else if (str_val == numeric_literals::infinity)
69 {
70 val = std::numeric_limits<double>::infinity();
71 }
72 else if (str_val == numeric_literals::infinity_negative)
73 {
74 val = -std::numeric_limits<double>::infinity();
75 }
76 else
77 {
78 throw std::invalid_argument("Unknown numeric literal");
79 }
80 }
81 }
82
83 namespace detail
84 {
85
86 template <class T>
87 struct has_utils_from_json
88 {
89 template <class U>
90 static U& ref();
91
92 template <class U>
93 static std::true_type check(
94 decltype(utils::from_json(ref<const nlohmann::json>(), ref<U>()))*);
95
96 template <class>
97 static std::false_type check(...);
98
99 static constexpr bool value =
100 decltype(check<std::decay_t<T>>(nullptr))::value;
101 };
102
103 template <class T>
104 constexpr bool has_utils_from_json_v = has_utils_from_json<T>::value;
105
106 template <class T>
107 struct has_utils_to_json
108 {
109 template <class U>
110 static U& ref();
111
112 template <class U>
113 static std::true_type check(decltype(utils::to_json(ref<nlohmann::json>(),
114 ref<const U>()))*);
115
116 template <class>
117 static std::false_type check(...);
118
119 static constexpr bool value =
120 decltype(check<std::decay_t<T>>(nullptr))::value;
121 };
122
123 template <class T>
124 constexpr bool has_utils_to_json_v = has_utils_to_json<T>::value;
125
eq(const auto & a,const auto & b)126 bool eq(const auto& a, const auto& b)
127 {
128 if constexpr (std::is_same<std::decay_t<decltype(a)>, double>())
129 {
130 if (std::isnan(a))
131 {
132 return std::isnan(b);
133 }
134 }
135 return a == b;
136 }
137
138 } // namespace detail
139
140 template <class, class...>
141 struct LabeledTuple;
142
143 template <class... Args, class... Labels>
144 struct LabeledTuple<std::tuple<Args...>, Labels...>
145 {
146 static_assert(sizeof...(Args) == sizeof...(Labels));
147
148 using tuple_type = std::tuple<Args...>;
149
150 LabeledTuple() = default;
151 LabeledTuple(const LabeledTuple&) = default;
152 LabeledTuple(LabeledTuple&&) = default;
153
LabeledTupleutils::LabeledTuple154 explicit LabeledTuple(tuple_type v) : value(std::move(v)) {}
LabeledTupleutils::LabeledTuple155 LabeledTuple(Args... args) : value(std::move(args)...) {}
156
157 LabeledTuple& operator=(const LabeledTuple&) = default;
158 LabeledTuple& operator=(LabeledTuple&&) = default;
159
to_jsonutils::LabeledTuple160 nlohmann::json to_json() const
161 {
162 nlohmann::json j;
163 to_json_all(j, std::make_index_sequence<sizeof...(Args)>());
164 return j;
165 }
166
to_tupleutils::LabeledTuple167 const tuple_type& to_tuple() const
168 {
169 return value;
170 }
171
from_jsonutils::LabeledTuple172 void from_json(const nlohmann::json& j)
173 {
174 from_json_all(j, std::make_index_sequence<sizeof...(Args)>());
175 }
176
dumputils::LabeledTuple177 std::string dump() const
178 {
179 return to_json().dump();
180 }
181
182 template <size_t Idx>
at_indexutils::LabeledTuple183 const auto& at_index() const
184 {
185 return std::get<Idx>(value);
186 }
187
188 template <size_t Idx>
at_indexutils::LabeledTuple189 auto& at_index()
190 {
191 return std::get<Idx>(value);
192 }
193
194 template <class Label>
at_labelutils::LabeledTuple195 const auto& at_label() const
196 {
197 return find_item<0, Label>(*this);
198 }
199
200 template <class Label>
at_labelutils::LabeledTuple201 auto& at_label()
202 {
203 return find_item<0, Label>(*this);
204 }
205
operator ==utils::LabeledTuple206 bool operator==(const LabeledTuple& other) const
207 {
208 return std::apply(
209 [&](auto&&... x) {
210 return std::apply(
211 [&](auto&&... y) {
212 return (true && ... && detail::eq(x, y));
213 },
214 value);
215 },
216 other.value);
217 }
218
operator <utils::LabeledTuple219 bool operator<(const LabeledTuple& other) const
220 {
221 return value < other.value;
222 }
223
224 private:
225 template <size_t... Idx>
to_json_allutils::LabeledTuple226 void to_json_all(nlohmann::json& j, std::index_sequence<Idx...>) const
227 {
228 (to_json_item<Idx>(j), ...);
229 }
230
231 template <size_t Idx>
to_json_itemutils::LabeledTuple232 void to_json_item(nlohmann::json& j) const
233 {
234 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
235 using T = std::tuple_element_t<Idx, tuple_type>;
236 nlohmann::json& item = j[Label::str()];
237 if constexpr (detail::has_utils_to_json_v<T>)
238 {
239 utils::to_json(item, std::get<Idx>(value));
240 }
241 else
242 {
243 item = std::get<Idx>(value);
244 }
245 }
246
247 template <size_t... Idx>
from_json_allutils::LabeledTuple248 void from_json_all(const nlohmann::json& j, std::index_sequence<Idx...>)
249 {
250 (from_json_item<Idx>(j), ...);
251 }
252
253 template <size_t Idx>
from_json_itemutils::LabeledTuple254 void from_json_item(const nlohmann::json& j)
255 {
256 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
257 using T = std::tuple_element_t<Idx, tuple_type>;
258 const nlohmann::json& item = j.at(Label::str());
259 if constexpr (detail::has_utils_from_json_v<T>)
260 {
261 T& v = std::get<Idx>(value);
262 utils::from_json(item, v);
263 }
264 else
265 {
266 std::get<Idx>(value) = item.get<T>();
267 }
268 }
269
270 template <size_t Idx, class Label, class Self>
find_itemutils::LabeledTuple271 static auto& find_item(Self& self)
272 {
273 if constexpr (std::is_same_v<Label, std::tuple_element_t<
274 Idx, std::tuple<Labels...>>>)
275 {
276 return std::get<Idx>(self.value);
277 }
278 else
279 {
280 static_assert(Idx + 1 < sizeof...(Args),
281 "Label not found in LabeledTuple");
282 return find_item<Idx + 1, Label>(self);
283 }
284 }
285
286 tuple_type value;
287 };
288
289 template <class... Args, class... Labels>
to_json(nlohmann::json & json,const LabeledTuple<std::tuple<Args...>,Labels...> & tuple)290 inline void to_json(nlohmann::json& json,
291 const LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
292 {
293 json = tuple.to_json();
294 }
295
296 template <class... Args, class... Labels>
from_json(const nlohmann::json & json,LabeledTuple<std::tuple<Args...>,Labels...> & tuple)297 inline void from_json(const nlohmann::json& json,
298 LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
299 {
300 tuple.from_json(json);
301 }
302
303 } // namespace utils
304