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 {} 156 LabeledTuple(Args... args) : value(std::move(args)...) 157 {} 158 159 LabeledTuple& operator=(const LabeledTuple&) = default; 160 LabeledTuple& operator=(LabeledTuple&&) = default; 161 162 nlohmann::json to_json() const 163 { 164 nlohmann::json j; 165 to_json_all(j, std::make_index_sequence<sizeof...(Args)>()); 166 return j; 167 } 168 169 const tuple_type& to_tuple() const 170 { 171 return value; 172 } 173 174 void from_json(const nlohmann::json& j) 175 { 176 from_json_all(j, std::make_index_sequence<sizeof...(Args)>()); 177 } 178 179 std::string dump() const 180 { 181 return to_json().dump(); 182 } 183 184 template <size_t Idx> 185 const auto& at_index() const 186 { 187 return std::get<Idx>(value); 188 } 189 190 template <size_t Idx> 191 auto& at_index() 192 { 193 return std::get<Idx>(value); 194 } 195 196 template <class Label> 197 const auto& at_label() const 198 { 199 return find_item<0, Label>(*this); 200 } 201 202 template <class Label> 203 auto& at_label() 204 { 205 return find_item<0, Label>(*this); 206 } 207 208 bool operator==(const LabeledTuple& other) const 209 { 210 return std::apply( 211 [&](auto&&... x) { 212 return std::apply( 213 [&](auto&&... y) { 214 return (true && ... && detail::eq(x, y)); 215 }, 216 value); 217 }, 218 other.value); 219 } 220 221 bool operator<(const LabeledTuple& other) const 222 { 223 return value < other.value; 224 } 225 226 private: 227 template <size_t... Idx> 228 void to_json_all(nlohmann::json& j, std::index_sequence<Idx...>) const 229 { 230 (to_json_item<Idx>(j), ...); 231 } 232 233 template <size_t Idx> 234 void to_json_item(nlohmann::json& j) const 235 { 236 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>; 237 using T = std::tuple_element_t<Idx, tuple_type>; 238 nlohmann::json& item = j[Label::str()]; 239 if constexpr (detail::has_utils_to_json_v<T>) 240 { 241 utils::to_json(item, std::get<Idx>(value)); 242 } 243 else 244 { 245 item = std::get<Idx>(value); 246 } 247 } 248 249 template <size_t... Idx> 250 void from_json_all(const nlohmann::json& j, std::index_sequence<Idx...>) 251 { 252 (from_json_item<Idx>(j), ...); 253 } 254 255 template <size_t Idx> 256 void from_json_item(const nlohmann::json& j) 257 { 258 using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>; 259 using T = std::tuple_element_t<Idx, tuple_type>; 260 const nlohmann::json& item = j.at(Label::str()); 261 if constexpr (detail::has_utils_from_json_v<T>) 262 { 263 T& v = std::get<Idx>(value); 264 utils::from_json(item, v); 265 } 266 else 267 { 268 std::get<Idx>(value) = item.get<T>(); 269 } 270 } 271 272 template <size_t Idx, class Label, class Self> 273 static auto& find_item(Self& self) 274 { 275 if constexpr (std::is_same_v<Label, std::tuple_element_t< 276 Idx, std::tuple<Labels...>>>) 277 { 278 return std::get<Idx>(self.value); 279 } 280 else 281 { 282 static_assert(Idx + 1 < sizeof...(Args), 283 "Label not found in LabeledTuple"); 284 return find_item<Idx + 1, Label>(self); 285 } 286 } 287 288 tuple_type value; 289 }; 290 291 template <class... Args, class... Labels> 292 inline void to_json(nlohmann::json& json, 293 const LabeledTuple<std::tuple<Args...>, Labels...>& tuple) 294 { 295 json = tuple.to_json(); 296 } 297 298 template <class... Args, class... Labels> 299 inline void from_json(const nlohmann::json& json, 300 LabeledTuple<std::tuple<Args...>, Labels...>& tuple) 301 { 302 tuple.from_json(json); 303 } 304 305 } // namespace utils 306