5#include "actionchain.h"
8#include "dispatchtypechained_p.h"
18 , d_ptr(new DispatchTypeChainedPrivate)
32 Actions endPoints = d->endPoints;
33 std::ranges::sort(endPoints, [](
const Action *a,
const Action *b) ->
bool {
39 for (
Action *endPoint : std::as_const(endPoints)) {
41 if (endPoint->numberOfArgs() == -1) {
44 for (
int i = 0; i < endPoint->numberOfArgs(); ++i) {
50 QString extra = DispatchTypeChainedPrivate::listExtraHttpMethods(endPoint);
51 QString consumes = DispatchTypeChainedPrivate::listExtraConsumes(endPoint);
53 Action *current = endPoint;
60 const QStringList pathParts = attributes.values(u
"PathPart"_s);
61 for (
const QString &part : pathParts) {
62 if (!part.isEmpty()) {
67 parent = attributes.value(u
"Chained"_s);
68 current = d->actions.value(
parent);
74 if (
parent.compare(u
"/") != 0) {
77 row.
append(u
'/' + endPoint->reverse());
82 unattachedTable.
append(row);
87 for (
const Action *p : parents) {
88 QString name = u
'/' + p->reverse();
90 QString extraHttpMethod = DispatchTypeChainedPrivate::listExtraHttpMethods(p);
91 if (!extraHttpMethod.
isEmpty()) {
92 name.
prepend(extraHttpMethod + u
' ');
95 const auto attributes = p->attributes();
96 auto it = attributes.constFind(u
"CaptureArgs"_s);
97 if (it != attributes.constEnd()) {
98 name.
append(u
" (" + it.value() + u
')');
103 QString ct = DispatchTypeChainedPrivate::listExtraConsumes(p);
108 if (p != parents[0]) {
109 name = u
"-> " + name;
120 line.
append(extra + u
' ');
122 line.
append(u
'/' + endPoint->reverse());
123 if (endPoint->numberOfArgs() == -1) {
130 line.
append(u
" :" + consumes);
134 rows[0][0] = u
'/' + parts.
join(u
'/');
140 if (!paths.isEmpty()) {
141 out << Utils::buildTable(paths,
146 u
"Loaded Chained actions:"_s);
149 if (!unattachedTable.
isEmpty()) {
150 out << Utils::buildTable(unattachedTable,
155 u
"Unattached Chained actions:"_s);
170 const BestActionMatch ret = d->recurseMatch(args.
size(), u
"/"_s, path.
mid(1).
split(u
'/'));
172 if (ret.isNull || chain.
isEmpty()) {
177 const auto parts = ret.parts;
178 for (
const auto &arg : parts) {
180 decodedArgs.
append(Utils::decodePercentEncoding(&aux));
187 for (
const auto a : ret.captures) {
188 captures.
append(a.toString());
191 request->
setMatch(u
'/' + action->reverse());
202 const QStringList chainedList = attributes.values(u
"Chained"_s);
207 if (chainedList.
size() > 1) {
208 qCCritical(CUTELYST_DISPATCHER_CHAINED)
209 <<
"Multiple Chained attributes not supported registering" << action->
reverse();
214 if (chainedTo == u
'/' + action->
name()) {
215 qCCritical(CUTELYST_DISPATCHER_CHAINED)
216 <<
"Actions cannot chain to themselves registering /" << action->
name();
220 const QStringList pathPart = attributes.values(u
"PathPart"_s);
224 if (pathPart.
size() == 1 && !pathPart[0].
isEmpty()) {
226 }
else if (pathPart.
size() > 1) {
227 qCCritical(CUTELYST_DISPATCHER_CHAINED)
228 <<
"Multiple PathPart attributes not supported registering" << action->
reverse();
233 qCCritical(CUTELYST_DISPATCHER_CHAINED)
234 <<
"Absolute parameters to PathPart not allowed registering" << action->
reverse();
238 attributes.
replace(u
"PathPart"_s, part);
241 auto &childrenOf = d->childrenOf[chainedTo][part];
242 childrenOf.insert(childrenOf.begin(), action);
244 d->actions[u
'/' + action->
reverse()] = action;
246 if (!d->checkArgsAttr(action, u
"Args"_s) || !d->checkArgsAttr(action, u
"CaptureArgs"_s)) {
250 if (attributes.contains(u
"Args"_s) && attributes.contains(u
"CaptureArgs"_s)) {
251 qCCritical(CUTELYST_DISPATCHER_CHAINED)
252 <<
"Combining Args and CaptureArgs attributes not supported registering"
257 if (!attributes.contains(u
"CaptureArgs"_s)) {
270 if (!(attributes.
contains(u
"Chained"_s) && !attributes.
contains(u
"CaptureArgs"_s))) {
271 qCWarning(CUTELYST_DISPATCHER_CHAINED)
272 <<
"uriForAction: action is not an end point" << action;
279 const Action *curr = action;
282 if (curr_attributes.
contains(u
"CaptureArgs"_s)) {
285 qCWarning(CUTELYST_DISPATCHER_CHAINED)
295 const QString pp = curr_attributes.
value(u
"PathPart"_s);
301 curr = d->actions.value(
parent);
304 if (
parent.compare(u
"/") != 0) {
306 qCWarning(CUTELYST_DISPATCHER_CHAINED) <<
"uriForAction: dangling action" <<
parent;
310 if (!localCaptures.
isEmpty()) {
312 qCWarning(CUTELYST_DISPATCHER_CHAINED)
313 <<
"uriForAction: too many captures" << localCaptures;
317 ret = u
'/' + parts.
join(u
'/');
326 if (qobject_cast<ActionChain *>(action)) {
341 curr = d->actions.value(
parent);
351 if (d->actions.isEmpty()) {
360BestActionMatch DispatchTypeChainedPrivate::recurseMatch(
int reqArgsSize,
364 BestActionMatch bestAction;
365 const auto it = childrenOf.constFind(parent);
366 if (it == childrenOf.constEnd()) {
370 const StringActionsMap &children = it.value();
372 std::ranges::sort(keys, [](
const QString &a,
const QString &b) ->
bool {
377 for (
const QString &tryPart : std::as_const(keys)) {
378 auto parts = pathParts;
379 if (!tryPart.isEmpty()) {
382 int tryPartCount = tryPart.
count(u
'/') + 1;
383 const auto possibleParts = parts.mid(0, tryPartCount);
387 for (
const auto part : possibleParts) {
389 possiblePartsString = part.toString();
392 possiblePartsString.
append(u
'/' + part);
396 if (tryPart != possiblePartsString) {
399 parts = parts.
mid(tryPartCount);
402 const Actions tryActions = children.value(tryPart);
403 for (
Action *action : tryActions) {
405 if (attributes.
contains(u
"CaptureArgs"_s)) {
406 const auto captureCount = action->numberOfCaptures();
408 if (parts.size() < captureCount) {
413 const auto captures = parts.mid(0, captureCount);
416 if (!action->matchCaptures(captures.size())) {
420 const auto localParts = parts.mid(captureCount);
423 const BestActionMatch ret =
424 recurseMatch(reqArgsSize, u
'/' + action->reverse(), localParts);
430 const auto actionCaptures = ret.captures;
431 const auto actionParts = ret.parts;
432 int bestActionParts = bestAction.parts.
size();
435 (bestAction.isNull || actionParts.size() < bestActionParts ||
436 (actionParts.size() == bestActionParts &&
437 actionCaptures.size() < bestAction.captures.size() &&
438 ret.n_pathParts > bestAction.n_pathParts))) {
440 int pathparts = attributes.
value(u
"PathPart"_s).count(u
'/') + 1;
441 bestAction.actions = bestActions;
442 bestAction.captures = captures + actionCaptures;
443 bestAction.parts = actionParts;
444 bestAction.n_pathParts = pathparts + ret.n_pathParts;
445 bestAction.isNull =
false;
448 if (!action->match(reqArgsSize + parts.size())) {
453 const int pathparts = attributes.
value(u
"PathPart"_s).count(u
'/') + 1;
461 if (bestAction.isNull || parts.size() < bestAction.parts.size() ||
462 (parts.isEmpty() && !argsAttr.
isEmpty() && action->numberOfArgs() == 0)) {
463 bestAction.actions = {action};
464 bestAction.captures = {};
465 bestAction.parts = parts;
466 bestAction.n_pathParts = pathparts;
467 bestAction.isNull =
false;
476bool DispatchTypeChainedPrivate::checkArgsAttr(
const Action *action,
const QString &name)
const
484 if (values.
size() > 1) {
485 qCCritical(CUTELYST_DISPATCHER_CHAINED)
486 <<
"Multiple" << name <<
"attributes not supported registering" << action->
reverse();
493 qCCritical(CUTELYST_DISPATCHER_CHAINED)
494 <<
"Invalid" << name <<
"(" << args <<
") for action" << action->
reverse() <<
"(use '"
495 << name <<
"' or '" << name <<
"(<number>)')";
502QString DispatchTypeChainedPrivate::listExtraHttpMethods(
const Action *action)
506 if (attributes.
contains(u
"HTTP_METHODS"_s)) {
508 ret = extra.
join(u
", ");
513QString DispatchTypeChainedPrivate::listExtraConsumes(
const Action *action)
517 if (attributes.
contains(u
"CONSUMES"_s)) {
519 ret = extra.
join(u
", ");
524#include "moc_dispatchtypechained.cpp"
Holds a chain of Cutelyst actions.
This class represents a Cutelyst Action.
void setAttributes(const ParamsMultiMap &attributes)
virtual qint8 numberOfCaptures() const
ParamsMultiMap attributes() const noexcept
QString attribute(const QString &name, const QString &defaultValue={}) const
QString reverse() const noexcept
QString name() const noexcept
Describes a chained dispatch type.
MatchType match(Context *c, QStringView path, const QStringList &args) const override
QString uriForAction(Action *action, const QStringList &captures) const override
QByteArray list() const override
bool registerAction(Action *action) override
Action * expandAction(const Context *c, Action *action) const final
~DispatchTypeChained() override
DispatchTypeChained(QObject *parent=nullptr)
Abstract class to described a dispatch type.
void setupMatchedAction(Context *c, Action *action) const
void setCaptures(const QStringList &captures)
void setArguments(const QStringList &arguments)
void setMatch(const QString &match)
The Cutelyst namespace holds all public Cutelyst API.
void append(QList::parameter_type value)
qsizetype count() const const
bool isEmpty() const const
QList< T > mid(qsizetype pos, qsizetype length) const const
void prepend(QList::parameter_type value)
qsizetype size() const const
bool contains(const Key &key) const const
T value(const Key &key, const T &defaultValue) const const
QList< T > values() const const
QObject * parent() const const
QString & append(QChar ch)
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) const const
QString number(double n, char format, int precision)
QString & prepend(QChar ch)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
QString join(QChar separator) const const
QStringView mid(qsizetype start, qsizetype length) const const
QList< QStringView > split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const