Files
whetstone_DSL/editor/src/WorkflowPersistence.h
Bill ec0509f6ef Step 323: Workflow Sidecar Persistence (12/12 tests)
Save/load workflow state to .whetstone/<project>.workflow.json with
directory creation, overwrite, delete, and multi-workflow support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:36:20 -07:00

109 lines
2.8 KiB
C++

#pragma once
// Step 323: Workflow Sidecar Persistence
//
// Save and load workflow state to .whetstone/<project>.workflow.json
// so workflows survive across sessions.
#include "WorkflowState.h"
#include <string>
#include <optional>
#include <fstream>
#include <sstream>
#include <sys/stat.h>
// --- SaveResult ---
struct SaveResult {
bool success = false;
std::string path;
int bytesWritten = 0;
int itemCount = 0;
};
// --- Path helper ---
inline std::string workflowSidecarPath(const std::string& workspaceRoot,
const std::string& projectName) {
return workspaceRoot + "/.whetstone/" + projectName + ".workflow.json";
}
// --- Create parent directories recursively ---
inline bool ensureDirectoryExists(const std::string& path) {
// Find last slash to get directory
auto pos = path.rfind('/');
if (pos == std::string::npos) return true;
std::string dir = path.substr(0, pos);
// Simple recursive mkdir
std::string current;
for (size_t i = 0; i < dir.size(); ++i) {
current += dir[i];
if (dir[i] == '/' || i == dir.size() - 1) {
mkdir(current.c_str(), 0755);
}
}
// Check if directory exists
struct stat st{};
return stat(dir.c_str(), &st) == 0 && S_ISDIR(st.st_mode);
}
// --- Save workflow ---
inline SaveResult saveWorkflow(const std::string& workspaceRoot,
const WorkflowState& state) {
SaveResult result;
result.path = workflowSidecarPath(workspaceRoot, state.projectName);
if (!ensureDirectoryExists(result.path)) {
return result;
}
json j = state.toJson();
std::string content = j.dump(2);
std::ofstream ofs(result.path);
if (!ofs.is_open()) return result;
ofs << content;
ofs.close();
if (ofs.fail()) return result;
result.success = true;
result.bytesWritten = static_cast<int>(content.size());
result.itemCount = state.queue.size();
return result;
}
// --- Load workflow ---
inline std::optional<WorkflowState> loadWorkflow(const std::string& workspaceRoot,
const std::string& projectName) {
std::string path = workflowSidecarPath(workspaceRoot, projectName);
std::ifstream ifs(path);
if (!ifs.is_open()) return std::nullopt;
std::ostringstream oss;
oss << ifs.rdbuf();
std::string content = oss.str();
if (content.empty()) return std::nullopt;
try {
json j = json::parse(content);
return WorkflowState::fromJson(j);
} catch (...) {
return std::nullopt;
}
}
// --- Delete workflow ---
inline bool deleteWorkflow(const std::string& workspaceRoot,
const std::string& projectName) {
std::string path = workflowSidecarPath(workspaceRoot, projectName);
return std::remove(path.c_str()) == 0;
}