mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Handle plugin metadata errors better
This commit is contained in:
parent
d1bcede492
commit
cf9d0af105
3 changed files with 119 additions and 58 deletions
|
@ -30,27 +30,42 @@ struct PluginMeta {
|
||||||
QString homepage;
|
QString homepage;
|
||||||
std::vector<QString> tags;
|
std::vector<QString> tags;
|
||||||
|
|
||||||
bool valid{};
|
std::vector<QString> errors;
|
||||||
std::vector<QString> invalidWhy;
|
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
return this->errors.empty();
|
||||||
|
}
|
||||||
|
|
||||||
explicit PluginMeta(const QJsonObject &obj)
|
explicit PluginMeta(const QJsonObject &obj)
|
||||||
: homepage(obj.value("homepage").toString(""))
|
: homepage(obj.value("homepage").toString(""))
|
||||||
{
|
{
|
||||||
auto nameObj = obj.value("name");
|
auto nameObj = obj.value("name");
|
||||||
if (!nameObj.isString())
|
if (nameObj.isString())
|
||||||
{
|
{
|
||||||
this->invalidWhy.emplace_back("name is not a string");
|
this->name = nameObj.toString();
|
||||||
this->valid = false;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto type = QString::fromStdString(
|
||||||
|
std::string(magic_enum::enum_name(nameObj.type())));
|
||||||
|
this->errors.emplace_back(
|
||||||
|
QString("name is not a string (its type is %1)").arg(type));
|
||||||
}
|
}
|
||||||
this->name = nameObj.toString();
|
|
||||||
|
|
||||||
auto descrObj = obj.value("description");
|
auto descrObj = obj.value("description");
|
||||||
if (!descrObj.isString())
|
if (descrObj.isString())
|
||||||
{
|
{
|
||||||
this->invalidWhy.emplace_back("description is not a string");
|
this->description = descrObj.toString();
|
||||||
this->valid = false;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto type = QString::fromStdString(
|
||||||
|
std::string(magic_enum::enum_name(descrObj.type())));
|
||||||
|
this->errors.emplace_back(
|
||||||
|
QString("description is not a string (its type is %1)")
|
||||||
|
.arg(type));
|
||||||
}
|
}
|
||||||
this->description = descrObj.toString();
|
|
||||||
|
|
||||||
auto authorsObj = obj.value("authors");
|
auto authorsObj = obj.value("authors");
|
||||||
if (authorsObj.isArray())
|
if (authorsObj.isArray())
|
||||||
|
@ -61,42 +76,60 @@ struct PluginMeta {
|
||||||
const auto &t = authorsArr.at(i);
|
const auto &t = authorsArr.at(i);
|
||||||
if (!t.isString())
|
if (!t.isString())
|
||||||
{
|
{
|
||||||
this->invalidWhy.push_back(
|
this->errors.push_back(
|
||||||
QString(
|
QString(
|
||||||
"authors element #%1 is not a string (it is a %2)")
|
"authors element #%1 is not a string (it is a %2)")
|
||||||
.arg(i)
|
.arg(i)
|
||||||
.arg(QString::fromStdString(
|
.arg(QString::fromStdString(
|
||||||
std::string(magic_enum::enum_name(t.type())))));
|
std::string(magic_enum::enum_name(t.type())))));
|
||||||
this->valid = false;
|
break;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
this->authors.push_back(t.toString());
|
this->authors.push_back(t.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->invalidWhy.emplace_back("authors is not an array");
|
auto type = QString::fromStdString(
|
||||||
this->valid = false;
|
std::string(magic_enum::enum_name(authorsObj.type())));
|
||||||
|
this->errors.emplace_back(
|
||||||
|
QString("authors is not an array (its type is %1)").arg(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto licenseObj = obj.value("license");
|
auto licenseObj = obj.value("license");
|
||||||
if (!licenseObj.isString())
|
if (licenseObj.isString())
|
||||||
{
|
{
|
||||||
this->invalidWhy.emplace_back("license is not a string");
|
this->license = licenseObj.toString();
|
||||||
this->valid = false;
|
|
||||||
}
|
|
||||||
this->license = licenseObj.toString();
|
|
||||||
|
|
||||||
auto v = semver::from_string_noexcept(
|
|
||||||
obj.value("version").toString().toStdString());
|
|
||||||
if (v.has_value())
|
|
||||||
{
|
|
||||||
this->version = v.value();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->invalidWhy.emplace_back("unable to parse version");
|
auto type = QString::fromStdString(
|
||||||
this->valid = false;
|
std::string(magic_enum::enum_name(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 = QString::fromStdString(
|
||||||
|
std::string(magic_enum::enum_name(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);
|
this->version = semver::version(0, 0, 0);
|
||||||
}
|
}
|
||||||
auto tagsObj = obj.value("tags");
|
auto tagsObj = obj.value("tags");
|
||||||
|
@ -104,8 +137,10 @@ struct PluginMeta {
|
||||||
{
|
{
|
||||||
if (!tagsObj.isArray())
|
if (!tagsObj.isArray())
|
||||||
{
|
{
|
||||||
this->invalidWhy.emplace_back("tags is not an array");
|
auto type = QString::fromStdString(
|
||||||
this->valid = false;
|
std::string(magic_enum::enum_name(licenseObj.type())));
|
||||||
|
this->errors.emplace_back(
|
||||||
|
QString("tags is not an array (its type is %1)").arg(type));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,12 +150,12 @@ struct PluginMeta {
|
||||||
const auto &t = tagsArr.at(i);
|
const auto &t = tagsArr.at(i);
|
||||||
if (!t.isString())
|
if (!t.isString())
|
||||||
{
|
{
|
||||||
this->invalidWhy.push_back(
|
this->errors.push_back(
|
||||||
QString("tags element #%1 is not a string (it is a %2)")
|
QString(
|
||||||
|
"tags element #%1 is not a string (its type is %2)")
|
||||||
.arg(i)
|
.arg(i)
|
||||||
.arg(QString::fromStdString(
|
.arg(QString::fromStdString(
|
||||||
std::string(magic_enum::enum_name(t.type())))));
|
std::string(magic_enum::enum_name(t.type())))));
|
||||||
this->valid = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->tags.push_back(t.toString());
|
this->tags.push_back(t.toString());
|
||||||
|
|
|
@ -92,14 +92,17 @@ bool PluginController::tryLoadFromDir(const QDir &pluginDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto meta = PluginMeta(doc.object());
|
auto meta = PluginMeta(doc.object());
|
||||||
if (!meta.invalidWhy.empty())
|
if (!meta.isValid())
|
||||||
{
|
{
|
||||||
qCDebug(chatterinoLua)
|
qCDebug(chatterinoLua)
|
||||||
<< "Plugin from" << pluginDir << "is invalid because:";
|
<< "Plugin from" << pluginDir << "is invalid because:";
|
||||||
for (const auto &why : meta.invalidWhy)
|
for (const auto &why : meta.errors)
|
||||||
{
|
{
|
||||||
qCDebug(chatterinoLua) << "- " << why;
|
qCDebug(chatterinoLua) << "- " << why;
|
||||||
}
|
}
|
||||||
|
auto plugin = std::make_unique<Plugin>(pluginDir.dirName(), nullptr,
|
||||||
|
meta, pluginDir);
|
||||||
|
this->plugins_.insert({pluginDir.dirName(), std::move(plugin)});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this->load(index, pluginDir, meta);
|
this->load(index, pluginDir, meta);
|
||||||
|
|
|
@ -87,6 +87,25 @@ void PluginsPage::rebuildContent()
|
||||||
}
|
}
|
||||||
auto plgroup = layout.emplace<QGroupBox>(headerText);
|
auto plgroup = layout.emplace<QGroupBox>(headerText);
|
||||||
auto pl = plgroup.setLayoutType<QFormLayout>();
|
auto pl = plgroup.setLayoutType<QFormLayout>();
|
||||||
|
|
||||||
|
if (!plugin->meta.isValid())
|
||||||
|
{
|
||||||
|
QString errors = "<ul>";
|
||||||
|
for (const auto &err : plugin->meta.errors)
|
||||||
|
{
|
||||||
|
errors += "<li>" + err.toHtmlEscaped() + "</li>";
|
||||||
|
}
|
||||||
|
errors += "</ul>";
|
||||||
|
|
||||||
|
auto *warningLabel = new QLabel(
|
||||||
|
"There were errors while loading metadata for this plugin:" +
|
||||||
|
errors);
|
||||||
|
warningLabel->setTextFormat(Qt::RichText);
|
||||||
|
warningLabel->setParent(this->dataFrame_);
|
||||||
|
warningLabel->setStyleSheet("color: #f00");
|
||||||
|
pl->addRow(warningLabel);
|
||||||
|
}
|
||||||
|
|
||||||
auto *descrText = new QLabel(plugin->meta.description);
|
auto *descrText = new QLabel(plugin->meta.description);
|
||||||
descrText->setWordWrap(true);
|
descrText->setWordWrap(true);
|
||||||
descrText->setStyleSheet("color: #bbb");
|
descrText->setStyleSheet("color: #bbb");
|
||||||
|
@ -122,31 +141,35 @@ void PluginsPage::rebuildContent()
|
||||||
}
|
}
|
||||||
pl->addRow("Commands", new QLabel(cmds));
|
pl->addRow("Commands", new QLabel(cmds));
|
||||||
|
|
||||||
QString enableOrDisableStr = "Enable";
|
if (plugin->meta.isValid())
|
||||||
if (PluginController::isEnabled(codename))
|
|
||||||
{
|
{
|
||||||
enableOrDisableStr = "Disable";
|
QString enableOrDisableStr = "Enable";
|
||||||
}
|
if (PluginController::isEnabled(codename))
|
||||||
|
{
|
||||||
|
enableOrDisableStr = "Disable";
|
||||||
|
}
|
||||||
|
|
||||||
auto *enableDisable = new QPushButton(enableOrDisableStr);
|
auto *enableDisable = new QPushButton(enableOrDisableStr);
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
enableDisable, &QPushButton::pressed, [name = codename, this]() {
|
enableDisable, &QPushButton::pressed,
|
||||||
std::vector<QString> val =
|
[name = codename, this]() {
|
||||||
getSettings()->enabledPlugins.getValue();
|
std::vector<QString> val =
|
||||||
if (PluginController::isEnabled(name))
|
getSettings()->enabledPlugins.getValue();
|
||||||
{
|
if (PluginController::isEnabled(name))
|
||||||
val.erase(std::remove(val.begin(), val.end(), name),
|
{
|
||||||
val.end());
|
val.erase(std::remove(val.begin(), val.end(), name),
|
||||||
}
|
val.end());
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
val.push_back(name);
|
{
|
||||||
}
|
val.push_back(name);
|
||||||
getSettings()->enabledPlugins.setValue(val);
|
}
|
||||||
getApp()->plugins->reload(name);
|
getSettings()->enabledPlugins.setValue(val);
|
||||||
this->rebuildContent();
|
getApp()->plugins->reload(name);
|
||||||
});
|
this->rebuildContent();
|
||||||
pl->addRow(enableDisable);
|
});
|
||||||
|
pl->addRow(enableDisable);
|
||||||
|
}
|
||||||
|
|
||||||
auto *reload = new QPushButton("Reload");
|
auto *reload = new QPushButton("Reload");
|
||||||
QObject::connect(reload, &QPushButton::pressed,
|
QObject::connect(reload, &QPushButton::pressed,
|
||||||
|
|
Loading…
Reference in a new issue