cutelyst 4.3.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
14using 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
143bool 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:36
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 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.
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