Commit 43d332f5 authored by jan.koester's avatar jan.koester
Browse files

test

parent 217ea2d3
Loading
Loading
Loading
Loading
+145 −0
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@
#include <memory>
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <functional>
#include <yaml.h>

#include "yamlconf.h"
@@ -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) {