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