// SPDX-License-Identifier: GPL-3.0-only /* * PolyMC - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This file incorporates work covered by the following copyright and * permission notice: * * Copyright 2013-2021 MultiMC Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include #include "Exception.h" namespace Json { class JsonException : public ::Exception { public: JsonException(const QString &message) : Exception(message) {} }; /// @throw FileSystemException void write(const QJsonDocument &doc, const QString &filename); /// @throw FileSystemException void write(const QJsonObject &object, const QString &filename); /// @throw FileSystemException void write(const QJsonArray &array, const QString &filename); QByteArray toText(const QJsonObject &obj); QByteArray toText(const QJsonArray &array); /// @throw JsonException QJsonDocument requireDocument(const QByteArray &data, const QString &what = "Document"); /// @throw JsonException QJsonDocument requireDocument(const QString &filename, const QString &what = "Document"); /// @throw JsonException QJsonObject requireObject(const QJsonDocument &doc, const QString &what = "Document"); /// @throw JsonException QJsonArray requireArray(const QJsonDocument &doc, const QString &what = "Document"); /////////////////// WRITING //////////////////// void writeString(QJsonObject & to, const QString &key, const QString &value); void writeStringList(QJsonObject & to, const QString &key, const QStringList &values); template QJsonValue toJson(const T &t) { return QJsonValue(t); } template<> QJsonValue toJson(const QUrl &url); template<> QJsonValue toJson(const QByteArray &data); template<> QJsonValue toJson(const QDateTime &datetime); template<> QJsonValue toJson(const QDir &dir); template<> QJsonValue toJson(const QUuid &uuid); template<> QJsonValue toJson(const QVariant &variant); template QJsonArray toJsonArray(const QList &container) { QJsonArray array; for (const T item : container) { array.append(toJson(item)); } return array; } ////////////////// READING //////////////////// /// @throw JsonException template T requireIsType(const QJsonValue &value, const QString &what = "Value"); /// @throw JsonException template<> double requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> bool requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> int requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> QJsonObject requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> QJsonArray requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> QJsonValue requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> QByteArray requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> QDateTime requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> QVariant requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> QString requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> QUuid requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> QDir requireIsType(const QJsonValue &value, const QString &what); /// @throw JsonException template<> QUrl requireIsType(const QJsonValue &value, const QString &what); // the following functions are higher level functions, that make use of the above functions for // type conversion template T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &what = "Value") { if (value.isUndefined() || value.isNull()) { return default_; } try { return requireIsType(value, what); } catch (const JsonException &) { return default_; } } /// @throw JsonException template T requireIsType(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); if (!parent.contains(key)) { throw JsonException(localWhat + "s parent does not contain " + localWhat); } return requireIsType(parent.value(key), localWhat); } template T ensureIsType(const QJsonObject &parent, const QString &key, const T default_ = T(), const QString &what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); if (!parent.contains(key)) { return default_; } return ensureIsType(parent.value(key), default_, localWhat); } template QVector requireIsArrayOf(const QJsonDocument &doc) { const QJsonArray array = requireArray(doc); QVector out; for (const QJsonValue val : array) { out.append(requireIsType(val, "Document")); } return out; } template QVector ensureIsArrayOf(const QJsonValue &value, const QString &what = "Value") { const QJsonArray array = ensureIsType(value, QJsonArray(), what); QVector out; for (const QJsonValue val : array) { out.append(requireIsType(val, what)); } return out; } template QVector ensureIsArrayOf(const QJsonValue &value, const QVector default_, const QString &what = "Value") { if (value.isUndefined()) { return default_; } return ensureIsArrayOf(value, what); } /// @throw JsonException template QVector requireIsArrayOf(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); if (!parent.contains(key)) { throw JsonException(localWhat + "s parent does not contain " + localWhat); } return ensureIsArrayOf(parent.value(key), localWhat); } template QVector ensureIsArrayOf(const QJsonObject &parent, const QString &key, const QVector &default_ = QVector(), const QString &what = "__placeholder__") { const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\''); if (!parent.contains(key)) { return default_; } return ensureIsArrayOf(parent.value(key), default_, localWhat); } // this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers #define JSON_HELPERFUNCTIONS(NAME, TYPE) \ inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \ { \ return requireIsType(value, what); \ } \ inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \ { \ return ensureIsType(value, default_, what); \ } \ inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \ { \ return requireIsType(parent, key, what); \ } \ inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \ { \ return ensureIsType(parent, key, default_, what); \ } JSON_HELPERFUNCTIONS(Array, QJsonArray) JSON_HELPERFUNCTIONS(Object, QJsonObject) JSON_HELPERFUNCTIONS(JsonValue, QJsonValue) JSON_HELPERFUNCTIONS(String, QString) JSON_HELPERFUNCTIONS(Boolean, bool) JSON_HELPERFUNCTIONS(Double, double) JSON_HELPERFUNCTIONS(Integer, int) JSON_HELPERFUNCTIONS(DateTime, QDateTime) JSON_HELPERFUNCTIONS(Url, QUrl) JSON_HELPERFUNCTIONS(ByteArray, QByteArray) JSON_HELPERFUNCTIONS(Dir, QDir) JSON_HELPERFUNCTIONS(Uuid, QUuid) JSON_HELPERFUNCTIONS(Variant, QVariant) #undef JSON_HELPERFUNCTIONS } using JSONValidationError = Json::JsonException;