diff options
| author | T <t@tjp.lol> | 2026-05-27 07:59:03 -0600 |
|---|---|---|
| committer | T <t@tjp.lol> | 2026-05-27 07:59:24 -0600 |
| commit | b72a405534d6be019573ee0a806014e2713fe55e (patch) | |
| tree | 7c4b8685f2d98382b5c0798e7ce1034cc1617bb2 /src/lua_runtime.zig | |
| parent | 2b937beb0b5635d89df84cfd112dabca34018afe (diff) | |
.panto/tools/ resolution
Diffstat (limited to 'src/lua_runtime.zig')
| -rw-r--r-- | src/lua_runtime.zig | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/src/lua_runtime.zig b/src/lua_runtime.zig index 44b64a4..fb00951 100644 --- a/src/lua_runtime.zig +++ b/src/lua_runtime.zig @@ -139,6 +139,74 @@ pub const LuaRuntime = struct { try self.harvestAndStoreHandlers(); } + /// Load a single-tool Lua script and register the table it returns + /// as if `panto.register_tool` had been called on that table. + /// + /// The script's top-level chunk must return a table with the same + /// shape that `panto.register_tool` accepts: + /// `{ name, description, schema, handler }`. This is the ergonomic + /// form supported under a `tools/` directory. + pub fn loadTool( + self: *LuaRuntime, + script_path: []const u8, + package_root: ?[]const u8, + ) !void { + const path_z = try self.allocator.dupeZ(u8, script_path); + defer self.allocator.free(path_z); + + lua_bridge.resetRegistrations(self.L); + + if (package_root) |root| { + const root_z = try self.allocator.dupeZ(u8, root); + defer self.allocator.free(root_z); + try prependPackagePath(self.L, root_z); + } + + // Run the file expecting exactly one returned value (the tool + // table). Use luaL_loadfilex + lua_pcallk directly so we can + // ask for a return value (the bridge's loadFile discards them). + // + // Push `panto.register_tool` *first*, then load+run the chunk so + // its return value naturally lands above it; calling pcall then + // consumes both in the right order. + const L = self.L; + _ = c.lua_getglobal(L, "panto"); + _ = c.lua_getfield(L, -1, "register_tool"); + c.lua_copy(L, -1, -2); // overwrite `panto` with `register_tool` + c.lua_settop(L, c.lua_gettop(L) - 1); // pop the duplicate + // Stack: ..., register_tool + + if (c.luaL_loadfilex(L, path_z.ptr, null) != 0) { + logTopAsError(L, "lua: failed to load tool"); + c.lua_settop(L, c.lua_gettop(L) - 2); // pop err + register_tool + return error.LuaLoadFailed; + } + if (c.lua_pcallk(L, 0, 1, 0, 0, null) != 0) { + logTopAsError(L, "lua: failed to run tool"); + c.lua_settop(L, c.lua_gettop(L) - 2); // pop err + register_tool + return error.LuaRunFailed; + } + // Stack: ..., register_tool, returned_value + if (c.lua_type(L, -1) != lua_bridge.T_TABLE) { + c.lua_settop(L, c.lua_gettop(L) - 2); // pop both + std.log.err( + "lua: tool script '{s}' must return a table", + .{script_path}, + ); + return error.BadRegistration; + } + + // Invoke register_tool(returned_table). Same validation, schema + // serialization, and registrations-table append logic as an + // extension's `panto.register_tool` call. + if (c.lua_pcallk(L, 1, 0, 0, 0, null) != 0) { + logTopAsError(L, "lua: register_tool failed for tool script"); + return error.LuaRunFailed; + } + + try self.harvestAndStoreHandlers(); + } + /// Walk the registrations table that the script just populated. /// For each entry: /// - Copy `name`, `description`, `schema_json` into owned bytes. |
