#include "dstalk/dstalk_host.h" #include "dstalk/dstalk_services.h" #include #include #include #include #include #include // ============================================================ // ConfigStore - independent TOML key-value store // ============================================================ namespace { class ConfigStore { public: int load_file(const char* path) { if (!path) return -1; std::ifstream file(path); if (!file.is_open()) return -1; std::stringstream ss; ss << file.rdbuf(); std::string data = ss.str(); std::string current_section; size_t pos = 0; while (pos < data.size()) { while (pos < data.size() && (data[pos] == ' ' || data[pos] == '\t')) pos++; if (pos >= data.size()) break; size_t nl = data.find('\n', pos); std::string line = (nl != std::string::npos) ? data.substr(pos, nl - pos) : data.substr(pos); pos = (nl != std::string::npos) ? nl + 1 : data.size(); while (!line.empty() && (line.back() == '\r' || line.back() == ' ')) line.pop_back(); if (line.empty() || line[0] == '#') continue; if (line[0] == '[' && line.back() == ']') { current_section = line.substr(1, line.size() - 2); continue; } size_t eq = line.find('='); if (eq == std::string::npos) continue; std::string key = line.substr(0, eq); while (!key.empty() && key.back() == ' ') key.pop_back(); if (key.empty()) continue; std::string val = line.substr(eq + 1); while (!val.empty() && (val.front() == ' ' || val.front() == '\t')) val.erase(0, 1); if (val.size() >= 2 && val.front() == '"' && val.back() == '"') val = val.substr(1, val.size() - 2); std::lock_guard lock(mutex_); std::string full_key = current_section.empty() ? key : current_section + "." + key; data_[full_key] = val; } return 0; } const char* get(const char* key) const { if (!key) return nullptr; std::lock_guard lock(mutex_); auto it = data_.find(key); if (it == data_.end()) return nullptr; return it->second.c_str(); } int set(const char* key, const char* value) { if (!key || !value) return -1; std::lock_guard lock(mutex_); data_[key] = value; return 0; } private: mutable std::mutex mutex_; std::unordered_map data_; }; } // anonymous namespace // ============================================================ // Global state // ============================================================ static const dstalk_host_api_t* g_host = nullptr; static ConfigStore g_config; // ============================================================ // Service implementations // ============================================================ static const char* config_get(const char* key) { return g_config.get(key); } static int config_set(const char* key, const char* value) { return g_config.set(key, value); } static int config_load_file(const char* path) { return g_config.load_file(path); } static dstalk_config_service_t g_service = { config_get, config_set, config_load_file }; // ============================================================ // Plugin lifecycle // ============================================================ static int on_init(const dstalk_host_api_t* host) { g_host = host; return host->register_service("config", 1, &g_service); } static void on_shutdown() { // nothing to clean up } static dstalk_plugin_info_t g_info = { "config", // name "1.0.0", // version "Configuration service with TOML file support", // description DSTALK_API_VERSION, // api_version {nullptr}, // dependencies (none) on_init, // on_init on_shutdown, // on_shutdown nullptr // on_event }; extern "C" DSTALK_PLUGIN_EXPORT dstalk_plugin_info_t* dstalk_plugin_init(void) { return &g_info; }