QXmpp Version: 1.10.0
QXmppFutureUtils_p.h
1// SPDX-FileCopyrightText: 2021 Linus Jahn <lnj@kaidan.im>
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4
5#ifndef QXMPPFUTUREUTILS_P_H
6#define QXMPPFUTUREUTILS_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the QXmpp API. This header file may change from
13// version to version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "QXmppIq.h"
19#include "QXmppPromise.h"
20#include "QXmppSendResult.h"
21
22#include <memory>
23#include <variant>
24
25#include <QFutureWatcher>
26#include <QObject>
27
28namespace QXmpp::Private {
29
30// helper for std::visit
31template<class... Ts>
32struct overloaded : Ts... {
33 using Ts::operator()...;
34};
35// explicit deduction guide (not needed as of C++20)
36template<class... Ts>
37overloaded(Ts...) -> overloaded<Ts...>;
38
39// Variation of std::visit allowing to forward unhandled types
40template<typename ReturnType, typename T, typename Visitor>
41auto visitForward(T variant, Visitor visitor)
42{
43 return std::visit([&](auto &&value) -> ReturnType {
44 using ValueType = std::decay_t<decltype(value)>;
45 if constexpr (std::is_invocable_v<Visitor, ValueType>) {
46 return visitor(std::move(value));
47 } else {
48 return value;
49 }
50 },
51 std::forward<T>(variant));
52}
53
54template<typename F, typename Ret, typename A, typename... Rest>
55A lambda_helper(Ret (F::*)(A, Rest...));
56
57template<typename F, typename Ret, typename A, typename... Rest>
58A lambda_helper(Ret (F::*)(A, Rest...) const);
59
60template<typename F>
61struct first_argument {
62 using type = decltype(lambda_helper(&F::operator()));
63};
64
65template<typename F>
66using first_argument_t = typename first_argument<F>::type;
67
68#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
69using QtFuture::makeReadyFuture;
70#else
71template<typename T>
72QFuture<T> makeReadyFuture(T &&value)
73{
74 QFutureInterface<T> interface(QFutureInterfaceBase::Started);
75 interface.reportResult(std::move(value));
76 interface.reportFinished();
77 return interface.future();
78}
79
80inline QFuture<void> makeReadyFuture()
81{
82 using State = QFutureInterfaceBase::State;
83 return QFutureInterface<void>(State(State::Started | State::Finished)).future();
84}
85#endif
86
87template<typename T>
88QXmppTask<T> makeReadyTask(T &&value)
89{
90 QXmppPromise<T> promise;
91 promise.finish(std::move(value));
92 return promise.task();
93}
94
95inline QXmppTask<void> makeReadyTask()
96{
97 QXmppPromise<void> promise;
98 promise.finish();
99 return promise.task();
100}
101
102template<typename T, typename Handler>
103void await(const QFuture<T> &future, QObject *context, Handler handler)
104{
105 auto *watcher = new QFutureWatcher<T>(context);
106 QObject::connect(watcher, &QFutureWatcherBase::finished,
107 context, [watcher, handler = std::move(handler)]() mutable {
108 handler(watcher->result());
109 watcher->deleteLater();
110 });
111 watcher->setFuture(future);
112}
113
114template<typename Handler>
115void await(const QFuture<void> &future, QObject *context, Handler handler)
116{
117 auto *watcher = new QFutureWatcher<void>(context);
118 QObject::connect(watcher, &QFutureWatcherBase::finished,
119 context, [watcher, handler = std::move(handler)]() mutable {
120 handler();
121 watcher->deleteLater();
122 });
123 watcher->setFuture(future);
124}
125
126template<typename Result, typename Input, typename Converter>
127auto chain(QXmppTask<Input> &&source, QObject *context, Converter task) -> QXmppTask<Result>
128{
129 QXmppPromise<Result> promise;
130
131 source.then(context, [=](Input &&input) mutable {
132 promise.finish(task(std::move(input)));
133 });
134 return promise.task();
135}
136
137template<typename IqType, typename Input, typename Converter>
138auto parseIq(Input &&sendResult, Converter convert) -> decltype(convert({}))
139{
140 using Result = decltype(convert({}));
141 return std::visit(overloaded {
142 [convert = std::move(convert)](const QDomElement &element) -> Result {
143 IqType iq;
144 iq.parse(element);
145 return convert(std::move(iq));
146 },
147 [](QXmppError &&error) -> Result {
148 return error;
149 },
150 },
151 std::move(sendResult));
152}
153
154template<typename IqType, typename Result, typename Input>
155auto parseIq(Input &&sendResult) -> Result
156{
157 return parseIq<IqType>(std::move(sendResult), [](IqType &&iq) -> Result {
158 // no conversion
159 return iq;
160 });
161}
162
163template<typename Input, typename Converter>
164auto chainIq(QXmppTask<Input> &&input, QObject *context, Converter convert) -> QXmppTask<decltype(convert({}))>
165{
166 using Result = decltype(convert({}));
167 using IqType = std::decay_t<first_argument_t<Converter>>;
168 return chain<Result>(std::move(input), context, [convert = std::move(convert)](Input &&input) -> Result {
169 return parseIq<IqType>(std::move(input), convert);
170 });
171}
172
173template<typename Result, typename Input>
174auto chainIq(QXmppTask<Input> &&input, QObject *context) -> QXmppTask<Result>
175{
176 // IQ type is first std::variant parameter
177 using IqType = std::decay_t<decltype(std::get<0>(Result {}))>;
178 return chain<Result>(std::move(input), context, [](Input &&sendResult) mutable {
179 return parseIq<IqType, Result>(sendResult);
180 });
181}
182
183template<typename T, typename Err, typename Function>
184auto mapSuccess(std::variant<T, Err> var, Function lambda)
185{
186 using MapResult = std::decay_t<decltype(lambda({}))>;
187 using MappedVariant = std::variant<MapResult, Err>;
188 return std::visit(overloaded {
189 [lambda = std::move(lambda)](T val) -> MappedVariant {
190 return lambda(std::move(val));
191 },
192 [](Err err) -> MappedVariant {
193 return err;
194 } },
195 std::move(var));
196}
197
198template<typename T, typename Err>
199auto mapToSuccess(std::variant<T, Err> var)
200{
201 return mapSuccess(std::move(var), [](T) {
202 return Success();
203 });
204}
205
206template<typename T, typename Err>
207auto chainSuccess(QXmppTask<std::variant<T, Err>> &&source, QObject *context) -> QXmppTask<std::variant<QXmpp::Success, QXmppError>>
208{
209 return chain<std::variant<QXmpp::Success, QXmppError>>(std::move(source), context, mapToSuccess<T, Err>);
210}
211
212template<typename Input, typename Converter>
213auto chainMapSuccess(QXmppTask<Input> &&source, QObject *context, Converter convert)
214{
215 return chain<std::variant<decltype(convert({})), QXmppError>>(std::move(source), context, [convert](Input &&input) {
216 return mapSuccess(std::move(input), convert);
217 });
218}
219
220} // namespace QXmpp::Private
221
222#endif // QXMPPFUTUREUTILS_P_H
Create and update QXmppTask objects to communicate results of asynchronous operations.
Definition: QXmppPromise.h:23
QXmppTask< T > task()
Definition: QXmppPromise.h:96
Definition: QXmppTask.h:62
Definition: QXmppError.h:17