aboutsummaryrefslogtreecommitdiff
path: root/lib/converters.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/converters.h')
-rw-r--r--lib/converters.h696
1 files changed, 338 insertions, 358 deletions
diff --git a/lib/converters.h b/lib/converters.h
index 36b7ff15..0085fa4b 100644
--- a/lib/converters.h
+++ b/lib/converters.h
@@ -1,413 +1,393 @@
/******************************************************************************
-* Copyright (C) 2017 Kitsune Ral <kitsune-ral@users.sf.net>
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 2.1 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-*/
+ * Copyright (C) 2017 Kitsune Ral <kitsune-ral@users.sf.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
#pragma once
#include "util.h"
-#include <QtCore/QJsonObject>
+#include <QtCore/QDate>
#include <QtCore/QJsonArray> // Includes <QtCore/QJsonValue>
#include <QtCore/QJsonDocument>
-#include <QtCore/QDate>
-#include <QtCore/QUrlQuery>
+#include <QtCore/QJsonObject>
#include <QtCore/QSet>
+#include <QtCore/QUrlQuery>
#include <QtCore/QVector>
#include <unordered_map>
#include <vector>
#if 0 // Waiting for C++17
-#include <experimental/optional>
+# include <experimental/optional>
template <typename T>
using optional = std::experimental::optional<T>;
#endif
// Enable std::unordered_map<QString, T>
-namespace std
-{
- template <> struct hash<QString>
+namespace std {
+template <>
+struct hash<QString> {
+ size_t operator()(const QString& s) const Q_DECL_NOEXCEPT
{
- size_t operator()(const QString& s) const Q_DECL_NOEXCEPT
- {
- return qHash(s
+ return qHash(s
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
- , uint(qGlobalQHashSeed())
+ ,
+ uint(qGlobalQHashSeed())
#endif
- );
- }
- };
-}
+ );
+ }
+};
+} // namespace std
class QVariant;
-namespace QMatrixClient
-{
- template <typename T>
- struct JsonObjectConverter
- {
- static void dumpTo(QJsonObject& jo, const T& pod) { jo = pod.toJson(); }
- static void fillFrom(const QJsonObject& jo, T& pod) { pod = T(jo); }
- };
+namespace QMatrixClient {
+template <typename T>
+struct JsonObjectConverter {
+ static void dumpTo(QJsonObject& jo, const T& pod) { jo = pod.toJson(); }
+ static void fillFrom(const QJsonObject& jo, T& pod) { pod = T(jo); }
+};
- template <typename T>
- struct JsonConverter
+template <typename T>
+struct JsonConverter {
+ static QJsonObject dump(const T& pod)
{
- static QJsonObject dump(const T& pod)
- {
- QJsonObject jo;
- JsonObjectConverter<T>::dumpTo(jo, pod);
- return jo;
- }
- static T doLoad(const QJsonObject& jo)
- {
- T pod;
- JsonObjectConverter<T>::fillFrom(jo, pod);
- return pod;
- }
- static T load(const QJsonValue& jv) { return doLoad(jv.toObject()); }
- static T load(const QJsonDocument& jd) { return doLoad(jd.object()); }
- };
-
- template <typename T>
- inline auto toJson(const T& pod)
+ QJsonObject jo;
+ JsonObjectConverter<T>::dumpTo(jo, pod);
+ return jo;
+ }
+ static T doLoad(const QJsonObject& jo)
{
- return JsonConverter<T>::dump(pod);
+ T pod;
+ JsonObjectConverter<T>::fillFrom(jo, pod);
+ return pod;
}
+ static T load(const QJsonValue& jv) { return doLoad(jv.toObject()); }
+ static T load(const QJsonDocument& jd) { return doLoad(jd.object()); }
+};
- inline auto toJson(const QJsonObject& jo) { return jo; }
+template <typename T>
+inline auto toJson(const T& pod)
+{
+ return JsonConverter<T>::dump(pod);
+}
- template <typename T>
- inline void fillJson(QJsonObject& json, const T& data)
- {
- JsonObjectConverter<T>::dumpTo(json, data);
- }
+inline auto toJson(const QJsonObject& jo) { return jo; }
- template <typename T>
- inline T fromJson(const QJsonValue& jv)
- {
- return JsonConverter<T>::load(jv);
- }
+template <typename T>
+inline void fillJson(QJsonObject& json, const T& data)
+{
+ JsonObjectConverter<T>::dumpTo(json, data);
+}
- template <typename T>
- inline T fromJson(const QJsonDocument& jd)
- {
- return JsonConverter<T>::load(jd);
- }
+template <typename T>
+inline T fromJson(const QJsonValue& jv)
+{
+ return JsonConverter<T>::load(jv);
+}
+
+template <typename T>
+inline T fromJson(const QJsonDocument& jd)
+{
+ return JsonConverter<T>::load(jd);
+}
+
+// Convenience fromJson() overloads that deduce T instead of requiring
+// the coder to explicitly type it. They still enforce the
+// overwrite-everything semantics of fromJson(), unlike fillFromJson()
- // Convenience fromJson() overloads that deduce T instead of requiring
- // the coder to explicitly type it. They still enforce the
- // overwrite-everything semantics of fromJson(), unlike fillFromJson()
+template <typename T>
+inline void fromJson(const QJsonValue& jv, T& pod)
+{
+ pod = jv.isUndefined() ? T() : fromJson<T>(jv);
+}
- template <typename T>
- inline void fromJson(const QJsonValue& jv, T& pod)
+template <typename T>
+inline void fromJson(const QJsonDocument& jd, T& pod)
+{
+ pod = fromJson<T>(jd);
+}
+
+template <typename T>
+inline void fillFromJson(const QJsonValue& jv, T& pod)
+{
+ if (jv.isObject())
+ JsonObjectConverter<T>::fillFrom(jv.toObject(), pod);
+ else if (!jv.isUndefined())
+ pod = fromJson<T>(jv);
+}
+
+// JsonConverter<> specialisations
+
+template <typename T>
+struct TrivialJsonDumper {
+ // Works for: QJsonValue (and all things it can consume),
+ // QJsonObject, QJsonArray
+ static auto dump(const T& val) { return val; }
+};
+
+template <>
+struct JsonConverter<bool> : public TrivialJsonDumper<bool> {
+ static auto load(const QJsonValue& jv) { return jv.toBool(); }
+};
+
+template <>
+struct JsonConverter<int> : public TrivialJsonDumper<int> {
+ static auto load(const QJsonValue& jv) { return jv.toInt(); }
+};
+
+template <>
+struct JsonConverter<double> : public TrivialJsonDumper<double> {
+ static auto load(const QJsonValue& jv) { return jv.toDouble(); }
+};
+
+template <>
+struct JsonConverter<float> : public TrivialJsonDumper<float> {
+ static auto load(const QJsonValue& jv) { return float(jv.toDouble()); }
+};
+
+template <>
+struct JsonConverter<qint64> : public TrivialJsonDumper<qint64> {
+ static auto load(const QJsonValue& jv) { return qint64(jv.toDouble()); }
+};
+
+template <>
+struct JsonConverter<QString> : public TrivialJsonDumper<QString> {
+ static auto load(const QJsonValue& jv) { return jv.toString(); }
+};
+
+template <>
+struct JsonConverter<QDateTime> {
+ static auto dump(const QDateTime& val) = delete; // not provided yet
+ static auto load(const QJsonValue& jv)
{
- pod = jv.isUndefined() ? T() : fromJson<T>(jv);
+ return QDateTime::fromMSecsSinceEpoch(fromJson<qint64>(jv), Qt::UTC);
}
+};
- template <typename T>
- inline void fromJson(const QJsonDocument& jd, T& pod)
+template <>
+struct JsonConverter<QDate> {
+ static auto dump(const QDate& val) = delete; // not provided yet
+ static auto load(const QJsonValue& jv)
{
- pod = fromJson<T>(jd);
+ return fromJson<QDateTime>(jv).date();
}
+};
+
+template <>
+struct JsonConverter<QJsonArray> : public TrivialJsonDumper<QJsonArray> {
+ static auto load(const QJsonValue& jv) { return jv.toArray(); }
+};
- template <typename T>
- inline void fillFromJson(const QJsonValue& jv, T& pod)
+template <>
+struct JsonConverter<QByteArray> {
+ static QString dump(const QByteArray& ba) { return ba.constData(); }
+ static auto load(const QJsonValue& jv)
{
- if (jv.isObject())
- JsonObjectConverter<T>::fillFrom(jv.toObject(), pod);
- else if (!jv.isUndefined())
- pod = fromJson<T>(jv);
+ return fromJson<QString>(jv).toLatin1();
}
+};
- // JsonConverter<> specialisations
+template <>
+struct JsonConverter<QVariant> {
+ static QJsonValue dump(const QVariant& v);
+ static QVariant load(const QJsonValue& jv);
+};
- template <typename T>
- struct TrivialJsonDumper
+template <typename T>
+struct JsonConverter<Omittable<T>> {
+ static QJsonValue dump(const Omittable<T>& from)
{
- // Works for: QJsonValue (and all things it can consume),
- // QJsonObject, QJsonArray
- static auto dump(const T& val) { return val; }
- };
-
- template <> struct JsonConverter<bool> : public TrivialJsonDumper<bool>
+ return from.omitted() ? QJsonValue() : toJson(from.value());
+ }
+ static Omittable<T> load(const QJsonValue& jv)
{
- static auto load(const QJsonValue& jv) { return jv.toBool(); }
- };
+ if (jv.isUndefined())
+ return none;
+ return fromJson<T>(jv);
+ }
+};
- template <> struct JsonConverter<int> : public TrivialJsonDumper<int>
+template <typename VectorT, typename T = typename VectorT::value_type>
+struct JsonArrayConverter {
+ static void dumpTo(QJsonArray& ar, const VectorT& vals)
{
- static auto load(const QJsonValue& jv) { return jv.toInt(); }
- };
-
- template <> struct JsonConverter<double>
- : public TrivialJsonDumper<double>
+ for (const auto& v : vals)
+ ar.push_back(toJson(v));
+ }
+ static auto dump(const VectorT& vals)
{
- static auto load(const QJsonValue& jv) { return jv.toDouble(); }
- };
-
- template <> struct JsonConverter<float> : public TrivialJsonDumper<float>
+ QJsonArray ja;
+ dumpTo(ja, vals);
+ return ja;
+ }
+ static auto load(const QJsonArray& ja)
{
- static auto load(const QJsonValue& jv) { return float(jv.toDouble()); }
- };
+ VectorT vect;
+ vect.reserve(typename VectorT::size_type(ja.size()));
+ for (const auto& i : ja)
+ vect.push_back(fromJson<T>(i));
+ return vect;
+ }
+ static auto load(const QJsonValue& jv) { return load(jv.toArray()); }
+ static auto load(const QJsonDocument& jd) { return load(jd.array()); }
+};
- template <> struct JsonConverter<qint64>
- : public TrivialJsonDumper<qint64>
- {
- static auto load(const QJsonValue& jv) { return qint64(jv.toDouble()); }
- };
+template <typename T>
+struct JsonConverter<std::vector<T>>
+ : public JsonArrayConverter<std::vector<T>> {};
- template <> struct JsonConverter<QString>
- : public TrivialJsonDumper<QString>
- {
- static auto load(const QJsonValue& jv) { return jv.toString(); }
- };
+template <typename T>
+struct JsonConverter<QVector<T>> : public JsonArrayConverter<QVector<T>> {};
- template <> struct JsonConverter<QDateTime>
- {
- static auto dump(const QDateTime& val) = delete; // not provided yet
- static auto load(const QJsonValue& jv)
- {
- return QDateTime::fromMSecsSinceEpoch(
- fromJson<qint64>(jv), Qt::UTC);
- }
- };
+template <typename T>
+struct JsonConverter<QList<T>> : public JsonArrayConverter<QList<T>> {};
- template <> struct JsonConverter<QDate>
+template <>
+struct JsonConverter<QStringList> : public JsonConverter<QList<QString>> {
+ static auto dump(const QStringList& sl)
{
- static auto dump(const QDate& val) = delete; // not provided yet
- static auto load(const QJsonValue& jv)
- {
- return fromJson<QDateTime>(jv).date();
- }
- };
+ return QJsonArray::fromStringList(sl);
+ }
+};
- template <> struct JsonConverter<QJsonArray>
- : public TrivialJsonDumper<QJsonArray>
+template <>
+struct JsonObjectConverter<QSet<QString>> {
+ static void dumpTo(QJsonObject& json, const QSet<QString>& s)
{
- static auto load(const QJsonValue& jv) { return jv.toArray(); }
- };
-
- template <> struct JsonConverter<QByteArray>
+ for (const auto& e : s)
+ json.insert(toJson(e), QJsonObject {});
+ }
+ static auto fillFrom(const QJsonObject& json, QSet<QString>& s)
{
- static QString dump(const QByteArray& ba) { return ba.constData(); }
- static auto load(const QJsonValue& jv)
- {
- return fromJson<QString>(jv).toLatin1();
- }
- };
+ s.reserve(s.size() + json.size());
+ for (auto it = json.begin(); it != json.end(); ++it)
+ s.insert(it.key());
+ return s;
+ }
+};
- template <> struct JsonConverter<QVariant>
+template <typename HashMapT>
+struct HashMapFromJson {
+ static void dumpTo(QJsonObject& json, const HashMapT& hashMap)
{
- static QJsonValue dump(const QVariant& v);
- static QVariant load(const QJsonValue& jv);
- };
-
- template <typename T>
- struct JsonConverter<Omittable<T>>
+ for (auto it = hashMap.begin(); it != hashMap.end(); ++it)
+ json.insert(it.key(), toJson(it.value()));
+ }
+ static void fillFrom(const QJsonObject& jo, HashMapT& h)
{
- static QJsonValue dump(const Omittable<T>& from)
- {
- return from.omitted() ? QJsonValue() : toJson(from.value());
- }
- static Omittable<T> load(const QJsonValue& jv)
- {
- if (jv.isUndefined())
- return none;
- return fromJson<T>(jv);
- }
- };
+ h.reserve(jo.size());
+ for (auto it = jo.begin(); it != jo.end(); ++it)
+ h[it.key()] = fromJson<typename HashMapT::mapped_type>(it.value());
+ }
+};
- template <typename VectorT,
- typename T = typename VectorT::value_type>
- struct JsonArrayConverter
- {
- static void dumpTo(QJsonArray& ar, const VectorT& vals)
- {
- for (const auto& v: vals)
- ar.push_back(toJson(v));
- }
- static auto dump(const VectorT& vals)
- {
- QJsonArray ja;
- dumpTo(ja, vals);
- return ja;
- }
- static auto load(const QJsonArray& ja)
- {
- VectorT vect; vect.reserve(typename VectorT::size_type(ja.size()));
- for (const auto& i: ja)
- vect.push_back(fromJson<T>(i));
- return vect;
- }
- static auto load(const QJsonValue& jv) { return load(jv.toArray()); }
- static auto load(const QJsonDocument& jd) { return load(jd.array()); }
- };
+template <typename T>
+struct JsonObjectConverter<std::unordered_map<QString, T>>
+ : public HashMapFromJson<std::unordered_map<QString, T>> {};
- template <typename T> struct JsonConverter<std::vector<T>>
- : public JsonArrayConverter<std::vector<T>>
- { };
+template <typename T>
+struct JsonObjectConverter<QHash<QString, T>>
+ : public HashMapFromJson<QHash<QString, T>> {};
- template <typename T> struct JsonConverter<QVector<T>>
- : public JsonArrayConverter<QVector<T>>
- { };
+// We could use std::conditional<> below but QT_VERSION* macros in C++ code
+// cause (kinda valid but useless and noisy) compiler warnings about
+// bitwise operations on signed integers; so use the preprocessor for now.
+using variant_map_t =
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
+ QVariantHash;
+#else
+ QVariantMap;
+#endif
+template <>
+struct JsonConverter<variant_map_t> {
+ static QJsonObject dump(const variant_map_t& vh);
+ static QVariantHash load(const QJsonValue& jv);
+};
- template <typename T> struct JsonConverter<QList<T>>
- : public JsonArrayConverter<QList<T>>
- { };
+// Conditional insertion into a QJsonObject
- template <> struct JsonConverter<QStringList>
- : public JsonConverter<QList<QString>>
+namespace _impl {
+ template <typename ValT>
+ inline void addTo(QJsonObject& o, const QString& k, ValT&& v)
{
- static auto dump(const QStringList& sl)
- {
- return QJsonArray::fromStringList(sl);
- }
- };
+ o.insert(k, toJson(v));
+ }
- template <> struct JsonObjectConverter<QSet<QString>>
+ template <typename ValT>
+ inline void addTo(QUrlQuery& q, const QString& k, ValT&& v)
{
- static void dumpTo(QJsonObject& json, const QSet<QString>& s)
- {
- for (const auto& e: s)
- json.insert(toJson(e), QJsonObject{});
- }
- static auto fillFrom(const QJsonObject& json, QSet<QString>& s)
- {
- s.reserve(s.size() + json.size());
- for (auto it = json.begin(); it != json.end(); ++it)
- s.insert(it.key());
- return s;
- }
- };
+ q.addQueryItem(k, QStringLiteral("%1").arg(v));
+ }
- template <typename HashMapT>
- struct HashMapFromJson
+ // OpenAPI is entirely JSON-based, which means representing bools as
+ // textual true/false, rather than 1/0.
+ inline void addTo(QUrlQuery& q, const QString& k, bool v)
{
- static void dumpTo(QJsonObject& json, const HashMapT& hashMap)
- {
- for (auto it = hashMap.begin(); it != hashMap.end(); ++it)
- json.insert(it.key(), toJson(it.value()));
- }
- static void fillFrom(const QJsonObject& jo, HashMapT& h)
- {
- h.reserve(jo.size());
- for (auto it = jo.begin(); it != jo.end(); ++it)
- h[it.key()] =
- fromJson<typename HashMapT::mapped_type>(it.value());
- }
- };
-
- template <typename T>
- struct JsonObjectConverter<std::unordered_map<QString, T>>
- : public HashMapFromJson<std::unordered_map<QString, T>>
- { };
-
- template <typename T>
- struct JsonObjectConverter<QHash<QString, T>>
- : public HashMapFromJson<QHash<QString, T>>
- { };
+ q.addQueryItem(k, v ? QStringLiteral("true") : QStringLiteral("false"));
+ }
- // We could use std::conditional<> below but QT_VERSION* macros in C++ code
- // cause (kinda valid but useless and noisy) compiler warnings about
- // bitwise operations on signed integers; so use the preprocessor for now.
- using variant_map_t =
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
- QVariantHash;
-#else
- QVariantMap;
-#endif
- template <> struct JsonConverter<variant_map_t>
+ inline void addTo(QUrlQuery& q, const QString& k, const QStringList& vals)
{
- static QJsonObject dump(const variant_map_t& vh);
- static QVariantHash load(const QJsonValue& jv);
- };
-
- // Conditional insertion into a QJsonObject
+ for (const auto& v : vals)
+ q.addQueryItem(k, v);
+ }
- namespace _impl
+ inline void addTo(QUrlQuery& q, const QString&, const QJsonObject& vals)
{
- template <typename ValT>
- inline void addTo(QJsonObject& o, const QString& k, ValT&& v)
- { o.insert(k, toJson(v)); }
-
- template <typename ValT>
- inline void addTo(QUrlQuery& q, const QString& k, ValT&& v)
- { q.addQueryItem(k, QStringLiteral("%1").arg(v)); }
+ for (auto it = vals.begin(); it != vals.end(); ++it)
+ q.addQueryItem(it.key(), it.value().toString());
+ }
- // OpenAPI is entirely JSON-based, which means representing bools as
- // textual true/false, rather than 1/0.
- inline void addTo(QUrlQuery& q, const QString& k, bool v)
+ // This one is for types that don't have isEmpty() and for all types
+ // when Force is true
+ template <typename ValT, bool Force = true, typename = bool>
+ struct AddNode {
+ template <typename ContT, typename ForwardedT>
+ static void impl(ContT& container, const QString& key,
+ ForwardedT&& value)
{
- q.addQueryItem(k, v ? QStringLiteral("true")
- : QStringLiteral("false"));
+ addTo(container, key, std::forward<ForwardedT>(value));
}
+ };
- inline void addTo(QUrlQuery& q, const QString& k, const QStringList& vals)
+ // This one is for types that have isEmpty() when Force is false
+ template <typename ValT>
+ struct AddNode<ValT, false, decltype(std::declval<ValT>().isEmpty())> {
+ template <typename ContT, typename ForwardedT>
+ static void impl(ContT& container, const QString& key,
+ ForwardedT&& value)
{
- for (const auto& v: vals)
- q.addQueryItem(k, v);
+ if (!value.isEmpty())
+ addTo(container, key, std::forward<ForwardedT>(value));
}
+ };
- inline void addTo(QUrlQuery& q, const QString&, const QJsonObject& vals)
+ // This one unfolds Omittable<> (also only when Force is false)
+ template <typename ValT>
+ struct AddNode<Omittable<ValT>, false> {
+ template <typename ContT, typename OmittableT>
+ static void impl(ContT& container, const QString& key,
+ const OmittableT& value)
{
- for (auto it = vals.begin(); it != vals.end(); ++it)
- q.addQueryItem(it.key(), it.value().toString());
+ if (!value.omitted())
+ addTo(container, key, value.value());
}
-
- // This one is for types that don't have isEmpty() and for all types
- // when Force is true
- template <typename ValT, bool Force = true, typename = bool>
- struct AddNode
- {
- template <typename ContT, typename ForwardedT>
- static void impl(ContT& container, const QString& key,
- ForwardedT&& value)
- {
- addTo(container, key, std::forward<ForwardedT>(value));
- }
- };
-
- // This one is for types that have isEmpty() when Force is false
- template <typename ValT>
- struct AddNode<ValT, false,
- decltype(std::declval<ValT>().isEmpty())>
- {
- template <typename ContT, typename ForwardedT>
- static void impl(ContT& container, const QString& key,
- ForwardedT&& value)
- {
- if (!value.isEmpty())
- addTo(container, key, std::forward<ForwardedT>(value));
- }
- };
-
- // This one unfolds Omittable<> (also only when Force is false)
- template <typename ValT>
- struct AddNode<Omittable<ValT>, false>
- {
- template <typename ContT, typename OmittableT>
- static void impl(ContT& container,
- const QString& key, const OmittableT& value)
- {
- if (!value.omitted())
- addTo(container, key, value.value());
- }
- };
+ };
#if 0
// This is a special one that unfolds optional<>
@@ -426,37 +406,37 @@ namespace QMatrixClient
};
#endif
- } // namespace _impl
-
- static constexpr bool IfNotEmpty = false;
-
- /*! Add a key-value pair to QJsonObject or QUrlQuery
- *
- * Adds a key-value pair(s) specified by \p key and \p value to
- * \p container, optionally (in case IfNotEmpty is passed for the first
- * template parameter) taking into account the value "emptiness".
- * With IfNotEmpty, \p value is NOT added to the container if and only if:
- * - it has a method `isEmpty()` and `value.isEmpty() == true`, or
- * - it's an `Omittable<>` and `value.omitted() == true`.
- *
- * If \p container is a QUrlQuery, an attempt to fit \p value into it is
- * made as follows:
- * - if \p value is a QJsonObject, \p key is ignored and pairs from \p value
- * are copied to \p container, assuming that the value in each pair
- * is a string;
- * - if \p value is a QStringList, it is "exploded" into a list of key-value
- * pairs with key equal to \p key and value taken from each list item;
- * - if \p value is a bool, its OpenAPI (i.e. JSON) representation is added
- * to the query (`true` or `false`, respectively).
- *
- * \tparam Force add the pair even if the value is empty. This is true
- * by default; passing IfNotEmpty or false for this parameter
- * enables emptiness checks as described above
- */
- template <bool Force = true, typename ContT, typename ValT>
- inline void addParam(ContT& container, const QString& key, ValT&& value)
- {
- _impl::AddNode<std::decay_t<ValT>, Force>
- ::impl(container, key, std::forward<ValT>(value));
- }
-} // namespace QMatrixClient
+} // namespace _impl
+
+static constexpr bool IfNotEmpty = false;
+
+/*! Add a key-value pair to QJsonObject or QUrlQuery
+ *
+ * Adds a key-value pair(s) specified by \p key and \p value to
+ * \p container, optionally (in case IfNotEmpty is passed for the first
+ * template parameter) taking into account the value "emptiness".
+ * With IfNotEmpty, \p value is NOT added to the container if and only if:
+ * - it has a method `isEmpty()` and `value.isEmpty() == true`, or
+ * - it's an `Omittable<>` and `value.omitted() == true`.
+ *
+ * If \p container is a QUrlQuery, an attempt to fit \p value into it is
+ * made as follows:
+ * - if \p value is a QJsonObject, \p key is ignored and pairs from \p value
+ * are copied to \p container, assuming that the value in each pair
+ * is a string;
+ * - if \p value is a QStringList, it is "exploded" into a list of key-value
+ * pairs with key equal to \p key and value taken from each list item;
+ * - if \p value is a bool, its OpenAPI (i.e. JSON) representation is added
+ * to the query (`true` or `false`, respectively).
+ *
+ * \tparam Force add the pair even if the value is empty. This is true
+ * by default; passing IfNotEmpty or false for this parameter
+ * enables emptiness checks as described above
+ */
+template <bool Force = true, typename ContT, typename ValT>
+inline void addParam(ContT& container, const QString& key, ValT&& value)
+{
+ _impl::AddNode<std::decay_t<ValT>, Force>::impl(container, key,
+ std::forward<ValT>(value));
+}
+} // namespace QMatrixClient