Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve: allow isActive(...) and exists(...) to use an ID number #6401

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
221 changes: 171 additions & 50 deletions src/TLuaInterpreter.cpp
Expand Up @@ -220,7 +220,11 @@ TLuaInterpreter::~TLuaInterpreter()
// With reduced repetition like that:
// bool showOnTop = getVerifiedBool(L, "createMapLabel", 14, "showOnTop", true);
//
// The "isOptional" parameter is optional, and will default to not-optional parameters! :)
// The "isOptional" parameter is optional but modifies the error message to say
// that an argument is optional and it will default to not-optional parameters!
// HOWEVER it does not actually handle the absence of an argument that is
// supposed to BE optional - that has to be done by the caller before it
// makes the call... 8-P
Comment on lines +223 to +227
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit of a bummer but I think the logic here might confuse others, especially not native English speakers

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are internal use functions though - only seen by us developers - who will need to remember this detail! 👿

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have non-native English speaking developers (and would love more), can you reword this to be simpler?

Copy link
Member Author

@SlySven SlySven Nov 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vadi2

-// The "isOptional" parameter is optional but modifies the error message to say
-// that an argument is optional and it will default to not-optional parameters!
-// HOWEVER it does not actually handle the absence of an argument that is
-// supposed to BE optional - that has to be done by the caller before it
-// makes the call... 8-P
+// The (optional here) "isOptional" parameter only modifies the error message
+// given to the user to say that the parameter need not be provided. It does not
+// (until someone works out how to!) allow this function to deal with the argument
+// at the given pos not being present - so it is up to the function that calls this to
+// check for that BEFORE trying to call this function. This applies to all the
+// "getVerifiedXxxx" functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

//
// See also: getVerifiedString, getVerifiedInt, getVerifiedFloat, errorArgumentType
bool TLuaInterpreter::getVerifiedBool(lua_State* L, const char* functionName, const int pos, const char* publicName, const bool isOptional)
Expand All @@ -234,6 +238,26 @@ bool TLuaInterpreter::getVerifiedBool(lua_State* L, const char* functionName, co
return lua_toboolean(L, pos);
}

// No documentation available in wiki - internal function
// See also: getVerifiedBool
/*static*/ std::pair<bool, QString> TLuaInterpreter::getVerifiedStringOrInteger(lua_State* L, const char* functionName, const int pos, const char* publicName, const bool isOptional)
{
if (lua_type(L, pos) == LUA_TNUMBER) {
// use lua_tonumber(...) and round because lua_tointeger(...) can return
// oversized values (long long int?) on Windows which do not always fit
// into an int:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

return {true, QString::number(qRound(lua_tonumber(L, pos)))};
}

if (lua_type(L, pos) == LUA_TSTRING) {
return {false, lua_tostring(L, pos)};
}

errorArgumentType(L, functionName, pos, publicName, "string or integer", isOptional);
lua_error(L);
Q_UNREACHABLE();
}

// No documentation available in wiki - internal function
// See also: getVerifiedBool
QString TLuaInterpreter::getVerifiedString(lua_State* L, const char* functionName, const int pos, const char* publicName, const bool isOptional)
Expand Down Expand Up @@ -7922,87 +7946,184 @@ int TLuaInterpreter::tempAlias(lua_State* L)
// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#exists
int TLuaInterpreter::exists(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "name");
QString type = getVerifiedString(L, __func__, 2, "type");
auto [isId, nameOrId] = getVerifiedStringOrInteger(L, __func__, 1, "item name or ID");
// Although we only use 4 ASCII strings the user may not enter a purely
// ASCII value which we might have to report...
QString type = getVerifiedString(L, __func__, 2, "item type").toLower();
bool isOk = false;
int id = nameOrId.toInt(&isOk);
if (isId && (!isOk || id < 0)) {
// Must be zero or more but doesn't seem to be, must return the
// original supplied argument as a string (rather than the nameOrId
// "number" as the latter will have been rounded to an integer) to
// show what was entered:
return warnArgumentValue(L, __func__, qsl("item ID as %1 does not seem to be parseable as a positive integer").arg(lua_tostring(L, 1)));
}

Host& host = getHostFromLua(L);
int count = 0;
type = type.toLower();
if (type == qsl("timer")) {
count = host.getTimerUnit()->mLookupTable.count(name);
} else if (type == qsl("trigger")) {
count = host.getTriggerUnit()->mLookupTable.count(name);
} else if (type == qsl("alias")) {
count = host.getAliasUnit()->mLookupTable.count(name);
} else if (type == qsl("keybind")) {
count = host.getKeyUnit()->mLookupTable.count(name);
} else if (type == qsl("button")) {
count = host.getActionUnit()->findActionsByName(name).size();
} else if (type == qsl("script")) {
count = host.getScriptUnit()->findScriptId(name).size();
if (!type.compare(QLatin1String("timer"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getTimerUnit()->getTimer(id);
lua_pushnumber(L, static_cast<bool>(pT) ? 1 : 0);
return 1;
}

count = host.getTimerUnit()->mLookupTable.count(nameOrId);
} else if (!type.compare(QLatin1String("trigger"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getTriggerUnit()->getTrigger(id);
lua_pushnumber(L, static_cast<bool>(pT) ? 1 : 0);
return 1;
}

count = host.getTriggerUnit()->mLookupTable.count(nameOrId);
} else if (!type.compare(QLatin1String("alias"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getAliasUnit()->getAlias(id);
lua_pushnumber(L, static_cast<bool>(pT) ? 1 : 0);
return 1;
}

count = host.getAliasUnit()->mLookupTable.count(nameOrId);
} else if (!type.compare(QLatin1String("keybind"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getKeyUnit()->getKey(id);
lua_pushnumber(L, static_cast<bool>(pT) ? 1 : 0);
return 1;
}

count = host.getKeyUnit()->mLookupTable.count(nameOrId);
} else if (!type.compare(QLatin1String("button"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getActionUnit()->getAction(id);
lua_pushnumber(L, static_cast<bool>(pT) ? 1 : 0);
return 1;
}

count = host.getActionUnit()->findActionsByName(nameOrId).size();
} else if (!type.compare(QLatin1String("script"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getScriptUnit()->getScript(id);
lua_pushnumber(L, static_cast<bool>(pT) ? 1 : 0);
return 1;
}

count = host.getScriptUnit()->findScriptId(nameOrId).size();
} else {
return warnArgumentValue(L, __func__, qsl(
"invalid item type '%1' given, it should be one of: 'alias', 'button', 'script', 'keybind', 'timer' or 'trigger'").arg(type));
}
// If we get here we have successfully identified a type and have looked for
// the item type with a specific NAME - so now just return the count of
// those found:
lua_pushnumber(L, count);
return 1;
}

// Documentation: https://wiki.mudlet.org/w/Manual:Lua_Functions#isActive
int TLuaInterpreter::isActive(lua_State* L)
{
QString name = getVerifiedString(L, __func__, 1, "item name");
auto [isId, nameOrId] = getVerifiedStringOrInteger(L, __func__, 1, "item name or ID");
// Although we only use 4 ASCII strings the user may not enter a purely
// ASCII value which we might have to report...
QString type = getVerifiedString(L, __func__, 2, "item type");
bool isOk = false;
int id = nameOrId.toInt(&isOk);
if (isId && (!isOk || id < 0)) {
// Must be zero or more but doesn't seem to be, must return the
// original supplied argument as a string (rather than the nameOrId
// "number" as the latter will have been rounded to an integer) to
// show what was entered:
return warnArgumentValue(L, __func__, qsl("item ID as %1 does not seem to be parseable as a positive integer").arg(lua_tostring(L, 1)));
}

Host& host = getHostFromLua(L);
int cnt = 0;
if (type.compare(QLatin1String("timer"), Qt::CaseInsensitive) == 0) {
QMap<QString, TTimer*>::const_iterator it1 = host.getTimerUnit()->mLookupTable.constFind(name);
while (it1 != host.getTimerUnit()->mLookupTable.cend() && it1.key() == name) {
if (it1.value()->isActive()) {
cnt++;
// Remember, QString::compare(...) returns zero for a match:
if (!type.compare(QLatin1String("timer"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getTimerUnit()->getTimer(id);
cnt = (static_cast<bool>(pT) && pT->isActive()) ? 1 : 0;
} else {
QMap<QString, TTimer*>::const_iterator it1 = host.getTimerUnit()->mLookupTable.constFind(nameOrId);
while (it1 != host.getTimerUnit()->mLookupTable.cend() && it1.key() == nameOrId) {
if (it1.value()->isActive()) {
++cnt;
}
++it1;
}
it1++;
}
} else if (type.compare(QLatin1String("trigger"), Qt::CaseInsensitive) == 0) {
QMap<QString, TTrigger*>::const_iterator it1 = host.getTriggerUnit()->mLookupTable.constFind(name);
while (it1 != host.getTriggerUnit()->mLookupTable.cend() && it1.key() == name) {
if (it1.value()->isActive()) {
cnt++;

} else if (!type.compare(QLatin1String("trigger"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getTriggerUnit()->getTrigger(id);
cnt = (static_cast<bool>(pT) && pT->isActive()) ? 1 : 0;
} else {
QMap<QString, TTrigger*>::const_iterator it1 = host.getTriggerUnit()->mLookupTable.constFind(nameOrId);
while (it1 != host.getTriggerUnit()->mLookupTable.cend() && it1.key() == nameOrId) {
if (it1.value()->isActive()) {
++cnt;
}
++it1;
}
it1++;
}
} else if (type.compare(QLatin1String("alias"), Qt::CaseInsensitive) == 0) {
QMap<QString, TAlias*>::const_iterator it1 = host.getAliasUnit()->mLookupTable.constFind(name);
while (it1 != host.getAliasUnit()->mLookupTable.cend() && it1.key() == name) {
if (it1.value()->isActive()) {
cnt++;

} else if (!type.compare(QLatin1String("alias"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getAliasUnit()->getAlias(id);
cnt = (static_cast<bool>(pT) && pT->isActive()) ? 1 : 0;
} else {
QMap<QString, TAlias*>::const_iterator it1 = host.getAliasUnit()->mLookupTable.constFind(nameOrId);
while (it1 != host.getAliasUnit()->mLookupTable.cend() && it1.key() == nameOrId) {
if (it1.value()->isActive()) {
++cnt;
}
++it1;
}
it1++;
}
} else if (type.compare(QLatin1String("keybind"), Qt::CaseInsensitive) == 0) {
QMap<QString, TKey*>::const_iterator it1 = host.getKeyUnit()->mLookupTable.constFind(name);
while (it1 != host.getKeyUnit()->mLookupTable.cend() && it1.key() == name) {
if (it1.value()->isActive()) {
cnt++;

} else if (!type.compare(QLatin1String("keybind"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getKeyUnit()->getKey(id);
cnt = (static_cast<bool>(pT) && pT->isActive()) ? 1 : 0;
} else {
QMap<QString, TKey*>::const_iterator it1 = host.getKeyUnit()->mLookupTable.constFind(nameOrId);
while (it1 != host.getKeyUnit()->mLookupTable.cend() && it1.key() == nameOrId) {
if (it1.value()->isActive()) {
++cnt;
}
++it1;
}
it1++;
}
} else if (type.compare(QLatin1String("button"), Qt::CaseInsensitive) == 0) {
QMap<int, TAction*> actions = host.getActionUnit()->getActionList();
for (auto action : actions) {
if (action->getName() == name && action->isActive()) {
++cnt;

} else if (!type.compare(QLatin1String("button"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getActionUnit()->getAction(id);
cnt = (static_cast<bool>(pT) && pT->isActive()) ? 1 : 0;
} else {
QMap<int, TAction*> actions = host.getActionUnit()->getActionList();
for (auto action : actions) {
if (action->getName() == nameOrId && action->isActive()) {
++cnt;
}
}
}
} else if (type.compare(QLatin1String("script"), Qt::CaseInsensitive) == 0) {
QMap<int, TScript*> scripts = host.getScriptUnit()->getScriptList();
for (auto script : scripts) {
if (script->getName() == name && script->isActive()) {
++cnt;

} else if (!type.compare(QLatin1String("script"), Qt::CaseInsensitive)) {
if (isId) {
auto pT = host.getScriptUnit()->getScript(id);
cnt = (static_cast<bool>(pT) && pT->isActive()) ? 1 : 0;
} else {
QMap<int, TScript*> scripts = host.getScriptUnit()->getScriptList();
for (auto script : scripts) {
if (script->getName() == nameOrId && script->isActive()) {
++cnt;
}
}
}

} else {
return warnArgumentValue(L, __func__, qsl(
"invalid item type '%1' given, it should be one (case insensitive) of: 'alias', 'button', 'script', 'keybind', 'timer' or 'trigger'").arg(type));
Expand Down
1 change: 1 addition & 0 deletions src/TLuaInterpreter.h
Expand Up @@ -694,6 +694,7 @@ public slots:
static int getVerifiedInt(lua_State*, const char* functionName, const int pos, const char* publicName, const bool isOptional = false);
static float getVerifiedFloat(lua_State*, const char* functionName, const int pos, const char* publicName, const bool isOptional = false);
static double getVerifiedDouble(lua_State*, const char* functionName, const int pos, const char* publicName, const bool isOptional = false);
static std::pair<bool, QString> getVerifiedStringOrInteger(lua_State*, const char* functionName, const int pos, const char* publicName, const bool isOptional = false);
static void errorArgumentType(lua_State*, const char* functionName, const int pos, const char* publicName, const char* publicType, const bool isOptional = false);
static int warnArgumentValue(lua_State*, const char* functionName, const QString& message, const bool useFalseInsteadofNil = false);
static int warnArgumentValue(lua_State*, const char* functionName, const char* message, const bool useFalseInsteadofNil = false);
Expand Down