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 
from_json(const nlohmann::json & j,sdbusplus::message::object_path & o)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 
from_json(const nlohmann::json & j,std::vector<sdbusplus::message::object_path> & o)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 
to_json(nlohmann::json & j,const double & val)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 
from_json(const nlohmann::json & j,double & val)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 
eq(const auto & a,const auto & b)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 
LabeledTupleutils::LabeledTuple1543a1c297aSPatrick Williams     explicit LabeledTuple(tuple_type v) : value(std::move(v)) {}
LabeledTupleutils::LabeledTuple1553a1c297aSPatrick 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 
to_jsonutils::LabeledTuple160d2238194SKrzysztof 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 
to_tupleutils::LabeledTuple167493e62ebSKrzysztof Grobelny     const tuple_type& to_tuple() const
168493e62ebSKrzysztof Grobelny     {
169493e62ebSKrzysztof Grobelny         return value;
170493e62ebSKrzysztof Grobelny     }
171493e62ebSKrzysztof Grobelny 
from_jsonutils::LabeledTuple172d2238194SKrzysztof 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 
dumputils::LabeledTuple1773a617023SSzymon Dompke     std::string dump() const
1783a617023SSzymon Dompke     {
1793a617023SSzymon Dompke         return to_json().dump();
1803a617023SSzymon Dompke     }
1813a617023SSzymon Dompke 
182d2238194SKrzysztof Grobelny     template <size_t Idx>
at_indexutils::LabeledTuple183d2238194SKrzysztof 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>
at_indexutils::LabeledTuple189d2238194SKrzysztof Grobelny     auto& at_index()
190d2238194SKrzysztof Grobelny     {
191d2238194SKrzysztof Grobelny         return std::get<Idx>(value);
192d2238194SKrzysztof Grobelny     }
193d2238194SKrzysztof Grobelny 
194d2238194SKrzysztof Grobelny     template <class Label>
at_labelutils::LabeledTuple195d2238194SKrzysztof 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>
at_labelutils::LabeledTuple201d2238194SKrzysztof Grobelny     auto& at_label()
202d2238194SKrzysztof Grobelny     {
203d2238194SKrzysztof Grobelny         return find_item<0, Label>(*this);
204d2238194SKrzysztof Grobelny     }
205d2238194SKrzysztof Grobelny 
operator ==utils::LabeledTuple206d2238194SKrzysztof Grobelny     bool operator==(const LabeledTuple& other) const
207d2238194SKrzysztof Grobelny     {
208*f535cad6SPatrick Williams         return std::apply([&](auto&&... x) {
209*f535cad6SPatrick Williams             return std::apply([&](auto&&... y) {
210*f535cad6SPatrick Williams                 return (true && ... && detail::eq(x, y));
211*f535cad6SPatrick Williams             }, value);
212*f535cad6SPatrick Williams         }, other.value);
213d2238194SKrzysztof Grobelny     }
214d2238194SKrzysztof Grobelny 
operator <utils::LabeledTuple215d2238194SKrzysztof Grobelny     bool operator<(const LabeledTuple& other) const
216d2238194SKrzysztof Grobelny     {
217d2238194SKrzysztof Grobelny         return value < other.value;
218e2362796SWludzik, Jozef     }
219e2362796SWludzik, Jozef 
220e2362796SWludzik, Jozef   private:
221e2362796SWludzik, Jozef     template <size_t... Idx>
to_json_allutils::LabeledTuple222d2238194SKrzysztof Grobelny     void to_json_all(nlohmann::json& j, std::index_sequence<Idx...>) const
223e2362796SWludzik, Jozef     {
224d2238194SKrzysztof Grobelny         (to_json_item<Idx>(j), ...);
225e2362796SWludzik, Jozef     }
226e2362796SWludzik, Jozef 
227e2362796SWludzik, Jozef     template <size_t Idx>
to_json_itemutils::LabeledTuple228d2238194SKrzysztof Grobelny     void to_json_item(nlohmann::json& j) const
229e2362796SWludzik, Jozef     {
230e2362796SWludzik, Jozef         using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
2310253f6d3SSzymon Dompke         using T = std::tuple_element_t<Idx, tuple_type>;
2320253f6d3SSzymon Dompke         nlohmann::json& item = j[Label::str()];
2330253f6d3SSzymon Dompke         if constexpr (detail::has_utils_to_json_v<T>)
2340253f6d3SSzymon Dompke         {
2350253f6d3SSzymon Dompke             utils::to_json(item, std::get<Idx>(value));
2360253f6d3SSzymon Dompke         }
2370253f6d3SSzymon Dompke         else
2380253f6d3SSzymon Dompke         {
2390253f6d3SSzymon Dompke             item = std::get<Idx>(value);
2400253f6d3SSzymon Dompke         }
241e2362796SWludzik, Jozef     }
242e2362796SWludzik, Jozef 
243e2362796SWludzik, Jozef     template <size_t... Idx>
from_json_allutils::LabeledTuple244d2238194SKrzysztof Grobelny     void from_json_all(const nlohmann::json& j, std::index_sequence<Idx...>)
245e2362796SWludzik, Jozef     {
246d2238194SKrzysztof Grobelny         (from_json_item<Idx>(j), ...);
247e2362796SWludzik, Jozef     }
248e2362796SWludzik, Jozef 
249e2362796SWludzik, Jozef     template <size_t Idx>
from_json_itemutils::LabeledTuple250d2238194SKrzysztof Grobelny     void from_json_item(const nlohmann::json& j)
251e2362796SWludzik, Jozef     {
252e2362796SWludzik, Jozef         using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
253d2238194SKrzysztof Grobelny         using T = std::tuple_element_t<Idx, tuple_type>;
254e2362796SWludzik, Jozef         const nlohmann::json& item = j.at(Label::str());
255e2362796SWludzik, Jozef         if constexpr (detail::has_utils_from_json_v<T>)
256e2362796SWludzik, Jozef         {
257e2362796SWludzik, Jozef             T& v = std::get<Idx>(value);
258e2362796SWludzik, Jozef             utils::from_json(item, v);
259e2362796SWludzik, Jozef         }
260e2362796SWludzik, Jozef         else
261e2362796SWludzik, Jozef         {
262e2362796SWludzik, Jozef             std::get<Idx>(value) = item.get<T>();
263e2362796SWludzik, Jozef         }
264e2362796SWludzik, Jozef     }
265d2238194SKrzysztof Grobelny 
266d2238194SKrzysztof Grobelny     template <size_t Idx, class Label, class Self>
find_itemutils::LabeledTuple267d2238194SKrzysztof Grobelny     static auto& find_item(Self& self)
268d2238194SKrzysztof Grobelny     {
269d2238194SKrzysztof Grobelny         if constexpr (std::is_same_v<Label, std::tuple_element_t<
270d2238194SKrzysztof Grobelny                                                 Idx, std::tuple<Labels...>>>)
271d2238194SKrzysztof Grobelny         {
272d2238194SKrzysztof Grobelny             return std::get<Idx>(self.value);
273d2238194SKrzysztof Grobelny         }
274d2238194SKrzysztof Grobelny         else
275d2238194SKrzysztof Grobelny         {
276b8cc78ddSKrzysztof Grobelny             static_assert(Idx + 1 < sizeof...(Args),
277b8cc78ddSKrzysztof Grobelny                           "Label not found in LabeledTuple");
278d2238194SKrzysztof Grobelny             return find_item<Idx + 1, Label>(self);
279d2238194SKrzysztof Grobelny         }
280d2238194SKrzysztof Grobelny     }
281d2238194SKrzysztof Grobelny 
282d2238194SKrzysztof Grobelny     tuple_type value;
283e2362796SWludzik, Jozef };
284e2362796SWludzik, Jozef 
285d2238194SKrzysztof Grobelny template <class... Args, class... Labels>
to_json(nlohmann::json & json,const LabeledTuple<std::tuple<Args...>,Labels...> & tuple)286dcc4e193SKrzysztof Grobelny inline void to_json(nlohmann::json& json,
287d2238194SKrzysztof Grobelny                     const LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
288d2238194SKrzysztof Grobelny {
289d2238194SKrzysztof Grobelny     json = tuple.to_json();
290d2238194SKrzysztof Grobelny }
291d2238194SKrzysztof Grobelny 
292d2238194SKrzysztof Grobelny template <class... Args, class... Labels>
from_json(const nlohmann::json & json,LabeledTuple<std::tuple<Args...>,Labels...> & tuple)293dcc4e193SKrzysztof Grobelny inline void from_json(const nlohmann::json& json,
294d2238194SKrzysztof Grobelny                       LabeledTuple<std::tuple<Args...>, Labels...>& tuple)
295d2238194SKrzysztof Grobelny {
296d2238194SKrzysztof Grobelny     tuple.from_json(json);
297d2238194SKrzysztof Grobelny }
298d2238194SKrzysztof Grobelny 
299e2362796SWludzik, Jozef } // namespace utils
300