cutelyst  4.5.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
dispatchtypepath.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "common.h"
6 #include "controller.h"
7 #include "dispatchtypepath_p.h"
8 #include "utils.h"
9 
10 #include <QBuffer>
11 #include <QDebug>
12 #include <QRegularExpression>
13 
14 using namespace Cutelyst;
15 
17  : DispatchType(parent)
18  , d_ptr(new DispatchTypePathPrivate)
19 {
20 }
21 
23 {
24  delete d_ptr;
25 }
26 
28 {
29  Q_D(const DispatchTypePath);
30 
31  const static QRegularExpression multipleSlashes(u"/{1,}"_qs);
32 
34 
35  auto keys = d->paths.keys();
36 
37  std::sort(keys.begin(), keys.end(), [](QStringView a, QStringView b) {
38  return a.compare(b, Qt::CaseInsensitive) < 0;
39  });
40  for (const auto &path : keys) {
41  const auto paths = d->paths.value(path);
42  for (Action *action : paths.actions) {
43  QString _path = u'/' + path;
44  if (action->attribute(u"Args"_qs).isEmpty()) {
45  _path.append(u"/...");
46  } else {
47  for (int i = 0; i < action->numberOfArgs(); ++i) {
48  _path.append(u"/*");
49  }
50  }
51  _path.replace(multipleSlashes, u"/"_qs);
52 
53  QString privateName = action->reverse();
54  if (!privateName.startsWith(u'/')) {
55  privateName.prepend(u'/');
56  }
57 
58  table.append({_path, privateName});
59  }
60  }
61 
62  return Utils::buildTable(table, {u"Path"_qs, u"Private"_qs}, u"Loaded Path actions:"_qs);
63 }
64 
67 {
68  Q_D(const DispatchTypePath);
69 
70  auto it = d->paths.constFind(path);
71  if (it == d->paths.constEnd()) {
72  return NoMatch;
73  }
74 
75  MatchType ret = NoMatch;
76  int numberOfArgs = args.size();
77  for (Action *action : it->actions) {
78  // If the number of args is -1 (not defined)
79  // it will slurp all args so we don't care
80  // about how many args was passed
81  if (action->numberOfArgs() == numberOfArgs) {
82  Request *request = c->request();
83  request->setArguments(args);
84  request->setMatch(it->name);
85  setupMatchedAction(c, action);
86  return ExactMatch;
87  } else if (action->numberOfArgs() == -1 && !c->action()) {
88  // Only setup partial matches if no action is
89  // currently set
90  Request *request = c->request();
91  request->setArguments(args);
92  request->setMatch(it->name);
93  setupMatchedAction(c, action);
94  ret = PartialMatch;
95  }
96  }
97  return ret;
98 }
99 
101 {
102  Q_D(DispatchTypePath);
103 
104  bool ret = false;
105  const auto attributes = action->attributes();
106  const auto range = attributes.equal_range(u"Path"_qs);
107  for (auto i = range.first; i != range.second; ++i) {
108  if (d->registerPath(*i, action)) {
109  ret = true;
110  }
111  }
112 
113  // We always register valid actions
114  return ret;
115 }
116 
118 {
119  Q_D(const DispatchTypePath);
120  return !d->paths.isEmpty();
121 }
122 
124 {
125  QString ret;
126  if (captures.isEmpty()) {
127  const auto attributes = action->attributes();
128  auto it = attributes.constFind(u"Path"_qs);
129  if (it != attributes.constEnd()) {
130  const QString &path = it.value();
131  if (path.isEmpty()) {
132  ret = u"/"_qs;
133  } else if (!path.startsWith(u'/')) {
134  ret = u'/' + path;
135  } else {
136  ret = path;
137  }
138  }
139  }
140  return ret;
141 }
142 
143 bool DispatchTypePathPrivate::registerPath(const QString &path, Action *action)
144 {
145  QString _path = path;
146  // TODO see if we can make controllers fix this
147  if (_path.isEmpty()) {
148  _path = u"/"_qs;
149  } else if (!_path.startsWith(u'/')) {
150  _path.prepend(u'/');
151  }
152 
153  auto it = paths.find(_path);
154  if (it != paths.end()) {
155  int actionNumberOfArgs = action->numberOfArgs();
156  auto &actions = it->actions;
157  for (const Action *regAction : actions) {
158  if (regAction->numberOfArgs() == actionNumberOfArgs) {
159  qCCritical(CUTELYST_DISPATCHER_PATH)
160  << "Not registering Action" << action->name() << "of controller"
161  << action->controller()->objectName() << "because it conflicts with"
162  << regAction->name() << "of controller"
163  << regAction->controller()->objectName();
164  return false;
165  }
166  }
167 
168  actions.push_back(action);
169  std::sort(actions.begin(), actions.end(), [](Action *a, Action *b) -> bool {
170  return a->numberOfArgs() < b->numberOfArgs();
171  });
172  } else {
173  paths.insert(_path, DispatchTypePathReplacement{_path, {action}});
174  }
175  return true;
176 }
177 
178 #include "moc_dispatchtypepath.cpp"
This class represents a Cutelyst Action.
Definition: action.h:35
virtual qint8 numberOfArgs() const
Definition: action.cpp:124
ParamsMultiMap attributes() const noexcept
Definition: action.cpp:68
Controller * controller() const noexcept
Definition: action.cpp:92
QString attribute(const QString &name, const QString &defaultValue={}) const
Definition: action.cpp:74
QString reverse() const noexcept
Definition: component.cpp:45
QString name() const noexcept
Definition: component.cpp:33
The Cutelyst Context.
Definition: context.h:42
Request * request
Definition: context.h:71
Action * action
Definition: context.h:47
Describes a path dispatch type.
DispatchTypePath(QObject *parent=nullptr)
QByteArray list() const override
MatchType match(Context *c, QStringView path, const QStringList &args) const override
QString uriForAction(Action *action, const QStringList &captures) const override
bool registerAction(Action *action) override
Abstract class to described a dispatch type.
Definition: dispatchtype.h:25
void setupMatchedAction(Context *c, Action *action) const
A request.
Definition: request.h:42
void setArguments(const QStringList &arguments)
Definition: request.cpp:155
void setMatch(const QString &match)
Definition: request.cpp:143
The Cutelyst namespace holds all public Cutelyst API.
void append(QList::parameter_type value)
bool isEmpty() const const
qsizetype size() const const
QMultiMap::const_iterator constFind(const Key &key) const const
QPair< QMultiMap::iterator, QMultiMap::iterator > equal_range(const Key &key)
QString & append(QChar ch)
bool isEmpty() const const
QString & prepend(QChar ch)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const