Loading backends/yaml/yamlconf.cpp +145 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,9 @@ #include <memory> #include <vector> #include <string> #include <fstream> #include <sstream> #include <functional> #include <yaml.h> #include "yamlconf.h" Loading Loading @@ -62,7 +65,149 @@ const char* confplus::Yaml::getAuthor(){ } void confplus::Yaml::saveConfig(const char *path,const Config *conf){ std::ofstream f(path); if (!f.is_open()) { throw "Failed to open file for writing!"; } // Detect whether a node is a sequence-of-mappings parent: // All children are leaf nodes and at least one has Elements > 1 auto isSeqMappingParent = [](const Config::ConfigData *node) -> bool { if (!node->childs()) return false; const Config::ConfigData *child = node->getChild(); size_t maxElems = 0; while (child) { if (child->childs()) return false; // has sub-children → not a flat seq-mapping if (child->getElements() > maxElems) maxElems = child->getElements(); child = child->next(); } return maxElems > 1; }; // Detect a simple sequence: leaf node with Elements > 1 whose parent is NOT a seq-mapping auto isSimpleSequence = [](const Config::ConfigData *node) -> bool { return !node->childs() && node->getElements() > 1; }; // Get value at position from a ConfigData node auto getValueAt = [&conf](const Config::ConfigData *node, size_t pos) -> std::string { try { return conf->getValue(node, pos); } catch (...) { return ""; } }; // Check if a string needs quoting in YAML auto needsQuote = [](const std::string &s) -> bool { if (s.empty()) return true; if (s == "true" || s == "false" || s == "yes" || s == "no" || s == "null" || s == "~") return true; // Check if it's a number bool isNum = true; for (size_t i = 0; i < s.size(); ++i) { char c = s[i]; if (i == 0 && (c == '-' || c == '+')) continue; if (c == '.') continue; if (c < '0' || c > '9') { isNum = false; break; } } if (isNum && !s.empty() && s != "-" && s != "+") return false; // numbers don't need quotes // Check for special characters for (char c : s) { if (c == ':' || c == '#' || c == '[' || c == ']' || c == '{' || c == '}' || c == ',' || c == '&' || c == '*' || c == '!' || c == '|' || c == '>' || c == '\'' || c == '"' || c == '%' || c == '@' || c == '`') return true; } if (s[0] == ' ' || s[s.size()-1] == ' ') return true; return false; }; auto quoteValue = [&needsQuote](const std::string &s) -> std::string { if (needsQuote(s)) return "\"" + s + "\""; return s; }; std::string indent; // Recursive writer std::function<void(const Config::ConfigData*, int)> writeNode; writeNode = [&](const Config::ConfigData *node, int depth) { std::string pad(depth * 2, ' '); while (node) { if (isSeqMappingParent(node)) { // Sequence-of-mappings: collect all child keys, emit as array of mappings f << pad << node->getKey() << ":\n"; // Collect children and find max elements std::vector<const Config::ConfigData*> children; size_t maxElems = 0; for (const Config::ConfigData *c = node->getChild(); c; c = c->next()) { children.push_back(c); if (c->getElements() > maxElems) maxElems = c->getElements(); } std::string innerPad((depth + 1) * 2, ' '); for (size_t i = 0; i < maxElems; ++i) { bool first = true; for (auto *c : children) { std::string val = getValueAt(c, i); if (val.empty() && i >= c->getElements()) continue; if (first) { f << innerPad << "- " << c->getKey() << ": " << quoteValue(val) << "\n"; first = false; } else { f << innerPad << " " << c->getKey() << ": " << quoteValue(val) << "\n"; } } } } else if (node->childs()) { // Mapping node with children f << pad << node->getKey() << ":\n"; writeNode(node->getChild(), depth + 1); } else if (isSimpleSequence(node)) { // Simple sequence (list of scalars) f << pad << node->getKey() << ":\n"; std::string innerPad((depth + 1) * 2, ' '); for (size_t i = 0; i < node->getElements(); ++i) { f << innerPad << "- " << quoteValue(getValueAt(node, i)) << "\n"; } } else { // Simple scalar value f << pad << node->getKey() << ": " << quoteValue(getValueAt(node, 0)) << "\n"; } node = node->next(); } }; // Add blank lines between top-level sections for readability const Config::ConfigData *top = conf->first(); bool firstTop = true; while (top) { if (!firstTop) f << "\n"; firstTop = false; if (top->childs()) { f << top->getKey() << ":\n"; writeNode(top->getChild(), 1); } else if (isSimpleSequence(top)) { f << top->getKey() << ":\n"; for (size_t i = 0; i < top->getElements(); ++i) { f << " - " << quoteValue(getValueAt(top, i)) << "\n"; } } else { f << top->getKey() << ": " << quoteValue(getValueAt(top, 0)) << "\n"; } top = top->next(); } f.close(); } void confplus::Yaml::loadConfig(const char* path, Config* conf) { Loading Loading
backends/yaml/yamlconf.cpp +145 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,9 @@ #include <memory> #include <vector> #include <string> #include <fstream> #include <sstream> #include <functional> #include <yaml.h> #include "yamlconf.h" Loading Loading @@ -62,7 +65,149 @@ const char* confplus::Yaml::getAuthor(){ } void confplus::Yaml::saveConfig(const char *path,const Config *conf){ std::ofstream f(path); if (!f.is_open()) { throw "Failed to open file for writing!"; } // Detect whether a node is a sequence-of-mappings parent: // All children are leaf nodes and at least one has Elements > 1 auto isSeqMappingParent = [](const Config::ConfigData *node) -> bool { if (!node->childs()) return false; const Config::ConfigData *child = node->getChild(); size_t maxElems = 0; while (child) { if (child->childs()) return false; // has sub-children → not a flat seq-mapping if (child->getElements() > maxElems) maxElems = child->getElements(); child = child->next(); } return maxElems > 1; }; // Detect a simple sequence: leaf node with Elements > 1 whose parent is NOT a seq-mapping auto isSimpleSequence = [](const Config::ConfigData *node) -> bool { return !node->childs() && node->getElements() > 1; }; // Get value at position from a ConfigData node auto getValueAt = [&conf](const Config::ConfigData *node, size_t pos) -> std::string { try { return conf->getValue(node, pos); } catch (...) { return ""; } }; // Check if a string needs quoting in YAML auto needsQuote = [](const std::string &s) -> bool { if (s.empty()) return true; if (s == "true" || s == "false" || s == "yes" || s == "no" || s == "null" || s == "~") return true; // Check if it's a number bool isNum = true; for (size_t i = 0; i < s.size(); ++i) { char c = s[i]; if (i == 0 && (c == '-' || c == '+')) continue; if (c == '.') continue; if (c < '0' || c > '9') { isNum = false; break; } } if (isNum && !s.empty() && s != "-" && s != "+") return false; // numbers don't need quotes // Check for special characters for (char c : s) { if (c == ':' || c == '#' || c == '[' || c == ']' || c == '{' || c == '}' || c == ',' || c == '&' || c == '*' || c == '!' || c == '|' || c == '>' || c == '\'' || c == '"' || c == '%' || c == '@' || c == '`') return true; } if (s[0] == ' ' || s[s.size()-1] == ' ') return true; return false; }; auto quoteValue = [&needsQuote](const std::string &s) -> std::string { if (needsQuote(s)) return "\"" + s + "\""; return s; }; std::string indent; // Recursive writer std::function<void(const Config::ConfigData*, int)> writeNode; writeNode = [&](const Config::ConfigData *node, int depth) { std::string pad(depth * 2, ' '); while (node) { if (isSeqMappingParent(node)) { // Sequence-of-mappings: collect all child keys, emit as array of mappings f << pad << node->getKey() << ":\n"; // Collect children and find max elements std::vector<const Config::ConfigData*> children; size_t maxElems = 0; for (const Config::ConfigData *c = node->getChild(); c; c = c->next()) { children.push_back(c); if (c->getElements() > maxElems) maxElems = c->getElements(); } std::string innerPad((depth + 1) * 2, ' '); for (size_t i = 0; i < maxElems; ++i) { bool first = true; for (auto *c : children) { std::string val = getValueAt(c, i); if (val.empty() && i >= c->getElements()) continue; if (first) { f << innerPad << "- " << c->getKey() << ": " << quoteValue(val) << "\n"; first = false; } else { f << innerPad << " " << c->getKey() << ": " << quoteValue(val) << "\n"; } } } } else if (node->childs()) { // Mapping node with children f << pad << node->getKey() << ":\n"; writeNode(node->getChild(), depth + 1); } else if (isSimpleSequence(node)) { // Simple sequence (list of scalars) f << pad << node->getKey() << ":\n"; std::string innerPad((depth + 1) * 2, ' '); for (size_t i = 0; i < node->getElements(); ++i) { f << innerPad << "- " << quoteValue(getValueAt(node, i)) << "\n"; } } else { // Simple scalar value f << pad << node->getKey() << ": " << quoteValue(getValueAt(node, 0)) << "\n"; } node = node->next(); } }; // Add blank lines between top-level sections for readability const Config::ConfigData *top = conf->first(); bool firstTop = true; while (top) { if (!firstTop) f << "\n"; firstTop = false; if (top->childs()) { f << top->getKey() << ":\n"; writeNode(top->getChild(), 1); } else if (isSimpleSequence(top)) { f << top->getKey() << ":\n"; for (size_t i = 0; i < top->getElements(); ++i) { f << " - " << quoteValue(getValueAt(top, i)) << "\n"; } } else { f << top->getKey() << ": " << quoteValue(getValueAt(top, 0)) << "\n"; } top = top->next(); } f.close(); } void confplus::Yaml::loadConfig(const char* path, Config* conf) { Loading