aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/lua/luaengine.h
blob: a683f43f2dac3a8d959fe429d66a2151a898a5d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#pragma once

#include "lua_global.h"

#include <extensionsystem/iplugin.h>
#include <extensionsystem/pluginspec.h>

#include <utils/expected.h>
#include <utils/filepath.h>

#include <sol/sol.hpp>

// this needs to be included after sol/sol.hpp!
#include "luaqttypes.h"

#include <QJsonValue>

#include <memory>

namespace Lua {
class LuaEnginePrivate;
class LuaPluginSpec;

namespace Internal {
class LuaPlugin;
}

struct CoroutineState
{
    bool isMainThread;
};

class LUA_EXPORT LuaEngine final : public QObject
{
    friend class Internal::LuaPlugin;

protected:
    LuaEngine();

public:
    using PackageProvider = std::function<sol::object(sol::state_view)>;

    ~LuaEngine();
    static LuaEngine &instance();

    Utils::expected_str<LuaPluginSpec *> loadPlugin(const Utils::FilePath &path);
    Utils::expected_str<void> prepareSetup(
        sol::state_view &lua, const LuaPluginSpec &pluginSpec, sol::optional<sol::table> hookTable);

    static void registerProvider(const QString &packageName, const PackageProvider &provider);
    static void autoRegister(const std::function<void(sol::state_view)> &registerFunction);
    static void registerHook(QString name, const std::function<void(sol::function)> &hookProvider);

    static Utils::expected_str<void> connectHooks(sol::state_view lua, const sol::table &hookTable);

    static bool isCoroutine(lua_State *state);

    static sol::table toTable(const sol::state_view &lua, const QJsonValue &v);
    static QJsonValue toJson(const sol::table &t);

    template<class T>
    static void checkKey(const sol::table &table, const QString &key)
    {
        if (table[key].template is<T>())
            return;
        if (!table[key].valid())
            throw sol::error("Expected " + key.toStdString() + " to be defined");
        throw sol::error(
            "Expected " + key.toStdString() + " to be of type " + sol::detail::demangle<T>());
    }

    static QStringList variadicToStringList(const sol::variadic_args &vargs);

    template<typename R, typename... Args>
    static Utils::expected_str<R> safe_call(const sol::protected_function &function, Args &&...args)
    {
        sol::protected_function_result result = function(std::forward<Args>(args)...);
        if (!result.valid()) {
            sol::error err = result;
            return Utils::make_unexpected(QString::fromLocal8Bit(err.what()));
        }
        try {
            return result.get<R>();
        } catch (std::runtime_error &e) {
            return Utils::make_unexpected(QString::fromLocal8Bit(e.what()));
        }
    }

    template<typename... Args>
    static Utils::expected_str<void> void_safe_call(
        const sol::protected_function &function, Args &&...args)
    {
        sol::protected_function_result result = function(std::forward<Args>(args)...);
        if (!result.valid()) {
            sol::error err = result;
            return Utils::make_unexpected(QString::fromLocal8Bit(err.what()));
        }
        return {};
    }

protected:
    Utils::expected_str<void> connectHooks(
        sol::state_view lua, const sol::table &table, const QString &path);

private:
    std::unique_ptr<LuaEnginePrivate> d;
};

} // namespace Lua