cutelyst  3.7.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 "dispatchtypepath_p.h"
6 
7 #include "common.h"
8 #include "controller.h"
9 #include "utils.h"
10 
11 #include <QBuffer>
12 #include <QRegularExpression>
13 #include <QDebug>
14 
15 using namespace Cutelyst;
16 
18  DispatchType(parent),
19  d_ptr(new DispatchTypePathPrivate)
20 {
21 }
22 
23 DispatchTypePath::~DispatchTypePath()
24 {
25  delete d_ptr;
26 }
27 
28 QByteArray DispatchTypePath::list() const
29 {
30  Q_D(const DispatchTypePath);
31 
32  QRegularExpression multipleSlashes(QLatin1String("/{1,}"));
33 
34  QVector<QStringList> table;
35 
36  QStringList keys = d->paths.keys();
37  keys.sort(Qt::CaseInsensitive);
38  for (const QString &path : keys) {
39  const auto paths = d->paths.value(path);
40  for (Action *action : paths) {
41  QString _path = QLatin1Char('/') + path;
42  if (action->attribute(QLatin1String("Args")).isEmpty()) {
43  _path.append(QLatin1String("/..."));
44  } else {
45  for (int i = 0; i < action->numberOfArgs(); ++i) {
46  _path.append(QLatin1String("/*"));
47  }
48  }
49  _path.replace(multipleSlashes, QLatin1String("/"));
50 
51  QString privateName = action->reverse();
52  if (!privateName.startsWith(QLatin1Char('/'))) {
53  privateName.prepend(QLatin1Char('/'));
54  }
55 
56  table.append({ _path, privateName });
57  }
58  }
59 
60  return Utils::buildTable(table, { QLatin1String("Path"), QLatin1String("Private") },
61  QLatin1String("Loaded Path actions:"));
62 }
63 
64 Cutelyst::DispatchType::MatchType DispatchTypePath::match(Context *c, const QString &path, const QStringList &args) const
65 {
66  Q_D(const DispatchTypePath);
67 
68  QString _path = path;
69  if (_path.isEmpty()) {
70  _path = QStringLiteral("/");
71  }
72 
73  const auto it = d->paths.constFind(_path);
74  if (it == d->paths.constEnd()) {
75  return NoMatch;
76  }
77 
78  MatchType ret = NoMatch;
79  int numberOfArgs = args.size();
80  for (Action *action : it.value()) {
81  // If the number of args is -1 (not defined)
82  // it will slurp all args so we don't care
83  // about how many args was passed
84  if (action->numberOfArgs() == numberOfArgs) {
85  Request *request = c->request();
86  request->setArguments(args);
87  request->setMatch(_path);
88  setupMatchedAction(c, action);
89  return ExactMatch;
90  } else if (action->numberOfArgs() == -1 &&
91  !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 
127 QString DispatchTypePath::uriForAction(Cutelyst::Action *action, const QStringList &captures) const
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) << "Not registering Action"
163  << action->name()
164  << "of controller"
165  << action->controller()->objectName()
166  << "because it conflicts with"
167  << regAction->name()
168  << "of controller"
169  << regAction->controller()->objectName();
170  return false;
171  }
172  }
173 
174  it.value().push_back(action);
175  std::sort(it.value().begin(), it.value().end(), [](Action *a, Action *b) -> bool {
176  return a->numberOfArgs() < b->numberOfArgs();
177  });
178  } else {
179  paths.insert(_path, { action });
180  }
181  return true;
182 }
183 
184 #include "moc_dispatchtypepath.cpp"
This class represents a Cutelyst Action.
Definition: action.h:35
virtual qint8 numberOfArgs() const noexcept
Definition: action.cpp:122
ParamsMultiMap attributes() const noexcept
Definition: action.cpp:66
Controller * controller() const
Definition: action.cpp:90
QString name() const
Definition: component.cpp:31
The Cutelyst Context.
Definition: context.h:39
virtual MatchType match(Context *c, const QString &path, const QStringList &args) const override
DispatchTypePath(QObject *parent=nullptr)
virtual QByteArray list() const override
list the registered actions To be implemented by subclasses
virtual bool inUse() override
virtual QString uriForAction(Action *action, const QStringList &captures) const override
virtual bool registerAction(Action *action) override
registerAction
void setupMatchedAction(Context *c, Action *action) const
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.
Definition: Mainpage.dox:8