Convert lua::peek to return a PeekResult

This commit is contained in:
Mm2PL 2024-07-13 23:41:47 +02:00
parent 9155242f76
commit 413dc34c47
No known key found for this signature in database
GPG key ID: 94AC9B80EFA15ED9
2 changed files with 99 additions and 54 deletions

View file

@ -156,31 +156,35 @@ StackIdx push(lua_State *L, const api::CompletionEvent &ev)
return idx; return idx;
} }
bool peek(lua_State *L, int *out, StackIdx idx) PeekResult peek(lua_State *L, int *out, StackIdx idx)
{ {
StackGuard guard(L); StackGuard guard(L);
if (lua_isnumber(L, idx) == 0) if (lua_isnumber(L, idx) == 0)
{ {
return false; return {
false,
{QString("Expected an integer got %1").arg(luaL_typename(L, idx))}};
} }
*out = lua_tointeger(L, idx); *out = lua_tointeger(L, idx);
return true; return {};
} }
bool peek(lua_State *L, bool *out, StackIdx idx) PeekResult peek(lua_State *L, bool *out, StackIdx idx)
{ {
StackGuard guard(L); StackGuard guard(L);
if (!lua_isboolean(L, idx)) if (!lua_isboolean(L, idx))
{ {
return false; return {
false,
{QString("Expected a boolean got %1").arg(luaL_typename(L, idx))}};
} }
*out = bool(lua_toboolean(L, idx)); *out = bool(lua_toboolean(L, idx));
return true; return {};
} }
bool peek(lua_State *L, double *out, StackIdx idx) PeekResult peek(lua_State *L, double *out, StackIdx idx)
{ {
StackGuard guard(L); StackGuard guard(L);
int ok{0}; int ok{0};
@ -188,76 +192,91 @@ bool peek(lua_State *L, double *out, StackIdx idx)
if (ok != 0) if (ok != 0)
{ {
*out = v; *out = v;
} return {};
return ok != 0;
} }
bool peek(lua_State *L, QString *out, StackIdx idx) return PeekResult::ofTypeError(L, idx, "integer or float");
}
PeekResult peek(lua_State *L, QString *out, StackIdx idx)
{ {
StackGuard guard(L); StackGuard guard(L);
size_t len{0}; size_t len{0};
const char *str = lua_tolstring(L, idx, &len); const char *str = lua_tolstring(L, idx, &len);
if (str == nullptr) if (str == nullptr)
{ {
return false; return PeekResult::ofTypeError(L, idx, "string");
} }
if (len >= INT_MAX) if (len >= INT_MAX)
{ {
assert(false && "string longer than INT_MAX, shit's fucked, yo"); return {false,
{QString("Strings bigger than 2.1 gigabytes are not allowed")}};
} }
*out = QString::fromUtf8(str, int(len)); *out = QString::fromUtf8(str, int(len));
return true; return {};
} }
bool peek(lua_State *L, QByteArray *out, StackIdx idx) PeekResult peek(lua_State *L, QByteArray *out, StackIdx idx)
{ {
StackGuard guard(L); StackGuard guard(L);
size_t len{0}; size_t len{0};
const char *str = lua_tolstring(L, idx, &len); const char *str = lua_tolstring(L, idx, &len);
if (str == nullptr) if (str == nullptr)
{ {
return false; return PeekResult::ofTypeError(L, idx, "string");
} }
if (len >= INT_MAX) if (len >= INT_MAX)
{ {
assert(false && "string longer than INT_MAX, shit's fucked, yo"); return {false,
{QString("Strings bigger than 2.1 gigabytes are not allowed")}};
} }
*out = QByteArray(str, int(len)); *out = QByteArray(str, int(len));
return true; return {};
} }
bool peek(lua_State *L, std::string *out, StackIdx idx) PeekResult peek(lua_State *L, std::string *out, StackIdx idx)
{ {
StackGuard guard(L); StackGuard guard(L);
size_t len{0}; size_t len{0};
const char *str = lua_tolstring(L, idx, &len); const char *str = lua_tolstring(L, idx, &len);
if (str == nullptr) if (str == nullptr)
{ {
return false; return PeekResult::ofTypeError(L, idx, "string");
} }
if (len >= INT_MAX) if (len >= INT_MAX)
{ {
assert(false && "string longer than INT_MAX, shit's fucked, yo"); return {false,
{QString("Strings bigger than 2.1 gigabytes are not allowed")}};
} }
*out = std::string(str, len); *out = std::string(str, len);
return true; return {};
} }
bool peek(lua_State *L, api::CompletionList *out, StackIdx idx) PeekResult peek(lua_State *L, api::CompletionList *out, StackIdx idx)
{ {
StackGuard guard(L); StackGuard guard(L);
int typ = lua_getfield(L, idx, "values"); int typ = lua_getfield(L, idx, "values");
if (typ != LUA_TTABLE) if (typ != LUA_TTABLE)
{ {
lua_pop(L, 1); lua_pop(L, 1);
return false; auto res = PeekResult::ofTypeError(L, idx, "string");
res.errorReason.emplace_back("While processing CompletionList.values");
return res;
} }
if (!lua::pop(L, &out->values, -1)) auto pres = lua::pop(L, &out->values, -1);
if (!pres)
{ {
return false; pres.errorReason.emplace_back("While processing CompletionList.values");
return pres;
} }
lua_getfield(L, idx, "hide_others"); lua_getfield(L, idx, "hide_others");
return lua::pop(L, &out->hideOthers); pres = lua::pop(L, &out->hideOthers);
if (!pres)
{
pres.errorReason.emplace_back(
"While processing CompletionList.hide_others");
}
return pres;
} }
QString toString(lua_State *L, StackIdx idx) QString toString(lua_State *L, StackIdx idx)
@ -290,5 +309,13 @@ void PeekResult::throwAsLuaError(lua_State *L)
assert(false && "unreachable"); assert(false && "unreachable");
} }
PeekResult PeekResult::ofTypeError(lua_State *L, StackIdx idx,
const QString &expect)
{
return PeekResult{
.ok = false,
.errorReason = {
QString("Expected %1, got %2").arg(expect, luaL_typename(L, idx))}};
}
} // namespace chatterino::lua } // namespace chatterino::lua
#endif #endif

View file

@ -1,9 +1,10 @@
#pragma once #pragma once
#ifdef CHATTERINO_HAVE_PLUGINS #ifdef CHATTERINO_HAVE_PLUGINS
# include "common/QLogging.hpp" # include "common/QLogging.hpp"
# include <boost/type_index.hpp>
extern "C" { extern "C" {
# include <lua.h> # include <lua.h>
# include <lualib.h> # include <lualib.h>
@ -19,7 +20,6 @@ extern "C" {
# include <variant> # include <variant>
# include <vector> # include <vector>
struct lua_State; struct lua_State;
class QJsonObject;
namespace chatterino { namespace chatterino {
struct CommandContext; struct CommandContext;
} // namespace chatterino } // namespace chatterino
@ -83,16 +83,19 @@ struct PeekResult {
* As luaL_error never returns this function does not either. * As luaL_error never returns this function does not either.
*/ */
[[noreturn]] void throwAsLuaError(lua_State *L); [[noreturn]] void throwAsLuaError(lua_State *L);
static PeekResult ofTypeError(lua_State *L, StackIdx idx,
const QString &expect);
}; };
// returns OK? // returns OK?
bool peek(lua_State *L, int *out, StackIdx idx = -1); PeekResult peek(lua_State *L, int *out, StackIdx idx = -1);
bool peek(lua_State *L, bool *out, StackIdx idx = -1); PeekResult peek(lua_State *L, bool *out, StackIdx idx = -1);
bool peek(lua_State *L, double *out, StackIdx idx = -1); PeekResult peek(lua_State *L, double *out, StackIdx idx = -1);
bool peek(lua_State *L, QString *out, StackIdx idx = -1); PeekResult peek(lua_State *L, QString *out, StackIdx idx = -1);
bool peek(lua_State *L, QByteArray *out, StackIdx idx = -1); PeekResult peek(lua_State *L, QByteArray *out, StackIdx idx = -1);
bool peek(lua_State *L, std::string *out, StackIdx idx = -1); PeekResult peek(lua_State *L, std::string *out, StackIdx idx = -1);
bool peek(lua_State *L, api::CompletionList *out, StackIdx idx = -1); PeekResult peek(lua_State *L, api::CompletionList *out, StackIdx idx = -1);
/** /**
* @brief Converts Lua object at stack index idx to a string. * @brief Converts Lua object at stack index idx to a string.
@ -170,12 +173,12 @@ StackIdx push(lua_State *L, std::optional<T> val)
} }
template <typename T> template <typename T>
bool peek(lua_State *L, std::optional<T> *out, StackIdx idx = -1) PeekResult peek(lua_State *L, std::optional<T> *out, StackIdx idx = -1)
{ {
if (lua_isnil(L, idx)) if (lua_isnil(L, idx))
{ {
*out = std::nullopt; *out = std::nullopt;
return true; return {};
} }
*out = T(); *out = T();
@ -183,7 +186,7 @@ bool peek(lua_State *L, std::optional<T> *out, StackIdx idx = -1)
} }
template <typename T> template <typename T>
bool peek(lua_State *L, std::vector<T> *vec, StackIdx idx = -1) PeekResult peek(lua_State *L, std::vector<T> *vec, StackIdx idx = -1)
{ {
StackGuard guard(L); StackGuard guard(L);
@ -192,37 +195,44 @@ bool peek(lua_State *L, std::vector<T> *vec, StackIdx idx = -1)
lua::stackDump(L, "!table"); lua::stackDump(L, "!table");
qCDebug(chatterinoLua) qCDebug(chatterinoLua)
<< "value is not a table, type is" << lua_type(L, idx); << "value is not a table, type is" << lua_type(L, idx);
return false; return {false,
{QString("Expected value to be a table, its actual type is %1")
.arg(lua_typename(L, lua_type(L, idx)))}};
} }
auto len = lua_rawlen(L, idx); auto len = lua_rawlen(L, idx);
if (len == 0) if (len == 0)
{ {
qCDebug(chatterinoLua) << "value has 0 length"; qCDebug(chatterinoLua) << "value has 0 length";
return true; return {};
} }
if (len > 1'000'000) if (len > 1'000'000)
{ {
qCDebug(chatterinoLua) << "value is too long"; qCDebug(chatterinoLua) << "value is too long";
return false; return {
false,
{QString("Table is too long, %1 > 1_000_000 elements").arg(len)}};
} }
// count like lua // count like lua
for (int i = 1; i <= len; i++) for (int i = 1; i <= len; i++)
{ {
lua_geti(L, idx, i); lua_geti(L, idx, i);
std::optional<T> obj; std::optional<T> obj;
if (!lua::peek(L, &obj)) PeekResult pres = lua::peek(L, &obj);
if (!pres)
{ {
//lua_seti(L, LUA_REGISTRYINDEX, 1); // lazy
qCDebug(chatterinoLua) qCDebug(chatterinoLua)
<< "Failed to convert lua object into c++: at array index " << i << "Failed to convert lua object into c++: at array index " << i
<< ":"; << ":";
stackDump(L, "bad conversion into string"); stackDump(L, "bad conversion");
return false; pres.errorReason.push_back(
QString("At element %1 of a table").arg(i));
return pres;
} }
lua_pop(L, 1); lua_pop(L, 1);
vec->push_back(obj.value()); vec->push_back(obj.value());
} }
return true; return {};
} }
/** /**
@ -230,21 +240,29 @@ bool peek(lua_State *L, std::vector<T> *vec, StackIdx idx = -1)
*/ */
template <typename T, template <typename T,
typename std::enable_if<std::is_enum_v<T>, bool>::type = true> typename std::enable_if<std::is_enum_v<T>, bool>::type = true>
bool peek(lua_State *L, T *out, StackIdx idx = -1) PeekResult peek(lua_State *L, T *out, StackIdx idx = -1)
{ {
std::string tmp; std::string tmp;
if (!lua::peek(L, &tmp, idx)) auto pres = lua::peek(L, &tmp, idx);
if (!pres)
{ {
return false; std::string type = boost::typeindex::type_id<T>().pretty_name();
pres.errorReason.push_back(QString("While converting to enum %1")
.arg(QString::fromStdString(type)));
return pres;
} }
std::optional<T> opt = magic_enum::enum_cast<T>(tmp); std::optional<T> opt = magic_enum::enum_cast<T>(tmp);
if (opt.has_value()) if (opt.has_value())
{ {
*out = opt.value(); *out = opt.value();
return true; return {};
} }
return false; std::string type = boost::typeindex::type_id<T>().pretty_name();
return {.ok = false,
.errorReason = {QString("Invalid enum value \"%1\" for enum %2")
.arg(QString::fromStdString(tmp),
QString::fromStdString(type))}};
} }
/** /**
@ -305,19 +323,19 @@ StackIdx push(lua_State *L, T inp)
* @brief Converts a Lua object into c++ and removes it from the stack. * @brief Converts a Lua object into c++ and removes it from the stack.
* If peek fails, the object is still removed from the stack. * If peek fails, the object is still removed from the stack.
* *
* Relies on bool peek(lua_State*, T*, StackIdx) existing. * Relies on PeekResult peek(lua_State*, T*, StackIdx) existing.
*/ */
template <typename T> template <typename T>
bool pop(lua_State *L, T *out, StackIdx idx = -1) PeekResult pop(lua_State *L, T *out, StackIdx idx = -1)
{ {
StackGuard guard(L, -1); StackGuard guard(L, -1);
auto ok = peek(L, out, idx); auto res = peek(L, out, idx);
if (idx < 0) if (idx < 0)
{ {
idx = lua_gettop(L) + idx + 1; idx = lua_gettop(L) + idx + 1;
} }
lua_remove(L, idx); lua_remove(L, idx);
return ok; return res;
} }
/** /**