Implement Report persistency

Now Report properties are stored in non-volatile memory. It allows
to restore Report after system restart. Persistency of a report is
controlled by Persistency property in Report interface.

Tested:
 - Passed unit tests
 - Verified that report is stored in /var/lib/telemetry dir
 - Verified that report is restored from storage after telemetry
   service start

Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
Change-Id: Iccfe21603eecffc4e174a4403f699b03de320db9
diff --git a/src/utils/labeled_tuple.hpp b/src/utils/labeled_tuple.hpp
new file mode 100644
index 0000000..7e75322
--- /dev/null
+++ b/src/utils/labeled_tuple.hpp
@@ -0,0 +1,117 @@
+#pragma once
+
+#include <nlohmann/json.hpp>
+#include <sdbusplus/message/types.hpp>
+
+namespace utils
+{
+
+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>());
+    }
+}
+
+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;
+
+} // namespace detail
+
+template <class, class...>
+struct LabeledTuple;
+
+template <class... Args, class... Labels>
+struct LabeledTuple<std::tuple<Args...>, Labels...>
+{
+    LabeledTuple() = delete;
+
+    static_assert(sizeof...(Args) == sizeof...(Labels));
+
+    static nlohmann::json to_json(const std::tuple<Args...>& tuple)
+    {
+        nlohmann::json j;
+        to_json_all(j, tuple, std::make_index_sequence<sizeof...(Args)>());
+        return j;
+    }
+
+    static std::tuple<Args...> from_json(const nlohmann::json& j)
+    {
+        std::tuple<Args...> value;
+        from_json_all(j, value, std::make_index_sequence<sizeof...(Args)>());
+        return value;
+    }
+
+  private:
+    template <size_t... Idx>
+    static void to_json_all(nlohmann::json& j, const std::tuple<Args...>& tuple,
+                            std::index_sequence<Idx...>)
+    {
+        (to_json_item<Idx>(j, tuple), ...);
+    }
+
+    template <size_t Idx>
+    static void to_json_item(nlohmann::json& j,
+                             const std::tuple<Args...>& tuple)
+    {
+        using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
+        j[Label::str()] = std::get<Idx>(tuple);
+    }
+
+    template <size_t... Idx>
+    static void from_json_all(const nlohmann::json& j,
+                              std::tuple<Args...>& value,
+                              std::index_sequence<Idx...>)
+    {
+        (from_json_item<Idx>(j, value), ...);
+    }
+
+    template <size_t Idx>
+    static void from_json_item(const nlohmann::json& j,
+                               std::tuple<Args...>& value)
+    {
+        using Label = std::tuple_element_t<Idx, std::tuple<Labels...>>;
+        using T = std::tuple_element_t<Idx, std::tuple<Args...>>;
+        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>();
+        }
+    }
+};
+
+} // namespace utils