Loading...
Searching...
No Matches
QXmppUtils_p.h
// SPDX-FileCopyrightText: 2022 Linus Jahn <lnj@kaidan.im>
// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de>
//
// SPDX-License-Identifier: LGPL-2.1-or-later
#ifndef QXMPPUTILS_P_H
#define QXMPPUTILS_P_H
#include "QXmppGlobal.h"
#include "QXmppXmlTags_p.h"
#include "Algorithms.h"
#include <functional>
#include <optional>
#include <stdint.h>
#include <type_traits>
#include <QByteArray>
#include <QDomElement>
class QDomElement;
class QXmlStreamWriter;
class QXmppNonza;
namespace QXmpp::Private {
class XmlWriter;
// Helper for Q(Any)StringView overloads that were added later
inline auto toString60(QStringView s)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
return s;
#else
return s.toString();
#endif
}
template<typename T>
concept IsStdOptional = requires {
typename std::remove_cvref_t<T>::value_type;
requires std::same_as<std::remove_cvref_t<T>, std::optional<typename std::remove_cvref_t<T>::value_type>>;
};
// sequential stanza IDs
QXMPP_PRIVATE_EXPORT extern QAtomicInt globalStanzaIdCounter;
// Base64
std::optional<QByteArray> parseBase64(const QString &);
inline QString serializeBase64(const QByteArray &data) { return QString::fromUtf8(data.toBase64()); }
// Integer parsing
template<typename Int = int>
std::optional<Int> parseInt(QStringView str);
template<typename Int>
inline QString serializeInt(Int value) { return QString::number(value); }
// Double parsing
std::optional<double> parseDouble(QStringView str);
std::optional<float> parseFloat(QStringView str);
// Booleans
std::optional<bool> parseBoolean(const QString &str);
QString serializeBoolean(bool);
//
// DOM
//
QXMPP_EXPORT QDomElement firstChildElement(const QDomElement &, QStringView tagName = {}, QStringView xmlNs = {});
QXMPP_EXPORT QDomElement nextSiblingElement(const QDomElement &, QStringView tagName = {}, QStringView xmlNs = {});
template<typename T>
inline auto firstChildElement(const QDomElement &el)
requires HasXmlTag<T>
{
auto [tag, ns] = T::XmlTag;
return firstChildElement(el, tag, ns);
}
template<typename T>
inline auto nextSiblingElement(const QDomElement &el)
requires HasXmlTag<T>
{
auto [tag, ns] = T::XmlTag;
return nextSiblingElement(el, tag, ns);
}
inline auto hasChild(const QDomElement &el, QStringView tagName = {}, QStringView xmlns = {})
{
return !firstChildElement(el, tagName, xmlns).isNull();
}
template<typename T>
inline auto hasChild(const QDomElement &el)
requires HasXmlTag<T>
{
return !firstChildElement<T>(el).isNull();
}
struct DomChildElements {
QDomElement parent;
QStringView tagName;
QStringView namespaceUri;
struct EndIterator { };
struct Iterator {
Iterator operator++()
{
el = nextSiblingElement(el, tagName, namespaceUri);
return *this;
}
bool operator!=(EndIterator) const { return !el.isNull(); }
const QDomElement &operator*() const { return el; }
QDomElement el;
QStringView tagName;
QStringView namespaceUri;
};
Iterator begin() const { return { firstChildElement(parent, tagName, namespaceUri), tagName, namespaceUri }; }
EndIterator end() const { return {}; }
};
inline DomChildElements iterChildElements(const QDomElement &el, QStringView tagName = {}, QStringView namespaceUri = {}) { return DomChildElements { el, tagName, namespaceUri }; }
template<typename T>
inline auto iterChildElements(const QDomElement &el)
requires HasXmlTag<T>
{
auto [tag, ns] = T::XmlTag;
return iterChildElements(el, tag, ns);
}
template<typename T>
concept DomParsableV1 = requires(T t) {
{ t.parse(QDomElement()) } -> std::same_as<void>;
};
template<typename T>
concept DomParsableV2 = requires(T t) {
{ t.parse(QDomElement()) } -> std::same_as<bool>;
};
template<typename T>
concept DomParsableV3 = requires {
{ T::fromDom(QDomElement()) } -> std::same_as<std::optional<T>>;
};
template<typename T>
concept DomParsable = DomParsableV1<T> || DomParsableV2<T> || DomParsableV3<T>;
// Parse T from a QDomElement.
template<typename T>
auto parseElement(const QDomElement &el) -> std::optional<T>
requires DomParsable<T>
{
if constexpr (DomParsableV3<T>) {
return T::fromDom(el);
} else if constexpr (DomParsableV2<T>) {
T element;
if (!element.parse(el)) {
return {};
}
return element;
} else if constexpr (DomParsableV1<T>) {
T element;
element.parse(el);
return element;
}
}
// Parse T with T::parse() if DOM element is not null (no namespace check).
template<typename T>
auto parseOptionalElement(const QDomElement &domEl) -> std::optional<T>
{
if (domEl.isNull()) {
return {};
}
return parseElement<T>(domEl);
}
// Parse T with T::parse() with first child element with the correct tag name and namespace.
template<typename T>
auto parseOptionalChildElement(const QDomElement &parentEl, QStringView tagName, QStringView xmlns)
requires(!HasXmlTag<T>)
{
return parseOptionalElement<T>(firstChildElement(parentEl, tagName, xmlns));
}
template<typename T>
auto parseOptionalChildElement(const QDomElement &parentEl)
requires HasXmlTag<T>
{
auto [tag, ns] = T::XmlTag;
return parseOptionalElement<T>(firstChildElement(parentEl, tag, ns));
}
// Parse all child elements matching the tag name and namespace into a container.
template<typename Container>
auto parseChildElements(const QDomElement &parentEl, QStringView tagName, QStringView xmlns)
-> Container
requires(!HasXmlTag<typename Container::value_type>)
{
using T = typename Container::value_type;
Container elements;
for (const auto &childEl : iterChildElements(parentEl, tagName, xmlns)) {
if (auto parsedEl = parseElement<T>(childEl)) {
elements.push_back(std::move(*parsedEl));
}
}
return elements;
}
template<typename Container>
auto parseChildElements(const QDomElement &parentEl) -> Container
requires HasXmlTag<typename Container::value_type>
{
using T = typename Container::value_type;
auto [tag, ns] = T::XmlTag;
Container elements;
for (const auto &childEl : iterChildElements(parentEl, tag, ns)) {
if (auto parsedEl = parseElement<T>(childEl)) {
elements.push_back(std::move(*parsedEl));
}
}
return elements;
}
template<typename Container = QList<QString>>
auto parseTextElements(const QDomElement &parent, QStringView tagName, QStringView xmlns)
-> Container
{
return transform<Container>(iterChildElements(parent, tagName, xmlns), &QDomElement::text);
}
template<typename Container = QList<QString>>
auto parseSingleAttributeElements(const QDomElement &parent, QStringView tagName, QStringView xmlns, const QString &attribute)
{
return transform<Container>(iterChildElements(parent, tagName, xmlns), [=](const QDomElement &el) {
return el.attribute(attribute);
});
}
QByteArray serializeXmlWriter(std::function<void(XmlWriter &)>);
QByteArray serializeQXmlStream(std::function<void(QXmlStreamWriter *)>);
template<typename T>
concept XmlWriterSerializeable =
std::same_as<
decltype(static_cast<void (std::remove_cvref_t<T>::*)(XmlWriter &)>(&std::remove_cvref_t<T>::toXml)),
void (std::remove_cvref_t<T>::*)(XmlWriter &)> ||
std::same_as<
decltype(static_cast<void (std::remove_cvref_t<T>::*)(XmlWriter &) const>(&std::remove_cvref_t<T>::toXml)),
void (std::remove_cvref_t<T>::*)(XmlWriter &) const>;
template<typename T>
concept QXmlStreamSerializeable =
std::same_as<
decltype(static_cast<void (std::remove_cvref_t<T>::*)(QXmlStreamWriter *) const>(&std::remove_cvref_t<T>::toXml)),
void (std::remove_cvref_t<T>::*)(QXmlStreamWriter *) const>;
template<typename T>
inline QByteArray serializeXml(const T &packet)
requires XmlWriterSerializeable<T> || QXmlStreamSerializeable<T>
{
if constexpr (XmlWriterSerializeable<T>) {
return serializeXmlWriter([&](XmlWriter &w) {
packet.toXml(w);
});
} else if constexpr (QXmlStreamSerializeable<T>) {
return serializeQXmlStream([&](QXmlStreamWriter *w) {
packet.toXml(w);
});
}
}
QXMPP_EXPORT QByteArray generateRandomBytes(size_t minimumByteCount, size_t maximumByteCount);
QXMPP_EXPORT void generateRandomBytes(uint8_t *bytes, size_t byteCount);
float calculateProgress(qint64 transferred, qint64 total);
QXMPP_EXPORT std::pair<QString, int> parseHostAddress(const QString &address);
} // namespace QXmpp::Private
#endif // QXMPPUTILS_P_H
QXmppNonza
Definition QXmppNonza.h:14