// 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 "QXmppPromise.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 QXmppTask makeReadyTask(T &&value) { QXmppPromise promise; promise.finish(std::move(value)); return promise.task(); } inline QXmppTask makeReadyTask() { QXmppPromise promise; promise.finish(); return promise.task(); } 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(QXmppTask &&source, QObject *context, Converter task) -> QXmppTask { QXmppPromise promise; source.then(context, [=](Input &&input) mutable { promise.finish(task(std::move(input))); }); return promise.task(); } 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) { if (auto err = iq.errorOptional()) { return QXmppError { err->text(), std::move(*err) }; } return QXmppError { QStringLiteral("Unknown error.") }; } return convert(std::move(iq)); }, [](QXmppError error) -> Result { return error; }, }, sendResult); } template auto parseIq(Input &&sendResult) -> Result { return parseIq(std::move(sendResult), [](IqType &&iq) -> Result { // no conversion return iq; }); } template auto chainIq(QXmppTask &&input, QObject *context, Converter convert) -> QXmppTask { 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(QXmppTask &&input, QObject *context) -> QXmppTask { // IQ type is first std::variant parameter using IqType = std::decay_t(Result {}))>; return chain(std::move(input), context, [](Input &&sendResult) mutable { 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)); } template static auto taskFromFuture(QFuture &&future) -> QXmppTask { QXmppPromise promise; auto *watcher = new QFutureWatcher(); QObject::connect(watcher, &QFutureWatcher::finished, [promise = std::move(promise), watcher]() mutable { if constexpr (std::is_void_v) { promise.finish(); } else { promise.finish(watcher->result()); } watcher->deleteLater(); }); watcher->setFuture(future); return promise.task(); } } // namespace QXmpp::Private #endif // QXMPPFUTUREUTILS_P_H