#pragma once // Shared TOML parser — used by both ConfigStore (core) and config plugin. // W12.2: Extracted from config_store.cpp:23-61 and config_plugin.cpp:28-66 // to eliminate the 74-line code duplication (W11.2 audit Finding 1). // Does NOT support: inline tables, arrays, multi-line strings, escape sequences. #include namespace dstalk { namespace toml { /// Parse a TOML string, calling on_kv(full_key, value) for each key-value pair. /// Supports [section] headers, key = "value" pairs, # comments, blank lines. template inline void parse(const std::string& content, F&& on_kv) { std::string current_section; size_t pos = 0; while (pos < content.size()) { // Trim left whitespace while (pos < content.size() && (content[pos] == ' ' || content[pos] == '\t')) pos++; if (pos >= content.size()) break; // Extract next line size_t nl = content.find('\n', pos); std::string line = (nl != std::string::npos) ? content.substr(pos, nl - pos) : content.substr(pos); pos = (nl != std::string::npos) ? nl + 1 : content.size(); // Trim right whitespace (including \r) while (!line.empty() && (line.back() == '\r' || line.back() == ' ')) line.pop_back(); // Skip empty lines and comments if (line.empty() || line[0] == '#') continue; // Section header: [section_name] if (line[0] == '[' && line.back() == ']') { current_section = line.substr(1, line.size() - 2); continue; } // Key = value 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::string full_key = current_section.empty() ? key : current_section + "." + key; on_kv(full_key, val); } } } // namespace toml } // namespace dstalk