cutelyst 4.3.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
action.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#include "action_p.h"
6#include "common.h"
7#include "context.h"
8#include "controller.h"
9
10using namespace Cutelyst;
11
13 : Component(new ActionPrivate, parent)
14{
15}
16
17Action::Action(ActionPrivate *ptr, QObject *parent)
18 : Component(ptr, parent)
19{
20}
21
22Component::Modifiers Action::modifiers() const
23{
24 return Component::OnlyExecute;
25}
26
27void Action::setMethod(const QMetaMethod &method)
28{
29 Q_D(Action);
30 d->method = method;
31 if (method.returnType() == QMetaType::Bool) {
32 d->evaluateBool = true;
33 }
34
35 if (method.parameterCount() == 2 && method.parameterType(1) == QMetaType::QStringList) {
36 d->listSignature = true;
37 }
38}
39
41{
42 Q_D(Action);
43 d->controller = controller;
44}
45
46void Action::setupAction(const QVariantHash &args, Application *app)
47{
48 Q_D(Action);
49
50 init(app, args);
51
52 d->ns = args.value(u"namespace"_qs).toString();
53
54 const auto attributes = args.value(u"attributes"_qs).value<ParamsMultiMap>();
55 d->attributes = attributes;
56
57 const QString argsAttr = attributes.value(u"Args"_qs);
58 if (!argsAttr.isEmpty()) {
59 d->numberOfArgs = qint8(argsAttr.toInt());
60 }
61
62 const QString capturesAttr = attributes.value(u"CaptureArgs"_qs);
63 if (!capturesAttr.isEmpty()) {
64 d->numberOfCaptures = qint8(capturesAttr.toInt());
65 }
66}
67
69{
70 Q_D(const Action);
71 return d->attributes;
72}
73
74QString Action::attribute(const QString &name, const QString &defaultValue) const
75{
76 Q_D(const Action);
77 return d->attributes.value(name, defaultValue);
78}
79
81{
82 Q_D(Action);
83 d->attributes = attributes;
84}
85
86QString Action::className() const noexcept
87{
88 Q_D(const Action);
89 return d->controller->objectName();
90}
91
93{
94 Q_D(const Action);
95 return d->controller;
96}
97
98bool Action::match(int numberOfArgs) const noexcept
99{
100 Q_D(const Action);
101 // If the number of args is -1 (not defined)
102 // it will slurp all args so we don't care
103 // about how many args was passed, otherwise
104 // count them
105 return d->numberOfArgs == -1 || d->numberOfArgs == numberOfArgs;
106}
107
108bool Action::matchCaptures(int numberOfCaptures) const noexcept
109{
110 Q_D(const Action);
111 // If the number of capture args is -1 (not defined)
112 // it will slurp all args so we don't care
113 // about how many args was passed, otherwise
114 // count them
115 return d->numberOfCaptures == -1 || d->numberOfCaptures == numberOfCaptures;
116}
117
118QString Action::ns() const noexcept
119{
120 Q_D(const Action);
121 return d->ns;
122}
123
125{
126 Q_D(const Action);
127 return d->numberOfArgs;
128}
129
131{
132 Q_D(const Action);
133 return d->numberOfCaptures;
134}
135
137{
138 Q_D(const Action);
139 if (c->detached()) {
140 return false;
141 }
142
143 bool ret;
144
145#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 0))
146
147 if (d->evaluateBool) {
148 bool methodRet;
149
150 if (d->listSignature) {
151 // clang-format off
152 ret = d->method.invoke(d->controller,
154 Q_RETURN_ARG(bool, methodRet),
155 Q_ARG(Cutelyst::Context*, c),
156 Q_ARG(QStringList, c->request()->args()));
157 // clang-format on
158 } else {
159 QStringList args = c->request()->args();
160 // Fill the missing arguments
161 args.append(d->emptyArgs);
162
163 // clang-format off
164 ret = d->method.invoke(d->controller,
166 Q_RETURN_ARG(bool, methodRet),
167 Q_ARG(Cutelyst::Context*, c),
168 Q_ARG(QString, args.at(0)),
169 Q_ARG(QString, args.at(1)),
170 Q_ARG(QString, args.at(2)),
171 Q_ARG(QString, args.at(3)),
172 Q_ARG(QString, args.at(4)),
173 Q_ARG(QString, args.at(5)),
174 Q_ARG(QString, args.at(6)),
175 Q_ARG(QString, args.at(7)),
176 Q_ARG(QString, args.at(8)));
177 // clang-format on
178 }
179
180 if (ret) {
181 c->setState(methodRet);
182 return methodRet;
183 }
184
185 // The method failed to be called which means we should detach
186 c->detach();
187 c->setState(false);
188
189 return false;
190 } else {
191 if (d->listSignature) {
192 // clang-format off
193 ret = d->method.invoke(d->controller,
195 Q_ARG(Cutelyst::Context*, c),
196 Q_ARG(QStringList, c->request()->args()));
197 // clang-format on
198 } else {
199 QStringList args = c->request()->args();
200 // Fill the missing arguments
201 args.append(d->emptyArgs);
202
203 // clang-format off
204 ret = d->method.invoke(d->controller,
206 Q_ARG(Cutelyst::Context*, c),
207 Q_ARG(QString, args.at(0)),
208 Q_ARG(QString, args.at(1)),
209 Q_ARG(QString, args.at(2)),
210 Q_ARG(QString, args.at(3)),
211 Q_ARG(QString, args.at(4)),
212 Q_ARG(QString, args.at(5)),
213 Q_ARG(QString, args.at(6)),
214 Q_ARG(QString, args.at(7)),
215 Q_ARG(QString, args.at(8)));
216 // clang-format on
217 }
218 c->setState(ret);
219 return ret;
220 }
221
222#else
223
224 /*
225 * Qt 6.5 introduced a new variadic version of QMetaMethod::invoke() that
226 * does not work with our current implementation above. The following code
227 * is a fast fix to use the new version but there might be better / more elegant
228 * approaches.
229 *
230 * See: https://codereview.qt-project.org/c/qt/qtbase/+/422745
231 *
232 * TODO: check for more flexible implementation
233 */
234
235 const QStringList args = c->request()->args();
236
237 if (d->evaluateBool) {
238 bool methodRet;
239
240 if (d->listSignature) {
241 ret = d->method.invoke(
242 d->controller, Qt::DirectConnection, qReturnArg(methodRet), c, args);
243 } else {
244 switch (d->method.parameterCount()) {
245 case 0:
246 ret = d->method.invoke(d->controller, Qt::DirectConnection, qReturnArg(methodRet));
247 break;
248 case 1:
249 ret =
250 d->method.invoke(d->controller, Qt::DirectConnection, qReturnArg(methodRet), c);
251 break;
252 case 2:
253 ret = d->method.invoke(
254 d->controller, Qt::DirectConnection, qReturnArg(methodRet), c, args.value(0));
255 break;
256 case 3:
257 ret = d->method.invoke(d->controller,
259 qReturnArg(methodRet),
260 c,
261 args.value(0),
262 args.value(1));
263 break;
264 case 4:
265 ret = d->method.invoke(d->controller,
267 qReturnArg(methodRet),
268 c,
269 args.value(0),
270 args.value(1),
271 args.value(2));
272 break;
273 case 5:
274 ret = d->method.invoke(d->controller,
276 qReturnArg(methodRet),
277 c,
278 args.value(0),
279 args.value(1),
280 args.value(2),
281 args.value(3));
282 break;
283 case 6:
284 ret = d->method.invoke(d->controller,
286 qReturnArg(methodRet),
287 c,
288 args.value(0),
289 args.value(1),
290 args.value(2),
291 args.value(3),
292 args.value(4));
293 break;
294 case 7:
295 ret = d->method.invoke(d->controller,
297 qReturnArg(methodRet),
298 c,
299 args.value(0),
300 args.value(1),
301 args.value(2),
302 args.value(3),
303 args.value(4),
304 args.value(5));
305 break;
306 case 8:
307 ret = d->method.invoke(d->controller,
309 qReturnArg(methodRet),
310 c,
311 args.value(0),
312 args.value(1),
313 args.value(2),
314 args.value(3),
315 args.value(4),
316 args.value(5),
317 args.value(6));
318 break;
319 case 9:
320 ret = d->method.invoke(d->controller,
322 qReturnArg(methodRet),
323 c,
324 args.value(0),
325 args.value(1),
326 args.value(2),
327 args.value(3),
328 args.value(4),
329 args.value(5),
330 args.value(6),
331 args.value(7));
332 break;
333 default:
334 ret = d->method.invoke(d->controller,
336 qReturnArg(methodRet),
337 c,
338 args.value(0),
339 args.value(1),
340 args.value(2),
341 args.value(3),
342 args.value(4),
343 args.value(5),
344 args.value(6),
345 args.value(7),
346 args.value(8));
347 break;
348 }
349 }
350
351 if (ret) {
352 c->setState(methodRet);
353 return methodRet;
354 }
355
356 // The method failed to be called which means we should detach
357 c->detach();
358 c->setState(false);
359
360 return false;
361 } else {
362 if (d->listSignature) {
363 ret = d->method.invoke(d->controller, Qt::DirectConnection, c, args);
364 } else {
365 switch (d->method.parameterCount()) {
366 case 0:
367 ret = d->method.invoke(d->controller, Qt::DirectConnection);
368 break;
369 case 1:
370 ret = d->method.invoke(d->controller, Qt::DirectConnection, c);
371 break;
372 case 2:
373 ret = d->method.invoke(d->controller, Qt::DirectConnection, c, args.value(0));
374 break;
375 case 3:
376 ret = d->method.invoke(
377 d->controller, Qt::DirectConnection, c, args.value(0), args.value(1));
378 break;
379 case 4:
380 ret = d->method.invoke(d->controller,
382 c,
383 args.value(0),
384 args.value(1),
385 args.value(2));
386 break;
387 case 5:
388 ret = d->method.invoke(d->controller,
390 c,
391 args.value(0),
392 args.value(1),
393 args.value(2),
394 args.value(3));
395 break;
396 case 6:
397 ret = d->method.invoke(d->controller,
399 c,
400 args.value(0),
401 args.value(1),
402 args.value(2),
403 args.value(3),
404 args.value(4));
405 break;
406 case 7:
407 ret = d->method.invoke(d->controller,
409 c,
410 args.value(0),
411 args.value(1),
412 args.value(2),
413 args.value(3),
414 args.value(4),
415 args.value(5));
416 break;
417 case 8:
418 ret = d->method.invoke(d->controller,
420 c,
421 args.value(0),
422 args.value(1),
423 args.value(2),
424 args.value(3),
425 args.value(4),
426 args.value(5),
427 args.value(6));
428 break;
429 case 9:
430 ret = d->method.invoke(d->controller,
432 c,
433 args.value(0),
434 args.value(1),
435 args.value(2),
436 args.value(3),
437 args.value(4),
438 args.value(5),
439 args.value(6),
440 args.value(7));
441 break;
442 default:
443 ret = d->method.invoke(d->controller,
445 c,
446 args.value(0),
447 args.value(1),
448 args.value(2),
449 args.value(3),
450 args.value(4),
451 args.value(5),
452 args.value(6),
453 args.value(7),
454 args.value(8));
455 break;
456 }
457 }
458 c->setState(ret);
459 return ret;
460 }
461
462#endif
463}
464
465#include "moc_action.cpp"
This class represents a Cutelyst Action.
Definition action.h:36
void setAttributes(const ParamsMultiMap &attributes)
Definition action.cpp:80
void setupAction(const QVariantHash &args, Application *app)
Definition action.cpp:46
QString ns() const noexcept
Definition action.cpp:118
virtual qint8 numberOfArgs() const
Definition action.cpp:124
bool doExecute(Context *c) override
Definition action.cpp:136
virtual bool match(int numberOfArgs) const noexcept
Definition action.cpp:98
void setMethod(const QMetaMethod &method)
Definition action.cpp:27
QString className() const noexcept
Definition action.cpp:86
Action(QObject *parent=nullptr)
Definition action.cpp:12
virtual qint8 numberOfCaptures() const
Definition action.cpp:130
virtual Modifiers modifiers() const override
Definition action.cpp:22
virtual bool matchCaptures(int numberOfCaptures) const noexcept
Definition action.cpp:108
ParamsMultiMap attributes() const noexcept
Definition action.cpp:68
Controller * controller() const noexcept
Definition action.cpp:92
void setController(Controller *controller)
Definition action.cpp:40
QString attribute(const QString &name, const QString &defaultValue={}) const
Definition action.cpp:74
The Cutelyst application.
Definition application.h:66
The Cutelyst Component base class.
Definition component.h:30
virtual bool init(Application *application, const QVariantHash &args)
Definition component.cpp:57
QString name() const noexcept
Definition component.cpp:33
The Cutelyst Context.
Definition context.h:42
void detach(Action *action=nullptr)
Definition context.cpp:339
void setState(bool state) noexcept
Definition context.cpp:79
Request * request
Definition context.h:71
bool detached() const noexcept
Definition context.cpp:333
Cutelyst Controller base class.
Definition controller.h:56
The Cutelyst namespace holds all public Cutelyst API.
void append(QList::parameter_type value)
QList::const_reference at(qsizetype i) const const
T value(qsizetype i) const const
int parameterCount() const const
int parameterType(int index) const const
int returnType() const const
T value(const Key &key, const T &defaultValue) const const
bool isEmpty() const const
int toInt(bool *ok, int base) const const
DirectConnection