mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
ROADMAP.md
This commit is contained in:
@@ -284,6 +284,17 @@ set(JSON_CONFIG_SOURCES
|
||||
src/services/impl/json_config_migration_service.cpp
|
||||
)
|
||||
|
||||
set(WORKFLOW_SOURCES
|
||||
src/services/impl/workflow_step_io_resolver.cpp
|
||||
src/services/impl/workflow_step_registry.cpp
|
||||
src/services/impl/workflow_executor.cpp
|
||||
src/services/impl/workflow_definition_parser.cpp
|
||||
src/services/impl/workflow_config_load_step.cpp
|
||||
src/services/impl/workflow_config_version_step.cpp
|
||||
src/services/impl/workflow_config_schema_step.cpp
|
||||
src/services/impl/workflow_default_step_registrar.cpp
|
||||
)
|
||||
|
||||
if(BUILD_SDL3_APP)
|
||||
add_executable(sdl3_app
|
||||
src/main.cpp
|
||||
@@ -291,6 +302,7 @@ if(BUILD_SDL3_APP)
|
||||
src/di/service_registry.cpp
|
||||
src/events/event_bus.cpp
|
||||
${JSON_CONFIG_SOURCES}
|
||||
${WORKFLOW_SOURCES}
|
||||
src/services/impl/config_compiler_service.cpp
|
||||
src/services/impl/command_line_service.cpp
|
||||
src/services/impl/json_config_writer_service.cpp
|
||||
|
||||
17
ROADMAP.md
17
ROADMAP.md
@@ -226,6 +226,22 @@ Option B: per-shader only
|
||||
}
|
||||
```
|
||||
|
||||
## Workflow Engine (n8n-Style Micro Steps)
|
||||
### Goals
|
||||
- Describe boot + frame pipelines as a declarative JSON workflow graph.
|
||||
- Keep each step tiny (<100 LOC), with explicit inputs/outputs and DI-backed plugin lookup.
|
||||
- Package common pipelines as templates so users don't start from scratch.
|
||||
|
||||
### Status
|
||||
- [~] Workflow core: step registry + executor + JSON definition parser.
|
||||
- [~] Default step package: `config.load`, `config.version.validate`, `config.schema.validate`.
|
||||
- [x] Workflow schema: `config/schema/workflow_v1.schema.json`.
|
||||
- [x] Template package: `config/workflows/templates/boot_default.json`.
|
||||
|
||||
### Next Steps
|
||||
- Wire boot pipeline to use workflow executor (config load/validate/migrate).
|
||||
- Add frame workflow template (BeginFrame → RenderGraph → Capture → Validate).
|
||||
|
||||
## Feature Matrix (What You Get, When You Get It)
|
||||
|
||||
| Feature | Status | Starter | Pro | Ultra | Enterprise |
|
||||
@@ -267,6 +283,7 @@ Option B: per-shader only
|
||||
- [~] Budget enforcement tests (GUI cache pruning + texture tracker covered; transient pool pending)
|
||||
- [~] Config-driven validation tour (checkpoint captures + image/ratio/luma/sample-point checks)
|
||||
- [ ] Smoke test: cube demo boots with config-first scene definition
|
||||
- [ ] Workflow parser tests (template loading + invalid step diagnostics)
|
||||
|
||||
## Test Strategy (Solid Coverage Plan)
|
||||
### Goals
|
||||
|
||||
45
config/schema/workflow_v1.schema.json
Normal file
45
config/schema/workflow_v1.schema.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "SDL3CPP Workflow v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"template": {
|
||||
"type": "string"
|
||||
},
|
||||
"steps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"plugin"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"plugin": {
|
||||
"type": "string"
|
||||
},
|
||||
"inputs": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"steps"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
34
config/workflows/templates/boot_default.json
Normal file
34
config/workflows/templates/boot_default.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"template": "boot.default",
|
||||
"steps": [
|
||||
{
|
||||
"id": "load_config",
|
||||
"plugin": "config.load",
|
||||
"inputs": {
|
||||
"path": "config.path"
|
||||
},
|
||||
"outputs": {
|
||||
"document": "config.document"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "validate_version",
|
||||
"plugin": "config.version.validate",
|
||||
"inputs": {
|
||||
"document": "config.document",
|
||||
"path": "config.path"
|
||||
},
|
||||
"outputs": {
|
||||
"version": "config.version"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "validate_schema",
|
||||
"plugin": "config.schema.validate",
|
||||
"inputs": {
|
||||
"document": "config.document",
|
||||
"path": "config.path"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -37,11 +37,16 @@
|
||||
#include "services/impl/logger_service.hpp"
|
||||
#include "services/impl/pipeline_compiler_service.hpp"
|
||||
#include "services/impl/validation_tour_service.hpp"
|
||||
#include "services/impl/workflow_default_step_registrar.hpp"
|
||||
#include "services/impl/workflow_executor.hpp"
|
||||
#include "services/impl/workflow_step_registry.hpp"
|
||||
#include "services/interfaces/i_platform_service.hpp"
|
||||
#include "services/interfaces/i_probe_service.hpp"
|
||||
#include "services/interfaces/i_render_graph_service.hpp"
|
||||
#include "services/interfaces/i_shader_system_registry.hpp"
|
||||
#include "services/interfaces/i_validation_tour_service.hpp"
|
||||
#include "services/interfaces/i_workflow_executor.hpp"
|
||||
#include "services/interfaces/i_workflow_step_registry.hpp"
|
||||
#include "services/interfaces/i_config_compiler_service.hpp"
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
@@ -223,6 +228,16 @@ void ServiceBasedApp::RegisterServices() {
|
||||
registry_.RegisterService<services::IProbeService, services::impl::ProbeService>(
|
||||
registry_.GetService<services::ILogger>());
|
||||
|
||||
// Workflow step registry + executor (declarative boot/frame pipelines)
|
||||
registry_.RegisterService<services::IWorkflowStepRegistry, services::impl::WorkflowStepRegistry>();
|
||||
registry_.RegisterService<services::IWorkflowExecutor, services::impl::WorkflowExecutor>(
|
||||
registry_.GetService<services::IWorkflowStepRegistry>(),
|
||||
registry_.GetService<services::ILogger>());
|
||||
services::impl::WorkflowDefaultStepRegistrar workflowRegistrar(
|
||||
registry_.GetService<services::ILogger>(),
|
||||
registry_.GetService<services::IProbeService>());
|
||||
workflowRegistrar.RegisterDefaults(registry_.GetService<services::IWorkflowStepRegistry>());
|
||||
|
||||
// Configuration service
|
||||
registry_.RegisterService<services::IConfigService, services::impl::JsonConfigService>(
|
||||
registry_.GetService<services::ILogger>(),
|
||||
|
||||
40
src/services/impl/workflow_config_load_step.cpp
Normal file
40
src/services/impl/workflow_config_load_step.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "workflow_config_load_step.hpp"
|
||||
#include "json_config_document_loader.hpp"
|
||||
#include "workflow_step_io_resolver.hpp"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
WorkflowConfigLoadStep::WorkflowConfigLoadStep(std::shared_ptr<ILogger> logger)
|
||||
: logger_(std::move(logger)) {}
|
||||
|
||||
std::string WorkflowConfigLoadStep::GetPluginId() const {
|
||||
return "config.load";
|
||||
}
|
||||
|
||||
void WorkflowConfigLoadStep::Execute(const WorkflowStepDefinition& step, WorkflowContext& context) {
|
||||
WorkflowStepIoResolver resolver;
|
||||
const std::string pathKey = resolver.GetRequiredInputKey(step, "path");
|
||||
std::filesystem::path pathValue;
|
||||
if (const auto* path = context.TryGet<std::filesystem::path>(pathKey)) {
|
||||
pathValue = *path;
|
||||
} else if (const auto* pathString = context.TryGet<std::string>(pathKey)) {
|
||||
pathValue = *pathString;
|
||||
} else {
|
||||
throw std::runtime_error("Workflow config.load missing path input '" + pathKey + "'");
|
||||
}
|
||||
|
||||
json_config::JsonConfigDocumentLoader loader(logger_);
|
||||
auto document = std::make_shared<rapidjson::Document>(loader.Load(pathValue));
|
||||
|
||||
const std::string outputKey = resolver.GetRequiredOutputKey(step, "document");
|
||||
context.Set(outputKey, std::move(document));
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
21
src/services/impl/workflow_config_load_step.hpp
Normal file
21
src/services/impl/workflow_config_load_step.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "../interfaces/i_workflow_step.hpp"
|
||||
#include "../interfaces/i_logger.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
class WorkflowConfigLoadStep : public IWorkflowStep {
|
||||
public:
|
||||
explicit WorkflowConfigLoadStep(std::shared_ptr<ILogger> logger);
|
||||
|
||||
std::string GetPluginId() const override;
|
||||
void Execute(const WorkflowStepDefinition& step, WorkflowContext& context) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
45
src/services/impl/workflow_config_schema_step.cpp
Normal file
45
src/services/impl/workflow_config_schema_step.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "workflow_config_schema_step.hpp"
|
||||
#include "json_config_schema_validator.hpp"
|
||||
#include "workflow_step_io_resolver.hpp"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
WorkflowConfigSchemaStep::WorkflowConfigSchemaStep(std::shared_ptr<ILogger> logger,
|
||||
std::shared_ptr<IProbeService> probeService)
|
||||
: logger_(std::move(logger)),
|
||||
probeService_(std::move(probeService)) {}
|
||||
|
||||
std::string WorkflowConfigSchemaStep::GetPluginId() const {
|
||||
return "config.schema.validate";
|
||||
}
|
||||
|
||||
void WorkflowConfigSchemaStep::Execute(const WorkflowStepDefinition& step, WorkflowContext& context) {
|
||||
WorkflowStepIoResolver resolver;
|
||||
const std::string documentKey = resolver.GetRequiredInputKey(step, "document");
|
||||
const std::string pathKey = resolver.GetRequiredInputKey(step, "path");
|
||||
const auto* document = context.TryGet<std::shared_ptr<rapidjson::Document>>(documentKey);
|
||||
if (!document || !(*document)) {
|
||||
throw std::runtime_error("Workflow config.schema.validate missing document input '" + documentKey + "'");
|
||||
}
|
||||
|
||||
std::filesystem::path pathValue;
|
||||
if (const auto* path = context.TryGet<std::filesystem::path>(pathKey)) {
|
||||
pathValue = *path;
|
||||
} else if (const auto* pathString = context.TryGet<std::string>(pathKey)) {
|
||||
pathValue = *pathString;
|
||||
} else {
|
||||
throw std::runtime_error("Workflow config.schema.validate missing path input '" + pathKey + "'");
|
||||
}
|
||||
|
||||
json_config::JsonConfigSchemaValidator validator(logger_, probeService_);
|
||||
validator.ValidateOrThrow(**document, pathValue);
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
24
src/services/impl/workflow_config_schema_step.hpp
Normal file
24
src/services/impl/workflow_config_schema_step.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "../interfaces/i_workflow_step.hpp"
|
||||
#include "../interfaces/i_logger.hpp"
|
||||
#include "../interfaces/i_probe_service.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
class WorkflowConfigSchemaStep : public IWorkflowStep {
|
||||
public:
|
||||
WorkflowConfigSchemaStep(std::shared_ptr<ILogger> logger,
|
||||
std::shared_ptr<IProbeService> probeService);
|
||||
|
||||
std::string GetPluginId() const override;
|
||||
void Execute(const WorkflowStepDefinition& step, WorkflowContext& context) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
std::shared_ptr<IProbeService> probeService_;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
46
src/services/impl/workflow_config_version_step.cpp
Normal file
46
src/services/impl/workflow_config_version_step.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "workflow_config_version_step.hpp"
|
||||
#include "json_config_version_validator.hpp"
|
||||
#include "workflow_step_io_resolver.hpp"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
WorkflowConfigVersionStep::WorkflowConfigVersionStep(std::shared_ptr<ILogger> logger)
|
||||
: logger_(std::move(logger)) {}
|
||||
|
||||
std::string WorkflowConfigVersionStep::GetPluginId() const {
|
||||
return "config.version.validate";
|
||||
}
|
||||
|
||||
void WorkflowConfigVersionStep::Execute(const WorkflowStepDefinition& step, WorkflowContext& context) {
|
||||
WorkflowStepIoResolver resolver;
|
||||
const std::string documentKey = resolver.GetRequiredInputKey(step, "document");
|
||||
const std::string pathKey = resolver.GetRequiredInputKey(step, "path");
|
||||
const auto* document = context.TryGet<std::shared_ptr<rapidjson::Document>>(documentKey);
|
||||
if (!document || !(*document)) {
|
||||
throw std::runtime_error("Workflow config.version.validate missing document input '" + documentKey + "'");
|
||||
}
|
||||
|
||||
std::filesystem::path pathValue;
|
||||
if (const auto* path = context.TryGet<std::filesystem::path>(pathKey)) {
|
||||
pathValue = *path;
|
||||
} else if (const auto* pathString = context.TryGet<std::string>(pathKey)) {
|
||||
pathValue = *pathString;
|
||||
} else {
|
||||
throw std::runtime_error("Workflow config.version.validate missing path input '" + pathKey + "'");
|
||||
}
|
||||
|
||||
json_config::JsonConfigVersionValidator validator(logger_);
|
||||
const auto version = validator.Validate(**document, pathValue);
|
||||
|
||||
const std::string outputKey = resolver.GetRequiredOutputKey(step, "version");
|
||||
context.Set(outputKey, version);
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
21
src/services/impl/workflow_config_version_step.hpp
Normal file
21
src/services/impl/workflow_config_version_step.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "../interfaces/i_workflow_step.hpp"
|
||||
#include "../interfaces/i_logger.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
class WorkflowConfigVersionStep : public IWorkflowStep {
|
||||
public:
|
||||
explicit WorkflowConfigVersionStep(std::shared_ptr<ILogger> logger);
|
||||
|
||||
std::string GetPluginId() const override;
|
||||
void Execute(const WorkflowStepDefinition& step, WorkflowContext& context) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
24
src/services/impl/workflow_default_step_registrar.cpp
Normal file
24
src/services/impl/workflow_default_step_registrar.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "workflow_default_step_registrar.hpp"
|
||||
#include "workflow_config_load_step.hpp"
|
||||
#include "workflow_config_schema_step.hpp"
|
||||
#include "workflow_config_version_step.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
WorkflowDefaultStepRegistrar::WorkflowDefaultStepRegistrar(std::shared_ptr<ILogger> logger,
|
||||
std::shared_ptr<IProbeService> probeService)
|
||||
: logger_(std::move(logger)),
|
||||
probeService_(std::move(probeService)) {}
|
||||
|
||||
void WorkflowDefaultStepRegistrar::RegisterDefaults(const std::shared_ptr<IWorkflowStepRegistry>& registry) const {
|
||||
if (!registry) {
|
||||
throw std::runtime_error("WorkflowDefaultStepRegistrar: registry is null");
|
||||
}
|
||||
registry->RegisterStep(std::make_shared<WorkflowConfigLoadStep>(logger_));
|
||||
registry->RegisterStep(std::make_shared<WorkflowConfigVersionStep>(logger_));
|
||||
registry->RegisterStep(std::make_shared<WorkflowConfigSchemaStep>(logger_, probeService_));
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
21
src/services/impl/workflow_default_step_registrar.hpp
Normal file
21
src/services/impl/workflow_default_step_registrar.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "../interfaces/i_logger.hpp"
|
||||
#include "../interfaces/i_probe_service.hpp"
|
||||
#include "../interfaces/i_workflow_step_registry.hpp"
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
class WorkflowDefaultStepRegistrar {
|
||||
public:
|
||||
WorkflowDefaultStepRegistrar(std::shared_ptr<ILogger> logger,
|
||||
std::shared_ptr<IProbeService> probeService);
|
||||
|
||||
void RegisterDefaults(const std::shared_ptr<IWorkflowStepRegistry>& registry) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
std::shared_ptr<IProbeService> probeService_;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
71
src/services/impl/workflow_definition_parser.cpp
Normal file
71
src/services/impl/workflow_definition_parser.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "workflow_definition_parser.hpp"
|
||||
#include "json_config_document_parser.hpp"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
namespace {
|
||||
std::string ReadRequiredString(const rapidjson::Value& object, const char* name) {
|
||||
if (!object.HasMember(name) || !object[name].IsString()) {
|
||||
throw std::runtime_error("Workflow member '" + std::string(name) + "' must be a string");
|
||||
}
|
||||
return object[name].GetString();
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> ReadStringMap(const rapidjson::Value& object,
|
||||
const char* name) {
|
||||
std::unordered_map<std::string, std::string> result;
|
||||
if (!object.HasMember(name)) {
|
||||
return result;
|
||||
}
|
||||
const auto& mapValue = object[name];
|
||||
if (!mapValue.IsObject()) {
|
||||
throw std::runtime_error("Workflow member '" + std::string(name) + "' must be an object");
|
||||
}
|
||||
for (auto it = mapValue.MemberBegin(); it != mapValue.MemberEnd(); ++it) {
|
||||
if (!it->value.IsString()) {
|
||||
throw std::runtime_error("Workflow map '" + std::string(name) + "' must map to strings");
|
||||
}
|
||||
result[it->name.GetString()] = it->value.GetString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
WorkflowDefinition WorkflowDefinitionParser::ParseFile(const std::filesystem::path& path) const {
|
||||
json_config::JsonConfigDocumentParser parser;
|
||||
rapidjson::Document document = parser.Parse(path, "workflow file");
|
||||
|
||||
if (!document.HasMember("steps") || !document["steps"].IsArray()) {
|
||||
throw std::runtime_error("Workflow must contain a 'steps' array");
|
||||
}
|
||||
|
||||
WorkflowDefinition workflow;
|
||||
if (document.HasMember("template")) {
|
||||
if (!document["template"].IsString()) {
|
||||
throw std::runtime_error("Workflow member 'template' must be a string");
|
||||
}
|
||||
workflow.templateName = document["template"].GetString();
|
||||
}
|
||||
|
||||
for (const auto& entry : document["steps"].GetArray()) {
|
||||
if (!entry.IsObject()) {
|
||||
throw std::runtime_error("Workflow steps must be objects");
|
||||
}
|
||||
WorkflowStepDefinition step;
|
||||
step.id = ReadRequiredString(entry, "id");
|
||||
step.plugin = ReadRequiredString(entry, "plugin");
|
||||
step.inputs = ReadStringMap(entry, "inputs");
|
||||
step.outputs = ReadStringMap(entry, "outputs");
|
||||
workflow.steps.push_back(std::move(step));
|
||||
}
|
||||
|
||||
return workflow;
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
14
src/services/impl/workflow_definition_parser.hpp
Normal file
14
src/services/impl/workflow_definition_parser.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "../interfaces/workflow_definition.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
class WorkflowDefinitionParser {
|
||||
public:
|
||||
WorkflowDefinition ParseFile(const std::filesystem::path& path) const;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
34
src/services/impl/workflow_executor.cpp
Normal file
34
src/services/impl/workflow_executor.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "workflow_executor.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
WorkflowExecutor::WorkflowExecutor(std::shared_ptr<IWorkflowStepRegistry> registry,
|
||||
std::shared_ptr<ILogger> logger)
|
||||
: registry_(std::move(registry)),
|
||||
logger_(std::move(logger)) {
|
||||
if (!registry_) {
|
||||
throw std::runtime_error("WorkflowExecutor requires a step registry");
|
||||
}
|
||||
}
|
||||
|
||||
void WorkflowExecutor::Execute(const WorkflowDefinition& workflow, WorkflowContext& context) {
|
||||
if (logger_) {
|
||||
logger_->Trace("WorkflowExecutor", "Execute",
|
||||
"steps=" + std::to_string(workflow.steps.size()),
|
||||
"Starting workflow execution");
|
||||
}
|
||||
for (const auto& step : workflow.steps) {
|
||||
auto handler = registry_->GetStep(step.plugin);
|
||||
if (!handler) {
|
||||
throw std::runtime_error("WorkflowExecutor: no step registered for plugin '" + step.plugin + "'");
|
||||
}
|
||||
handler->Execute(step, context);
|
||||
}
|
||||
if (logger_) {
|
||||
logger_->Trace("WorkflowExecutor", "Execute", "", "Workflow execution complete");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
21
src/services/impl/workflow_executor.hpp
Normal file
21
src/services/impl/workflow_executor.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "../interfaces/i_workflow_executor.hpp"
|
||||
#include "../interfaces/i_workflow_step_registry.hpp"
|
||||
#include "../interfaces/i_logger.hpp"
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
class WorkflowExecutor : public IWorkflowExecutor {
|
||||
public:
|
||||
WorkflowExecutor(std::shared_ptr<IWorkflowStepRegistry> registry,
|
||||
std::shared_ptr<ILogger> logger);
|
||||
|
||||
void Execute(const WorkflowDefinition& workflow, WorkflowContext& context) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<IWorkflowStepRegistry> registry_;
|
||||
std::shared_ptr<ILogger> logger_;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
25
src/services/impl/workflow_step_io_resolver.cpp
Normal file
25
src/services/impl/workflow_step_io_resolver.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "workflow_step_io_resolver.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
std::string WorkflowStepIoResolver::GetRequiredInputKey(const WorkflowStepDefinition& step,
|
||||
const std::string& name) const {
|
||||
auto it = step.inputs.find(name);
|
||||
if (it == step.inputs.end()) {
|
||||
throw std::runtime_error("Workflow step '" + step.id + "' missing input '" + name + "'");
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::string WorkflowStepIoResolver::GetRequiredOutputKey(const WorkflowStepDefinition& step,
|
||||
const std::string& name) const {
|
||||
auto it = step.outputs.find(name);
|
||||
if (it == step.outputs.end()) {
|
||||
throw std::runtime_error("Workflow step '" + step.id + "' missing output '" + name + "'");
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
15
src/services/impl/workflow_step_io_resolver.hpp
Normal file
15
src/services/impl/workflow_step_io_resolver.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "../interfaces/workflow_step_definition.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
class WorkflowStepIoResolver {
|
||||
public:
|
||||
std::string GetRequiredInputKey(const WorkflowStepDefinition& step, const std::string& name) const;
|
||||
std::string GetRequiredOutputKey(const WorkflowStepDefinition& step, const std::string& name) const;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
26
src/services/impl/workflow_step_registry.cpp
Normal file
26
src/services/impl/workflow_step_registry.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "workflow_step_registry.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
void WorkflowStepRegistry::RegisterStep(std::shared_ptr<IWorkflowStep> step) {
|
||||
if (!step) {
|
||||
throw std::runtime_error("WorkflowStepRegistry::RegisterStep: step is null");
|
||||
}
|
||||
const std::string pluginId = step->GetPluginId();
|
||||
auto [it, inserted] = steps_.emplace(pluginId, std::move(step));
|
||||
if (!inserted) {
|
||||
throw std::runtime_error("WorkflowStepRegistry::RegisterStep: duplicate plugin '" + pluginId + "'");
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<IWorkflowStep> WorkflowStepRegistry::GetStep(const std::string& pluginId) const {
|
||||
auto it = steps_.find(pluginId);
|
||||
if (it == steps_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
18
src/services/impl/workflow_step_registry.hpp
Normal file
18
src/services/impl/workflow_step_registry.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "../interfaces/i_workflow_step_registry.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sdl3cpp::services::impl {
|
||||
|
||||
class WorkflowStepRegistry : public IWorkflowStepRegistry {
|
||||
public:
|
||||
void RegisterStep(std::shared_ptr<IWorkflowStep> step) override;
|
||||
std::shared_ptr<IWorkflowStep> GetStep(const std::string& pluginId) const override;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::shared_ptr<IWorkflowStep>> steps_;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services::impl
|
||||
15
src/services/interfaces/i_workflow_executor.hpp
Normal file
15
src/services/interfaces/i_workflow_executor.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "workflow_context.hpp"
|
||||
#include "workflow_definition.hpp"
|
||||
|
||||
namespace sdl3cpp::services {
|
||||
|
||||
class IWorkflowExecutor {
|
||||
public:
|
||||
virtual ~IWorkflowExecutor() = default;
|
||||
|
||||
virtual void Execute(const WorkflowDefinition& workflow, WorkflowContext& context) = 0;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services
|
||||
18
src/services/interfaces/i_workflow_step.hpp
Normal file
18
src/services/interfaces/i_workflow_step.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "workflow_context.hpp"
|
||||
#include "workflow_step_definition.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace sdl3cpp::services {
|
||||
|
||||
class IWorkflowStep {
|
||||
public:
|
||||
virtual ~IWorkflowStep() = default;
|
||||
|
||||
virtual std::string GetPluginId() const = 0;
|
||||
virtual void Execute(const WorkflowStepDefinition& step, WorkflowContext& context) = 0;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services
|
||||
18
src/services/interfaces/i_workflow_step_registry.hpp
Normal file
18
src/services/interfaces/i_workflow_step_registry.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "i_workflow_step.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace sdl3cpp::services {
|
||||
|
||||
class IWorkflowStepRegistry {
|
||||
public:
|
||||
virtual ~IWorkflowStepRegistry() = default;
|
||||
|
||||
virtual void RegisterStep(std::shared_ptr<IWorkflowStep> step) = 0;
|
||||
virtual std::shared_ptr<IWorkflowStep> GetStep(const std::string& pluginId) const = 0;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services
|
||||
33
src/services/interfaces/workflow_context.hpp
Normal file
33
src/services/interfaces/workflow_context.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <any>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sdl3cpp::services {
|
||||
|
||||
class WorkflowContext {
|
||||
public:
|
||||
template <typename T>
|
||||
void Set(const std::string& key, T value) {
|
||||
values_[key] = std::move(value);
|
||||
}
|
||||
|
||||
bool Contains(const std::string& key) const {
|
||||
return values_.find(key) != values_.end();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* TryGet(const std::string& key) const {
|
||||
auto it = values_.find(key);
|
||||
if (it == values_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::any_cast<T>(&it->second);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::any> values_;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services
|
||||
15
src/services/interfaces/workflow_definition.hpp
Normal file
15
src/services/interfaces/workflow_definition.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "workflow_step_definition.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sdl3cpp::services {
|
||||
|
||||
struct WorkflowDefinition {
|
||||
std::string templateName;
|
||||
std::vector<WorkflowStepDefinition> steps;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services
|
||||
15
src/services/interfaces/workflow_step_definition.hpp
Normal file
15
src/services/interfaces/workflow_step_definition.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sdl3cpp::services {
|
||||
|
||||
struct WorkflowStepDefinition {
|
||||
std::string id;
|
||||
std::string plugin;
|
||||
std::unordered_map<std::string, std::string> inputs;
|
||||
std::unordered_map<std::string, std::string> outputs;
|
||||
};
|
||||
|
||||
} // namespace sdl3cpp::services
|
||||
Reference in New Issue
Block a user