cutelyst 3.9.1
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(QLatin1String("namespace")).toString();
53
54 const auto attributes = args.value(QLatin1String("attributes")).value<ParamsMultiMap>();
55 d->attributes = attributes;
56
57 const QString argsAttr = attributes.value(QLatin1String("Args"));
58 if (!argsAttr.isEmpty()) {
59 d->numberOfArgs = qint8(argsAttr.toInt());
60 }
61
62 const QString capturesAttr = attributes.value(QLatin1String("CaptureArgs"));
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
87{
88 Q_D(const Action);
89 return QString::fromLatin1(d->controller->metaObject()->className());
90}
91
92Controller *Action::controller() const
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
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
124qint8 Action::numberOfArgs() const noexcept
125{
126 Q_D(const Action);
127 return d->numberOfArgs;
128}
129
130qint8 Action::numberOfCaptures() const noexcept
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 ret = d->method.invoke(d->controller,
153 Q_RETURN_ARG(bool, methodRet),
154 Q_ARG(Cutelyst::Context *, c),
155 Q_ARG(QStringList, c->request()->args()));
156 } else {
157 QStringList args = c->request()->args();
158 // Fill the missing arguments
159 args.append(d->emptyArgs);
160
161 ret = d->method.invoke(d->controller,
163 Q_RETURN_ARG(bool, methodRet),
164 Q_ARG(Cutelyst::Context *, c),
165 Q_ARG(QString, args.at(0)),
166 Q_ARG(QString, args.at(1)),
167 Q_ARG(QString, args.at(2)),
168 Q_ARG(QString, args.at(3)),
169 Q_ARG(QString, args.at(4)),
170 Q_ARG(QString, args.at(5)),
171 Q_ARG(QString, args.at(6)),
172 Q_ARG(QString, args.at(7)),
173 Q_ARG(QString, args.at(8)));
174 }
175
176 if (ret) {
177 c->setState(methodRet);
178 return methodRet;
179 }
180
181 // The method failed to be called which means we should detach
182 c->detach();
183 c->setState(false);
184
185 return false;
186 } else {
187 if (d->listSignature) {
188 ret = d->method.invoke(d->controller,
190 Q_ARG(Cutelyst::Context *, c),
191 Q_ARG(QStringList, c->request()->args()));
192 } else {
193 QStringList args = c->request()->args();
194 // Fill the missing arguments
195 args.append(d->emptyArgs);
196
197 ret = d->method.invoke(d->controller,
199 Q_ARG(Cutelyst::Context *, c),
200 Q_ARG(QString, args.at(0)),
201 Q_ARG(QString, args.at(1)),
202 Q_ARG(QString, args.at(2)),
203 Q_ARG(QString, args.at(3)),
204 Q_ARG(QString, args.at(4)),
205 Q_ARG(QString, args.at(5)),
206 Q_ARG(QString, args.at(6)),
207 Q_ARG(QString, args.at(7)),
208 Q_ARG(QString, args.at(8)));
209 }
210 c->setState(ret);
211 return ret;
212 }
213
214#else
215
216 /*
217 * Qt 6.5 introduced a new variadic version of QMetaMethod::invoke() that
218 * does not work with our current implementation above. The following code
219 * is a fast fix to use the new version but there might be better / more elegant
220 * approaches.
221 *
222 * See: https://codereview.qt-project.org/c/qt/qtbase/+/422745
223 *
224 * TODO: check for more flexible implementation
225 */
226
227 const QStringList args = c->request()->args();
228
229 if (d->evaluateBool) {
230 bool methodRet;
231
232 if (d->listSignature) {
233 ret = d->method.invoke(
234 d->controller, Qt::DirectConnection, qReturnArg(methodRet), c, args);
235 } else {
236 switch (d->method.parameterCount()) {
237 case 0:
238 ret = d->method.invoke(d->controller, Qt::DirectConnection, qReturnArg(methodRet));
239 break;
240 case 1:
241 ret =
242 d->method.invoke(d->controller, Qt::DirectConnection, qReturnArg(methodRet), c);
243 break;
244 case 2:
245 ret = d->method.invoke(
246 d->controller, Qt::DirectConnection, qReturnArg(methodRet), c, args.value(0));
247 break;
248 case 3:
249 ret = d->method.invoke(d->controller,
251 qReturnArg(methodRet),
252 c,
253 args.value(0),
254 args.value(1));
255 break;
256 case 4:
257 ret = d->method.invoke(d->controller,
259 qReturnArg(methodRet),
260 c,
261 args.value(0),
262 args.value(1),
263 args.value(2));
264 break;
265 case 5:
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 args.value(3));
274 break;
275 case 6:
276 ret = d->method.invoke(d->controller,
278 qReturnArg(methodRet),
279 c,
280 args.value(0),
281 args.value(1),
282 args.value(2),
283 args.value(3),
284 args.value(4));
285 break;
286 case 7:
287 ret = d->method.invoke(d->controller,
289 qReturnArg(methodRet),
290 c,
291 args.value(0),
292 args.value(1),
293 args.value(2),
294 args.value(3),
295 args.value(4),
296 args.value(5));
297 break;
298 case 8:
299 ret = d->method.invoke(d->controller,
301 qReturnArg(methodRet),
302 c,
303 args.value(0),
304 args.value(1),
305 args.value(2),
306 args.value(3),
307 args.value(4),
308 args.value(5),
309 args.value(6));
310 break;
311 case 9:
312 ret = d->method.invoke(d->controller,
314 qReturnArg(methodRet),
315 c,
316 args.value(0),
317 args.value(1),
318 args.value(2),
319 args.value(3),
320 args.value(4),
321 args.value(5),
322 args.value(6),
323 args.value(7));
324 break;
325 default:
326 ret = d->method.invoke(d->controller,
328 qReturnArg(methodRet),
329 c,
330 args.value(0),
331 args.value(1),
332 args.value(2),
333 args.value(3),
334 args.value(4),
335 args.value(5),
336 args.value(6),
337 args.value(7),
338 args.value(8));
339 break;
340 }
341 }
342
343 if (ret) {
344 c->setState(methodRet);
345 return methodRet;
346 }
347
348 // The method failed to be called which means we should detach
349 c->detach();
350 c->setState(false);
351
352 return false;
353 } else {
354 if (d->listSignature) {
355 ret = d->method.invoke(d->controller, Qt::DirectConnection, c, args);
356 } else {
357 switch (d->method.parameterCount()) {
358 case 0:
359 ret = d->method.invoke(d->controller, Qt::DirectConnection);
360 break;
361 case 1:
362 ret = d->method.invoke(d->controller, Qt::DirectConnection, c);
363 break;
364 case 2:
365 ret = d->method.invoke(d->controller, Qt::DirectConnection, c, args.value(0));
366 break;
367 case 3:
368 ret = d->method.invoke(
369 d->controller, Qt::DirectConnection, c, args.value(0), args.value(1));
370 break;
371 case 4:
372 ret = d->method.invoke(d->controller,
374 c,
375 args.value(0),
376 args.value(1),
377 args.value(2));
378 break;
379 case 5:
380 ret = d->method.invoke(d->controller,
382 c,
383 args.value(0),
384 args.value(1),
385 args.value(2),
386 args.value(3));
387 break;
388 case 6:
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 args.value(4));
397 break;
398 case 7:
399 ret = d->method.invoke(d->controller,
401 c,
402 args.value(0),
403 args.value(1),
404 args.value(2),
405 args.value(3),
406 args.value(4),
407 args.value(5));
408 break;
409 case 8:
410 ret = d->method.invoke(d->controller,
412 c,
413 args.value(0),
414 args.value(1),
415 args.value(2),
416 args.value(3),
417 args.value(4),
418 args.value(5),
419 args.value(6));
420 break;
421 case 9:
422 ret = d->method.invoke(d->controller,
424 c,
425 args.value(0),
426 args.value(1),
427 args.value(2),
428 args.value(3),
429 args.value(4),
430 args.value(5),
431 args.value(6),
432 args.value(7));
433 break;
434 default:
435 ret = d->method.invoke(d->controller,
437 c,
438 args.value(0),
439 args.value(1),
440 args.value(2),
441 args.value(3),
442 args.value(4),
443 args.value(5),
444 args.value(6),
445 args.value(7),
446 args.value(8));
447 break;
448 }
449 }
450 c->setState(ret);
451 return ret;
452 }
453
454#endif
455}
456
457#include "moc_action.cpp"
void setAttributes(const ParamsMultiMap &attributes)
Definition action.cpp:80
void setupAction(const QVariantHash &args, Application *app)
Definition action.cpp:46
virtual qint8 numberOfArgs() const noexcept
Definition action.cpp:124
QString ns() const noexcept
Definition action.cpp:118
virtual bool doExecute(Context *c) override
Definition action.cpp:136
virtual qint8 numberOfCaptures() const noexcept
Definition action.cpp:130
QString className() const
Definition action.cpp:86
virtual bool match(int numberOfArgs) const noexcept
Definition action.cpp:98
void setMethod(const QMetaMethod &method)
Definition action.cpp:27
Action(QObject *parent=nullptr)
Definition action.cpp:12
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
void setController(Controller *controller)
Definition action.cpp:40
Controller * controller() const
Definition action.cpp:92
QString attribute(const QString &name, const QString &defaultValue={}) const
Definition action.cpp:74
The Cutelyst Application.
Definition application.h:43
virtual bool init(Application *application, const QVariantHash &args)
Definition component.cpp:57
Component(QObject *parent=nullptr)
Definition component.cpp:11
QString name() const
Definition component.cpp:33
The Cutelyst Context.
Definition context.h:39
void detach(Action *action=nullptr)
Definition context.cpp:345
void setState(bool state) noexcept
Sets the state of the current executed action, setting to false will make the dispatcher skip non pro...
Definition context.cpp:78
bool detached() const noexcept
Definition context.cpp:339
The Cutelyst namespace holds all public Cutelyst API.
Definition Mainpage.dox:8
QMultiMap< QString, QString > ParamsMultiMap
void append(const T &value)
const T & at(int i) const const
T value(int i) const const
int parameterCount() const const
int parameterType(int index) const const
int returnType() const const
QObject(QObject *parent)
QObject * parent() const const
QString fromLatin1(const char *str, int size)
bool isEmpty() const const
int toInt(bool *ok, int base) const const
DirectConnection