cutelyst  5.0.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 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::ranges::sort(
39  keys, [](QStringView a, QStringView b) { return a.compare(b, Qt::CaseInsensitive) < 0; });
40  for (const auto &path : std::as_const(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"_s).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"/"_s);
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,
63  {
64  u"Path"_s,
65  u"Private"_s,
66  },
67  u"Loaded Path actions:"_s);
68 }
69 
72 {
73  Q_D(const DispatchTypePath);
74 
75  auto it = d->paths.constFind(path);
76  if (it == d->paths.constEnd()) {
77  return NoMatch;
78  }
79 
80  MatchType ret = NoMatch;
81  int numberOfArgs = args.size();
82  for (Action *action : it->actions) {
83  // If the number of args is -1 (not defined)
84  // it will slurp all args so we don't care
85  // about how many args was passed
86  if (action->numberOfArgs() == numberOfArgs) {
87  Request *request = c->request();
88  request->setArguments(args);
89  request->setMatch(it->name);
90  setupMatchedAction(c, action);
91  return ExactMatch;
92  } else if (action->numberOfArgs() == -1 && !c->action()) {
93  // Only setup partial matches if no action is
94  // currently set
95  Request *request = c->request();
96  request->setArguments(args);
97  request->setMatch(it->name);
98  setupMatchedAction(c, action);
99  ret = PartialMatch;
100  }
101  }
102  return ret;
103 }
104 
106 {
107  Q_D(DispatchTypePath);
108 
109  bool ret = false;
110  const auto attributes = action->attributes();
111  const auto range = attributes.equal_range(u"Path"_s);
112  for (auto i = range.first; i != range.second; ++i) {
113  if (d->registerPath(*i, action)) {
114  ret = true;
115  }
116  }
117 
118  // We always register valid actions
119  return ret;
120 }
121 
123 {
124  Q_D(const DispatchTypePath);
125  return !d->paths.isEmpty();
126 }
127 
129 {
130  QString ret;
131  if (captures.isEmpty()) {
132  const auto attributes = action->attributes();
133  auto it = attributes.constFind(u"Path"_s);
134  if (it != attributes.constEnd()) {
135  const QString &path = it.value();
136  if (path.isEmpty()) {
137  ret = u"/"_s;
138  } else if (!path.startsWith(u'/')) {
139  ret = u'/' + path;
140  } else {
141  ret = path;
142  }
143  }
144  }
145  return ret;
146 }
147 
148 bool DispatchTypePathPrivate::registerPath(const QString &path, Action *action)
149 {
150  QString _path = path;
151  // TODO see if we can make controllers fix this
152  if (_path.isEmpty()) {
153  _path = u"/"_s;
154  } else if (!_path.startsWith(u'/')) {
155  _path.prepend(u'/');
156  }
157 
158  auto it = paths.find(_path);
159  if (it != paths.end()) {
160  qint8 actionNumberOfArgs = action->numberOfArgs();
161  auto &actions = it->actions;
162  bool conflict = std::ranges::any_of(actions, [&](const Action *regAction) {
163  if (regAction->numberOfArgs() == actionNumberOfArgs) {
164  qCCritical(CUTELYST_DISPATCHER_PATH)
165  << "Not registering Action" << action->name() << "of controller"
166  << action->controller()->objectName() << "because it conflicts with"
167  << regAction->name() << "of controller"
168  << regAction->controller()->objectName();
169  return true;
170  }
171  return false;
172  });
173  if (conflict) {
174  return false;
175  }
176 
177  actions.push_back(action);
178  std::ranges::sort(actions, [](const Action *a, const Action *b) -> bool {
179  return a->numberOfArgs() < b->numberOfArgs();
180  });
181  } else {
182  paths.insert(_path,
183  DispatchTypePathReplacement{
184  .name = _path,
185  .actions = {action},
186  });
187  }
188  return true;
189 }
190 
191 #include "moc_dispatchtypepath.cpp"
Controller * controller() const noexcept
Definition: action.cpp:92
ParamsMultiMap attributes() const noexcept
Definition: action.cpp:68
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:143
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:155
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:124
QString attribute(const QString &name, const QString &defaultValue={}) const
Definition: action.cpp:74
MatchType match(Context *c, QStringView path, const QStringList &args) const override