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
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
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) { return (true && ... && detail::eq(x, y)); },
212 value);
213 },
214 other.value);
215 }
216
operator <utils::LabeledTuple217 bool operator<(const LabeledTuple& other) const
218 {
219 return value < other.value;
220 }
221
222 private:
223 template <size_t... Idx>
to_json_allutils::LabeledTuple224 void to_json_all(nlohmann::json& j, std::index_sequence<Idx...>) const
225 {
226 (to_json_item<Idx>(j), ...);
227 }
228
229 template <size_t Idx>
to_json_itemutils::LabeledTuple230 void to_json_item(nlohmann::json& j) const
231 {
232 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
233 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 }
243 }
244
245 template <size_t... Idx>
from_json_allutils::LabeledTuple246 void from_json_all(const nlohmann::json& j, std::index_sequence<Idx...>)
247 {
248 (from_json_item<Idx>(j), ...);
249 }
250
251 template <size_t Idx>
from_json_itemutils::LabeledTuple252 void from_json_item(const nlohmann::json& j)
253 {
254 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
255 using T = std::tuple_element_t<Idx, tuple_type>;
256 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 }
267
268 template <size_t Idx, class Label, class Self>
find_itemutils::LabeledTuple269 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 {
278 static_assert(Idx + 1 < sizeof...(Args),
279 "Label not found in LabeledTuple");
280 return find_item<Idx + 1, Label>(self);
281 }
282 }
283
284 tuple_type value;
285 };
286
287 template <class... Args, class... Labels>
to_json(nlohmann::json & json,const LabeledTuple<std::tuple<Args...>,Labels...> & tuple)288 inline void to_json(nlohmann::json& json,
289 const LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
290 {
291 json = tuple.to_json();
292 }
293
294 template <class... Args, class... Labels>
from_json(const nlohmann::json & json,LabeledTuple<std::tuple<Args...>,Labels...> & tuple)295 inline void from_json(const nlohmann::json& json,
296 LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
297 {
298 tuple.from_json(json);
299 }
300
301 } // namespace utils
302