blob: c519dd4c2961d557a25b03b8685e6484d34ccad1 [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
Patrick Williams3a1c2972023-05-10 07:51:04 -0500154 explicit LabeledTuple(tuple_type v) : value(std::move(v)) {}
155 LabeledTuple(Args... args) : value(std::move(args)...) {}
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000156
157 LabeledTuple& operator=(const LabeledTuple&) = default;
158 LabeledTuple& operator=(LabeledTuple&&) = default;
159
160 nlohmann::json to_json() const
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100161 {
162 nlohmann::json j;
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000163 to_json_all(j, std::make_index_sequence<sizeof...(Args)>());
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100164 return j;
165 }
166
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100167 const tuple_type& to_tuple() const
168 {
169 return value;
170 }
171
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000172 void from_json(const nlohmann::json& j)
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100173 {
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000174 from_json_all(j, std::make_index_sequence<sizeof...(Args)>());
175 }
176
Szymon Dompke3a617022021-07-19 18:23:02 +0200177 std::string dump() const
178 {
179 return to_json().dump();
180 }
181
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000182 template <size_t Idx>
183 const auto& at_index() const
184 {
185 return std::get<Idx>(value);
186 }
187
188 template <size_t Idx>
189 auto& at_index()
190 {
191 return std::get<Idx>(value);
192 }
193
194 template <class Label>
195 const auto& at_label() const
196 {
197 return find_item<0, Label>(*this);
198 }
199
200 template <class Label>
201 auto& at_label()
202 {
203 return find_item<0, Label>(*this);
204 }
205
206 bool operator==(const LabeledTuple& other) const
207 {
Szymon Dompke0253f6d2022-09-15 23:18:19 +0200208 return std::apply(
209 [&](auto&&... x) {
Patrick Williams3a1c2972023-05-10 07:51:04 -0500210 return std::apply(
211 [&](auto&&... y) { return (true && ... && detail::eq(x, y)); },
212 value);
Patrick Williamsc7935fa2023-10-20 11:19:30 -0500213 },
Szymon Dompke0253f6d2022-09-15 23:18:19 +0200214 other.value);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000215 }
216
217 bool operator<(const LabeledTuple& other) const
218 {
219 return value < other.value;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100220 }
221
222 private:
223 template <size_t... Idx>
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000224 void to_json_all(nlohmann::json& j, std::index_sequence<Idx...>) const
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100225 {
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000226 (to_json_item<Idx>(j), ...);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100227 }
228
229 template <size_t Idx>
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000230 void to_json_item(nlohmann::json& j) const
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100231 {
232 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
Szymon Dompke0253f6d2022-09-15 23:18:19 +0200233 using T = std::tuple_element_t<Idx, tuple_type>;
234 nlohmann::json& item = j[Label::str()];
235 if constexpr (detail::has_utils_to_json_v<T>)
236 {
237 utils::to_json(item, std::get<Idx>(value));
238 }
239 else
240 {
241 item = std::get<Idx>(value);
242 }
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100243 }
244
245 template <size_t... Idx>
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000246 void from_json_all(const nlohmann::json& j, std::index_sequence<Idx...>)
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100247 {
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000248 (from_json_item<Idx>(j), ...);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100249 }
250
251 template <size_t Idx>
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000252 void from_json_item(const nlohmann::json& j)
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100253 {
254 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000255 using T = std::tuple_element_t<Idx, tuple_type>;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100256 const nlohmann::json& item = j.at(Label::str());
257 if constexpr (detail::has_utils_from_json_v<T>)
258 {
259 T& v = std::get<Idx>(value);
260 utils::from_json(item, v);
261 }
262 else
263 {
264 std::get<Idx>(value) = item.get<T>();
265 }
266 }
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000267
268 template <size_t Idx, class Label, class Self>
269 static auto& find_item(Self& self)
270 {
271 if constexpr (std::is_same_v<Label, std::tuple_element_t<
272 Idx, std::tuple<Labels...>>>)
273 {
274 return std::get<Idx>(self.value);
275 }
276 else
277 {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100278 static_assert(Idx + 1 < sizeof...(Args),
279 "Label not found in LabeledTuple");
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000280 return find_item<Idx + 1, Label>(self);
281 }
282 }
283
284 tuple_type value;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100285};
286
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000287template <class... Args, class... Labels>
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000288inline void to_json(nlohmann::json& json,
289 const LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000290{
291 json = tuple.to_json();
292}
293
294template <class... Args, class... Labels>
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000295inline void from_json(const nlohmann::json& json,
296 LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000297{
298 tuple.from_json(json);
299}
300
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100301} // namespace utils