8#include "QXmppUtils_p.h"
11#include "StringLiterals.h"
19#include <QXmlStreamWriter>
21namespace QXmpp::Private {
25#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
26inline auto toString65(QStringView s) {
return s.toString(); }
27inline auto toString65(
const QByteArray &s) {
return QString::fromUtf8(s); }
28inline const QString &toString65(
const QString &s) {
return s; }
29inline QString toString65(QString &&s) {
return std::move(s); }
31#define toString65(x) x
35struct StringSerializer {
36 static decltype(
auto) serialize(
auto &&s) {
return std::forward<decltype(s)>(s); }
37 static bool hasValue(
auto &&s) {
return !s.isEmpty(); }
41concept IsOptionalStringSerializable =
requires(
const T &value) {
42 { StringSerializer<T>::hasValue(value) } -> std::convertible_to<bool>;
46struct StringSerializer<std::optional<U>> {
47 static auto serialize(
auto &&value)
50 return StringSerializer<U>::serialize(*value);
52 return std::remove_cvref_t<
decltype(StringSerializer<U>::serialize(*value))> {};
54 static bool hasValue(
const auto &value) {
return value.has_value(); }
58struct StringSerializer<bool> {
59 static auto serialize(
bool value) {
return value ? u
"true"_s : u
"false"_s; }
60 static bool hasValue(
bool) {
return true; }
63template<
typename Number>
64 requires std::is_integral_v<Number> || std::is_floating_point_v<Number>
65struct StringSerializer<Number> {
66 static auto serialize(Number value) {
return QString::number(value); }
69template<
typename Enum>
70 requires std::is_enum_v<Enum>
71struct StringSerializer<Enum> {
72 static auto serialize(Enum value) {
return Enums::toString(value); }
73 static bool hasValue(Enum value)
74 requires Enums::NullableEnum<Enum>
76 return value != Enums::Data<Enum>::NullValue;
81struct StringSerializer<QDateTime> {
82 static QString serialize(
const QDateTime &datetime);
83 static bool hasValue(
const QDateTime &datetime) {
return datetime.isValid(); }
87struct StringSerializer<QUuid> {
88 static auto serialize(QUuid uuid) {
return uuid.toString(QUuid::WithoutBraces); }
89 static bool hasValue(QUuid uuid) {
return !uuid.isNull(); }
93struct StringSerializer<QUrl> {
94 static auto serialize(
const QUrl &url) {
return url.toString(); }
95 static bool hasValue(
const QUrl &url) {
return !url.isEmpty(); }
99struct StringSerializer<QMimeType> {
100 static auto serialize(
const QMimeType &mimeType) {
return mimeType.name(); }
101 static bool hasValue(
const QMimeType &mimeType) {
return mimeType.isValid(); }
105 const QByteArray &data;
107 static Base64 fromByteArray(
const QByteArray &d) {
return { d }; }
111struct StringSerializer<Base64> {
112 static auto serialize(Base64 value) {
return value.data.toBase64(); }
113 static bool hasValue(Base64 value) {
return !value.data.isEmpty(); }
116struct DefaultedBool {
122struct StringSerializer<DefaultedBool> {
123 static auto serialize(
auto &&v) {
return v.value ? u
"true"_s : u
"false"_s; }
124 static bool hasValue(
auto &&v) {
return v.value != v.defaultValue; }
129decltype(
auto) xmlS(T &&value)
131 return toString65(StringSerializer<std::decay_t<T>>::serialize(std::forward<T>(value)));
135concept XmlSerializeable = XmlWriterSerializeable<T> || QXmlStreamSerializeable<T>;
138concept XmlSerializeableRange =
139 std::ranges::range<T> && XmlSerializeable<std::ranges::range_value_t<T>>;
144 explicit XmlWriter(QXmlStreamWriter *writer) noexcept : w(writer) { }
145 operator QXmlStreamWriter *()
const noexcept {
return w; }
146 QXmlStreamWriter *writer() const noexcept {
return w; }
148 template<XmlSerializeable T>
149 void write(T &&value)
153 template<IsStdOptional T>
160 template<
typename Container>
161 requires(XmlSerializeableRange<Container> && !XmlSerializeable<Container>)
162 void write(Container &&container)
164 for (
const auto &value : container) {
168 template<std::invocable Function>
169 void write(Function &&f)
175#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
176 using String = QAnyStringView;
178 using String =
const QString &;
181 void writeStartElement(String name) { w->writeStartElement(name); }
182 QXMPP_PRIVATE_EXPORT
void writeStartElement(String name, String xmlns);
183 void writeEndElement() { w->writeEndElement(); }
184 void writeEmptyElement(String name) { w->writeEmptyElement(name); }
185 QXMPP_PRIVATE_EXPORT
void writeEmptyElement(String name, String xmlns);
186 QXMPP_PRIVATE_EXPORT
void writeTextOrEmptyElement(String name, String value);
187 QXMPP_PRIVATE_EXPORT
void writeTextOrEmptyElement(String name, String xmlns, String value);
188 QXMPP_PRIVATE_EXPORT
void writeSingleAttributeElement(String name, String attribute, String value);
190 template<
typename Tag,
typename... Values>
191 friend struct Element;
193 template<IsOptionalStringSerializable Enum>
194 friend struct OptionalEnumElement;
196 template<
typename Tag,
typename Value>
197 friend struct TextElement;
199 template<
typename Tag, IsOptionalStringSerializable Value>
200 friend struct OptionalTextElement;
202 template<std::ranges::range Container>
203 friend struct SingleAttributeElements;
208template<
typename Tag,
typename... Values>
211 std::tuple<Values...> values;
213 template<
typename... V>
214 Element(Tag tag, V &&...values)
215 : tag(std::forward<Tag>(tag)), values(std::forward<V>(values)...)
219 void toXml(XmlWriter &w)
221 if constexpr (
sizeof...(Values) == 0) {
222 if constexpr (IsXmlTag<Tag>) {
223 auto &[name, xmlns] = tag;
224 w.writeEmptyElement(xmlS(name), xmlS(xmlns));
226 w.writeEmptyElement(xmlS(tag));
229 if constexpr (IsXmlTag<Tag>) {
230 auto &[name, xmlns] = tag;
231 w.writeStartElement(xmlS(name), xmlS(xmlns));
233 w.writeStartElement(xmlS(tag));
235 std::apply([&w](
auto &&...value) { (w.write(value), ...); }, values);
241template<
typename Name,
typename... Values>
242 requires(!IsXmlTag<Name>)
243Element(Name &&, Values &&...) -> Element<Name, Values...>;
245template<IsXmlTag Tag = Tag<QStringView, QStringView>,
typename... Values>
246Element(Tag &&, Values &&...) -> Element<Tag, Values...>;
248template<
typename Value>
253 void toXml(XmlWriter &w)
const
255 w.writer()->writeAttribute(xmlS(name), xmlS(value));
259template<
typename Value>
260Attribute(QStringView, Value &&) -> Attribute<Value>;
262template<IsOptionalStringSerializable Value>
263struct OptionalAttribute {
267 void toXml(QXmlStreamWriter *w)
const
269 if (StringSerializer<std::decay_t<Value>>::hasValue(value)) {
270 w->writeAttribute(xmlS(name), xmlS(value));
275template<
typename Value>
276OptionalAttribute(QStringView, Value &&) -> OptionalAttribute<Value>;
278template<
typename Value>
283 Characters(V &&value) : value(std::forward<V>(value)) { }
285 void toXml(QXmlStreamWriter *w)
const
287 w->writeCharacters(xmlS(value));
291template<
typename Value>
292Characters(Value &&) -> Characters<Value>;
294template<IsOptionalStringSerializable Value>
295struct OptionalCharacters {
299 OptionalCharacters(V &&value) : value(std::forward<V>(value)) { }
301 void toXml(QXmlStreamWriter *w)
const
303 if (StringSerializer<std::decay_t<Value>>::hasValue(value)) {
304 w->writeCharacters(xmlS(value));
310OptionalCharacters(T &&) -> OptionalCharacters<T>;
312struct DefaultNamespace {
315 void toXml(QXmlStreamWriter *w)
const
317 w->writeDefaultNamespace(xmlS(xmlns));
325 void toXml(QXmlStreamWriter *w)
const
327 w->writeNamespace(xmlS(xmlns), xmlS(prefix));
331template<IsOptionalStringSerializable Enum>
332struct OptionalEnumElement {
334 QStringView xmlns = {};
336 void toXml(XmlWriter &w)
const
338 if (StringSerializer<Enum>::hasValue(enumeration)) {
339 if (xmlns.isNull()) {
340 w.writeEmptyElement(xmlS(enumeration));
342 w.writeEmptyElement(xmlS(enumeration), xmlS(xmlns));
348template<IsOptionalStringSerializable Enum>
349OptionalEnumElement(Enum) -> OptionalEnumElement<Enum>;
350template<IsOptionalStringSerializable Enum, std::convertible_to<QStringView> StringView>
351OptionalEnumElement(Enum, StringView) -> OptionalEnumElement<Enum>;
353template<
typename Tag,
typename Value>
359 TextElement(Tag tag, V &&value) : tag(tag), value(std::forward<V>(value)) { }
361 void toXml(XmlWriter &w)
363 if constexpr (IsXmlTag<Tag>) {
364 auto &[name, xmlns] = tag;
365 w.writeTextOrEmptyElement(xmlS(name), xmlS(xmlns), xmlS(value));
367 w.writeTextOrEmptyElement(xmlS(tag), xmlS(value));
372template<
typename Name,
typename Value>
373 requires(!IsXmlTag<Name>)
374TextElement(Name &&, Value &&) -> TextElement<Name, Value>;
376template<IsXmlTag Tag = Tag<QStringView, QStringView>,
typename Value>
377TextElement(Tag &&, Value &&) -> TextElement<Tag, Value>;
379template<
typename Tag, std::ranges::range Range>
385 TextElements(Tag tag, R &&values) : tag(tag), values(std::forward<R>(values)) { }
387 void toXml(XmlWriter &w)
389 for (
const auto &value : values) {
390 w.write(TextElement { tag, value });
395template<
typename Name,
typename Range>
396 requires(!IsXmlTag<Name>)
397TextElements(Name &&, Range &&) -> TextElements<Name, Range>;
399template<IsXmlTag Tag = Tag<QStringView, QStringView>,
typename Range>
400TextElements(Tag &&, Range &&) -> TextElements<Tag, Range>;
402template<
typename Tag, IsOptionalStringSerializable Value>
403struct OptionalTextElement {
408 OptionalTextElement(Tag tag, V &&value) : tag(tag), value(std::forward<V>(value)) { }
410 void toXml(XmlWriter &w)
412 if (StringSerializer<std::decay_t<Value>>::hasValue(value)) {
413 if constexpr (IsXmlTag<Tag>) {
414 auto &[name, xmlns] = tag;
415 w.writeTextOrEmptyElement(xmlS(name), xmlS(xmlns), xmlS(value));
417 w.writeTextOrEmptyElement(xmlS(tag), xmlS(value));
423template<
typename Name,
typename Value>
424 requires(!IsXmlTag<Name>)
425OptionalTextElement(Name &&, Value &&) -> OptionalTextElement<Name, Value>;
427template<IsXmlTag Tag = Tag<QStringView, QStringView>,
typename Value>
428OptionalTextElement(Tag, Value &&) -> OptionalTextElement<Tag, Value>;
430template<std::ranges::range Range>
431struct SingleAttributeElements {
433 QStringView attribute;
437 SingleAttributeElements(QStringView name, QStringView attribute, R &&values)
438 : name(name), attribute(attribute), values(std::forward<R>(values)) { }
440 void toXml(XmlWriter &w)
442 for (
const auto &value : values) {
443 w.writeSingleAttributeElement(xmlS(name), xmlS(attribute), xmlS(value));
448template<
typename Range>
449SingleAttributeElements(QStringView, QStringView, Range &&) -> SingleAttributeElements<Range>;
451template<
typename... Values>
452struct OptionalContent {
454 std::tuple<Values...> values;
456 template<std::convertible_to<
bool> Condition,
typename... V>
457 OptionalContent(Condition condition, V &&...values)
458 : condition(condition), values(std::forward<V>(values)...) { }
460 void toXml(XmlWriter &w)
463 std::apply([&w](
auto &&...value) { (w.write(value), ...); }, values);
468template<std::convertible_to<
bool> Condition,
typename... Values>
469OptionalContent(Condition, Values &&...) -> OptionalContent<Values...>;