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