Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
serialization.h
1// SPDX-FileCopyrightText: © 2024-2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <memory>
7#include <string>
8
9#include "utils/exceptions.h"
10#include "utils/qt.h"
11#include "utils/traits.h"
12#include "utils/types.h"
13
14#include <QUuid>
15
16#include <au/au.hh>
17#include <boost/unordered/concurrent_flat_map_fwd.hpp>
18#include <nlohmann/json.hpp>
19
21
22inline void
23to_json (nlohmann::json &j, const QString &s)
24{
25 j = s.toStdString ();
26}
27inline void
28from_json (const nlohmann::json &j, QString &s)
29{
30 s = QString::fromUtf8 (j.get<std::string> ());
31}
32
33inline void
34to_json (nlohmann::json &j, const QUuid &uuid)
35{
36 j = uuid.toString (QUuid::WithoutBraces);
37}
38inline void
39from_json (const nlohmann::json &j, QUuid &uuid)
40{
41 uuid = QUuid::fromString (j.get<QString> ());
42}
43
44namespace zrythm::utils::serialization
45{
46
47static constexpr auto kSchemaVersionKey = "schemaVersion"sv;
48static constexpr auto kAppVersionKey = "appVersion"sv;
49static constexpr auto kDocumentTypeKey = "documentType"sv;
50static constexpr auto kVariantTypeKey = "type"sv;
51static constexpr auto kVariantNonObjectValueKey = "nonObjectValue"sv;
52
63template <StdVariant Variant, ObjectBuilder BuilderT>
64inline auto
65variant_from_json_with_builder (
66 const nlohmann::json &j,
67 Variant &var,
68 const BuilderT &builder)
69{
70 const auto index =
71 j.at (zrythm::utils::serialization::kVariantTypeKey).get<int> ();
72
73 // Create a copy of j without the "type" field for value deserialization
74 nlohmann::json value_json = j;
75 value_json.erase (zrythm::utils::serialization::kVariantTypeKey);
76
77 auto creator = [&]<size_t... I> (std::index_sequence<I...>) {
78 Variant result{};
79
80 auto create_type_if_current_index = [&]<size_t N> () {
81 using Type = std::variant_alternative_t<N, Variant>;
82 using StrippedType = std::remove_pointer_t<Type>;
83 if (N == index)
84 {
85 auto object_ptr = builder.template build<StrippedType> ();
86
87 if constexpr (std::is_copy_constructible_v<StrippedType>)
88 {
89 if (value_json.contains (
90 utils::serialization::kVariantNonObjectValueKey))
91 {
92 value_json[utils::serialization::kVariantNonObjectValueKey]
93 .get_to (*object_ptr);
94 result = *object_ptr;
95 return;
96 }
97 }
98
99 if constexpr (std::is_pointer_v<Type>)
100 {
101 value_json.get_to (*object_ptr);
102 result = object_ptr.release ();
103 }
104 else if constexpr (std::is_copy_constructible_v<Type>)
105 {
106 value_json.get_to (*object_ptr);
107 result = *object_ptr;
108 }
109 else if constexpr (is_derived_from_template_v<QObjectUniquePtr, Type>)
110 {
111 Type t = std::move (*object_ptr);
112 value_json.get_to (*t);
113 result = std::move (t);
114 }
115 else
116 {
117 DEBUG_TEMPLATE_PARAM (Type)
118 static_assert (
119 false,
120 "Only variant of pointers or copy-constructible types is currently supported");
121 }
122 }
123 };
124
125 (create_type_if_current_index.template operator()<I> (), ...);
126
127 return result;
128 };
129
130 var = creator (std::make_index_sequence<std::variant_size_v<Variant>>{});
131}
132
133}; // namespace zrythm::utils::serialization
134
135namespace nlohmann
136{
138template <typename T> struct adl_serializer<T *>
139{
140 static void to_json (json &j, const T * ptr)
141 {
142 if (ptr)
143 j = *ptr;
144 else
145 j = nullptr;
146 }
147 // from_json would be unsafe due to raw memory allocation, so handle each case
148 // manually in each type
149};
150
152template <typename... Args> struct adl_serializer<std::variant<Args...>>
153{
154 static void to_json (json &j, std::variant<Args...> const &v)
155 {
156 std::visit (
157 [&] (auto &&value) {
158 j[zrythm::utils::serialization::kVariantTypeKey] = v.index ();
159 // Merge the value's JSON fields directly into j
160 json value_json = std::forward<decltype (value)> (value);
161 if (value_json.is_object ())
162 {
163 j.update (value_json);
164 }
165 else
166 {
167 j[zrythm::utils::serialization::kVariantNonObjectValueKey] =
168 value_json;
169 }
170 },
171 v);
172 }
173};
174
176template <typename T> struct adl_serializer<std::unique_ptr<T>>
177{
178 static void to_json (json &json_value, const std::unique_ptr<T> &ptr)
179 {
180 if (ptr.get ())
181 {
182 json_value = *ptr;
183 }
184 else
185 {
186 json_value = nullptr;
187 }
188 }
189 static void from_json (const json &json_value, std::unique_ptr<T> &ptr)
190 requires std::is_default_constructible_v<T>
191 {
192 ptr = std::make_unique<T> ();
193 json_value.get_to (*ptr);
194 }
195};
196
198template <typename T> struct adl_serializer<std::shared_ptr<T>>
199{
200 static void to_json (json &json_value, const std::shared_ptr<T> &ptr)
201 {
202 if (ptr.get ())
203 {
204 json_value = *ptr;
205 }
206 else
207 {
208 json_value = nullptr;
209 }
210 }
211 static void from_json (const json &json_value, std::shared_ptr<T> &ptr)
212 requires std::is_default_constructible_v<T>
213 {
214 ptr = std::make_shared<T> ();
215 json_value.get_to (*ptr);
216 }
217};
218
220template <typename T> struct adl_serializer<zrythm::utils::QObjectUniquePtr<T>>
221{
222 static void
223 to_json (json &json_value, const zrythm::utils::QObjectUniquePtr<T> &ptr)
224 {
225 if (ptr.get ())
226 {
227 json_value = *ptr;
228 }
229 else
230 {
231 json_value = nullptr;
232 }
233 }
234 static void
235 from_json (const json &json_value, zrythm::utils::QObjectUniquePtr<T> &ptr)
236 requires std::is_default_constructible_v<T>
237 {
238 ptr = new T ();
239 json_value.get_to (*ptr);
240 }
241};
242
244template <StrongTypedef T> struct adl_serializer<T>
245{
246 static void to_json (json &json_value, const T &strong_type)
247 {
248 json_value = type_safe::get (strong_type);
249 }
250 static void from_json (const json &json_value, T &strong_type)
251 {
252 json_value.get_to (type_safe::get (strong_type));
253 }
254};
255
256template <typename Key, typename T>
257struct adl_serializer<boost::unordered::concurrent_flat_map<Key, T>>
258{
259 using ConcurrentHashMap = boost::unordered::concurrent_flat_map<Key, T>;
260 // convert to std::map since nlohmann::json knows how to serialize it
261 static void to_json (json &j, const ConcurrentHashMap &map)
262 {
263 std::map<Key, T> temp;
264 map.visit_all ([&temp] (const auto &kv) {
265 temp.emplace (kv.first, kv.second);
266 });
267 j = temp;
268 }
269
270 static void from_json (const json &j, ConcurrentHashMap &map)
271 {
272 auto temp = j.get<std::map<Key, T>> (); // Deserialize to std::map first
273 map.clear ();
274 for (const auto &kv : temp)
275 {
276 map.emplace (kv.first, kv.second);
277 }
278 }
279};
280
282template <typename Unit, typename Rep>
283struct adl_serializer<au::Quantity<Unit, Rep>>
284{
285 static void to_json (json &j, const au::Quantity<Unit, Rep> &quantity)
286 {
287 j = quantity.in (Unit{});
288 }
289
290 static void from_json (const json &j, au::Quantity<Unit, Rep> &quantity)
291 {
292 Rep raw_value;
293 j.get_to (raw_value);
294 quantity = au::QuantityMaker<Unit>{}(raw_value);
295 }
296};
297
298} // namespace nlohmann
A unique pointer for QObject objects that also works with QObject-based ownership.
Definition qt.h:38
Base class for exceptions in Zrythm.
Definition exceptions.h:20