| #pragma once |
| |
| #include <nlohmann/json.hpp> |
| #include <sdbusplus/message/types.hpp> |
| |
| #include <cmath> |
| #include <limits> |
| |
| namespace utils |
| { |
| |
| namespace numeric_literals |
| { |
| constexpr std::string_view NaN = "NaN"; |
| constexpr std::string_view infinity = "inf"; |
| constexpr std::string_view infinity_negative = "-inf"; |
| } // namespace numeric_literals |
| |
| inline void from_json(const nlohmann::json& j, |
| sdbusplus::message::object_path& o) |
| { |
| o = j.get<std::string>(); |
| } |
| |
| inline void from_json(const nlohmann::json& j, |
| std::vector<sdbusplus::message::object_path>& o) |
| { |
| o.clear(); |
| for (const nlohmann::json& item : j) |
| { |
| o.emplace_back(item.get<std::string>()); |
| } |
| } |
| |
| inline void to_json(nlohmann::json& j, const double& val) |
| { |
| if (std::isnan(val)) |
| { |
| j = numeric_literals::NaN; |
| } |
| else if (val == std::numeric_limits<double>::infinity()) |
| { |
| j = numeric_literals::infinity; |
| } |
| else if (val == -std::numeric_limits<double>::infinity()) |
| { |
| j = numeric_literals::infinity_negative; |
| } |
| else |
| { |
| j = val; |
| } |
| } |
| |
| inline void from_json(const nlohmann::json& j, double& val) |
| { |
| if (j.is_number()) |
| { |
| val = j.get<double>(); |
| } |
| else |
| { |
| auto str_val = j.get<std::string>(); |
| if (str_val == numeric_literals::NaN) |
| { |
| val = std::numeric_limits<double>::quiet_NaN(); |
| } |
| else if (str_val == numeric_literals::infinity) |
| { |
| val = std::numeric_limits<double>::infinity(); |
| } |
| else if (str_val == numeric_literals::infinity_negative) |
| { |
| val = -std::numeric_limits<double>::infinity(); |
| } |
| else |
| { |
| throw std::invalid_argument("Unknown numeric literal"); |
| } |
| } |
| } |
| |
| namespace detail |
| { |
| |
| template <class T> |
| struct has_utils_from_json |
| { |
| template <class U> |
| static U& ref(); |
| |
| template <class U> |
| static std::true_type check( |
| decltype(utils::from_json(ref<const nlohmann::json>(), ref<U>()))*); |
| |
| template <class> |
| static std::false_type check(...); |
| |
| static constexpr bool value = |
| decltype(check<std::decay_t<T>>(nullptr))::value; |
| }; |
| |
| template <class T> |
| constexpr bool has_utils_from_json_v = has_utils_from_json<T>::value; |
| |
| template <class T> |
| struct has_utils_to_json |
| { |
| template <class U> |
| static U& ref(); |
| |
| template <class U> |
| static std::true_type |
| check(decltype(utils::to_json(ref<nlohmann::json>(), ref<const U>()))*); |
| |
| template <class> |
| static std::false_type check(...); |
| |
| static constexpr bool value = |
| decltype(check<std::decay_t<T>>(nullptr))::value; |
| }; |
| |
| template <class T> |
| constexpr bool has_utils_to_json_v = has_utils_to_json<T>::value; |
| |
| bool eq(const auto& a, const auto& b) |
| { |
| if constexpr (std::is_same<std::decay_t<decltype(a)>, double>()) |
| { |
| if (std::isnan(a)) |
| { |
| return std::isnan(b); |
| } |
| } |
| return a == b; |
| } |
| |
| } // namespace detail |
| |
| template <class, class...> |
| struct LabeledTuple; |
| |
| template <class... Args, class... Labels> |
| struct LabeledTuple<std::tuple<Args...>, Labels...> |
| { |
| static_assert(sizeof...(Args) == sizeof...(Labels)); |
| |
| using tuple_type = std::tuple<Args...>; |
| |
| LabeledTuple() = default; |
| LabeledTuple(const LabeledTuple&) = default; |
| LabeledTuple(LabeledTuple&&) = default; |
| |
| explicit LabeledTuple(tuple_type v) : value(std::move(v)) {} |
| LabeledTuple(Args... args) : value(std::move(args)...) {} |
| |
| LabeledTuple& operator=(const LabeledTuple&) = default; |
| LabeledTuple& operator=(LabeledTuple&&) = default; |
| |
| nlohmann::json to_json() const |
| { |
| nlohmann::json j; |
| to_json_all(j, std::make_index_sequence<sizeof...(Args)>()); |
| return j; |
| } |
| |
| const tuple_type& to_tuple() const |
| { |
| return value; |
| } |
| |
| void from_json(const nlohmann::json& j) |
| { |
| from_json_all(j, std::make_index_sequence<sizeof...(Args)>()); |
| } |
| |
| std::string dump() const |
| { |
| return to_json().dump(); |
| } |
| |
| template <size_t Idx> |
| const auto& at_index() const |
| { |
| return std::get<Idx>(value); |
| } |
| |
| template <size_t Idx> |
| auto& at_index() |
| { |
| return std::get<Idx>(value); |
| } |
| |
| template <class Label> |
| const auto& at_label() const |
| { |
| return find_item<0, Label>(*this); |
| } |
| |
| template <class Label> |
| auto& at_label() |
| { |
| return find_item<0, Label>(*this); |
| } |
| |
| bool operator==(const LabeledTuple& other) const |
| { |
| return std::apply( |
| [&](auto&&... x) { |
| return std::apply( |
| [&](auto&&... y) { return (true && ... && detail::eq(x, y)); }, |
| value); |
| }, |
| other.value); |
| } |
| |
| bool operator<(const LabeledTuple& other) const |
| { |
| return value < other.value; |
| } |
| |
| private: |
| template <size_t... Idx> |
| void to_json_all(nlohmann::json& j, std::index_sequence<Idx...>) const |
| { |
| (to_json_item<Idx>(j), ...); |
| } |
| |
| template <size_t Idx> |
| void to_json_item(nlohmann::json& j) const |
| { |
| using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>; |
| using T = std::tuple_element_t<Idx, tuple_type>; |
| nlohmann::json& item = j[Label::str()]; |
| if constexpr (detail::has_utils_to_json_v<T>) |
| { |
| utils::to_json(item, std::get<Idx>(value)); |
| } |
| else |
| { |
| item = std::get<Idx>(value); |
| } |
| } |
| |
| template <size_t... Idx> |
| void from_json_all(const nlohmann::json& j, std::index_sequence<Idx...>) |
| { |
| (from_json_item<Idx>(j), ...); |
| } |
| |
| template <size_t Idx> |
| void from_json_item(const nlohmann::json& j) |
| { |
| using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>; |
| using T = std::tuple_element_t<Idx, tuple_type>; |
| const nlohmann::json& item = j.at(Label::str()); |
| if constexpr (detail::has_utils_from_json_v<T>) |
| { |
| T& v = std::get<Idx>(value); |
| utils::from_json(item, v); |
| } |
| else |
| { |
| std::get<Idx>(value) = item.get<T>(); |
| } |
| } |
| |
| template <size_t Idx, class Label, class Self> |
| static auto& find_item(Self& self) |
| { |
| if constexpr (std::is_same_v<Label, std::tuple_element_t< |
| Idx, std::tuple<Labels...>>>) |
| { |
| return std::get<Idx>(self.value); |
| } |
| else |
| { |
| static_assert(Idx + 1 < sizeof...(Args), |
| "Label not found in LabeledTuple"); |
| return find_item<Idx + 1, Label>(self); |
| } |
| } |
| |
| tuple_type value; |
| }; |
| |
| template <class... Args, class... Labels> |
| inline void to_json(nlohmann::json& json, |
| const LabeledTuple<std::tuple<Args...>, Labels...>& tuple) |
| { |
| json = tuple.to_json(); |
| } |
| |
| template <class... Args, class... Labels> |
| inline void from_json(const nlohmann::json& json, |
| LabeledTuple<std::tuple<Args...>, Labels...>& tuple) |
| { |
| tuple.from_json(json); |
| } |
| |
| } // namespace utils |