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 
10 using namespace Cutelyst;
11 
13  : Component(new ActionPrivate, parent)
14 {
15 }
16 
17 Action::Action(ActionPrivate *ptr, QObject *parent)
18  : Component(ptr, parent)
19 {
20 }
21 
22 Component::Modifiers Action::modifiers() const
23 {
24  return Component::OnlyExecute;
25 }
26 
27 void 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 
46 void 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 
74 QString Action::attribute(const QString &name, const QString &defaultValue) const
75 {
76  Q_D(const Action);
77  return d->attributes.value(name, defaultValue);
78 }
79 
80 void Action::setAttributes(const ParamsMultiMap &attributes)
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 
93 {
94  Q_D(const Action);
95  return d->controller;
96 }
97 
98 bool 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 
108 bool 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 
118 QString Action::ns() const noexcept
119 {
120  Q_D(const Action);
121  return d->ns;
122 }
123 
124 qint8 Action::numberOfArgs() const noexcept
125 {
126  Q_D(const Action);
127  return d->numberOfArgs;
128 }
129 
130 qint8 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"
ParamsMultiMap attributes() const noexcept
Definition: action.cpp:68
virtual bool doExecute(Context *c) override
Definition: action.cpp:136
const_reference at(qsizetype i) const const
void detach(Action *action=nullptr)
Definition: context.cpp:345
The Cutelyst Component base class.
Definition: component.h:25
virtual bool matchCaptures(int numberOfCaptures) const noexcept
Definition: action.cpp:108
This class represents a Cutelyst Action.
Definition: action.h:34
T value(qsizetype i) const const
The Cutelyst Context.
Definition: context.h:38
Cutelyst Controller base class
Definition: controller.h:87
virtual qint8 numberOfArgs() const noexcept
Definition: action.cpp:124
QString name() const
Definition: component.cpp:33
bool detached() const noexcept
Definition: context.cpp:339
void setAttributes(const ParamsMultiMap &attributes)
Definition: action.cpp:80
int toInt(bool *ok, int base) const const
bool isEmpty() const const
int parameterCount() const const
virtual bool init(Application *application, const QVariantHash &args)
Definition: component.cpp:57
virtual qint8 numberOfCaptures() const noexcept
Definition: action.cpp:130
Controller * controller() const
Definition: action.cpp:92
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
int parameterType(int index) const const
Action(QObject *parent=nullptr)
Definition: action.cpp:12
QString ns() const noexcept
Definition: action.cpp:118
virtual Modifiers modifiers() const override
Definition: action.cpp:22
void setController(Controller *controller)
Definition: action.cpp:40
int returnType() const const
QString fromLatin1(QByteArrayView str)
void append(QList< T > &&value)
void setupAction(const QVariantHash &args, Application *app)
Definition: action.cpp:46
virtual bool match(int numberOfArgs) const noexcept
Definition: action.cpp:98
The Cutelyst Application.
Definition: application.h:42
void setMethod(const QMetaMethod &method)
Definition: action.cpp:27
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
QString attribute(const QString &name, const QString &defaultValue={}) const
Definition: action.cpp:74
DirectConnection
T value(const Key &key, const T &defaultValue) const const
QString className() const
Definition: action.cpp:86