Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
format.h
1// SPDX-FileCopyrightText: © 2024-2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "zrythm-config.h"
7
8#include <filesystem>
9#include <source_location>
10
11#include "utils/qt.h"
12#include "utils/traits.h"
13#include "utils/utf8_string.h"
14
15#include <QObject>
16#include <QString>
17#include <QUuid>
18
19#include <au/au.hh>
20#include <juce_wrapper.h>
21
22#if defined(__clang__)
23# pragma clang diagnostic push
24# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
25#endif
26#include <boost/describe.hpp>
27#if defined(__clang__)
28# pragma clang diagnostic pop
29#endif
30#include <fmt/format.h>
31#include <fmt/ranges.h>
32#include <magic_enum.hpp>
33
39
40using namespace zrythm;
41
42#define DEFINE_FORMATTER_PRELUDE \
43 bool translate_ = false; \
44 template <typename ParseContext> constexpr auto parse (ParseContext &ctx) \
45 { \
46 auto it = ctx.begin (); \
47 translate_ = (it != ctx.end () && *it == 't'); \
48 return it + translate_; \
49 }
50
51#define VA_ARGS_SIZE(...) \
52 std::tuple_size<decltype (std::make_tuple (__VA_ARGS__))>::value
53
54#define DEFINE_ENUM_FORMATTER(enum_type, enum_name, ...) \
55 namespace fmt \
56 { \
57 template <> struct formatter<enum_type> \
58 { \
59 DEFINE_FORMATTER_PRELUDE \
60\
61 template <typename FormatContext> \
62 auto format (const enum_type &val, FormatContext &ctx) const \
63 { \
64 static constexpr std::array<const char *, VA_ARGS_SIZE (__VA_ARGS__)> \
65 enum_strings = { __VA_ARGS__ }; \
66 const char * str = enum_strings.at (static_cast<int> (val)); \
67 return format_to ( \
68 ctx.out (), FMT_STRING ("{}"), \
69 translate_ \
70 ? utils::Utf8String::from_qstring (QObject::tr (str)).c_str () \
71 : str); \
72 } \
73 }; \
74 } \
75\
76 inline const char * const * enum_name##_get_strings () \
77 { \
78 static constexpr std::array<const char *, VA_ARGS_SIZE (__VA_ARGS__)> \
79 enum_strings = { __VA_ARGS__ }; \
80 return enum_strings.data (); \
81 } \
82\
83 inline utils::Utf8String \
84 enum_name##_to_string (enum_type val, bool translate = false) \
85 { \
86 if (translate) \
87 { \
88 return utils::Utf8String::from_utf8_encoded_string ( \
89 fmt::format ("{:t}", val)); \
90 } \
91 return utils::Utf8String::from_utf8_encoded_string ( \
92 fmt::format ("{}", val)); \
93 } \
94\
95 inline enum_type enum_name##_from_string ( \
96 const utils::Utf8String &str, bool translate = false) \
97 { \
98 for (size_t i = 0; i < VA_ARGS_SIZE (__VA_ARGS__); ++i) \
99 { \
100 if ( \
101 str == enum_name##_to_string (static_cast<enum_type> (i), translate)) \
102 { \
103 return static_cast<enum_type> (i); \
104 } \
105 } \
106 z_error ("Enum value from '{}' not found", str); \
107 return static_cast<enum_type> (0); \
108 }
109
114template <typename... Args>
115std::string
116format_str (std::string_view format, Args &&... args)
117{
118 return fmt::vformat (format, fmt::make_format_args (args...));
119}
120
121template <typename... Args>
122QString
123format_qstr (const QString &format, Args &&... args)
124{
126 format_str (utils::Utf8String::from_qstring (format).str (), args...))
127 .to_qstring ();
128}
129
130// Formatter for std::filesystem::path
131template <>
132struct fmt::formatter<std::filesystem::path> : fmt::formatter<std::string_view>
133{
134 template <typename FormatContext>
135 auto format (const std::filesystem::path &p, FormatContext &ctx) const
136 {
137 return fmt::formatter<std::string_view>::format (
138 utils::Utf8String::from_path (p).view (), ctx);
139 }
140};
141
142// Formatter for juce::String
143template <>
144struct fmt::formatter<juce::String> : fmt::formatter<std::string_view>
145{
146 template <typename FormatContext>
147 auto format (const juce::String &s, FormatContext &ctx) const
148 {
149 return fmt::formatter<std::string_view>::format (
150 utils::Utf8String::from_juce_string (s).view (), ctx);
151 }
152};
153
154// Formatter for juce::File
155template <> struct fmt::formatter<juce::File> : fmt::formatter<std::string_view>
156{
157 template <typename FormatContext>
158 auto format (const juce::File &s, FormatContext &ctx) const
159 {
160 return fmt::formatter<std::string_view>::format (
161 utils::Utf8String::from_juce_string (s.getFullPathName ()).view (), ctx);
162 }
163};
164
165// Formatter for QString
166template <> struct fmt::formatter<QString> : fmt::formatter<std::string_view>
167{
168 template <typename FormatContext>
169 auto format (const QString &s, FormatContext &ctx) const
170 {
171 return fmt::formatter<std::string_view>::format (
172 utils::Utf8String::from_qstring (s).view (), ctx);
173 }
174};
175
176// Generic formatter for smart pointers (std::shared_ptr and std::unique_ptr)
177template <SmartPtr Ptr>
178struct fmt::formatter<Ptr> : fmt::formatter<std::string_view>
179{
180 template <typename FormatContext>
181 auto format (const Ptr &ptr, FormatContext &ctx) const
182 {
183 if (ptr)
184 {
185 return fmt::formatter<std::string_view>::format (
186 fmt::format ("{}", *ptr), ctx);
187 }
188
189 return fmt::formatter<std::string_view>::format ("(null)", ctx);
190 }
191};
192
193// Formatter for std::optional
194template <typename T>
195struct fmt::formatter<std::optional<T>> : fmt::formatter<std::string_view>
196{
197 template <typename FormatContext>
198 auto format (const std::optional<T> &opt, FormatContext &ctx) const
199 {
200 if (opt.has_value ())
201 {
202 return fmt::formatter<std::string_view>::format (
203 fmt::format ("{}", *opt), ctx);
204 }
205 return fmt::formatter<std::string_view>::format ("(nullopt)", ctx);
206 }
207};
208
209// Formatter for std::atomic
210template <typename T>
211struct fmt::formatter<std::atomic<T>> : fmt::formatter<std::string_view>
212{
213 template <typename FormatContext>
214 auto format (const std::atomic<T> &opt, FormatContext &ctx) const
215 {
216 return fmt::formatter<std::string_view>::format (
217 fmt::format ("{}", opt.load ()), ctx);
218 }
219};
220
221// Formatter for QPointer
222template <typename T>
223struct fmt::formatter<QPointer<T>> : fmt::formatter<std::string_view>
224{
225 template <typename FormatContext>
226 auto format (const QPointer<T> &opt, FormatContext &ctx) const
227 {
228 if (!opt.isNull ())
229 {
230 return fmt::formatter<std::string_view>::format (
231 fmt::format ("{}", *opt), ctx);
232 }
233 return fmt::formatter<std::string_view>::format ("(null)", ctx);
234 }
235};
236
237// Formatter for utils::QObjectUniquePtr
238template <typename T>
239struct fmt::formatter<utils::QObjectUniquePtr<T>>
240 : fmt::formatter<std::string_view>
241{
242 template <typename FormatContext>
243 auto format (const utils::QObjectUniquePtr<T> &opt, FormatContext &ctx) const
244 {
245 if (opt)
246 {
247 return fmt::formatter<std::string_view>::format (
248 fmt::format ("{}", *opt), ctx);
249 }
250 return fmt::formatter<std::string_view>::format ("(null)", ctx);
251 }
252};
253
254// Formatter for QUuid
255template <> struct fmt::formatter<QUuid> : fmt::formatter<std::string_view>
256{
257 template <typename FormatContext>
258 auto format (const QUuid &uuid, FormatContext &ctx) const
259 {
260 return fmt::formatter<QString>{}.format (
261 uuid.toString (QUuid::WithoutBraces), ctx);
262 }
263};
264
265// Formatter for source_location
266template <>
267struct fmt::formatter<std::source_location> : fmt::formatter<std::string_view>
268{
269 template <typename FormatContext>
270 auto format (const std::source_location &loc, FormatContext &ctx) const
271 {
272 return fmt::formatter<std::string_view>::format (
273 fmt::format (
274 "{}:{}:{}:{}", loc.file_name (), loc.function_name (), loc.line (),
275 loc.column ()),
276 ctx);
277 }
278};
279
280// Formatter for std::variant
281template <typename... Ts>
282struct fmt::formatter<std::variant<Ts...>> : fmt::formatter<std::string_view>
283{
284 template <typename FormatContext>
285 auto format (const std::variant<Ts...> &variant, FormatContext &ctx) const
286 {
287 return std::visit (
288 [&] (auto &&val) {
289 return fmt::formatter<std::string_view>::format (
290 fmt::format ("{}", val), ctx);
291 },
292 variant);
293 }
294};
295
296// Formatter for enum classes
297template <EnumType T>
298struct fmt::formatter<T> : fmt::formatter<std::string_view>
299{
300 template <typename FormatContext>
301 auto format (const T &val, FormatContext &ctx) const
302 {
303 return fmt::formatter<std::string_view>::format (
304 fmt::format ("{}", ENUM_NAME (val)), ctx);
305 }
306};
307
308// Formatter for Au quantities
309template <typename U, typename R>
310struct fmt::formatter<::au::Quantity<U, R>>
311 : ::au::QuantityFormatter<U, R, ::fmt::formatter>
312{
313};
314
315// Universal formatter for all types described by BOOST_DESCRIBE_STRUCT or
316// BOOST_DESCRIBE_CLASS.
317template <class T>
318struct fmt::formatter<
319 T,
320 char,
321 std::enable_if_t<
322 boost::describe::has_describe_bases<T>::value
323 && boost::describe::has_describe_members<T>::value && !std::is_union_v<T>>>
324{
325 constexpr auto parse (format_parse_context &ctx)
326 {
327 const auto * it = ctx.begin ();
328 const auto * end = ctx.end ();
329
330 if (it != end && *it != '}')
331 {
332 throw std::format_error ("invalid format");
333 }
334
335 return it;
336 }
337
338 auto format (T const &t, format_context &ctx) const
339 {
340 using namespace boost::describe;
341
342 using Bd = describe_bases<T, mod_any_access>;
343 using Md = describe_members<T, mod_any_access>;
344
345 auto out = ctx.out ();
346
347 *out++ = '{';
348
349 bool first = true;
350
351 boost::mp11::mp_for_each<Bd> ([&] (auto D) {
352 if (!first)
353 {
354 *out++ = ',';
355 }
356
357 first = false;
358
359 out = fmt::format_to (out, " {}", (typename decltype (D)::type const &) t);
360 });
361
362 boost::mp11::mp_for_each<Md> ([&] (auto D) {
363 if (!first)
364 {
365 *out++ = ',';
366 }
367
368 first = false;
369
370 out = fmt::format_to (out, " .{}={}", D.name, t.*D.pointer);
371 });
372
373 if (!first)
374 {
375 *out++ = ' ';
376 }
377
378 *out++ = '}';
379
380 return out;
381 }
382};
383
A unique pointer for QObject objects that also works with QObject-based ownership.
Definition qt.h:38
static constexpr Utf8String from_utf8_encoded_string(std::string_view str)
Construct from a std::string_view that we are 100% sure is UTF8-encoded.
Definition utf8_string.h:81
std::string format_str(std::string_view format, Args &&... args)
Used when fmt::format can't be used (when the format string is not a literal).
Definition format.h:116
String utilities.
Definition algorithms.h:12