QXmpp Version: 1.14.1
Loading...
Searching...
No Matches
Async.h
1// SPDX-FileCopyrightText: 2021 Linus Jahn <lnj@kaidan.im>
2// SPDX-FileCopyrightText: 2025 Filipe Azevedo <pasnox@gmail.com>
3//
4// SPDX-License-Identifier: LGPL-2.1-or-later
5
6#ifndef ASYNC_H
7#define ASYNC_H
8
9#include "QXmppAsync_p.h"
10#include "QXmppGlobal.h"
11
12#include <memory>
13#include <variant>
14
15#include <QFutureWatcher>
16
17namespace QXmpp::Private {
18
19template<typename Function>
20auto later(QObject *context, Function function)
21{
22 QMetaObject::invokeMethod(context, std::forward<Function>(function), Qt::QueuedConnection);
23}
24
25#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
26template<typename T>
27QFuture<T> makeReadyFuture(T &&value) { return QtFuture::makeReadyValueFuture(std::move(value)); }
28#elif QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
29using QtFuture::makeReadyFuture;
30#else
31template<typename T>
32QFuture<T> makeReadyFuture(T &&value)
33{
34 QFutureInterface<T> interface(QFutureInterfaceBase::Started);
35 interface.reportResult(std::move(value));
36 interface.reportFinished();
37 return interface.future();
38}
39
40inline QFuture<void> makeReadyFuture()
41{
42 using State = QFutureInterfaceBase::State;
43 return QFutureInterface<void>(State(State::Started | State::Finished)).future();
44}
45#endif
46
47template<typename T, typename Handler>
48void await(const QFuture<T> &future, QObject *context, Handler handler)
49{
50 auto *watcher = new QFutureWatcher<T>(context);
51 QObject::connect(watcher, &QFutureWatcherBase::finished,
52 context, [watcher, handler = std::move(handler)]() mutable {
53 handler(watcher->result());
54 watcher->deleteLater();
55 });
56 watcher->setFuture(future);
57}
58
59template<typename Handler>
60void await(const QFuture<void> &future, QObject *context, Handler handler)
61{
62 auto *watcher = new QFutureWatcher<void>(context);
63 QObject::connect(watcher, &QFutureWatcherBase::finished,
64 context, [watcher, handler = std::move(handler)]() mutable {
65 handler();
66 watcher->deleteLater();
67 });
68 watcher->setFuture(future);
69}
70
71template<typename T, typename Err, typename Function>
72auto mapSuccess(std::variant<T, Err> var, Function lambda)
73{
74 using MapResult = std::decay_t<decltype(lambda({}))>;
75 using MappedVariant = std::variant<MapResult, Err>;
76 return std::visit(overloaded {
77 [lambda = std::move(lambda)](T val) -> MappedVariant {
78 return lambda(std::move(val));
79 },
80 [](Err err) -> MappedVariant {
81 return err;
82 } },
83 std::move(var));
84}
85
86template<typename T, typename Err>
87auto mapToSuccess(std::variant<T, Err> var)
88{
89 return mapSuccess(std::move(var), [](T) {
90 return Success();
91 });
92}
93
94template<typename T, typename Err>
95auto chainSuccess(QXmppTask<std::variant<T, Err>> &&source, QObject *context) -> QXmppTask<std::variant<QXmpp::Success, QXmppError>>
96{
97 return chain<std::variant<QXmpp::Success, QXmppError>>(std::move(source), context, mapToSuccess<T, Err>);
98}
99
100template<typename Input, typename Converter>
101auto chainMapSuccess(QXmppTask<Input> &&source, QObject *context, Converter convert)
102{
103 return chain<std::variant<decltype(convert({})), QXmppError>>(std::move(source), context, [convert](Input &&input) {
104 return mapSuccess(std::move(input), convert);
105 });
106}
107
108// Creates a task for multiple tasks without a return value.
109template<typename T>
110QXmppTask<void> joinVoidTasks(QObject *context, QList<QXmppTask<T>> &&tasks)
111{
112 int taskCount = tasks.size();
113 auto finishedTaskCount = std::make_shared<int>();
114
115 QXmppPromise<void> promise;
116
117 for (auto &task : tasks) {
118 task.then(context, [=]() mutable {
119 if (++(*finishedTaskCount) == taskCount) {
120 promise.finish();
121 }
122 });
123 }
124
125 return promise.task();
126}
127
128template<typename Params, typename Response>
129struct AttachableRequests {
130 struct Request {
131 Params params;
132 std::vector<QXmppPromise<Response>> promises;
133 };
134
135 std::vector<Request> requests;
136
138 std::optional<QXmppTask<Response>> attach(const Params &key)
139 {
140 auto itr = std::ranges::find(requests, key, &Request::params);
141 if (itr != requests.end()) {
142 QXmppPromise<Response> p;
143 auto task = p.task();
144 itr->promises.push_back(std::move(p));
145 return task;
146 }
147
148 return std::nullopt;
149 }
150
151 QXmppTask<Response> makeNew(Params key)
152 {
153 Q_ASSERT(!contains(requests, key, &Request::params));
154
155 QXmppPromise<Response> p;
156 auto task = p.task();
157 requests.push_back(Request { key, { std::move(p) } });
158 return task;
159 }
160
161 void finish(const Params &key, Response &&response)
162 {
163 auto itr = std::ranges::find(requests, key, &Request::params);
164 Q_ASSERT(itr != requests.end());
165 if (itr == requests.end()) {
166 return;
167 }
168
169 auto promises = std::move(itr->promises);
170 requests.erase(itr);
171
172 for (auto it = promises.begin(); it != promises.end(); ++it) {
173 // copy unless this is the last iteration (then do move)
174 it->finish(std::next(it) == promises.end() ? std::move(response) : response);
175 }
176 }
177
178 QXmppTask<Response> produce(Params key, std::function<QXmppTask<Response>(Params)> requestFunction, QObject *context)
179 {
180 if (auto task = attach(key)) {
181 return std::move(*task);
182 }
183 auto task = makeNew(key);
184 requestFunction(key).then(context, [this, key](auto &&response) {
185 finish(key, std::move(response));
186 });
187 return task;
188 }
189};
190
191template<typename T>
192struct MultiPromise {
193 std::vector<QXmppPromise<T>> promises;
194
195 void finish(T &&response)
196 {
197 for (auto it = promises.begin(); it != promises.end(); ++it) {
198 // copy unless this is the last iteration (then do move)
199 it->finish(std::next(it) == promises.end() ? std::move(response) : response);
200 }
201 }
202 QXmppTask<T> generateTask()
203 {
204 promises.push_back(QXmppPromise<T>());
205 return promises.back().task();
206 }
207};
208
209template<>
210struct MultiPromise<void> {
211 std::vector<QXmppPromise<void>> promises;
212
213 void finish()
214 {
215 for (auto &p : promises) {
216 p.finish();
217 }
218 }
219 QXmppTask<void> generateTask()
220 {
221 promises.push_back(QXmppPromise<void>());
222 return promises.back().task();
223 }
224};
225
226} // namespace QXmpp::Private
227
228#endif // ASYNC_H
QXmppTask< T > task()
Definition QXmppPromise.h:86