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 19 inline void from_json(const nlohmann::json& j, 20 sdbusplus::message::object_path& o) 21 { 22 o = j.get<std::string>(); 23 } 24 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 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 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 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 154 explicit LabeledTuple(tuple_type v) : value(std::move(v)) {} 155 LabeledTuple(Args... args) : value(std::move(args)...) {} 156 157 LabeledTuple& operator=(const LabeledTuple&) = default; 158 LabeledTuple& operator=(LabeledTuple&&) = default; 159 160 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 167 const tuple_type& to_tuple() const 168 { 169 return value; 170 } 171 172 void from_json(const nlohmann::json& j) 173 { 174 from_json_all(j, std::make_index_sequence<sizeof...(Args)>()); 175 } 176 177 std::string dump() const 178 { 179 return to_json().dump(); 180 } 181 182 template <size_t Idx> 183 const auto& at_index() const 184 { 185 return std::get<Idx>(value); 186 } 187 188 template <size_t Idx> 189 auto& at_index() 190 { 191 return std::get<Idx>(value); 192 } 193 194 template <class Label> 195 const auto& at_label() const 196 { 197 return find_item<0, Label>(*this); 198 } 199 200 template <class Label> 201 auto& at_label() 202 { 203 return find_item<0, Label>(*this); 204 } 205 206 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 217 bool operator<(const LabeledTuple& other) const 218 { 219 return value < other.value; 220 } 221 222 private: 223 template <size_t... Idx> 224 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> 230 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> 246 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> 252 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> 269 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> 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> 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