cutelyst  4.4.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 
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(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 
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 
86 QString Action::className() const noexcept
87 {
88  Q_D(const Action);
89  return d->controller->objectName();
90 }
91 
92 Controller *Action::controller() const noexcept
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
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:35
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