QXmpp  Version: 1.15.1
XmlWriter.h
1 // SPDX-FileCopyrightText: 2025 Linus Jahn <lnj@kaidan.im>
2 //
3 // SPDX-License-Identifier: LGPL-2.1-or-later
4 
5 #ifndef XMLWRITER_H
6 #define XMLWRITER_H
7 
8 #include "QXmppUtils_p.h"
9 
10 #include "Enums.h"
11 #include "StringLiterals.h"
12 
13 #include <optional>
14 
15 #include <QDateTime>
16 #include <QMimeType>
17 #include <QUrl>
18 #include <QUuid>
19 #include <QXmlStreamWriter>
20 
21 namespace QXmpp::Private {
22 
23 class XmlWriter;
24 
25 #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
26 inline auto toString65(QStringView s) { return s.toString(); }
27 inline auto toString65(const QByteArray &s) { return QString::fromUtf8(s); }
28 inline const QString &toString65(const QString &s) { return s; }
29 inline QString toString65(QString &&s) { return std::move(s); }
30 #else
31 #define toString65(x) x
32 #endif
33 
34 template<typename T>
35 struct StringSerializer {
36  static decltype(auto) serialize(auto &&s) { return std::forward<decltype(s)>(s); }
37  static bool hasValue(auto &&s) { return !s.isEmpty(); }
38 };
39 
40 template<typename T>
41 concept IsOptionalStringSerializable = requires(const T &value) {
42  { StringSerializer<T>::hasValue(value) } -> std::convertible_to<bool>;
43 };
44 
45 template<typename U>
46 struct StringSerializer<std::optional<U>> {
47  static auto serialize(auto &&value)
48  {
49  if (value) {
50  return StringSerializer<U>::serialize(*value);
51  }
52  return std::remove_cvref_t<decltype(StringSerializer<U>::serialize(*value))> {};
53  }
54  static bool hasValue(const auto &value) { return value.has_value(); }
55 };
56 
57 template<>
58 struct StringSerializer<bool> {
59  static auto serialize(bool value) { return value ? u"true"_s : u"false"_s; }
60  static bool hasValue(bool) { return true; }
61 };
62 
63 template<typename Number>
64  requires std::is_integral_v<Number> || std::is_floating_point_v<Number>
65 struct StringSerializer<Number> {
66  static auto serialize(Number value) { return QString::number(value); }
67 };
68 
69 template<typename Enum>
70  requires std::is_enum_v<Enum>
71 struct StringSerializer<Enum> {
72  static auto serialize(Enum value) { return Enums::toString(value); }
73  static bool hasValue(Enum value)
74  requires Enums::NullableEnum<Enum>
75  {
76  return value != Enums::Data<Enum>::NullValue;
77  }
78 };
79 
80 template<>
81 struct StringSerializer<QDateTime> {
82  static QString serialize(const QDateTime &datetime);
83  static bool hasValue(const QDateTime &datetime) { return datetime.isValid(); }
84 };
85 
86 template<>
87 struct StringSerializer<QUuid> {
88  static auto serialize(QUuid uuid) { return uuid.toString(QUuid::WithoutBraces); }
89  static bool hasValue(QUuid uuid) { return !uuid.isNull(); }
90 };
91 
92 template<>
93 struct StringSerializer<QUrl> {
94  static auto serialize(const QUrl &url) { return url.toString(); }
95  static bool hasValue(const QUrl &url) { return !url.isEmpty(); }
96 };
97 
98 template<>
99 struct StringSerializer<QMimeType> {
100  static auto serialize(const QMimeType &mimeType) { return mimeType.name(); }
101  static bool hasValue(const QMimeType &mimeType) { return mimeType.isValid(); }
102 };
103 
104 struct Base64 {
105  const QByteArray &data;
106 
107  static Base64 fromByteArray(const QByteArray &d) { return { d }; }
108 };
109 
110 template<>
111 struct StringSerializer<Base64> {
112  static auto serialize(Base64 value) { return value.data.toBase64(); }
113  static bool hasValue(Base64 value) { return !value.data.isEmpty(); }
114 };
115 
116 struct DefaultedBool {
117  bool value;
118  bool defaultValue;
119 };
120 
121 template<>
122 struct 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; }
125 };
126 
127 // Serializes value to string and converts to type writeable to QXmlStreamWriter
128 template<typename T>
129 decltype(auto) xmlS(T &&value)
130 {
131  return toString65(StringSerializer<std::decay_t<T>>::serialize(std::forward<T>(value)));
132 }
133 
134 template<typename T>
135 concept XmlSerializeable = XmlWriterSerializeable<T> || QXmlStreamSerializeable<T>;
136 
137 template<typename T>
138 concept XmlSerializeableRange =
139  std::ranges::range<T> && XmlSerializeable<std::ranges::range_value_t<T>>;
140 
141 class XmlWriter
142 {
143 public:
144  explicit XmlWriter(QXmlStreamWriter *writer) noexcept : w(writer) { }
145  operator QXmlStreamWriter *() const noexcept { return w; }
146  QXmlStreamWriter *writer() const noexcept { return w; }
147 
148  template<XmlSerializeable T>
149  void write(T &&value)
150  {
151  value.toXml(*this);
152  }
153  template<IsStdOptional T>
154  void write(T &&opt)
155  {
156  if (opt) {
157  write(*opt);
158  }
159  }
160  template<typename Container>
161  requires(XmlSerializeableRange<Container> && !XmlSerializeable<Container>)
162  void write(Container &&container)
163  {
164  for (const auto &value : container) {
165  write(value);
166  }
167  }
168  template<std::invocable Function>
169  void write(Function &&f)
170  {
171  f();
172  }
173 
174 private:
175 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
176  using String = QAnyStringView;
177 #else
178  using String = const QString &;
179 #endif
180 
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);
189 
190  template<typename Tag, typename... Values>
191  friend struct Element;
192 
193  template<IsOptionalStringSerializable Enum>
194  friend struct OptionalEnumElement;
195 
196  template<typename Tag, typename Value>
197  friend struct TextElement;
198 
199  template<typename Tag, IsOptionalStringSerializable Value>
200  friend struct OptionalTextElement;
201 
202  template<std::ranges::range Container>
203  friend struct SingleAttributeElements;
204 
205  QXmlStreamWriter *w;
206 };
207 
208 template<typename Tag, typename... Values>
209 struct Element {
210  Tag tag;
211  std::tuple<Values...> values;
212 
213  template<typename... V>
214  Element(Tag tag, V &&...values)
215  : tag(std::forward<Tag>(tag)), values(std::forward<V>(values)...)
216  {
217  }
218 
219  void toXml(XmlWriter &w)
220  {
221  if constexpr (sizeof...(Values) == 0) {
222  if constexpr (IsXmlTag<Tag>) {
223  auto &[name, xmlns] = tag;
224  w.writeEmptyElement(xmlS(name), xmlS(xmlns));
225  } else {
226  w.writeEmptyElement(xmlS(tag));
227  }
228  } else {
229  if constexpr (IsXmlTag<Tag>) {
230  auto &[name, xmlns] = tag;
231  w.writeStartElement(xmlS(name), xmlS(xmlns));
232  } else {
233  w.writeStartElement(xmlS(tag));
234  }
235  std::apply([&w](auto &&...value) { (w.write(value), ...); }, values);
236  w.writeEndElement();
237  }
238  }
239 };
240 
241 template<typename Name, typename... Values>
242  requires(!IsXmlTag<Name>)
243 Element(Name &&, Values &&...) -> Element<Name, Values...>;
244 
245 template<IsXmlTag Tag = Tag<QStringView, QStringView>, typename... Values>
246 Element(Tag &&, Values &&...) -> Element<Tag, Values...>;
247 
248 template<typename Value>
249 struct Attribute {
250  QStringView name;
251  Value value;
252 
253  void toXml(XmlWriter &w) const
254  {
255  w.writer()->writeAttribute(xmlS(name), xmlS(value));
256  }
257 };
258 
259 template<typename Value>
260 Attribute(QStringView, Value &&) -> Attribute<Value>;
261 
262 template<IsOptionalStringSerializable Value>
263 struct OptionalAttribute {
264  QStringView name;
265  Value value;
266 
267  void toXml(QXmlStreamWriter *w) const
268  {
269  if (StringSerializer<std::decay_t<Value>>::hasValue(value)) {
270  w->writeAttribute(xmlS(name), xmlS(value));
271  }
272  }
273 };
274 
275 template<typename Value>
276 OptionalAttribute(QStringView, Value &&) -> OptionalAttribute<Value>;
277 
278 template<typename Value>
279 struct Characters {
280  Value value;
281 
282  template<typename V>
283  Characters(V &&value) : value(std::forward<V>(value)) { }
284 
285  void toXml(QXmlStreamWriter *w) const
286  {
287  w->writeCharacters(xmlS(value));
288  }
289 };
290 
291 template<typename Value>
292 Characters(Value &&) -> Characters<Value>;
293 
294 template<IsOptionalStringSerializable Value>
295 struct OptionalCharacters {
296  Value value;
297 
298  template<typename V>
299  OptionalCharacters(V &&value) : value(std::forward<V>(value)) { }
300 
301  void toXml(QXmlStreamWriter *w) const
302  {
303  if (StringSerializer<std::decay_t<Value>>::hasValue(value)) {
304  w->writeCharacters(xmlS(value));
305  }
306  }
307 };
308 
309 template<typename T>
310 OptionalCharacters(T &&) -> OptionalCharacters<T>;
311 
312 struct DefaultNamespace {
313  QStringView xmlns;
314 
315  void toXml(QXmlStreamWriter *w) const
316  {
317  w->writeDefaultNamespace(xmlS(xmlns));
318  }
319 };
320 
321 struct Namespace {
322  QStringView prefix;
323  QStringView xmlns;
324 
325  void toXml(QXmlStreamWriter *w) const
326  {
327  w->writeNamespace(xmlS(xmlns), xmlS(prefix));
328  }
329 };
330 
331 template<IsOptionalStringSerializable Enum>
332 struct OptionalEnumElement {
333  Enum enumeration;
334  QStringView xmlns = {};
335 
336  void toXml(XmlWriter &w) const
337  {
338  if (StringSerializer<Enum>::hasValue(enumeration)) {
339  if (xmlns.isNull()) {
340  w.writeEmptyElement(xmlS(enumeration));
341  } else {
342  w.writeEmptyElement(xmlS(enumeration), xmlS(xmlns));
343  }
344  }
345  }
346 };
347 
348 template<IsOptionalStringSerializable Enum>
349 OptionalEnumElement(Enum) -> OptionalEnumElement<Enum>;
350 template<IsOptionalStringSerializable Enum, std::convertible_to<QStringView> StringView>
351 OptionalEnumElement(Enum, StringView) -> OptionalEnumElement<Enum>;
352 
353 template<typename Tag, typename Value>
354 struct TextElement {
355  Tag tag;
356  Value value;
357 
358  template<typename V>
359  TextElement(Tag tag, V &&value) : tag(tag), value(std::forward<V>(value)) { }
360 
361  void toXml(XmlWriter &w)
362  {
363  if constexpr (IsXmlTag<Tag>) {
364  auto &[name, xmlns] = tag;
365  w.writeTextOrEmptyElement(xmlS(name), xmlS(xmlns), xmlS(value));
366  } else {
367  w.writeTextOrEmptyElement(xmlS(tag), xmlS(value));
368  }
369  }
370 };
371 
372 template<typename Name, typename Value>
373  requires(!IsXmlTag<Name>)
374 TextElement(Name &&, Value &&) -> TextElement<Name, Value>;
375 
376 template<IsXmlTag Tag = Tag<QStringView, QStringView>, typename Value>
377 TextElement(Tag &&, Value &&) -> TextElement<Tag, Value>;
378 
379 template<typename Tag, std::ranges::range Range>
380 struct TextElements {
381  Tag tag;
382  Range values;
383 
384  template<typename R>
385  TextElements(Tag tag, R &&values) : tag(tag), values(std::forward<R>(values)) { }
386 
387  void toXml(XmlWriter &w)
388  {
389  for (const auto &value : values) {
390  w.write(TextElement { tag, value });
391  }
392  }
393 };
394 
395 template<typename Name, typename Range>
396  requires(!IsXmlTag<Name>)
397 TextElements(Name &&, Range &&) -> TextElements<Name, Range>;
398 
399 template<IsXmlTag Tag = Tag<QStringView, QStringView>, typename Range>
400 TextElements(Tag &&, Range &&) -> TextElements<Tag, Range>;
401 
402 template<typename Tag, IsOptionalStringSerializable Value>
403 struct OptionalTextElement {
404  Tag tag;
405  Value value;
406 
407  template<typename V>
408  OptionalTextElement(Tag tag, V &&value) : tag(tag), value(std::forward<V>(value)) { }
409 
410  void toXml(XmlWriter &w)
411  {
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));
416  } else {
417  w.writeTextOrEmptyElement(xmlS(tag), xmlS(value));
418  }
419  }
420  }
421 };
422 
423 template<typename Name, typename Value>
424  requires(!IsXmlTag<Name>)
425 OptionalTextElement(Name &&, Value &&) -> OptionalTextElement<Name, Value>;
426 
427 template<IsXmlTag Tag = Tag<QStringView, QStringView>, typename Value>
428 OptionalTextElement(Tag, Value &&) -> OptionalTextElement<Tag, Value>;
429 
430 template<std::ranges::range Range>
431 struct SingleAttributeElements {
432  QStringView name;
433  QStringView attribute;
434  Range values;
435 
436  template<typename R>
437  SingleAttributeElements(QStringView name, QStringView attribute, R &&values)
438  : name(name), attribute(attribute), values(std::forward<R>(values)) { }
439 
440  void toXml(XmlWriter &w)
441  {
442  for (const auto &value : values) {
443  w.writeSingleAttributeElement(xmlS(name), xmlS(attribute), xmlS(value));
444  }
445  }
446 };
447 
448 template<typename Range>
449 SingleAttributeElements(QStringView, QStringView, Range &&) -> SingleAttributeElements<Range>;
450 
451 template<typename... Values>
452 struct OptionalContent {
453  bool condition;
454  std::tuple<Values...> values;
455 
456  template<std::convertible_to<bool> Condition, typename... V>
457  OptionalContent(Condition condition, V &&...values)
458  : condition(condition), values(std::forward<V>(values)...) { }
459 
460  void toXml(XmlWriter &w)
461  {
462  if (condition) {
463  std::apply([&w](auto &&...value) { (w.write(value), ...); }, values);
464  }
465  }
466 };
467 
468 template<std::convertible_to<bool> Condition, typename... Values>
469 OptionalContent(Condition, Values &&...) -> OptionalContent<Values...>;
470 
471 } // namespace QXmpp::Private
472 
473 #endif // XMLWRITER_H
Definition: QXmppTask.h:646
bool hasValue(const Result< T > &r)
Definition: QXmppGlobal.h:216
Definition: Algorithms.h:14