diff --git a/CHANGELOG.md b/CHANGELOG.md index 578b76aadd3..495b4734eb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,6 +95,7 @@ - Dev: Removed unused variables. (#6748) - Dev: Updated `miniaudio` to 0.11.24. (#6754) - Dev: Remove unused `reloadChannelAndSubscriberEmotes`. (#6756) +- Dev: Moved `PluginMeta` to its own file. (#6757) ## 2.5.4 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e9a1255acaa..5027f39449d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -281,6 +281,8 @@ set(SOURCE_FILES controllers/plugins/PluginController.hpp controllers/plugins/Plugin.cpp controllers/plugins/Plugin.hpp + controllers/plugins/PluginMeta.cpp + controllers/plugins/PluginMeta.hpp controllers/plugins/PluginPermission.cpp controllers/plugins/PluginPermission.hpp controllers/plugins/SolTypes.cpp diff --git a/src/controllers/plugins/Plugin.cpp b/src/controllers/plugins/Plugin.cpp index f13937a980a..fcd7c827266 100644 --- a/src/controllers/plugins/Plugin.cpp +++ b/src/controllers/plugins/Plugin.cpp @@ -9,12 +9,8 @@ # include "common/QLogging.hpp" # include "controllers/commands/CommandController.hpp" # include "controllers/plugins/PluginPermission.hpp" -# include "util/QMagicEnum.hpp" # include -# include -# include -# include # include # include # include @@ -25,174 +21,6 @@ namespace chatterino { -PluginMeta::PluginMeta(const QJsonObject &obj) -{ - auto homepageObj = obj.value("homepage"); - if (homepageObj.isString()) - { - this->homepage = homepageObj.toString(); - } - else if (!homepageObj.isUndefined()) - { - auto type = qmagicenum::enumName(homepageObj.type()); - this->errors.emplace_back( - QString("homepage is defined but is not a string (its type is %1)") - .arg(type)); - } - auto nameObj = obj.value("name"); - if (nameObj.isString()) - { - this->name = nameObj.toString(); - } - else - { - auto type = qmagicenum::enumName(nameObj.type()); - this->errors.emplace_back( - QString("name is not a string (its type is %1)").arg(type)); - } - - auto descrObj = obj.value("description"); - if (descrObj.isString()) - { - this->description = descrObj.toString(); - } - else - { - auto type = qmagicenum::enumName(descrObj.type()); - this->errors.emplace_back( - QString("description is not a string (its type is %1)").arg(type)); - } - - auto authorsObj = obj.value("authors"); - if (authorsObj.isArray()) - { - auto authorsArr = authorsObj.toArray(); - for (int i = 0; i < authorsArr.size(); i++) - { - const auto &t = authorsArr.at(i); - if (!t.isString()) - { - auto type = qmagicenum::enumName(t.type()); - this->errors.push_back( - QString("authors element #%1 is not a string (it is a %2)") - .arg(i) - .arg(type)); - break; - } - this->authors.push_back(t.toString()); - } - } - else - { - auto type = qmagicenum::enumName(authorsObj.type()); - this->errors.emplace_back( - QString("authors is not an array (its type is %1)").arg(type)); - } - - auto licenseObj = obj.value("license"); - if (licenseObj.isString()) - { - this->license = licenseObj.toString(); - } - else - { - auto type = qmagicenum::enumName(licenseObj.type()); - this->errors.emplace_back( - QString("license is not a string (its type is %1)").arg(type)); - } - - auto verObj = obj.value("version"); - if (verObj.isString()) - { - auto v = semver::from_string_noexcept(verObj.toString().toStdString()); - if (v.has_value()) - { - this->version = v.value(); - } - else - { - this->errors.emplace_back("unable to parse version (use semver)"); - this->version = semver::version(0, 0, 0); - } - } - else - { - auto type = qmagicenum::enumName(verObj.type()); - this->errors.emplace_back( - QString("version is not a string (its type is %1)").arg(type)); - this->version = semver::version(0, 0, 0); - } - auto permsObj = obj.value("permissions"); - if (!permsObj.isUndefined()) - { - if (!permsObj.isArray()) - { - auto type = qmagicenum::enumName(permsObj.type()); - this->errors.emplace_back( - QString("permissions is not an array (its type is %1)") - .arg(type)); - return; - } - - auto permsArr = permsObj.toArray(); - for (int i = 0; i < permsArr.size(); i++) - { - const auto &t = permsArr.at(i); - if (!t.isObject()) - { - auto type = qmagicenum::enumName(t.type()); - this->errors.push_back(QString("permissions element #%1 is not " - "an object (its type is %2)") - .arg(i) - .arg(type)); - return; - } - auto parsed = PluginPermission(t.toObject()); - if (parsed.isValid()) - { - // ensure no invalid permissions slip through this - this->permissions.push_back(parsed); - } - else - { - for (const auto &err : parsed.errors) - { - this->errors.push_back( - QString("permissions element #%1: %2").arg(i).arg(err)); - } - } - } - } - - auto tagsObj = obj.value("tags"); - if (!tagsObj.isUndefined()) - { - if (!tagsObj.isArray()) - { - auto type = qmagicenum::enumName(tagsObj.type()); - this->errors.emplace_back( - QString("tags is not an array (its type is %1)").arg(type)); - return; - } - - auto tagsArr = tagsObj.toArray(); - for (int i = 0; i < tagsArr.size(); i++) - { - const auto &t = tagsArr.at(i); - if (!t.isString()) - { - auto type = qmagicenum::enumName(t.type()); - this->errors.push_back( - QString("tags element #%1 is not a string (its type is %2)") - .arg(i) - .arg(type)); - return; - } - this->tags.push_back(t.toString()); - } - } -} - bool Plugin::registerCommand(const QString &name, sol::protected_function function) { diff --git a/src/controllers/plugins/Plugin.hpp b/src/controllers/plugins/Plugin.hpp index 78b6eb76b21..999f3909ff9 100644 --- a/src/controllers/plugins/Plugin.hpp +++ b/src/controllers/plugins/Plugin.hpp @@ -7,8 +7,7 @@ #ifdef CHATTERINO_HAVE_PLUGINS # include "controllers/plugins/api/EventType.hpp" # include "controllers/plugins/api/HTTPRequest.hpp" -# include "controllers/plugins/LuaUtilities.hpp" -# include "controllers/plugins/PluginPermission.hpp" +# include "controllers/plugins/PluginMeta.hpp" # include # include @@ -32,45 +31,6 @@ enum class LogLevel; namespace chatterino { -struct PluginMeta { - // for more info on these fields see docs/plugin-info.schema.json - - // display name of the plugin - QString name; - - // description shown to the user - QString description; - - // plugin authors shown to the user - std::vector authors; - - // license name - QString license; - - // version of the plugin - semver::version version; - - // optionally a homepage link - QString homepage; - - // optionally tags that might help in searching for the plugin - std::vector tags; - - std::vector permissions; - - // errors that occurred while parsing info.json - std::vector errors; - - bool isValid() const - { - return this->errors.empty(); - } - - explicit PluginMeta(const QJsonObject &obj); - // This is for tests - PluginMeta() = default; -}; - class Plugin { public: diff --git a/src/controllers/plugins/PluginMeta.cpp b/src/controllers/plugins/PluginMeta.cpp new file mode 100644 index 00000000000..945516ccdaf --- /dev/null +++ b/src/controllers/plugins/PluginMeta.cpp @@ -0,0 +1,188 @@ +// SPDX-FileCopyrightText: 2026 Contributors to Chatterino +// +// SPDX-License-Identifier: MIT + +#ifdef CHATTERINO_HAVE_PLUGINS +# include "controllers/plugins/PluginMeta.hpp" + +# include "util/QMagicEnum.hpp" + +# include +# include +# include + +namespace chatterino { + +// NOLINTBEGIN(clazy-reserve-candidates) +PluginMeta::PluginMeta(const QJsonObject &obj) +{ + auto homepageObj = obj.value("homepage"); + if (homepageObj.isString()) + { + this->homepage = homepageObj.toString(); + } + else if (!homepageObj.isUndefined()) + { + auto type = qmagicenum::enumName(homepageObj.type()); + this->errors.emplace_back( + QString("homepage is defined but is not a string (its type is %1)") + .arg(type)); + } + auto nameObj = obj.value("name"); + if (nameObj.isString()) + { + this->name = nameObj.toString(); + } + else + { + auto type = qmagicenum::enumName(nameObj.type()); + this->errors.emplace_back( + QString("name is not a string (its type is %1)").arg(type)); + } + + auto descrObj = obj.value("description"); + if (descrObj.isString()) + { + this->description = descrObj.toString(); + } + else + { + auto type = qmagicenum::enumName(descrObj.type()); + this->errors.emplace_back( + QString("description is not a string (its type is %1)").arg(type)); + } + + auto authorsObj = obj.value("authors"); + if (authorsObj.isArray()) + { + auto authorsArr = authorsObj.toArray(); + for (int i = 0; i < authorsArr.size(); i++) + { + const auto &t = authorsArr.at(i); + if (!t.isString()) + { + auto type = qmagicenum::enumName(t.type()); + this->errors.push_back( + QString("authors element #%1 is not a string (it is a %2)") + .arg(i) + .arg(type)); + break; + } + this->authors.push_back(t.toString()); + } + } + else + { + auto type = qmagicenum::enumName(authorsObj.type()); + this->errors.emplace_back( + QString("authors is not an array (its type is %1)").arg(type)); + } + + auto licenseObj = obj.value("license"); + if (licenseObj.isString()) + { + this->license = licenseObj.toString(); + } + else + { + auto type = qmagicenum::enumName(licenseObj.type()); + this->errors.emplace_back( + QString("license is not a string (its type is %1)").arg(type)); + } + + auto verObj = obj.value("version"); + if (verObj.isString()) + { + auto v = semver::from_string_noexcept(verObj.toString().toStdString()); + if (v.has_value()) + { + this->version = v.value(); + } + else + { + this->errors.emplace_back("unable to parse version (use semver)"); + this->version = semver::version(0, 0, 0); + } + } + else + { + auto type = qmagicenum::enumName(verObj.type()); + this->errors.emplace_back( + QString("version is not a string (its type is %1)").arg(type)); + this->version = semver::version(0, 0, 0); + } + auto permsObj = obj.value("permissions"); + if (!permsObj.isUndefined()) + { + if (!permsObj.isArray()) + { + auto type = qmagicenum::enumName(permsObj.type()); + this->errors.emplace_back( + QString("permissions is not an array (its type is %1)") + .arg(type)); + return; + } + + auto permsArr = permsObj.toArray(); + for (int i = 0; i < permsArr.size(); i++) + { + const auto &t = permsArr.at(i); + if (!t.isObject()) + { + auto type = qmagicenum::enumName(t.type()); + this->errors.push_back(QString("permissions element #%1 is not " + "an object (its type is %2)") + .arg(i) + .arg(type)); + return; + } + auto parsed = PluginPermission(t.toObject()); + if (parsed.isValid()) + { + // ensure no invalid permissions slip through this + this->permissions.push_back(parsed); + } + else + { + for (const auto &err : parsed.errors) + { + this->errors.push_back( + QString("permissions element #%1: %2").arg(i).arg(err)); + } + } + } + } + + auto tagsObj = obj.value("tags"); + if (!tagsObj.isUndefined()) + { + if (!tagsObj.isArray()) + { + auto type = qmagicenum::enumName(tagsObj.type()); + this->errors.emplace_back( + QString("tags is not an array (its type is %1)").arg(type)); + return; + } + + auto tagsArr = tagsObj.toArray(); + for (int i = 0; i < tagsArr.size(); i++) + { + const auto &t = tagsArr.at(i); + if (!t.isString()) + { + auto type = qmagicenum::enumName(t.type()); + this->errors.push_back( + QString("tags element #%1 is not a string (its type is %2)") + .arg(i) + .arg(type)); + return; + } + this->tags.push_back(t.toString()); + } + } +} +// NOLINTEND(clazy-reserve-candidates) + +} // namespace chatterino + +#endif diff --git a/src/controllers/plugins/PluginMeta.hpp b/src/controllers/plugins/PluginMeta.hpp new file mode 100644 index 00000000000..c18a3a37339 --- /dev/null +++ b/src/controllers/plugins/PluginMeta.hpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2026 Contributors to Chatterino +// +// SPDX-License-Identifier: MIT + +#pragma once + +#ifdef CHATTERINO_HAVE_PLUGINS +# include "controllers/plugins/PluginPermission.hpp" + +# include +# include +# include + +# include + +namespace chatterino { + +struct PluginMeta { + // for more info on these fields see docs/plugin-info.schema.json + + // display name of the plugin + QString name; + + // description shown to the user + QString description; + + // plugin authors shown to the user + std::vector authors; + + // license name + QString license; + + // version of the plugin + semver::version version; + + // optionally a homepage link + QString homepage; + + // optionally tags that might help in searching for the plugin + std::vector tags; + + std::vector permissions; + + // errors that occurred while parsing info.json + std::vector errors; + + bool isValid() const + { + return this->errors.empty(); + } + + explicit PluginMeta(const QJsonObject &obj); + // This is for tests + PluginMeta() = default; +}; + +} // namespace chatterino + +#endif