moved code for coherence

This commit is contained in:
2026-02-12 22:21:56 +01:00
parent 04ee42eaa7
commit 210c2d6231
12 changed files with 8 additions and 11 deletions

73
src/lua_func/handle.lua Normal file
View File

@@ -0,0 +1,73 @@
local utils = require("utils")
local tm = require("tm")
local unpack = unpack or table.unpack
local handle = {}
local function _get_func_by_path(file_path, func_name)
-- 1. Load the file from the path
-- loadfile returns a 'chunk' (a function that runs the file's code)
local chunk, load_err = loadfile(file_path)
if not chunk then
return nil, "Failed to load file: " .. tostring(load_err)
end
-- 2. Execute the chunk to get the module's return value
-- Most Lua modules end with 'return { ... }'
local ok, module = pcall(chunk)
if not ok then
return nil, "Error executing file: " .. tostring(module)
end
-- 3. Validate the module is a table and contains the function
if type(module) ~= "table" then
return nil, "Module did not return a table (returned " .. type(module) .. ")"
end
local target_func = module[func_name]
if type(target_func) ~= "function" then
return nil, "Function '" .. func_name .. "' not found in '" .. file_path .. "'"
end
return target_func
end
function handle.func_call(params)
local file = params.file
local fname = params.fname
local prms = params.params
local res = nil
local succ, ret
local pfile = file
-- 1. modify the file path if it is relative
if utils.is_relative_path(pfile) then
local td = tm.gd("test_directory")
pfile = utils.join_paths(td, file)
end
-- 2. retrieve the function "fname"
local func, err = _get_func_by_path(pfile, fname)
-- 3. Execute the function
if err == nil then
print(string.format("Function executed from '%s'", pfile))
utils.log("func_call function found '%s', '%s'", file, fname)
succ, ret = pcall(func, unpack(prms))
utils.log("func_call returned '%s', '%s'", tostring(succ), tostring(ret))
if succ then
res = ret
else
err = ret
end
end
-- 4. Returns result
return res, err
end
return handle

130
src/lua_func/json-rpc.lua Normal file
View File

@@ -0,0 +1,130 @@
local json = require("cjson")
local utils = require("utils")
local JSONRPC = {}
JSONRPC.__index = JSONRPC
function JSONRPC.new(sock)
local self = setmetatable({}, JSONRPC)
self.sock = sock -- Function to transmit string data to transport (TCP/Websocket)
self.methods = {} -- Methods the server provides to the client
self.pending = {} -- Requests sent to client waiting for response
self.next_id = 1
self.sock:settimeout(0.2)
return self
end
--- Register a method the client can call
function JSONRPC:register(name, callback)
self.methods[name] = callback
end
--- Handle incoming raw data from the transport layer
function JSONRPC:handle_message(raw_data)
utils.log("received: '%s'", raw_data)
local ok, msg = pcall(json.decode, raw_data)
if not ok then return self:_send_error(nil, "received message parse error in lua server") end
-- 1. Check if it's a Response (has 'result' or 'error' and 'id')
if (msg.id ~= nil) and (msg.result ~= nil or msg.error ~= nil) then
self.pending[msg.id] = msg
return
end
-- 2. Check if it's a Request
if msg.method then
return self:_handle_request(msg)
end
end
--- INTERNAL: Handle requests from the client
function JSONRPC:_handle_request(req)
local method = self.methods[req.method]
local ok, ret
local res, err
if not method then
if req.id then self:_send_error(req.id, string.format("Method '%s' not registered in lua server")) end
return
end
ok, ret, err = pcall(method, req.params)
utils.log("Call returned ok='%s', ret='%s'", tostring(ok), tostring(ret))
-- Only send response if it's not a Notification (notifications have no ID)
if req.id then
if ok then
res = ret
if res == nil then
self:_send_error(req.id, tostring(err))
else
self:_send({ jsonrpc = "2.0", result = { returned_value = res }, id = req.id })
end
else
self:_send_error(req.id, tostring(err))
end
end
end
--- Call a method on the client
function JSONRPC:call(method, params)
local id = self.next_id
self.next_id = self.next_id + 1
self:_send({
jsonrpc = "2.0",
method = method,
params = params,
id = id
})
-- ---- Wait for response (re-entrant loop)
while true do
self:poll()
if self.pending[id] then
local resp = self.pending[id]
self.pending[id] = nil
if resp.error then
error(resp.error.message)
end
return resp.result
end
end
end
function JSONRPC:_send(data)
local j = json.encode(data)
utils.log("sending: '%s'", j)
return self.sock:send(j .. "\n")
end
function JSONRPC:_send_error(id, message)
self:_send({
jsonrpc = "2.0",
error = message,
id = id
})
end
function JSONRPC:poll()
local line, err = self.sock:receive("*l")
if line then
self:handle_message(line)
elseif err ~= "timeout" and err ~= nil then
utils.log("Connection ended: %s", err)
return false
end
return true
end
function JSONRPC:loop()
while true do
if not self:poll() then
break
end
end
end
return JSONRPC

102
src/lua_func/main.lua Normal file
View File

@@ -0,0 +1,102 @@
-- =========================
-- Options par défaut
-- =========================
local config = {
host = "0.0.0.0",
port = 9000,
timeout = 60,
verbose = false,
}
local function usage()
print([[
Usage: lua lua_func [options]
Options:
--host <ip> Adresse d'écoute (default: 0.0.0.0)
--port <port> Port TCP (default: 9000)
--timeout <sec> Timeout client en secondes (default: 60)
--verbose Logs détaillés
--help Affiche cette aide
]])
os.exit(0)
end
-- =========================
-- Parsing des arguments
-- =========================
local i = 1
while i <= #arg do
local a = arg[i]
if a == "--host" then
i = i + 1
config.host = arg[i]
elseif a == "--port" then
i = i + 1
config.port = tonumber(arg[i])
elseif a == "--timeout" then
i = i + 1
config.timeout = tonumber(arg[i])
elseif a == "--verbose" then
config.verbose = true
elseif a == "--help" then
usage()
else
print("Unknown option:", a)
usage()
end
i = i + 1
end
local socket = require("socket")
local JSONRPC = require("json-rpc") -- The module from the previous response
local utils = require("utils")
local tm = require("tm")
local handle = require("handle")
utils.verbose = config.verbose
-- Create the master socket
local server_sock = socket.tcp()
local ok, err = server_sock:bind(config.host, config.port)
if not ok then
utils.log("error : %s", err)
os.exit(1)
end
server_sock:listen(1)
local ip, port = server_sock:getsockname()
utils.log("listening on %s:%d for %.1f secs", ip, port, config.timeout)
server_sock:settimeout(config.timeout) -- Prevents hanging on dead connections
-- Main Server Loop
local client_sock, err = server_sock:accept()
if err then
utils.log("connection failed: %s", err)
os.exit(0)
end
utils.log("Client connected")
-- Initialize the RPC instance for this specific connection
local rpc = JSONRPC.new(client_sock)
tm._init_api(rpc)
utils.setup_remote_print(rpc)
rpc:register("func_call", handle.func_call)
-- Communication Loop for this client
rpc:loop()
client_sock:close()
utils.log("Server stopped")

29
src/lua_func/tm.lua Normal file
View File

@@ -0,0 +1,29 @@
local tm = {}
local SUPPORTED_API = {
"gd",
"setgd",
"delgd",
}
-- underlying function
function tm._init_api(rpc)
tm._rpc = rpc
local function _api_request(fname, ...)
local args = {...}
return tm._rpc:call(fname, args)
end
for _, fname in ipairs(SUPPORTED_API) do
-- create a closure that calls common_handler with fname
tm[fname] = function(...)
return _api_request(fname, ...)
end
end
end
return tm

71
src/lua_func/utils.lua Normal file
View File

@@ -0,0 +1,71 @@
local utils = {}
utils.verbose = false
function utils.log(fmt, ...)
if utils.verbose then
-- print("[lua_func server]", ...)
io.stdout:write(string.format("[lua_func server] - " .. fmt .. "\n", ...))
end
end
utils.sep = package.config:sub(1,1)
function utils.join_paths(p1, p2)
return p1 .. utils.sep .. p2
end
function utils.is_absolute_path(path)
if not path or path == "" then return false end
-- 1. Check for POSIX absolute path (starts with /)
if string.sub(path, 1, 1) == "/" then
return true
end
-- 2. Check for Windows drive letter (e.g., C:\ or D:/)
-- Pattern: %a (letter) followed by : (colon)
if string.match(path, "^%a:[/\\]") or string.match(path, "^%a:$") then
return true
end
-- 3. Check for Windows UNC/Network paths (starts with \\ or //)
if string.match(path, "^[/\\][/\\]") then
return true
end
return false
end
function utils.is_relative_path(path)
return not utils.is_absolute_path(path)
end
function utils.setup_remote_print(rpc)
-- Store the original print if you still need to log to the server console
_G.native_print = _G.native_print or _G.print
-- Define the new local print
_G.print = function (...)
local args = {...}
local output = {}
for i, v in ipairs(args) do
table.insert(output, tostring(v))
end
local message = table.concat(output, "\t")
utils.log("Printed value: '%s'", message)
if string.sub(message, -1) ~= "\n" then
message = message .. "\n"
end
pcall(function()
rpc:call("print", message )
end)
-- Optional: Still print to the server's local console
-- utils.log("[Remote Log Sent]: " .. message)
end
end
return utils