diff --git a/CMakeLists.txt b/CMakeLists.txt index d241127..a9795c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ qt_add_executable(applightcontrol registergroupsmodel.h registergroupsmodel.cpp deviceregistervaluehelper.h deviceregistervaluehelper.cpp registergroupmodel.h registergroupmodel.cpp + projectloader.h projectloader.cpp ) qt_add_qml_module(applightcontrol diff --git a/projectloader.cpp b/projectloader.cpp new file mode 100644 index 0000000..fded6b4 --- /dev/null +++ b/projectloader.cpp @@ -0,0 +1 @@ +#include "projectloader.h" diff --git a/projectloader.h b/projectloader.h new file mode 100644 index 0000000..ac7e721 --- /dev/null +++ b/projectloader.h @@ -0,0 +1,249 @@ +#ifndef PROJECTLOADER_H +#define PROJECTLOADER_H + +#include +#include + +#include +#include + +#include "lightproject.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; + + } + + 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.isObject()) + return std::unexpected("Not a JSON object"); + return load(json.toObject()); + } + + template + std::expected load(const QJsonArray &json) { + T result; + + for (size_t i = 0; i < json.size(); i++) { + 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 + 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 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++) { + 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 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())); + } + + 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())); + } + } + + 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) { + RegisterGroupConfig 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; + } + } // namespace + + std::expected loadProject(const QJsonObject &json) { + return load(json); + } +} // namespace ProjectLoader + +#endif // PROJECTLOADER_H