Revision control

++ // Copyright (C) 2025 Jarek Kobus
++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++
++ #include <QtTaskTree/qconditional.h>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QtTaskTree {
++
++ /*!
++ \class QtTaskTree::If
++ \inheaderfile qconditional.h
++ \inmodule QtTaskTree
++ \brief An "if" element used in conditional expressions.
++ \reentrant
++
++ An initial condition element of the conditional expressions.
++ Must always be followed by \l Then element.
++
++ The conditional expressions in the TaskTree module enable processing of
++ branch conditions and their bodies in an asynchronous way. For example:
++
++ \code
++ const Group recipe {
++ If (conditionTask1) >> Then {
++ bodyTask1
++ } >> ElseIf (conditionTask2) >> Then {
++ bodyTask2
++ } >> Else {
++ bodyTask3
++ }
++ };
++ \endcode
++
++ When the above recipe is run with QTaskTree, the task tree starts the
++ asynchronous \c conditionTask1 first. After it's finished,
++ depending on the result, the task tree may either execute the
++ \c bodyTask1 when the \c conditionTask1 finished with success,
++ or dispatch another condition by executing \c conditionTask2 otherwise.
++ In case the \c bodyTask1 is executed, no other tasks are executed,
++ and the result of \c bodyTask1 is the final result of the whole
++ conditional expression.
++
++ \sa Then, Else, ElseIf
++ */
++
++ class IfPrivate : public QSharedData
++ {
++ public:
++ IfPrivate(const ExecutableItem &condition) : m_condition(condition) {}
++ ExecutableItem m_condition;
++ };
++
++ /*!
++ \fn template <typename Handler, std::enable_if_t<!std::is_base_of_v<ExecutableItem, std::decay_t<Handler>>, bool> = true> If::If(Handler &&handler)
++ \overload
++
++ A helper constructor accepting the synchronous \a handler to be executed
++ when evaluating the initial condition by the running QTaskTree.
++
++ It's a shortcut for:
++ \code
++ If (QSyncTask(handler))
++ \endcode
++
++ See \l QSyncTask for more information on what handler types are acceptable.
++
++ \sa QSyncTask, ElseIf
++ */
++
++ /*!
++ Creates an initial condition element with \a condition task to be used in
++ conditional expression. The running QTaskTree executes the passed
++ \a condition first, and after it's finished, either the \l Then branch
++ is executed (on success), or optionally provided \l Else branch
++ or \l ElseIf condition otherwise.
++
++ The passed \a condition may consist of multiple tasks enclosed
++ in \l Group element, or be a conjunction or disjunction of them, like:
++
++ \code
++ const Group subRecipe {
++ parallel,
++ parallelConditionTask1,
++ parallelConditionTask2
++ };
++
++ const Group recipe {
++ If (conditionTask1 && !conditionTask2) >> Then {
++ bodyTask1
++ } >> ElseIf (subRecipe) >> Then {
++ bodyTask2
++ } >> Else {
++ bodyTask3
++ }
++ };
++ \endcode
++ */
++ If::If(const ExecutableItem &condition) : d(new IfPrivate{condition}) {}
++
++ ExecutableItem If::condition() const { return d->m_condition; }
++
++ /*!
++ \class QtTaskTree::ElseIf
++ \inheaderfile qconditional.h
++ \inmodule QtTaskTree
++ \brief An "else if" element used in conditional expressions.
++ \reentrant
++
++ An alternative condition element of the conditional expressions.
++ Must always be preceeded and followed by \l Then element.
++
++ \sa If, Then, Else
++ */
++
++ class ElseIfPrivate : public QSharedData
++ {
++ public:
++ ElseIfPrivate(const ExecutableItem &condition) : m_condition(condition) {}
++ ExecutableItem m_condition;
++ };
++
++ /*!
++ \fn template <typename Handler, std::enable_if_t<!std::is_base_of_v<ExecutableItem, std::decay_t<Handler>>, bool> = true> ElseIf::ElseIf(Handler &&handler)
++ \overload
++
++ A helper constructor accepting the synchronous \a handler to be executed
++ when evaluating the initial condition by the running QTaskTree.
++
++ It's a shortcut for:
++ \code
++ ElseIf (QSyncTask(handler))
++ \endcode
++
++ See \l QSyncTask for more information on what handler types are acceptable.
++
++ \sa QSyncTask, If
++ */
++
++ /*!
++ Creates an alternative condition element with \a condition task
++ to be used in conditional expression.
++
++ \sa If
++ */
++ ElseIf::ElseIf(const ExecutableItem &condition) : d(new ElseIfPrivate{condition}) {}
++
++ ExecutableItem ElseIf::condition() const { return d->m_condition; }
++
++ /*!
++ \class QtTaskTree::Else
++ \inheaderfile qconditional.h
++ \inmodule QtTaskTree
++ \brief An "else" element used in conditional expressions.
++ \reentrant
++
++ A body of an else branch of the conditional expressions.
++ Must always be preceeded by \l Then element.
++
++ \sa If, Then, ElseIf
++ */
++
++ class ElsePrivate : public QSharedData
++ {
++ public:
++ ElsePrivate(const Group &body) : m_body(body) {}
++ Group m_body;
++ };
++
++ /*!
++ Creates an else branch body element of the conditional expressions
++ with \a children items. If the previous condition finishes with an error,
++ the preceeding \l Then body is skipped, and \c this branch is selected.
++ In this case the running QTaskTree executes the \a children,
++ and the result of this execution is the final result of the whole
++ conditional expression.
++ */
++ Else::Else(const GroupItems &children) : d(new ElsePrivate{{children}}) {}
++
++ /*!
++ \overload
++
++ Constructs an else branch body element from \c std::initializer_list
++ given by \a children.
++ */
++ Else::Else(std::initializer_list<GroupItem> children) : d(new ElsePrivate{{children}}) {}
++
++ Group Else::body() const { return d->m_body; }
++
++ /*!
++ \class QtTaskTree::Then
++ \inheaderfile qconditional.h
++ \inmodule QtTaskTree
++ \brief A "then" element used in conditional expressions.
++ \reentrant
++
++ A branch body element of the conditional expressions.
++ Must always be preceeded by \l If or \l ElseIf element.
++ May be followed by \l Else or \l ElseIf element.
++
++ \sa If, Else, ElseIf
++ */
++
++ class ThenPrivate : public QSharedData
++ {
++ public:
++ ThenPrivate(const Group &body) : m_body(body) {}
++ Group m_body;
++ };
++
++ /*!
++ Creates a branch body element with \a children items, to be used in
++ conditional expression. If the preceeding \l If or \l ElseIf element
++ finishes with success, the running QTaskTree executes the \a children,
++ and the result of this execution is the final result of the whole
++ conditional expression.
++ */
++ Then::Then(const GroupItems &children) : d(new ThenPrivate{{children}}) {}
++
++ /*!
++ \overload
++
++ Constructs a branch body element from \c std::initializer_list
++ given by \a children.
++ */
++ Then::Then(std::initializer_list<GroupItem> children) : d(new ThenPrivate{{children}}) {}
++
++ Group Then::body() const { return d->m_body; }
++
++ class ConditionData final
++ {
++ public:
++ std::optional<ExecutableItem> m_condition = std::nullopt;
++ Group m_body;
++ };
++
++ static Group conditionRecipe(const Storage<bool> &bodyExecutedStorage, const ConditionData &condition)
++ {
++ const auto onSetup = [bodyExecutedStorage] {
++ return *bodyExecutedStorage ? SetupResult::StopWithSuccess : SetupResult::Continue;
++ };
++
++ const auto onBodyDone = [bodyExecutedStorage] { *bodyExecutedStorage = true; };
++
++ const Group bodyTask { condition.m_body, onGroupDone(onBodyDone) };
++
++ return {
++ onGroupSetup(onSetup),
++ condition.m_condition ? Group { !*condition.m_condition || bodyTask } : bodyTask
++ };
++ }
++
++ static ExecutableItem conditionsRecipe(const QList<ConditionData> &conditions)
++ {
++ Storage<bool> bodyExecutedStorage;
++
++ GroupItems recipes;
++ for (const ConditionData &condition : conditions)
++ recipes << conditionRecipe(bodyExecutedStorage, condition);
++
++ return Group { bodyExecutedStorage, recipes };
++ }
++
++ class ThenItemPrivate : public QSharedData
++ {
++ public:
++ ThenItemPrivate(const QList<ConditionData> &conditions) : m_conditions(conditions) {}
++ QList<ConditionData> m_conditions;
++ };
++
++ ThenItem::operator ExecutableItem() const
++ {
++ return conditionsRecipe(d->m_conditions);
++ }
++
++ ThenItem::ThenItem(const If &ifItem, const Then &thenItem)
++ : d(new ThenItemPrivate{{{ifItem.condition(), thenItem.body()}}}) {}
++
++ ThenItem::ThenItem(const ElseIfItem &elseIfItem, const Then &thenItem)
++ : d(new ThenItemPrivate{elseIfItem.conditions()})
++ {
++ d->m_conditions.append({elseIfItem.nextCondition(), thenItem.body()});
++ }
++
++ QList<ConditionData> ThenItem::conditions() const { return d->m_conditions; }
++
++ class ElseItemPrivate : public QSharedData
++ {
++ public:
++ ElseItemPrivate(const QList<ConditionData> &conditions) : m_conditions(conditions) {}
++ QList<ConditionData> m_conditions;
++ };
++
++ ElseItem::operator ExecutableItem() const
++ {
++ return conditionsRecipe(d->m_conditions);
++ }
++
++ ElseItem::ElseItem(const ThenItem &thenItem, const Else &elseItem)
++ : d(new ElseItemPrivate{thenItem.conditions()})
++ {
++ d->m_conditions.append({{}, elseItem.body()});
++ }
++
++ class ElseIfItemPrivate : public QSharedData
++ {
++ public:
++ ElseIfItemPrivate(const QList<ConditionData> &conditions, const ExecutableItem &nextCondition)
++ : m_conditions(conditions)
++ , m_nextCondition(nextCondition)
++ {}
++ QList<ConditionData> m_conditions;
++ ExecutableItem m_nextCondition;
++ };
++
++ ElseIfItem::ElseIfItem(const ThenItem &thenItem, const ElseIf &elseIfItem)
++ : d(new ElseIfItemPrivate{thenItem.conditions(), elseIfItem.condition()}) { }
++
++ QList<ConditionData> ElseIfItem::conditions() const { return d->m_conditions; }
++
++ ExecutableItem ElseIfItem::nextCondition() const { return d->m_nextCondition; }
++
++ ThenItem operator>>(const If &ifItem, const Then &thenItem)
++ {
++ return {ifItem, thenItem};
++ }
++
++ ThenItem operator>>(const ElseIfItem &elseIfItem, const Then &thenItem)
++ {
++ return {elseIfItem, thenItem};
++ }
++
++ ElseIfItem operator>>(const ThenItem &thenItem, const ElseIf &elseIfItem)
++ {
++ return {thenItem, elseIfItem};
++ }
++
++ ElseItem operator>>(const ThenItem &thenItem, const Else &elseItem)
++ {
++ return {thenItem, elseItem};
++ }
++
++ QT_TASKTREE_DEFINE_SMF(Else)
++ QT_TASKTREE_DEFINE_SMF(ElseIf)
++ QT_TASKTREE_DEFINE_SMF(ElseIfItem)
++ QT_TASKTREE_DEFINE_SMF(ElseItem)
++ QT_TASKTREE_DEFINE_SMF(If)
++ QT_TASKTREE_DEFINE_SMF(Then)
++ QT_TASKTREE_DEFINE_SMF(ThenItem)
++
++ } // namespace QtTaskTree
++
++ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QtTaskTree::ElsePrivate)
++ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QtTaskTree::ElseIfPrivate)
++ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QtTaskTree::ElseIfItemPrivate)
++ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QtTaskTree::ElseItemPrivate)
++ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QtTaskTree::IfPrivate)
++ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QtTaskTree::ThenPrivate)
++ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QtTaskTree::ThenItemPrivate)
++
++ QT_END_NAMESPACE