QXmpp Version: 1.11.0
Loading...
Searching...
No Matches
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, 6, 0)
69template<typename T>
70QFuture<T> makeReadyFuture(T &&value) { return QtFuture::makeReadyValueFuture(std::move(value)); }
71#elif QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
72using QtFuture::makeReadyFuture;
73#else
74template<typename T>
75QFuture<T> makeReadyFuture(T &&value)
76{
77 QFutureInterface<T> interface(QFutureInterfaceBase::Started);
78 interface.reportResult(std::move(value));
79 interface.reportFinished();
80 return interface.future();
81}
82
83inline QFuture<void> makeReadyFuture()
84{
85 using State = QFutureInterfaceBase::State;
86 return QFutureInterface<void>(State(State::Started | State::Finished)).future();
87}
88#endif
89
90template<typename T>
91QXmppTask<T> makeReadyTask(T &&value)
92{
93 QXmppPromise<T> promise;
94 promise.finish(std::move(value));
95 return promise.task();
96}
97
98inline QXmppTask<void> makeReadyTask()
99{
100 QXmppPromise<void> promise;
101 promise.finish();
102 return promise.task();
103}
104
105template<typename T, typename Handler>
106void await(const QFuture<T> &future, QObject *context, Handler handler)
107{
108 auto *watcher = new QFutureWatcher<T>(context);
109 QObject::connect(watcher, &QFutureWatcherBase::finished,
110 context, [watcher, handler = std::move(handler)]() mutable {
111 handler(watcher->result());
112 watcher->deleteLater();
113 });
114 watcher->setFuture(future);
115}
116
117template<typename Handler>
118void await(const QFuture<void> &future, QObject *context, Handler handler)
119{
120 auto *watcher = new QFutureWatcher<void>(context);
121 QObject::connect(watcher, &QFutureWatcherBase::finished,
122 context, [watcher, handler = std::move(handler)]() mutable {
123 handler();
124 watcher->deleteLater();
125 });
126 watcher->setFuture(future);
127}
128
129template<typename Result, typename Input, typename Converter>
130auto chain(QXmppTask<Input> &&source, QObject *context, Converter task) -> QXmppTask<Result>
131{
132 QXmppPromise<Result> promise;
133
134 source.then(context, [=](Input &&input) mutable {
135 promise.finish(task(std::move(input)));
136 });
137 return promise.task();
138}
139
140template<typename IqType, typename Input, typename Converter>
141auto parseIq(Input &&sendResult, Converter convert) -> decltype(convert({}))
142{
143 using Result = decltype(convert({}));
144 return std::visit(overloaded {
145 [convert = std::move(convert)](const QDomElement &element) -> Result {
146 IqType iq;
147 iq.parse(element);
148 return convert(std::move(iq));
149 },
150 [](QXmppError &&error) -> Result {
151 return error;
152 },
153 },
154 std::move(sendResult));
155}
156
157template<typename IqType, typename Result, typename Input>
158auto parseIq(Input &&sendResult) -> Result
159{
160 return parseIq<IqType>(std::move(sendResult), [](IqType &&iq) -> Result {
161 // no conversion
162 return iq;
163 });
164}
165
166template<typename Input, typename Converter>
167auto chainIq(QXmppTask<Input> &&input, QObject *context, Converter convert) -> QXmppTask<decltype(convert({}))>
168{
169 using Result = decltype(convert({}));
170 using IqType = std::decay_t<first_argument_t<Converter>>;
171 return chain<Result>(std::move(input), context, [convert = std::move(convert)](Input &&input) -> Result {
172 return parseIq<IqType>(std::move(input), convert);
173 });
174}
175
176template<typename Result, typename Input>
177auto chainIq(QXmppTask<Input> &&input, QObject *context) -> QXmppTask<Result>
178{
179 // IQ type is first std::variant parameter
180 using IqType = std::decay_t<decltype(std::get<0>(Result {}))>;
181 return chain<Result>(std::move(input), context, [](Input &&sendResult) mutable {
182 return parseIq<IqType, Result>(sendResult);
183 });
184}
185
186template<typename T, typename Err, typename Function>
187auto mapSuccess(std::variant<T, Err> var, Function lambda)
188{
189 using MapResult = std::decay_t<decltype(lambda({}))>;
190 using MappedVariant = std::variant<MapResult, Err>;
191 return std::visit(overloaded {
192 [lambda = std::move(lambda)](T val) -> MappedVariant {
193 return lambda(std::move(val));
194 },
195 [](Err err) -> MappedVariant {
196 return err;
197 } },
198 std::move(var));
199}
200
201template<typename T, typename Err>
202auto mapToSuccess(std::variant<T, Err> var)
203{
204 return mapSuccess(std::move(var), [](T) {
205 return Success();
206 });
207}
208
209template<typename T, typename Err>
210auto chainSuccess(QXmppTask<std::variant<T, Err>> &&source, QObject *context) -> QXmppTask<std::variant<QXmpp::Success, QXmppError>>
211{
212 return chain<std::variant<QXmpp::Success, QXmppError>>(std::move(source), context, mapToSuccess<T, Err>);
213}
214
215template<typename Input, typename Converter>
216auto chainMapSuccess(QXmppTask<Input> &&source, QObject *context, Converter convert)
217{
218 return chain<std::variant<decltype(convert({})), QXmppError>>(std::move(source), context, [convert](Input &&input) {
219 return mapSuccess(std::move(input), convert);
220 });
221}
222
223} // namespace QXmpp::Private
224
225#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