summaryrefslogtreecommitdiff
path: root/src/lua_runtime.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/lua_runtime.zig')
-rw-r--r--src/lua_runtime.zig68
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.