// SPDX-FileCopyrightText: 2021 Linus Jahn // // SPDX-License-Identifier: LGPL-2.1-or-later #ifndef QXMPPFUTUREUTILS_P_H #define QXMPPFUTUREUTILS_P_H // // W A R N I N G // ------------- // // This file is not part of the QXmpp API. This header file may change from // version to version without notice, or even be removed. // // We mean it. // #include "QXmppIq.h" #include "QXmppSendResult.h" #include #include #include #include namespace QXmpp::Private { // helper for std::visit template struct overloaded : Ts... { using Ts::operator()...; }; // explicit deduction guide (not needed as of C++20) template overloaded(Ts...) -> overloaded; // Variation of std::visit allowing to forward unhandled types template auto visitForward(T variant, Visitor visitor) { return std::visit([&](auto &&value) -> ReturnType { using ValueType = std::decay_t; if constexpr (std::is_invocable_v) { return visitor(std::move(value)); } else { return value; } }, std::forward(variant)); } template A lambda_helper(Ret (F::*)(A, Rest...)); template A lambda_helper(Ret (F::*)(A, Rest...) const); template struct first_argument { using type = decltype(lambda_helper(&F::operator())); }; template using first_argument_t = typename first_argument::type; #if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) using QtFuture::makeReadyFuture; #else template QFuture makeReadyFuture(T &&value) { QFutureInterface interface(QFutureInterfaceBase::Started); interface.reportResult(std::move(value)); interface.reportFinished(); return interface.future(); } inline QFuture makeReadyFuture() { using State = QFutureInterfaceBase::State; return QFutureInterface(State(State::Started | State::Finished)).future(); } #endif template void awaitLast(const QFuture &future, QObject *context, Handler handler) { auto *watcher = new QFutureWatcher(context); QObject::connect(watcher, &QFutureWatcherBase::finished, context, [watcher, handler = std::move(handler)]() mutable { auto future = watcher->future(); handler(future.resultAt(future.resultCount() - 1)); watcher->deleteLater(); }); watcher->setFuture(future); } template void await(const QFuture &future, QObject *context, Handler handler) { auto *watcher = new QFutureWatcher(context); QObject::connect(watcher, &QFutureWatcherBase::finished, context, [watcher, handler = std::move(handler)]() mutable { handler(watcher->result()); watcher->deleteLater(); }); watcher->setFuture(future); } template void await(const QFuture &future, QObject *context, Handler handler) { auto *watcher = new QFutureWatcher(context); QObject::connect(watcher, &QFutureWatcherBase::finished, context, [watcher, handler = std::move(handler)]() mutable { handler(); watcher->deleteLater(); }); watcher->setFuture(future); } template auto chain(const QFuture &source, QObject *context, Converter task) -> QFuture { QFutureInterface resultInterface(QFutureInterfaceBase::Started); auto *watcher = new QFutureWatcher(context); QObject::connect(watcher, &QFutureWatcherBase::finished, context, [=]() mutable { resultInterface.reportResult(task(watcher->result())); resultInterface.reportFinished(); watcher->deleteLater(); }); watcher->setFuture(source); return resultInterface.future(); } template auto parseIq(Input &&sendResult, Converter convert) -> decltype(convert({})) { using Result = decltype(convert({})); return std::visit(overloaded { [convert = std::move(convert)](const QDomElement &element) -> Result { IqType iq; iq.parse(element); if (iq.type() == QXmppIq::Error) { return iq.error(); } return convert(std::move(iq)); }, [](QXmpp::SendError error) -> Result { using Error = QXmppStanza::Error; return Error(Error::Wait, Error::UndefinedCondition, QStringLiteral("Couldn't send request: ") + error.text); }, }, sendResult); } template auto parseIq(Input &&sendResult) -> Result { return parseIq(std::move(sendResult), [](IqType &&iq) -> Result { // no conversion return iq; }); } template auto chainIq(QFuture &&input, QObject *context, Converter convert) -> QFuture { using Result = decltype(convert({})); using IqType = std::decay_t>; return chain(std::move(input), context, [convert { std::move(convert) }](Input &&input) -> Result { return parseIq(std::move(input), convert); }); } template auto chainIq(QFuture &&input, QObject *context) -> QFuture { // IQ type is first std::variant parameter using IqType = std::decay_t(Result {}))>; return chain(std::move(input), context, [](Input &&sendResult) { return parseIq(sendResult); }); } template void reportFinishedResult(QFutureInterface &interface, const T &result) { interface.reportResult(result); interface.reportFinished(); } template auto mapSuccess(std::variant var, Function lambda) { using MapResult = std::decay_t; using MappedVariant = std::variant; return std::visit(overloaded { [lambda = std::move(lambda)](T val) -> MappedVariant { return lambda(std::move(val)); }, [](Err err) -> MappedVariant { return err; } }, std::move(var)); } } // namespace QXmpp::Private #endif // QXMPPFUTUREUTILS_P_H