datactl/streammeta.cpp

datactl/streammeta.cpp

datactl/streammeta.cpp

Namespaces

Name
Syntalos

Source code

/*
 * Copyright (C) 2025-2026 Matthias Klumpp <matthias@tenstral.net>
 *
 * Licensed under the GNU Lesser General Public License Version 3
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the license, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "streammeta.h"

#include <sstream>

namespace Syntalos
{

static void writeJsonString(std::ostream &os, const std::string &s)
{
    os << '"';
    for (unsigned char c : s) {
        switch (c) {
        case '"':
            os << "\\\"";
            break;
        case '\\':
            os << "\\\\";
            break;
        case '\b':
            os << "\\b";
            break;
        case '\f':
            os << "\\f";
            break;
        case '\n':
            os << "\\n";
            break;
        case '\r':
            os << "\\r";
            break;
        case '\t':
            os << "\\t";
            break;
        default:
            if (c < 0x20) {
                char buf[8];
                std::snprintf(buf, sizeof(buf), "\\u%04x", c);
                os << buf;
            } else {
                os << static_cast<char>(c);
            }
        }
    }
    os << '"';
}

void writeJson(std::ostream &os, const MetaValue &val)
{
    std::visit(
        [&os](const auto &v) {
            using T = std::decay_t<decltype(v)>;
            if constexpr (std::is_same_v<T, std::nullptr_t>) {
                os << "null";
            } else if constexpr (std::is_same_v<T, bool>) {
                os << (v ? "true" : "false");
            } else if constexpr (std::is_same_v<T, int64_t>) {
                os << v;
            } else if constexpr (std::is_same_v<T, double>) {
                os << v;
            } else if constexpr (std::is_same_v<T, std::string>) {
                writeJsonString(os, v);
            } else if constexpr (std::is_same_v<T, MetaSize>) {
                os << "{\"width\":" << v.width << ",\"height\":" << v.height << '}';
            } else if constexpr (std::is_same_v<T, MetaArray>) {
                os << '[';
                for (size_t i = 0; i < v.size(); ++i) {
                    if (i > 0)
                        os << ',';
                    writeJson(os, v[i]);
                }
                os << ']';
            } else if constexpr (std::is_same_v<T, MetaStringMap>) {
                writeJsonObject(os, v);
            }
        },
        static_cast<const MetaValue::Base &>(val));
}

void writeJsonObject(std::ostream &os, const std::map<std::string, MetaValue> &obj)
{
    os << '{';
    bool first = true;
    for (const auto &[k, v] : obj) {
        if (!first)
            os << ',';
        first = false;
        writeJsonString(os, k);
        os << ':';
        writeJson(os, v);
    }
    os << '}';
}

std::string toJsonString(const MetaValue &v)
{
    std::ostringstream os;
    writeJson(os, v);
    return os.str();
}

std::string toJsonObjectString(const MetaStringMap &m)
{
    std::ostringstream os;
    writeJsonObject(os, m);
    return os.str();
}

std::expected<MetaStringMap, std::string> stringMapFromJson(const std::string &json)
{
    size_t i = 0;
    auto skip = [&]() {
        while (i < json.size() && (json[i] == ' ' || json[i] == '\t' || json[i] == '\n' || json[i] == '\r'))
            ++i;
    };
    auto readStr = [&]() -> std::string {
        if (i >= json.size() || json[i] != '"')
            return {};
        ++i;
        std::string s;
        while (i < json.size() && json[i] != '"') {
            if (json[i] == '\\') {
                ++i;
            }
            s += json[i++];
        }
        ++i; // closing quote
        return s;
    };

    MetaStringMap result;
    skip();
    if (i < json.size() && json[i] == '{') {
        ++i;
        while (i < json.size() && json[i] != '}') {
            skip();
            if (json[i] == '"') {
                const auto key = readStr();
                skip();
                if (i < json.size() && json[i] == ':')
                    ++i;
                skip();
                if (i < json.size() && json[i] == '"') {
                    result[key] = MetaValue{readStr()};
                } else {
                    // number or boolean
                    size_t start = i;
                    while (i < json.size() && json[i] != ',' && json[i] != '}' && json[i] != ' ')
                        ++i;
                    const auto numStr = json.substr(start, i - start);
                    try {
                        result[key] = MetaValue{static_cast<int64_t>(std::stoll(numStr))};
                    } catch (...) {
                        try {
                            result[key] = MetaValue{std::stod(numStr)};
                        } catch (...) {
                        }
                    }
                }
            }
            skip();
            if (i < json.size() && json[i] == ',')
                ++i;
        }
    }

    return result;
}

} // namespace Syntalos

Updated on 2026-04-24 at 06:25:18 +0000