#include #include #include #include #include #include #include "projectloader.h" namespace ProjectLoader { namespace { namespace detail { template class Ref> struct is_specialization_t : std::false_type {}; template class Ref, typename... Args> struct is_specialization_t, Ref>: std::true_type {}; // broken //template //constexpr bool is_specialization_v = is_specialization_t::value; template concept Listific = is_specialization_t::value || is_specialization_t::value || std::same_as || std::same_as || std::same_as; template concept OtherLoadableFromArray = std::same_as; template concept LoadableFromArray = Listific || OtherLoadableFromArray; template concept LoadableFromObject = std::same_as || std::same_as || std::same_as || std::same_as || std::same_as || std::same_as; template concept JsonNumber = (std::integral && !std::same_as && !std::is_enum_v) || std::floating_point; // TODO: Can we match on Q_ENUMs only? template concept LoadableQEnum = std::is_enum_v; template concept SameAsQualified = std::same_as, U>; } // namespace detail // forward declarations for loading stuff from QJsonValue template std::expected load(const QJsonValue &json); template std::expected load(const QJsonValue &json); template std::expected load(const QJsonValue &json); // iterateMembers stuff template T, typename CB> void iterateMembers(T &ref, CB &&cb) { cb("deviceTypes", ref.deviceTypes); cb("devices", ref.devices); cb("presets", ref.presets); } template T, typename CB> void iterateMembers(T &ref, CB &&cb) { cb("id", ref.id); cb("name", ref.name); cb("iconName", ref.iconName); cb("registers", ref.registers); } template T, typename CB> void iterateMembers(T &ref, CB &&cb) { cb("id", ref.id); cb("name", ref.name); cb("deviceTypeId", ref.deviceTypeId); cb("address", ref.address); cb("position", ref.position); } template T, typename CB> void iterateMembers(T &ref, CB &&cb) { cb("id", ref.id); cb("name", ref.name); cb("msecsPerStep", ref.msecsPerStep); cb("steps", ref.steps); } template T, typename CB> void iterateMembers(T &ref, CB &&cb) { cb("type", ref.type); } template T, typename CB> void iterateMembers(T &ref, CB &&cb) { cb("sliders", ref.sliders); } template std::expected load(const QJsonArray &json) { T result; for (size_t i = 0; i < json.size(); i++) { const auto &el = json[i]; auto res = load(el); if (res.has_value()) { result.push_back(res.value()); } else { return std::unexpected(QString("%1: %2").arg(i).arg(res.error())); } } return result; } template T> std::expected load(const QJsonArray &json) { QVector3D vec; if (json.size() != 3) { return std::unexpected("Vector has wrong number of components"); } for (size_t i = 0; i < json.size(); i++) { const auto &el = json[i]; if (auto val = load(el); val) { vec[i] = val.value(); } else { return std::unexpected(QString("%1: %2").arg(i).arg(val.error())); } } return vec; } template std::expected load(const QJsonValue &json) { if (!json.isArray()) return std::unexpected("Not a JSON array"); return load(json.toArray()); } template std::expected load(const QJsonValue &json) { if (json.isDouble()) { return (T)json.toDouble(); } else { return std::unexpected(QString("Not a number")); } } template T> std::expected load(const QJsonValue &json) { if (json.isString()) { return json.toString(); } else { return std::unexpected(QString("Not a string")); } } template std::expected load(const QJsonValue &json) { if (!json.isString()) { return std::unexpected("Not a JSON string"); } QMetaEnum me = QMetaEnum::fromType(); if (!me.isValid()) { return std::unexpected("Invalid QMetaEnum"); } bool ok = false; auto value = me.keyToValue(json.toString().toStdString().c_str(), &ok); if (ok) { return (T)value; } else { return std::unexpected(QString("No value found for enum key %1").arg(json.toString())); } } template std::expected loadIfExists(const QJsonObject &json, const QString &key) { if (json.contains(key)) { return load(json[key]); } else { return std::unexpected(QString("does not exist")); } } template std::expected load(const QJsonObject &json) { T t; std::optional> error; iterateMembers(t, [&json, &error] (const char *name, auto &field) { if (error) return; if (auto result = loadIfExists>(json, name)) { field = std::move(result.value()); } else { error = std::unexpected(QString("%1: %2").arg(name).arg(result.error())); } }); if (error) return *error; return t; } template std::expected load(const QJsonValue &json) { if (!json.isObject()) return std::unexpected("Not a JSON object"); return load(json.toObject()); } } // namespace std::expected loadProject(const QJsonDocument &json) { if (!json.isObject()) { return std::unexpected("JsonDocument is not an object"); } return load(json.object()); } namespace { template std::expected save(const T &val); template std::expected save(const T &val) { return QJsonValue((double)val); } template T> std::expected save(const T &val) { return QJsonValue(val); } template T> std::expected save(const T &val) { return QJsonArray{val.x(), val.y(), val.z()}; } template std::expected save(const T &val) { QMetaEnum me = QMetaEnum::fromType(); if (!me.isValid()) { return std::unexpected("Invalid QMetaEnum"); } if (auto key = me.valueToKey((int)val); key) { return QJsonValue(key); } else { return std::unexpected(QString("No key found for enum value %1").arg((int)val)); } } template std::expected save(const T &val) { QJsonObject json; std::optional> error; iterateMembers(val, [&json, &error] (const char *name, auto &field) { if (error) return; if (auto result = save>(field)) { json[name] = result.value(); } else { error = std::unexpected(QString("%1: %2").arg(name).arg(result.error())); } }); if (error) return *error; return json; } template std::expected save(const T &val) { QJsonArray arr; size_t i = 0; for (auto it = std::cbegin(val); it != std::cend(val); it++, i++) { auto json = save(*it); if (json) { arr.push_back(json.value()); } else { return std::unexpected(QString("%1: %2").arg(i).arg(json.error())); } } return arr; } } // namespace std::expected saveProject(const LightProject &val) { if (auto project = save(val); project) { return QJsonDocument(project.value()); } else { return std::unexpected(QString("Failed to save project: %1").arg(project.error())); } } } // namespace ProjectLoader