Lua: Create Process bindings

Change-Id: I4b8cca8433df306acdb13e9651c08ab1e01ffa82
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
Marcus Tillmanns
2024-09-19 09:05:38 +02:00
parent 22e192e45a
commit 469124a89a
2 changed files with 142 additions and 0 deletions

View File

@@ -10,10 +10,24 @@ using namespace Utils;
namespace Lua::Internal { namespace Lua::Internal {
FilePath toFilePath(std::variant<FilePath, QString> &&v)
{
return std::visit(
[](auto &&arg) -> FilePath {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, QString>)
return FilePath::fromUserInput(arg);
else
return arg;
},
v);
}
void setupProcessModule() void setupProcessModule()
{ {
registerProvider("Process", [](sol::state_view lua) -> sol::object { registerProvider("Process", [](sol::state_view lua) -> sol::object {
const ScriptPluginSpec *pluginSpec = lua.get<ScriptPluginSpec *>("PluginSpec"); const ScriptPluginSpec *pluginSpec = lua.get<ScriptPluginSpec *>("PluginSpec");
QObject *guard = pluginSpec->connectionGuard.get();
sol::table async = lua.script("return require('async')", "_process_").get<sol::table>(); sol::table async = lua.script("return require('async')", "_process_").get<sol::table>();
sol::function wrap = async["wrap"]; sol::function wrap = async["wrap"];
@@ -46,6 +60,100 @@ void setupProcessModule()
process["runInTerminal"] = wrap(process["runInTerminal_cb"]); process["runInTerminal"] = wrap(process["runInTerminal_cb"]);
process["commandOutput"] = wrap(process["commandOutput_cb"]); process["commandOutput"] = wrap(process["commandOutput_cb"]);
process["create"] = [](const sol::table &parameter) {
const auto cmd = toFilePath(parameter.get<std::variant<FilePath, QString>>("command"));
const QStringList arguments
= parameter.get_or<QStringList, const char *, QStringList>("arguments", {});
const std::optional<FilePath> workingDirectory = parameter.get<std::optional<FilePath>>(
"workingDirectory");
const auto stdOut = parameter.get<std::optional<sol::function>>("stdout");
const auto stdErr = parameter.get<std::optional<sol::function>>("stderr");
const auto stdIn = parameter.get<sol::optional<QString>>("stdin");
auto p = std::make_unique<Process>();
p->setCommand({cmd, arguments});
if (workingDirectory)
p->setWorkingDirectory(*workingDirectory);
if (stdIn)
p->setWriteData(stdIn->toUtf8());
if (stdOut) {
// clang-format off
QObject::connect(p.get(), &Process::readyReadStandardOutput,
p.get(),
[p = p.get(), cb = *stdOut]() {
void_safe_call(cb, p->readAllStandardOutput());
});
// clang-format on
}
if (stdErr) {
// clang-format off
QObject::connect(p.get(), &Process::readyReadStandardError,
p.get(),
[p = p.get(), cb = *stdErr]() {
void_safe_call(cb, p->readAllStandardError());
});
// clang-format on
}
return p;
};
process.new_usertype<Process>(
"Process",
sol::no_constructor,
"start_cb",
[guard](Process *process, sol::function callback) {
if (process->state() != QProcess::NotRunning)
callback(false, "Process is already running");
struct Connections
{
QMetaObject::Connection startedConnection;
QMetaObject::Connection doneConnection;
};
std::shared_ptr<Connections> connections = std::make_shared<Connections>();
// clang-format off
connections->startedConnection
= QObject::connect(process, &Process::started,
guard, [callback, process, connections]() {
process->disconnect(connections->doneConnection);
callback(true);
},
Qt::SingleShotConnection);
connections->doneConnection
= QObject::connect(process, &Process::done,
guard, [callback, process, connections]() {
process->disconnect(connections->startedConnection);
callback(false, process->errorString());
},
Qt::SingleShotConnection);
// clang-format on
process->start();
},
"stop_cb",
[](Process *process, sol::function callback) {
if (process->state() != QProcess::Running)
callback(false, "Process is not running");
// clang-format off
QObject::connect(process, &Process::done,
process, [callback, process]() {
callback(true);
process->disconnect();
},
Qt::SingleShotConnection);
// clang-format on
process->stop();
},
"isRunning",
&Process::isRunning);
process["Process"]["start"] = wrap(process["Process"]["start_cb"]);
process["Process"]["stop"] = wrap(process["Process"]["stop_cb"]);
return process; return process;
}); });
} }

View File

@@ -14,4 +14,38 @@ function process.runInTerminal(cmd) end
---@return string output The output of the command. ---@return string output The output of the command.
function process.commandOutput(cmd) end function process.commandOutput(cmd) end
---@class Process
process.Process = {}
---@class ProcessParameters
---@field command FilePath The command to run.
---@field arguments? string[] The arguments to pass to the command.
---@field workingDirectory? FilePath The working directory for the command.
---@field stdin? string The input to write to stdin.
---@field stdout? function The callback to call when the process writes to stdout.
---@field stderr? function The callback to call when the process writes to stderr.
process.ProcessParameters = {}
---Creates a new process object.
---@param parameters ProcessParameters
---@return Process
function process.create(parameters) end
---Start the process.
---@async
---@return boolean isRunning Whether the process was started successfully.
function process.Process:start() end
---Stop the process.
---@async
---@return boolean didStop Whether the process was stopped successfully.
---@return number exitCode The exit code of the process.
---@return string error The error message if the process could not be stopped.
function process.Process:stop() end
---Returns whether the process is running.
---@return boolean isRunning Whether the process is running.
function process.Process:isRunning() end
---
return process return process