cutelyst 5.0.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;
15using 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
148bool 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"
This class represents a Cutelyst Action.
Definition action.h:35
virtual qint8 numberOfArgs() const
Definition action.cpp:124
ParamsMultiMap attributes() const noexcept
Definition action.cpp:68
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
T value(qsizetype i) 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
int compare(QChar ch) const const
CaseInsensitive