xref: /openbmc/telemetry/src/utils/labeled_tuple.hpp (revision 0253f6d3cbcad7714bea2e8c67845bb9ed18a2ee)
1e2362796SWludzik, Jozef #pragma once
2e2362796SWludzik, Jozef 
3e2362796SWludzik, Jozef #include <nlohmann/json.hpp>
4e2362796SWludzik, Jozef #include <sdbusplus/message/types.hpp>
5e2362796SWludzik, Jozef 
6*0253f6d3SSzymon Dompke #include <cmath>
7*0253f6d3SSzymon Dompke #include <limits>
8*0253f6d3SSzymon Dompke 
9e2362796SWludzik, Jozef namespace utils
10e2362796SWludzik, Jozef {
11e2362796SWludzik, Jozef 
12*0253f6d3SSzymon Dompke namespace numeric_literals
13*0253f6d3SSzymon Dompke {
14*0253f6d3SSzymon Dompke constexpr std::string_view NaN = "NaN";
15*0253f6d3SSzymon Dompke constexpr std::string_view infinity = "inf";
16*0253f6d3SSzymon Dompke constexpr std::string_view infinity_negative = "-inf";
17*0253f6d3SSzymon Dompke } // namespace numeric_literals
18*0253f6d3SSzymon 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 
35*0253f6d3SSzymon Dompke inline void to_json(nlohmann::json& j, const double& val)
36*0253f6d3SSzymon Dompke {
37*0253f6d3SSzymon Dompke     if (std::isnan(val))
38*0253f6d3SSzymon Dompke     {
39*0253f6d3SSzymon Dompke         j = numeric_literals::NaN;
40*0253f6d3SSzymon Dompke     }
41*0253f6d3SSzymon Dompke     else if (val == std::numeric_limits<double>::infinity())
42*0253f6d3SSzymon Dompke     {
43*0253f6d3SSzymon Dompke         j = numeric_literals::infinity;
44*0253f6d3SSzymon Dompke     }
45*0253f6d3SSzymon Dompke     else if (val == -std::numeric_limits<double>::infinity())
46*0253f6d3SSzymon Dompke     {
47*0253f6d3SSzymon Dompke         j = numeric_literals::infinity_negative;
48*0253f6d3SSzymon Dompke     }
49*0253f6d3SSzymon Dompke     else
50*0253f6d3SSzymon Dompke     {
51*0253f6d3SSzymon Dompke         j = val;
52*0253f6d3SSzymon Dompke     }
53*0253f6d3SSzymon Dompke }
54*0253f6d3SSzymon Dompke 
55*0253f6d3SSzymon Dompke inline void from_json(const nlohmann::json& j, double& val)
56*0253f6d3SSzymon Dompke {
57*0253f6d3SSzymon Dompke     if (j.is_number())
58*0253f6d3SSzymon Dompke     {
59*0253f6d3SSzymon Dompke         val = j.get<double>();
60*0253f6d3SSzymon Dompke     }
61*0253f6d3SSzymon Dompke     else
62*0253f6d3SSzymon Dompke     {
63*0253f6d3SSzymon Dompke         auto str_val = j.get<std::string>();
64*0253f6d3SSzymon Dompke         if (str_val == numeric_literals::NaN)
65*0253f6d3SSzymon Dompke         {
66*0253f6d3SSzymon Dompke             val = std::numeric_limits<double>::quiet_NaN();
67*0253f6d3SSzymon Dompke         }
68*0253f6d3SSzymon Dompke         else if (str_val == numeric_literals::infinity)
69*0253f6d3SSzymon Dompke         {
70*0253f6d3SSzymon Dompke             val = std::numeric_limits<double>::infinity();
71*0253f6d3SSzymon Dompke         }
72*0253f6d3SSzymon Dompke         else if (str_val == numeric_literals::infinity_negative)
73*0253f6d3SSzymon Dompke         {
74*0253f6d3SSzymon Dompke             val = -std::numeric_limits<double>::infinity();
75*0253f6d3SSzymon Dompke         }
76*0253f6d3SSzymon Dompke         else
77*0253f6d3SSzymon Dompke         {
78*0253f6d3SSzymon Dompke             throw std::invalid_argument("Unknown numeric literal");
79*0253f6d3SSzymon Dompke         }
80*0253f6d3SSzymon Dompke     }
81*0253f6d3SSzymon Dompke }
82*0253f6d3SSzymon 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 
106*0253f6d3SSzymon Dompke template <class T>
107*0253f6d3SSzymon Dompke struct has_utils_to_json
108*0253f6d3SSzymon Dompke {
109*0253f6d3SSzymon Dompke     template <class U>
110*0253f6d3SSzymon Dompke     static U& ref();
111*0253f6d3SSzymon Dompke 
112*0253f6d3SSzymon Dompke     template <class U>
113*0253f6d3SSzymon Dompke     static std::true_type
114*0253f6d3SSzymon Dompke         check(decltype(utils::to_json(ref<nlohmann::json>(), ref<const U>()))*);
115*0253f6d3SSzymon Dompke 
116*0253f6d3SSzymon Dompke     template <class>
117*0253f6d3SSzymon Dompke     static std::false_type check(...);
118*0253f6d3SSzymon Dompke 
119*0253f6d3SSzymon Dompke     static constexpr bool value =
120*0253f6d3SSzymon Dompke         decltype(check<std::decay_t<T>>(nullptr))::value;
121*0253f6d3SSzymon Dompke };
122*0253f6d3SSzymon Dompke 
123*0253f6d3SSzymon Dompke template <class T>
124*0253f6d3SSzymon Dompke constexpr bool has_utils_to_json_v = has_utils_to_json<T>::value;
125*0253f6d3SSzymon Dompke 
126*0253f6d3SSzymon Dompke bool eq(const auto& a, const auto& b)
127*0253f6d3SSzymon Dompke {
128*0253f6d3SSzymon Dompke     if constexpr (std::is_same<std::decay_t<decltype(a)>, double>())
129*0253f6d3SSzymon Dompke     {
130*0253f6d3SSzymon Dompke         if (std::isnan(a))
131*0253f6d3SSzymon Dompke         {
132*0253f6d3SSzymon Dompke             return std::isnan(b);
133*0253f6d3SSzymon Dompke         }
134*0253f6d3SSzymon Dompke     }
135*0253f6d3SSzymon Dompke     return a == b;
136*0253f6d3SSzymon Dompke }
137*0253f6d3SSzymon 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 
154fbeb5bf4SKrzysztof Grobelny     explicit LabeledTuple(tuple_type v) : value(std::move(v))
155d2238194SKrzysztof Grobelny     {}
156d2238194SKrzysztof Grobelny     LabeledTuple(Args... args) : value(std::move(args)...)
157d2238194SKrzysztof Grobelny     {}
158d2238194SKrzysztof Grobelny 
159d2238194SKrzysztof Grobelny     LabeledTuple& operator=(const LabeledTuple&) = default;
160d2238194SKrzysztof Grobelny     LabeledTuple& operator=(LabeledTuple&&) = default;
161d2238194SKrzysztof Grobelny 
162d2238194SKrzysztof Grobelny     nlohmann::json to_json() const
163e2362796SWludzik, Jozef     {
164e2362796SWludzik, Jozef         nlohmann::json j;
165d2238194SKrzysztof Grobelny         to_json_all(j, std::make_index_sequence<sizeof...(Args)>());
166e2362796SWludzik, Jozef         return j;
167e2362796SWludzik, Jozef     }
168e2362796SWludzik, Jozef 
169493e62ebSKrzysztof Grobelny     const tuple_type& to_tuple() const
170493e62ebSKrzysztof Grobelny     {
171493e62ebSKrzysztof Grobelny         return value;
172493e62ebSKrzysztof Grobelny     }
173493e62ebSKrzysztof Grobelny 
174d2238194SKrzysztof Grobelny     void from_json(const nlohmann::json& j)
175e2362796SWludzik, Jozef     {
176d2238194SKrzysztof Grobelny         from_json_all(j, std::make_index_sequence<sizeof...(Args)>());
177d2238194SKrzysztof Grobelny     }
178d2238194SKrzysztof Grobelny 
1793a617023SSzymon Dompke     std::string dump() const
1803a617023SSzymon Dompke     {
1813a617023SSzymon Dompke         return to_json().dump();
1823a617023SSzymon Dompke     }
1833a617023SSzymon Dompke 
184d2238194SKrzysztof Grobelny     template <size_t Idx>
185d2238194SKrzysztof Grobelny     const auto& at_index() const
186d2238194SKrzysztof Grobelny     {
187d2238194SKrzysztof Grobelny         return std::get<Idx>(value);
188d2238194SKrzysztof Grobelny     }
189d2238194SKrzysztof Grobelny 
190d2238194SKrzysztof Grobelny     template <size_t Idx>
191d2238194SKrzysztof Grobelny     auto& at_index()
192d2238194SKrzysztof Grobelny     {
193d2238194SKrzysztof Grobelny         return std::get<Idx>(value);
194d2238194SKrzysztof Grobelny     }
195d2238194SKrzysztof Grobelny 
196d2238194SKrzysztof Grobelny     template <class Label>
197d2238194SKrzysztof Grobelny     const auto& at_label() const
198d2238194SKrzysztof Grobelny     {
199d2238194SKrzysztof Grobelny         return find_item<0, Label>(*this);
200d2238194SKrzysztof Grobelny     }
201d2238194SKrzysztof Grobelny 
202d2238194SKrzysztof Grobelny     template <class Label>
203d2238194SKrzysztof Grobelny     auto& at_label()
204d2238194SKrzysztof Grobelny     {
205d2238194SKrzysztof Grobelny         return find_item<0, Label>(*this);
206d2238194SKrzysztof Grobelny     }
207d2238194SKrzysztof Grobelny 
208d2238194SKrzysztof Grobelny     bool operator==(const LabeledTuple& other) const
209d2238194SKrzysztof Grobelny     {
210*0253f6d3SSzymon Dompke         return std::apply(
211*0253f6d3SSzymon Dompke             [&](auto&&... x) {
212*0253f6d3SSzymon Dompke                 return std::apply(
213*0253f6d3SSzymon Dompke                     [&](auto&&... y) {
214*0253f6d3SSzymon Dompke                         return (true && ... && detail::eq(x, y));
215*0253f6d3SSzymon Dompke                     },
216*0253f6d3SSzymon Dompke                     value);
217*0253f6d3SSzymon Dompke             },
218*0253f6d3SSzymon Dompke             other.value);
219d2238194SKrzysztof Grobelny     }
220d2238194SKrzysztof Grobelny 
221d2238194SKrzysztof Grobelny     bool operator<(const LabeledTuple& other) const
222d2238194SKrzysztof Grobelny     {
223d2238194SKrzysztof Grobelny         return value < other.value;
224e2362796SWludzik, Jozef     }
225e2362796SWludzik, Jozef 
226e2362796SWludzik, Jozef   private:
227e2362796SWludzik, Jozef     template <size_t... Idx>
228d2238194SKrzysztof Grobelny     void to_json_all(nlohmann::json& j, std::index_sequence<Idx...>) const
229e2362796SWludzik, Jozef     {
230d2238194SKrzysztof Grobelny         (to_json_item<Idx>(j), ...);
231e2362796SWludzik, Jozef     }
232e2362796SWludzik, Jozef 
233e2362796SWludzik, Jozef     template <size_t Idx>
234d2238194SKrzysztof Grobelny     void to_json_item(nlohmann::json& j) const
235e2362796SWludzik, Jozef     {
236e2362796SWludzik, Jozef         using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
237*0253f6d3SSzymon Dompke         using T = std::tuple_element_t<Idx, tuple_type>;
238*0253f6d3SSzymon Dompke         nlohmann::json& item = j[Label::str()];
239*0253f6d3SSzymon Dompke         if constexpr (detail::has_utils_to_json_v<T>)
240*0253f6d3SSzymon Dompke         {
241*0253f6d3SSzymon Dompke             utils::to_json(item, std::get<Idx>(value));
242*0253f6d3SSzymon Dompke         }
243*0253f6d3SSzymon Dompke         else
244*0253f6d3SSzymon Dompke         {
245*0253f6d3SSzymon Dompke             item = std::get<Idx>(value);
246*0253f6d3SSzymon Dompke         }
247e2362796SWludzik, Jozef     }
248e2362796SWludzik, Jozef 
249e2362796SWludzik, Jozef     template <size_t... Idx>
250d2238194SKrzysztof Grobelny     void from_json_all(const nlohmann::json& j, std::index_sequence<Idx...>)
251e2362796SWludzik, Jozef     {
252d2238194SKrzysztof Grobelny         (from_json_item<Idx>(j), ...);
253e2362796SWludzik, Jozef     }
254e2362796SWludzik, Jozef 
255e2362796SWludzik, Jozef     template <size_t Idx>
256d2238194SKrzysztof Grobelny     void from_json_item(const nlohmann::json& j)
257e2362796SWludzik, Jozef     {
258e2362796SWludzik, Jozef         using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
259d2238194SKrzysztof Grobelny         using T = std::tuple_element_t<Idx, tuple_type>;
260e2362796SWludzik, Jozef         const nlohmann::json& item = j.at(Label::str());
261e2362796SWludzik, Jozef         if constexpr (detail::has_utils_from_json_v<T>)
262e2362796SWludzik, Jozef         {
263e2362796SWludzik, Jozef             T& v = std::get<Idx>(value);
264e2362796SWludzik, Jozef             utils::from_json(item, v);
265e2362796SWludzik, Jozef         }
266e2362796SWludzik, Jozef         else
267e2362796SWludzik, Jozef         {
268e2362796SWludzik, Jozef             std::get<Idx>(value) = item.get<T>();
269e2362796SWludzik, Jozef         }
270e2362796SWludzik, Jozef     }
271d2238194SKrzysztof Grobelny 
272d2238194SKrzysztof Grobelny     template <size_t Idx, class Label, class Self>
273d2238194SKrzysztof Grobelny     static auto& find_item(Self& self)
274d2238194SKrzysztof Grobelny     {
275d2238194SKrzysztof Grobelny         if constexpr (std::is_same_v<Label, std::tuple_element_t<
276d2238194SKrzysztof Grobelny                                                 Idx, std::tuple<Labels...>>>)
277d2238194SKrzysztof Grobelny         {
278d2238194SKrzysztof Grobelny             return std::get<Idx>(self.value);
279d2238194SKrzysztof Grobelny         }
280d2238194SKrzysztof Grobelny         else
281d2238194SKrzysztof Grobelny         {
282b8cc78ddSKrzysztof Grobelny             static_assert(Idx + 1 < sizeof...(Args),
283b8cc78ddSKrzysztof Grobelny                           "Label not found in LabeledTuple");
284d2238194SKrzysztof Grobelny             return find_item<Idx + 1, Label>(self);
285d2238194SKrzysztof Grobelny         }
286d2238194SKrzysztof Grobelny     }
287d2238194SKrzysztof Grobelny 
288d2238194SKrzysztof Grobelny     tuple_type value;
289e2362796SWludzik, Jozef };
290e2362796SWludzik, Jozef 
291d2238194SKrzysztof Grobelny template <class... Args, class... Labels>
292dcc4e193SKrzysztof Grobelny inline void to_json(nlohmann::json& json,
293d2238194SKrzysztof Grobelny                     const LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
294d2238194SKrzysztof Grobelny {
295d2238194SKrzysztof Grobelny     json = tuple.to_json();
296d2238194SKrzysztof Grobelny }
297d2238194SKrzysztof Grobelny 
298d2238194SKrzysztof Grobelny template <class... Args, class... Labels>
299dcc4e193SKrzysztof Grobelny inline void from_json(const nlohmann::json& json,
300d2238194SKrzysztof Grobelny                       LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
301d2238194SKrzysztof Grobelny {
302d2238194SKrzysztof Grobelny     tuple.from_json(json);
303d2238194SKrzysztof Grobelny }
304d2238194SKrzysztof Grobelny 
305e2362796SWludzik, Jozef } // namespace utils
306