cutelyst  4.9.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 
10 using namespace Cutelyst;
11 using namespace Qt::Literals::StringLiterals;
12 
14  : Component(new ActionPrivate, parent)
15 {
16 }
17 
18 Action::Action(ActionPrivate *ptr, QObject *parent)
19  : Component(ptr, parent)
20 {
21 }
22 
23 Component::Modifiers Action::modifiers() const
24 {
25  return Component::OnlyExecute;
26 }
27 
28 void 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 
47 void 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 
75 QString Action::attribute(const QString &name, const QString &defaultValue) const
76 {
77  Q_D(const Action);
78  return d->attributes.value(name, defaultValue);
79 }
80 
81 void Action::setAttributes(const ParamsMultiMap &attributes)
82 {
83  Q_D(Action);
84  d->attributes = attributes;
85 }
86 
87 QString Action::className() const noexcept
88 {
89  Q_D(const Action);
90  return d->controller->objectName();
91 }
92 
93 Controller *Action::controller() const noexcept
94 {
95  Q_D(const Action);
96  return d->controller;
97 }
98 
99 bool 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 
109 bool 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 
119 QString Action::ns() const noexcept
120 {
121  Q_D(const Action);
122  return d->ns;
123 }
124 
125 qint8 Action::numberOfArgs() const
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"
Controller * controller() const noexcept
Definition: action.cpp:93
ParamsMultiMap attributes() const noexcept
Definition: action.cpp:69
Request request
Definition: context.h:72
bool doExecute(Context *c) override
Definition: action.cpp:137
virtual qint8 numberOfCaptures() const
Definition: action.cpp:131
const_reference at(qsizetype i) const const
void detach(Action *action=nullptr)
Definition: context.cpp:340
The Cutelyst Component base class.
Definition: component.h:30
virtual bool matchCaptures(int numberOfCaptures) const noexcept
Definition: action.cpp:109
This class represents a Cutelyst Action.
Definition: action.h:34
T value(qsizetype i) const const
The Cutelyst Context.
Definition: context.h:42
Cutelyst Controller base class.
Definition: controller.h:55
bool detached() const noexcept
Definition: context.cpp:334
void setAttributes(const ParamsMultiMap &attributes)
Definition: action.cpp:81
int toInt(bool *ok, int base) const const
bool isEmpty() const const
int parameterCount() const const
QString name() const noexcept
Definition: component.cpp:33
virtual bool init(Application *application, const QVariantHash &args)
Definition: component.cpp:57
The Cutelyst namespace holds all public Cutelyst API.
int parameterType(int index) const const
Action(QObject *parent=nullptr)
Definition: action.cpp:13
QString ns() const noexcept
Definition: action.cpp:119
virtual Modifiers modifiers() const override
Definition: action.cpp:23
void setController(Controller *controller)
Definition: action.cpp:41
int returnType() const const
void append(QList< T > &&value)
void setupAction(const QVariantHash &args, Application *app)
Definition: action.cpp:47
virtual bool match(int numberOfArgs) const noexcept
Definition: action.cpp:99
The Cutelyst application.
Definition: application.h:72
void setMethod(const QMetaMethod &method)
Definition: action.cpp:28
void setState(bool state) noexcept
Definition: context.cpp:80
virtual qint8 numberOfArgs() const
Definition: action.cpp:125
QString attribute(const QString &name, const QString &defaultValue={}) const
Definition: action.cpp:75
DirectConnection
QString className() const noexcept
Definition: action.cpp:87
T value(const Key &key, const T &defaultValue) const const