#pragma once #include "Model/EvaluatedValue.hpp" #include "cplt_fwd.hpp" #include #include #include #include #include class WorkflowConnection { public: static constexpr auto kInvalidId = std::numeric_limits::max(); size_t Source; size_t Destination; int SourcePin; int DestinationPin; public: WorkflowConnection(); bool IsValid() const; }; class WorkflowNode { public: static constexpr auto kInvalidId = std::numeric_limits::max(); enum Type { InputType, TransformType, OutputType, }; enum Kind { KD_NumericAddition, KD_NumericSubtraction, KD_NumericMultiplication, KD_NumericDivision, KD_NumericExpression, KD_TextFormatting, KD_DocumentTemplateExpansion, KD_FormInput, KD_DatabaseRowsInput, InvalidKind, KindCount = InvalidKind, }; protected: struct InputPin { size_t Connection = WorkflowConnection::kInvalidId; BaseValue::Kind MatchingType = BaseValue::KindInvalid; bool ConnectionToConst = false; /// A constant connection connects from a user-specified constant value, feeding to a valid \c Destination and \c DestinationPin (i.e. input pins). bool IsConstantConnection() const; bool IsConnected() const; }; struct OutputPin { size_t Connection = WorkflowConnection::kInvalidId; BaseValue::Kind MatchingType = BaseValue::KindInvalid; bool IsConnected() const; }; friend class Workflow; friend class WorkflowEvaluationError; friend class WorkflowEvaluationContext; Workflow* mWorkflow; size_t mId; std::vector mInputs; std::vector mOutputs; Type mType; Kind mKind; public: WorkflowNode(Type type, Kind kind); virtual ~WorkflowNode() = default; WorkflowNode(const WorkflowNode&) = delete; WorkflowNode& operator=(const WorkflowNode&) = delete; WorkflowNode(WorkflowNode&&) = default; WorkflowNode& operator=(WorkflowNode&&) = default; size_t GetId() const; Type GetType() const; Kind GetKind() const; void ConnectInput(int nodeId, WorkflowNode& output, int outputNodeId); void DisconnectInput(int nodeId); bool IsInputConnected(int nodeId) const; void ConnectOutput(int nodeId, WorkflowNode& input, int inputNodeId); void DisconnectOutput(int nodeId); bool IsOutputConnected(int nodeId) const; virtual void Evaluate(WorkflowEvaluationContext& ctx) = 0; protected: InputPin& InsertInputPin(int atIdx); void RemoveInputPin(int pin); void SwapInputPin(int a, int b); OutputPin& InsertOutputPin(int atIdx); void RemoveOutputPin(int pin); void SwapOutputPin(int a, int b); /* For \c Workflow to invoke, override by implementations */ void OnAttach(Workflow& workflow, size_t newId) {} void OnDetach() {} }; class Workflow { private: friend class WorkflowEvaluationContext; std::vector mConnections; std::vector> mNodes; std::vector> mConstants; public: WorkflowConnection* GetConnectionById(size_t id); WorkflowNode* GetStepById(size_t id); BaseValue* GetConstantById(size_t id); void AddStep(std::unique_ptr step); void RemoveStep(size_t id); void Connect(WorkflowNode& source, int sourceNode, WorkflowNode& destination, int destinationNode); void DisconnectBySource(WorkflowNode& source, int sourceNode); void DisconnectByDestination(WorkflowNode& destination, int destinationNode); private: std::pair AllocWorkflowConnection(); std::pair&, size_t> AllocWorkflowStep(); }; class WorkflowEvaluationError { public: enum MessageType : int16_t { Error, Warning, }; enum PinType : int16_t { NoPin, InputPin, OutputPin, }; public: std::string Message; size_t NodeId; int PinId; PinType PinType; MessageType Type; }; class WorkflowEvaluationContext { private: struct RuntimeNode; struct RuntimeConnection; Workflow* mWorkflow; std::vector mNodes; std::vector mConnections; std::vector mErrors; std::vector mWarnings; public: WorkflowEvaluationContext(Workflow& workflow); BaseValue* GetConnectionValue(size_t id, bool constant); BaseValue* GetConnectionValue(const WorkflowNode::InputPin& inputPin); void SetConnectionValue(size_t id, std::unique_ptr value); void SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr value); void ReportError(std::string message, const WorkflowNode& node, int pinId, bool inputPin); void ReportError(std::string message, const WorkflowNode& node); void ReportWarning(std::string message, const WorkflowNode& node, int pinId, bool inputPin); void ReportWarning(std::string message, const WorkflowNode& node); /// Run until all possible paths have been evaluated. void Run(); };