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