#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; 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; } // 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); 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 T> std::expected load(const QJsonObject &json) { LightProject lp; if (auto deviceTypes = loadIfExists(json, "deviceTypes"); deviceTypes) { lp.deviceTypes = std::move(deviceTypes.value()); } else { return std::unexpected(QString("deviceTypes: %1").arg(deviceTypes.error())); } if (auto devices = loadIfExists(json, "devices"); devices) { lp.devices = std::move(devices.value()); } else { return std::unexpected(QString("devices: %1").arg(devices.error())); } if (auto presets = loadIfExists(json, "presets"); presets) { lp.presets = std::move(presets.value()); } else { return std::unexpected(QString("presets: %1").arg(presets.error())); } return lp; } template T> std::expected load(const QJsonObject &json) { DeviceTypeConfig dtc; if (auto val = loadIfExists(json, "id"); val) { dtc.id = val.value(); } else { return std::unexpected(QString("id: %1").arg(val.error())); } if (auto val = loadIfExists(json, "name"); val) { dtc.name = val.value(); } else { return std::unexpected(QString("name: %1").arg(val.error())); } if (auto val = loadIfExists(json, "iconName"); val) { dtc.iconName = val.value(); } else { return std::unexpected(QString("iconName: %1").arg(val.error())); } if (auto val = loadIfExists(json, "registers"); val) { dtc.registers = std::move(val.value()); } else { return std::unexpected(QString("registers: %1").arg(val.error())); } return dtc; } template T> std::expected load(const QJsonObject &json) { DeviceTypeRegisterConfig dtrc; if (auto val = loadIfExists(json, "type"); val) { dtrc.type = val.value(); } else { return std::unexpected(QString("type: %1").arg(val.error())); } return dtrc; } template T> std::expected load(const QJsonObject &json) { DeviceConfig dc; if (auto val = loadIfExists(json, "id"); val) { dc.id = val.value(); } else { return std::unexpected(QString("id: %1").arg(val.error())); } if (auto val = loadIfExists(json, "name"); val) { dc.name = val.value(); } else { return std::unexpected(QString("name: %1").arg(val.error())); } if (auto val = loadIfExists(json, "deviceTypeId"); val) { dc.deviceTypeId = val.value(); } else { return std::unexpected(QString("deviceTypeId: %1").arg(val.error())); } if (auto val = loadIfExists(json, "address"); val) { dc.address = val.value(); } else { return std::unexpected(QString("address: %1").arg(val.error())); } if (auto val = loadIfExists(json, "position"); val) { dc.position = val.value(); } else { return std::unexpected(QString("position: %1").arg(val.error())); } return dc; } template T> std::expected load(const QJsonObject &json) { PresetConfig rgc; if (auto val = loadIfExists(json, "id"); val) { rgc.id = val.value(); } else { return std::unexpected(QString("id: %1").arg(val.error())); } if (auto val = loadIfExists(json, "name"); val) { rgc.name = val.value(); } else { return std::unexpected(QString("name: %1").arg(val.error())); } if (auto val = loadIfExists(json, "sliders"); val) { rgc.sliders = val.value(); } else { return std::unexpected(QString("sliders: %1").arg(val.error())); } return rgc; } 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 T> std::expected save(const T &val) { QJsonObject json; if (auto id = save(val.id); id) { json["id"] = id.value(); } else { return std::unexpected(QString("id: %1").arg(id.error())); } if (auto name = save(val.name); name) { json["name"] = name.value(); } else { return std::unexpected(QString("name: %1").arg(name.error())); } if (auto iconName = save(val.iconName); iconName) { json["iconName"] = iconName.value(); } else { return std::unexpected(QString("iconName: %1").arg(iconName.error())); } if (auto registers = save(val.registers); registers) { json["registers"] = registers.value(); } else { return std::unexpected(QString("registers: %1").arg(registers.error())); } return json; } template T> std::expected save(const T &val) { QJsonObject json; if (auto type = save(val.type); type) { json["type"] = type.value(); } else { return std::unexpected(QString("type: %1").arg(type.error())); } return json; } template T> std::expected save(const T &val) { QJsonObject json; if (auto id = save(val.id); id) { json["id"] = id.value(); } else { return std::unexpected(QString("id: %1").arg(id.error())); } if (auto name = save(val.name); name) { json["name"] = name.value(); } else { return std::unexpected(QString("name: %1").arg(name.error())); } if (auto deviceTypeId = save(val.deviceTypeId); deviceTypeId) { json["deviceTypeId"] = deviceTypeId.value(); } else { return std::unexpected(QString("deviceTypeId: %1").arg(deviceTypeId.error())); } if (auto address = save(val.address); address) { json["address"] = address.value(); } else { return std::unexpected(QString("address: %1").arg(address.error())); } if (auto position = save(val.position); position) { json["position"] = position.value(); } else { return std::unexpected(QString("position: %1").arg(position.error())); } return json; } template T> std::expected save(const T &val) { QJsonObject json; if (auto id = save(val.id); id) { json["id"] = id.value(); } else { return std::unexpected(QString("id: %1").arg(id.error())); } if (auto name = save(val.name); name) { json["name"] = name.value(); } else { return std::unexpected(QString("name: %1").arg(name.error())); } if (auto sliders = save(val.sliders); sliders) { json["sliders"] = sliders.value(); } else { return std::unexpected(QString("sliders: %1").arg(sliders.error())); } return json; } template std::expected save(const T &val) { QJsonArray arr; for (size_t i = 0; i < val.size(); i++) { const auto &el = val[i]; auto json = save(el); if (json) { arr.push_back(json.value()); } else { return std::unexpected(QString("%1: %2").arg(i).arg(json.error())); } } return arr; } template T> std::expected save(const T &val) { QJsonObject json; if (auto deviceTypes = save(val.deviceTypes); deviceTypes) { json["deviceTypes"] = deviceTypes.value(); } else { return std::unexpected(QString("deviceTypes: %1").arg(deviceTypes.error())); } if (auto devices = save(val.devices); devices) { json["devices"] = devices.value(); } else { return std::unexpected(QString("devices: %1").arg(devices.error())); } if (auto presets = save(val.presets); presets) { json["presets"] = presets.value(); } else { return std::unexpected(QString("presets: %1").arg(presets.error())); } return json; } } // 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