blob: c4120c974a191ce4b6400a1a89699d36711e6f31 [file] [log] [blame]
Wludzik, Jozefe2362792020-10-27 17:23:55 +01001#pragma once
2
3#include <nlohmann/json.hpp>
4#include <sdbusplus/message/types.hpp>
5
Szymon Dompke0253f6d2022-09-15 23:18:19 +02006#include <cmath>
7#include <limits>
8
Wludzik, Jozefe2362792020-10-27 17:23:55 +01009namespace utils
10{
11
Szymon Dompke0253f6d2022-09-15 23:18:19 +020012namespace numeric_literals
13{
14constexpr std::string_view NaN = "NaN";
15constexpr std::string_view infinity = "inf";
16constexpr std::string_view infinity_negative = "-inf";
17} // namespace numeric_literals
18
Wludzik, Jozefe2362792020-10-27 17:23:55 +010019inline void from_json(const nlohmann::json& j,
20 sdbusplus::message::object_path& o)
21{
22 o = j.get<std::string>();
23}
24
25inline 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
Szymon Dompke0253f6d2022-09-15 23:18:19 +020035inline 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
55inline 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
Wludzik, Jozefe2362792020-10-27 17:23:55 +010083namespace detail
84{
85
86template <class T>
87struct 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
103template <class T>
104constexpr bool has_utils_from_json_v = has_utils_from_json<T>::value;
105
Szymon Dompke0253f6d2022-09-15 23:18:19 +0200106template <class T>
107struct has_utils_to_json
108{
109 template <class U>
110 static U& ref();
111
112 template <class U>
113 static std::true_type
114 check(decltype(utils::to_json(ref<nlohmann::json>(), 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
123template <class T>
124constexpr bool has_utils_to_json_v = has_utils_to_json<T>::value;
125
126bool 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
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100138} // namespace detail
139
140template <class, class...>
141struct LabeledTuple;
142
143template <class... Args, class... Labels>
144struct LabeledTuple<std::tuple<Args...>, Labels...>
145{
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100146 static_assert(sizeof...(Args) == sizeof...(Labels));
147
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000148 using tuple_type = std::tuple<Args...>;
149
150 LabeledTuple() = default;
151 LabeledTuple(const LabeledTuple&) = default;
152 LabeledTuple(LabeledTuple&&) = default;
153
Krzysztof Grobelnyfbeb5bf2022-01-03 09:41:29 +0100154 explicit LabeledTuple(tuple_type v) : value(std::move(v))
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000155 {}
156 LabeledTuple(Args... args) : value(std::move(args)...)
157 {}
158
159 LabeledTuple& operator=(const LabeledTuple&) = default;
160 LabeledTuple& operator=(LabeledTuple&&) = default;
161
162 nlohmann::json to_json() const
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100163 {
164 nlohmann::json j;
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000165 to_json_all(j, std::make_index_sequence<sizeof...(Args)>());
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100166 return j;
167 }
168
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100169 const tuple_type& to_tuple() const
170 {
171 return value;
172 }
173
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000174 void from_json(const nlohmann::json& j)
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100175 {
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000176 from_json_all(j, std::make_index_sequence<sizeof...(Args)>());
177 }
178
Szymon Dompke3a617022021-07-19 18:23:02 +0200179 std::string dump() const
180 {
181 return to_json().dump();
182 }
183
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000184 template <size_t Idx>
185 const auto& at_index() const
186 {
187 return std::get<Idx>(value);
188 }
189
190 template <size_t Idx>
191 auto& at_index()
192 {
193 return std::get<Idx>(value);
194 }
195
196 template <class Label>
197 const auto& at_label() const
198 {
199 return find_item<0, Label>(*this);
200 }
201
202 template <class Label>
203 auto& at_label()
204 {
205 return find_item<0, Label>(*this);
206 }
207
208 bool operator==(const LabeledTuple& other) const
209 {
Szymon Dompke0253f6d2022-09-15 23:18:19 +0200210 return std::apply(
211 [&](auto&&... x) {
212 return std::apply(
213 [&](auto&&... y) {
214 return (true && ... && detail::eq(x, y));
215 },
216 value);
217 },
218 other.value);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000219 }
220
221 bool operator<(const LabeledTuple& other) const
222 {
223 return value < other.value;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100224 }
225
226 private:
227 template <size_t... Idx>
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000228 void to_json_all(nlohmann::json& j, std::index_sequence<Idx...>) const
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100229 {
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000230 (to_json_item<Idx>(j), ...);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100231 }
232
233 template <size_t Idx>
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000234 void to_json_item(nlohmann::json& j) const
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100235 {
236 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
Szymon Dompke0253f6d2022-09-15 23:18:19 +0200237 using T = std::tuple_element_t<Idx, tuple_type>;
238 nlohmann::json& item = j[Label::str()];
239 if constexpr (detail::has_utils_to_json_v<T>)
240 {
241 utils::to_json(item, std::get<Idx>(value));
242 }
243 else
244 {
245 item = std::get<Idx>(value);
246 }
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100247 }
248
249 template <size_t... Idx>
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000250 void from_json_all(const nlohmann::json& j, std::index_sequence<Idx...>)
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100251 {
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000252 (from_json_item<Idx>(j), ...);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100253 }
254
255 template <size_t Idx>
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000256 void from_json_item(const nlohmann::json& j)
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100257 {
258 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000259 using T = std::tuple_element_t<Idx, tuple_type>;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100260 const nlohmann::json& item = j.at(Label::str());
261 if constexpr (detail::has_utils_from_json_v<T>)
262 {
263 T& v = std::get<Idx>(value);
264 utils::from_json(item, v);
265 }
266 else
267 {
268 std::get<Idx>(value) = item.get<T>();
269 }
270 }
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000271
272 template <size_t Idx, class Label, class Self>
273 static auto& find_item(Self& self)
274 {
275 if constexpr (std::is_same_v<Label, std::tuple_element_t<
276 Idx, std::tuple<Labels...>>>)
277 {
278 return std::get<Idx>(self.value);
279 }
280 else
281 {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100282 static_assert(Idx + 1 < sizeof...(Args),
283 "Label not found in LabeledTuple");
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000284 return find_item<Idx + 1, Label>(self);
285 }
286 }
287
288 tuple_type value;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100289};
290
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000291template <class... Args, class... Labels>
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000292inline void to_json(nlohmann::json& json,
293 const LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000294{
295 json = tuple.to_json();
296}
297
298template <class... Args, class... Labels>
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000299inline void from_json(const nlohmann::json& json,
300 LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000301{
302 tuple.from_json(json);
303}
304
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100305} // namespace utils