blob: 062b9084b5629e2e4444c6039671bdffdc15299a [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 {
Patrick Williamsf535cad2024-08-16 15:21:20 -0400208 return std::apply([&](auto&&... x) {
209 return std::apply([&](auto&&... y) {
210 return (true && ... && detail::eq(x, y));
211 }, value);
212 }, other.value);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000213 }
214
215 bool operator<(const LabeledTuple& other) const
216 {
217 return value < other.value;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100218 }
219
220 private:
221 template <size_t... Idx>
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000222 void to_json_all(nlohmann::json& j, std::index_sequence<Idx...>) const
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100223 {
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000224 (to_json_item<Idx>(j), ...);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100225 }
226
227 template <size_t Idx>
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000228 void to_json_item(nlohmann::json& j) const
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100229 {
230 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
Szymon Dompke0253f6d2022-09-15 23:18:19 +0200231 using T = std::tuple_element_t<Idx, tuple_type>;
232 nlohmann::json& item = j[Label::str()];
233 if constexpr (detail::has_utils_to_json_v<T>)
234 {
235 utils::to_json(item, std::get<Idx>(value));
236 }
237 else
238 {
239 item = std::get<Idx>(value);
240 }
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100241 }
242
243 template <size_t... Idx>
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000244 void from_json_all(const nlohmann::json& j, std::index_sequence<Idx...>)
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100245 {
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000246 (from_json_item<Idx>(j), ...);
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_item(const nlohmann::json& j)
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100251 {
252 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000253 using T = std::tuple_element_t<Idx, tuple_type>;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100254 const nlohmann::json& item = j.at(Label::str());
255 if constexpr (detail::has_utils_from_json_v<T>)
256 {
257 T& v = std::get<Idx>(value);
258 utils::from_json(item, v);
259 }
260 else
261 {
262 std::get<Idx>(value) = item.get<T>();
263 }
264 }
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000265
266 template <size_t Idx, class Label, class Self>
267 static auto& find_item(Self& self)
268 {
269 if constexpr (std::is_same_v<Label, std::tuple_element_t<
270 Idx, std::tuple<Labels...>>>)
271 {
272 return std::get<Idx>(self.value);
273 }
274 else
275 {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100276 static_assert(Idx + 1 < sizeof...(Args),
277 "Label not found in LabeledTuple");
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000278 return find_item<Idx + 1, Label>(self);
279 }
280 }
281
282 tuple_type value;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100283};
284
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000285template <class... Args, class... Labels>
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000286inline void to_json(nlohmann::json& json,
287 const LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000288{
289 json = tuple.to_json();
290}
291
292template <class... Args, class... Labels>
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000293inline void from_json(const nlohmann::json& json,
294 LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000295{
296 tuple.from_json(json);
297}
298
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100299} // namespace utils