Reject more template mistakes
Empty keys, duplicate layer names and section ids, repeat on non-keysym keys and stretch on letters/keys placeholders. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
#include "LayoutLoader.h"
|
#include "LayoutLoader.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <algorithm>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
@@ -48,6 +49,10 @@ std::optional<KeyDef> parseKey(const QJsonValue &value, QString *error)
|
|||||||
KeyDef key;
|
KeyDef key;
|
||||||
if (value.isString()) {
|
if (value.isString()) {
|
||||||
key.label = value.toString();
|
key.label = value.toString();
|
||||||
|
if (key.label.isEmpty()) {
|
||||||
|
*error = QStringLiteral("empty key \"\"");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
if (!value.isObject()) {
|
if (!value.isObject()) {
|
||||||
@@ -104,6 +109,14 @@ std::optional<KeyDef> parseKey(const QJsonValue &value, QString *error)
|
|||||||
*error = QStringLiteral("modifier must be one of %1").arg(ModifierNames.join(u", "));
|
*error = QStringLiteral("modifier must be one of %1").arg(ModifierNames.join(u", "));
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
if (key.repeat && key.type != KeyType::Keysym) {
|
||||||
|
*error = QStringLiteral("\"%1\": only keysym keys repeat").arg(typeName);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
if (key.stretch && (key.type == KeyType::Letters || key.type == KeyType::Keys)) {
|
||||||
|
*error = QStringLiteral("\"%1\" rows can't stretch; their keys come from the keymap").arg(typeName);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
if (key.width <= 0) {
|
if (key.width <= 0) {
|
||||||
*error = QStringLiteral("key \"%1\" has a non-positive width").arg(key.label);
|
*error = QStringLiteral("key \"%1\" has a non-positive width").arg(key.label);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -197,6 +210,10 @@ std::optional<Layout> parse(const QByteArray &json, QString *error)
|
|||||||
auto layer = parseLayer(layerValue.toObject(), error);
|
auto layer = parseLayer(layerValue.toObject(), error);
|
||||||
if (!layer)
|
if (!layer)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
if (layout.layer(layer->name)) {
|
||||||
|
*error = QStringLiteral("duplicate layer \"%1\"").arg(layer->name);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
layout.layers.append(*layer);
|
layout.layers.append(*layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +221,10 @@ std::optional<Layout> parse(const QByteArray &json, QString *error)
|
|||||||
auto section = parseSection(sectionValue.toObject(), error);
|
auto section = parseSection(sectionValue.toObject(), error);
|
||||||
if (!section)
|
if (!section)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
if (std::ranges::any_of(layout.sections, [&](const Section &s) { return s.id == section->id; })) {
|
||||||
|
*error = QStringLiteral("duplicate section \"%1\"").arg(section->id);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
layout.sections.append(*section);
|
layout.sections.append(*section);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,34 @@ private Q_SLOTS:
|
|||||||
QVERIFY(error.contains(u"warp"));
|
QVERIFY(error.contains(u"warp"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rejectsInvalidTemplates_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("json");
|
||||||
|
QTest::addColumn<QString>("why");
|
||||||
|
const auto row = [](const char *key) {
|
||||||
|
return QByteArray(R"({"id": "t", "layers": [{"name": "l", "rows": [[)") + key + "]]}]}";
|
||||||
|
};
|
||||||
|
QTest::newRow("empty key") << row(R"("")") << u"empty"_s;
|
||||||
|
QTest::newRow("repeat") << row(R"({"type": "backspace", "repeat": true})") << u"repeat"_s;
|
||||||
|
QTest::newRow("stretch") << row(R"({"type": "letters", "row": 0, "stretch": true})") << u"stretch"_s;
|
||||||
|
QTest::newRow("layer twice")
|
||||||
|
<< QByteArray(R"({"id": "t", "layers": [{"name": "l", "rows": [["a"]]}, {"name": "l", "rows": [["b"]]}]})")
|
||||||
|
<< u"duplicate layer"_s;
|
||||||
|
QTest::newRow("section twice")
|
||||||
|
<< QByteArray(R"({"id": "t", "layers": [{"name": "l", "rows": [["a"]]}], "sections": [)"
|
||||||
|
R"({"id": "s", "rows": [["b"]]}, {"id": "s", "rows": [["c"]]}]})")
|
||||||
|
<< u"duplicate section"_s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rejectsInvalidTemplates()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, json);
|
||||||
|
QFETCH(QString, why);
|
||||||
|
QString error;
|
||||||
|
QVERIFY(!LayoutLoader::parse(json, &error));
|
||||||
|
QVERIFY2(error.contains(why), qPrintable(error));
|
||||||
|
}
|
||||||
|
|
||||||
void rejectsMissingId()
|
void rejectsMissingId()
|
||||||
{
|
{
|
||||||
QString error;
|
QString error;
|
||||||
|
|||||||
Reference in New Issue
Block a user