xref: /openbmc/telemetry/src/utils/labeled_tuple.hpp (revision 3a1c297a36bcd78d33ee45c603cb1b46e4619f49)
1e2362796SWludzik, Jozef #pragma once
2e2362796SWludzik, Jozef 
3e2362796SWludzik, Jozef #include <nlohmann/json.hpp>
4e2362796SWludzik, Jozef #include <sdbusplus/message/types.hpp>
5e2362796SWludzik, Jozef 
60253f6d3SSzymon Dompke #include <cmath>
70253f6d3SSzymon Dompke #include <limits>
80253f6d3SSzymon Dompke 
9e2362796SWludzik, Jozef namespace utils
10e2362796SWludzik, Jozef {
11e2362796SWludzik, Jozef 
120253f6d3SSzymon Dompke namespace numeric_literals
130253f6d3SSzymon Dompke {
140253f6d3SSzymon Dompke constexpr std::string_view NaN = "NaN";
150253f6d3SSzymon Dompke constexpr std::string_view infinity = "inf";
160253f6d3SSzymon Dompke constexpr std::string_view infinity_negative = "-inf";
170253f6d3SSzymon Dompke } // namespace numeric_literals
180253f6d3SSzymon Dompke 
19e2362796SWludzik, Jozef inline void from_json(const nlohmann::json& j,
20e2362796SWludzik, Jozef                       sdbusplus::message::object_path& o)
21e2362796SWludzik, Jozef {
22e2362796SWludzik, Jozef     o = j.get<std::string>();
23e2362796SWludzik, Jozef }
24e2362796SWludzik, Jozef 
25e2362796SWludzik, Jozef inline void from_json(const nlohmann::json& j,
26e2362796SWludzik, Jozef                       std::vector<sdbusplus::message::object_path>& o)
27e2362796SWludzik, Jozef {
28e2362796SWludzik, Jozef     o.clear();
29e2362796SWludzik, Jozef     for (const nlohmann::json& item : j)
30e2362796SWludzik, Jozef     {
31e2362796SWludzik, Jozef         o.emplace_back(item.get<std::string>());
32e2362796SWludzik, Jozef     }
33e2362796SWludzik, Jozef }
34e2362796SWludzik, Jozef 
350253f6d3SSzymon Dompke inline void to_json(nlohmann::json& j, const double& val)
360253f6d3SSzymon Dompke {
370253f6d3SSzymon Dompke     if (std::isnan(val))
380253f6d3SSzymon Dompke     {
390253f6d3SSzymon Dompke         j = numeric_literals::NaN;
400253f6d3SSzymon Dompke     }
410253f6d3SSzymon Dompke     else if (val == std::numeric_limits<double>::infinity())
420253f6d3SSzymon Dompke     {
430253f6d3SSzymon Dompke         j = numeric_literals::infinity;
440253f6d3SSzymon Dompke     }
450253f6d3SSzymon Dompke     else if (val == -std::numeric_limits<double>::infinity())
460253f6d3SSzymon Dompke     {
470253f6d3SSzymon Dompke         j = numeric_literals::infinity_negative;
480253f6d3SSzymon Dompke     }
490253f6d3SSzymon Dompke     else
500253f6d3SSzymon Dompke     {
510253f6d3SSzymon Dompke         j = val;
520253f6d3SSzymon Dompke     }
530253f6d3SSzymon Dompke }
540253f6d3SSzymon Dompke 
550253f6d3SSzymon Dompke inline void from_json(const nlohmann::json& j, double& val)
560253f6d3SSzymon Dompke {
570253f6d3SSzymon Dompke     if (j.is_number())
580253f6d3SSzymon Dompke     {
590253f6d3SSzymon Dompke         val = j.get<double>();
600253f6d3SSzymon Dompke     }
610253f6d3SSzymon Dompke     else
620253f6d3SSzymon Dompke     {
630253f6d3SSzymon Dompke         auto str_val = j.get<std::string>();
640253f6d3SSzymon Dompke         if (str_val == numeric_literals::NaN)
650253f6d3SSzymon Dompke         {
660253f6d3SSzymon Dompke             val = std::numeric_limits<double>::quiet_NaN();
670253f6d3SSzymon Dompke         }
680253f6d3SSzymon Dompke         else if (str_val == numeric_literals::infinity)
690253f6d3SSzymon Dompke         {
700253f6d3SSzymon Dompke             val = std::numeric_limits<double>::infinity();
710253f6d3SSzymon Dompke         }
720253f6d3SSzymon Dompke         else if (str_val == numeric_literals::infinity_negative)
730253f6d3SSzymon Dompke         {
740253f6d3SSzymon Dompke             val = -std::numeric_limits<double>::infinity();
750253f6d3SSzymon Dompke         }
760253f6d3SSzymon Dompke         else
770253f6d3SSzymon Dompke         {
780253f6d3SSzymon Dompke             throw std::invalid_argument("Unknown numeric literal");
790253f6d3SSzymon Dompke         }
800253f6d3SSzymon Dompke     }
810253f6d3SSzymon Dompke }
820253f6d3SSzymon Dompke 
83e2362796SWludzik, Jozef namespace detail
84e2362796SWludzik, Jozef {
85e2362796SWludzik, Jozef 
86e2362796SWludzik, Jozef template <class T>
87e2362796SWludzik, Jozef struct has_utils_from_json
88e2362796SWludzik, Jozef {
89e2362796SWludzik, Jozef     template <class U>
90e2362796SWludzik, Jozef     static U& ref();
91e2362796SWludzik, Jozef 
92e2362796SWludzik, Jozef     template <class U>
93e2362796SWludzik, Jozef     static std::true_type check(
94e2362796SWludzik, Jozef         decltype(utils::from_json(ref<const nlohmann::json>(), ref<U>()))*);
95e2362796SWludzik, Jozef 
96e2362796SWludzik, Jozef     template <class>
97e2362796SWludzik, Jozef     static std::false_type check(...);
98e2362796SWludzik, Jozef 
99e2362796SWludzik, Jozef     static constexpr bool value =
100e2362796SWludzik, Jozef         decltype(check<std::decay_t<T>>(nullptr))::value;
101e2362796SWludzik, Jozef };
102e2362796SWludzik, Jozef 
103e2362796SWludzik, Jozef template <class T>
104e2362796SWludzik, Jozef constexpr bool has_utils_from_json_v = has_utils_from_json<T>::value;
105e2362796SWludzik, Jozef 
1060253f6d3SSzymon Dompke template <class T>
1070253f6d3SSzymon Dompke struct has_utils_to_json
1080253f6d3SSzymon Dompke {
1090253f6d3SSzymon Dompke     template <class U>
1100253f6d3SSzymon Dompke     static U& ref();
1110253f6d3SSzymon Dompke 
1120253f6d3SSzymon Dompke     template <class U>
1130253f6d3SSzymon Dompke     static std::true_type
1140253f6d3SSzymon Dompke         check(decltype(utils::to_json(ref<nlohmann::json>(), ref<const U>()))*);
1150253f6d3SSzymon Dompke 
1160253f6d3SSzymon Dompke     template <class>
1170253f6d3SSzymon Dompke     static std::false_type check(...);
1180253f6d3SSzymon Dompke 
1190253f6d3SSzymon Dompke     static constexpr bool value =
1200253f6d3SSzymon Dompke         decltype(check<std::decay_t<T>>(nullptr))::value;
1210253f6d3SSzymon Dompke };
1220253f6d3SSzymon Dompke 
1230253f6d3SSzymon Dompke template <class T>
1240253f6d3SSzymon Dompke constexpr bool has_utils_to_json_v = has_utils_to_json<T>::value;
1250253f6d3SSzymon Dompke 
1260253f6d3SSzymon Dompke bool eq(const auto& a, const auto& b)
1270253f6d3SSzymon Dompke {
1280253f6d3SSzymon Dompke     if constexpr (std::is_same<std::decay_t<decltype(a)>, double>())
1290253f6d3SSzymon Dompke     {
1300253f6d3SSzymon Dompke         if (std::isnan(a))
1310253f6d3SSzymon Dompke         {
1320253f6d3SSzymon Dompke             return std::isnan(b);
1330253f6d3SSzymon Dompke         }
1340253f6d3SSzymon Dompke     }
1350253f6d3SSzymon Dompke     return a == b;
1360253f6d3SSzymon Dompke }
1370253f6d3SSzymon Dompke 
138e2362796SWludzik, Jozef } // namespace detail
139e2362796SWludzik, Jozef 
140e2362796SWludzik, Jozef template <class, class...>
141e2362796SWludzik, Jozef struct LabeledTuple;
142e2362796SWludzik, Jozef 
143e2362796SWludzik, Jozef template <class... Args, class... Labels>
144e2362796SWludzik, Jozef struct LabeledTuple<std::tuple<Args...>, Labels...>
145e2362796SWludzik, Jozef {
146e2362796SWludzik, Jozef     static_assert(sizeof...(Args) == sizeof...(Labels));
147e2362796SWludzik, Jozef 
148d2238194SKrzysztof Grobelny     using tuple_type = std::tuple<Args...>;
149d2238194SKrzysztof Grobelny 
150d2238194SKrzysztof Grobelny     LabeledTuple() = default;
151d2238194SKrzysztof Grobelny     LabeledTuple(const LabeledTuple&) = default;
152d2238194SKrzysztof Grobelny     LabeledTuple(LabeledTuple&&) = default;
153d2238194SKrzysztof Grobelny 
154*3a1c297aSPatrick Williams     explicit LabeledTuple(tuple_type v) : value(std::move(v)) {}
155*3a1c297aSPatrick Williams     LabeledTuple(Args... args) : value(std::move(args)...) {}
156d2238194SKrzysztof Grobelny 
157d2238194SKrzysztof Grobelny     LabeledTuple& operator=(const LabeledTuple&) = default;
158d2238194SKrzysztof Grobelny     LabeledTuple& operator=(LabeledTuple&&) = default;
159d2238194SKrzysztof Grobelny 
160d2238194SKrzysztof Grobelny     nlohmann::json to_json() const
161e2362796SWludzik, Jozef     {
162e2362796SWludzik, Jozef         nlohmann::json j;
163d2238194SKrzysztof Grobelny         to_json_all(j, std::make_index_sequence<sizeof...(Args)>());
164e2362796SWludzik, Jozef         return j;
165e2362796SWludzik, Jozef     }
166e2362796SWludzik, Jozef 
167493e62ebSKrzysztof Grobelny     const tuple_type& to_tuple() const
168493e62ebSKrzysztof Grobelny     {
169493e62ebSKrzysztof Grobelny         return value;
170493e62ebSKrzysztof Grobelny     }
171493e62ebSKrzysztof Grobelny 
172d2238194SKrzysztof Grobelny     void from_json(const nlohmann::json& j)
173e2362796SWludzik, Jozef     {
174d2238194SKrzysztof Grobelny         from_json_all(j, std::make_index_sequence<sizeof...(Args)>());
175d2238194SKrzysztof Grobelny     }
176d2238194SKrzysztof Grobelny 
1773a617023SSzymon Dompke     std::string dump() const
1783a617023SSzymon Dompke     {
1793a617023SSzymon Dompke         return to_json().dump();
1803a617023SSzymon Dompke     }
1813a617023SSzymon Dompke 
182d2238194SKrzysztof Grobelny     template <size_t Idx>
183d2238194SKrzysztof Grobelny     const auto& at_index() const
184d2238194SKrzysztof Grobelny     {
185d2238194SKrzysztof Grobelny         return std::get<Idx>(value);
186d2238194SKrzysztof Grobelny     }
187d2238194SKrzysztof Grobelny 
188d2238194SKrzysztof Grobelny     template <size_t Idx>
189d2238194SKrzysztof Grobelny     auto& at_index()
190d2238194SKrzysztof Grobelny     {
191d2238194SKrzysztof Grobelny         return std::get<Idx>(value);
192d2238194SKrzysztof Grobelny     }
193d2238194SKrzysztof Grobelny 
194d2238194SKrzysztof Grobelny     template <class Label>
195d2238194SKrzysztof Grobelny     const auto& at_label() const
196d2238194SKrzysztof Grobelny     {
197d2238194SKrzysztof Grobelny         return find_item<0, Label>(*this);
198d2238194SKrzysztof Grobelny     }
199d2238194SKrzysztof Grobelny 
200d2238194SKrzysztof Grobelny     template <class Label>
201d2238194SKrzysztof Grobelny     auto& at_label()
202d2238194SKrzysztof Grobelny     {
203d2238194SKrzysztof Grobelny         return find_item<0, Label>(*this);
204d2238194SKrzysztof Grobelny     }
205d2238194SKrzysztof Grobelny 
206d2238194SKrzysztof Grobelny     bool operator==(const LabeledTuple& other) const
207d2238194SKrzysztof Grobelny     {
2080253f6d3SSzymon Dompke         return std::apply(
2090253f6d3SSzymon Dompke             [&](auto&&... x) {
2100253f6d3SSzymon Dompke             return std::apply(
211*3a1c297aSPatrick Williams                 [&](auto&&... y) { return (true && ... && detail::eq(x, y)); },
2120253f6d3SSzymon Dompke                 value);
2130253f6d3SSzymon Dompke             },
2140253f6d3SSzymon Dompke             other.value);
215d2238194SKrzysztof Grobelny     }
216d2238194SKrzysztof Grobelny 
217d2238194SKrzysztof Grobelny     bool operator<(const LabeledTuple& other) const
218d2238194SKrzysztof Grobelny     {
219d2238194SKrzysztof Grobelny         return value < other.value;
220e2362796SWludzik, Jozef     }
221e2362796SWludzik, Jozef 
222e2362796SWludzik, Jozef   private:
223e2362796SWludzik, Jozef     template <size_t... Idx>
224d2238194SKrzysztof Grobelny     void to_json_all(nlohmann::json& j, std::index_sequence<Idx...>) const
225e2362796SWludzik, Jozef     {
226d2238194SKrzysztof Grobelny         (to_json_item<Idx>(j), ...);
227e2362796SWludzik, Jozef     }
228e2362796SWludzik, Jozef 
229e2362796SWludzik, Jozef     template <size_t Idx>
230d2238194SKrzysztof Grobelny     void to_json_item(nlohmann::json& j) const
231e2362796SWludzik, Jozef     {
232e2362796SWludzik, Jozef         using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
2330253f6d3SSzymon Dompke         using T = std::tuple_element_t<Idx, tuple_type>;
2340253f6d3SSzymon Dompke         nlohmann::json& item = j[Label::str()];
2350253f6d3SSzymon Dompke         if constexpr (detail::has_utils_to_json_v<T>)
2360253f6d3SSzymon Dompke         {
2370253f6d3SSzymon Dompke             utils::to_json(item, std::get<Idx>(value));
2380253f6d3SSzymon Dompke         }
2390253f6d3SSzymon Dompke         else
2400253f6d3SSzymon Dompke         {
2410253f6d3SSzymon Dompke             item = std::get<Idx>(value);
2420253f6d3SSzymon Dompke         }
243e2362796SWludzik, Jozef     }
244e2362796SWludzik, Jozef 
245e2362796SWludzik, Jozef     template <size_t... Idx>
246d2238194SKrzysztof Grobelny     void from_json_all(const nlohmann::json& j, std::index_sequence<Idx...>)
247e2362796SWludzik, Jozef     {
248d2238194SKrzysztof Grobelny         (from_json_item<Idx>(j), ...);
249e2362796SWludzik, Jozef     }
250e2362796SWludzik, Jozef 
251e2362796SWludzik, Jozef     template <size_t Idx>
252d2238194SKrzysztof Grobelny     void from_json_item(const nlohmann::json& j)
253e2362796SWludzik, Jozef     {
254e2362796SWludzik, Jozef         using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
255d2238194SKrzysztof Grobelny         using T = std::tuple_element_t<Idx, tuple_type>;
256e2362796SWludzik, Jozef         const nlohmann::json& item = j.at(Label::str());
257e2362796SWludzik, Jozef         if constexpr (detail::has_utils_from_json_v<T>)
258e2362796SWludzik, Jozef         {
259e2362796SWludzik, Jozef             T& v = std::get<Idx>(value);
260e2362796SWludzik, Jozef             utils::from_json(item, v);
261e2362796SWludzik, Jozef         }
262e2362796SWludzik, Jozef         else
263e2362796SWludzik, Jozef         {
264e2362796SWludzik, Jozef             std::get<Idx>(value) = item.get<T>();
265e2362796SWludzik, Jozef         }
266e2362796SWludzik, Jozef     }
267d2238194SKrzysztof Grobelny 
268d2238194SKrzysztof Grobelny     template <size_t Idx, class Label, class Self>
269d2238194SKrzysztof Grobelny     static auto& find_item(Self& self)
270d2238194SKrzysztof Grobelny     {
271d2238194SKrzysztof Grobelny         if constexpr (std::is_same_v<Label, std::tuple_element_t<
272d2238194SKrzysztof Grobelny                                                 Idx, std::tuple<Labels...>>>)
273d2238194SKrzysztof Grobelny         {
274d2238194SKrzysztof Grobelny             return std::get<Idx>(self.value);
275d2238194SKrzysztof Grobelny         }
276d2238194SKrzysztof Grobelny         else
277d2238194SKrzysztof Grobelny         {
278b8cc78ddSKrzysztof Grobelny             static_assert(Idx + 1 < sizeof...(Args),
279b8cc78ddSKrzysztof Grobelny                           "Label not found in LabeledTuple");
280d2238194SKrzysztof Grobelny             return find_item<Idx + 1, Label>(self);
281d2238194SKrzysztof Grobelny         }
282d2238194SKrzysztof Grobelny     }
283d2238194SKrzysztof Grobelny 
284d2238194SKrzysztof Grobelny     tuple_type value;
285e2362796SWludzik, Jozef };
286e2362796SWludzik, Jozef 
287d2238194SKrzysztof Grobelny template <class... Args, class... Labels>
288dcc4e193SKrzysztof Grobelny inline void to_json(nlohmann::json& json,
289d2238194SKrzysztof Grobelny                     const LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
290d2238194SKrzysztof Grobelny {
291d2238194SKrzysztof Grobelny     json = tuple.to_json();
292d2238194SKrzysztof Grobelny }
293d2238194SKrzysztof Grobelny 
294d2238194SKrzysztof Grobelny template <class... Args, class... Labels>
295dcc4e193SKrzysztof Grobelny inline void from_json(const nlohmann::json& json,
296d2238194SKrzysztof Grobelny                       LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
297d2238194SKrzysztof Grobelny {
298d2238194SKrzysztof Grobelny     tuple.from_json(json);
299d2238194SKrzysztof Grobelny }
300d2238194SKrzysztof Grobelny 
301e2362796SWludzik, Jozef } // namespace utils
302