diff --git a/src/tests.zig b/src/tests.zig index 5690781..c4aa79d 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -15,6 +15,15 @@ const builtin = @import("builtin"); const webui = @import("webui"); const compat_tuple = @import("compat_tuple"); +fn memFind(comptime T: type, haystack: []const T, needle: []const T) ?usize { + if (comptime @hasDecl(std.mem, "find")) return std.mem.find(T, haystack, needle); + return std.mem.indexOf(T, haystack, needle); +} + +fn memFindScalar(comptime T: type, haystack: []const T, needle: T) ?usize { + if (comptime @hasDecl(std.mem, "findScalar")) return std.mem.findScalar(T, haystack, needle); + return std.mem.indexOfScalar(T, haystack, needle); +} // ============================================================================= // Pure-Zig tests (no C calls) // ============================================================================= @@ -31,6 +40,16 @@ test "constants" { try std.testing.expectEqual(@as(usize, 16), webui.WEBUI_MAX_ARG); } +test "convenience helpers are exposed" { + try std.testing.expect(@hasDecl(webui, "runFmt")); + try std.testing.expect(@hasDecl(webui, "scriptResult")); + try std.testing.expect(@hasDecl(webui.Event, "runClientFmt")); + try std.testing.expect(@hasDecl(webui.Event, "scriptClientResult")); + try std.testing.expect(@hasDecl(webui.Event, "returnFmt")); + try std.testing.expect(@hasDecl(webui.Event, "getRawSlice")); + try std.testing.expect(@hasDecl(webui.Event, "getRawSliceAt")); +} + test "Browser enum integer values match C ABI" { try std.testing.expectEqual(@as(usize, 0), @intFromEnum(webui.Browser.NoBrowser)); try std.testing.expectEqual(@as(usize, 1), @intFromEnum(webui.Browser.AnyBrowser)); @@ -174,7 +193,7 @@ test "getMimeType resolves common extensions" { // a non-empty string that mentions javascript. const js_mime = webui.getMimeType("app.js"); try std.testing.expect(js_mime.len > 0); - try std.testing.expect(std.mem.indexOf(u8, js_mime, "javascript") != null); + try std.testing.expect(memFind(u8, js_mime, "javascript") != null); } test "encode then decode roundtrips" { @@ -183,7 +202,7 @@ test "encode then decode roundtrips" { defer webui.free(encoded); try std.testing.expect(encoded.len > 0); // Base64 of ASCII has no NUL bytes embedded. - try std.testing.expect(std.mem.indexOfScalar(u8, encoded, 0) == null); + try std.testing.expect(memFindScalar(u8, encoded, 0) == null); // Build a NUL-terminated copy for the decode call (the C API insists). var buf: [128]u8 = undefined; diff --git a/src/webui.zig b/src/webui.zig index f958fdb..c291d8e 100644 --- a/src/webui.zig +++ b/src/webui.zig @@ -585,9 +585,14 @@ pub fn run(self: webui, script_content: [:0]const u8) void { c.webui_run(self.window_handle, script_content.ptr); } +/// Format JavaScript into `buffer`, then run it without waiting for a response. +pub fn runFmt(self: webui, buffer: []u8, comptime fmt: []const u8, args: anytype) !void { + const script_content = try std.fmt.bufPrintZ(buffer, fmt, args); + self.run(script_content); +} + /// Run JavaScript and get the response back. Work only in single client mode. /// Make sure your local buffer can hold the response. -/// Return True if there is no execution error pub fn script(self: webui, script_content: [:0]const u8, timeout: usize, buffer: []u8) !void { const success = c.webui_script( self.window_handle, @@ -599,6 +604,19 @@ pub fn script(self: webui, script_content: [:0]const u8, timeout: usize, buffer: if (!success) return WebUIError.ScriptError; } +/// Run JavaScript and return the written response slice. +pub fn scriptResult(self: webui, script_content: [:0]const u8, timeout: usize, buffer: []u8) ![:0]u8 { + try self.script(script_content, timeout, buffer); + return responseSlice(buffer); +} + +fn responseSlice(buffer: []u8) WebUIError![:0]u8 { + for (buffer, 0..) |byte, i| { + if (byte == 0) return buffer[0..i :0]; + } + return WebUIError.ScriptError; +} + /// Chose between Deno and Nodejs as runtime for .js and .ts files. pub fn setRuntime(self: webui, runtime: Runtime) void { c.webui_set_runtime(self.window_handle, runtime); @@ -1120,6 +1138,12 @@ pub const Event = extern struct { c.webui_run_client(self, script_content.ptr); } + /// Format JavaScript into `buffer`, then run it on the event client. + pub fn runClientFmt(self: *Event, buffer: []u8, comptime fmt: []const u8, args: anytype) !void { + const script_content = try std.fmt.bufPrintZ(buffer, fmt, args); + self.runClient(script_content); + } + /// Run JavaScript and get the response back. Single client. /// Make sure your local buffer can hold the response. pub fn scriptClient( @@ -1138,6 +1162,17 @@ pub const Event = extern struct { if (!success) return WebUIError.ScriptError; } + /// Run JavaScript on the event client and return the written response slice. + pub fn scriptClientResult( + self: *Event, + script_content: [:0]const u8, + timeout: usize, + buffer: []u8, + ) ![:0]u8 { + try self.scriptClient(script_content, timeout, buffer); + return responseSlice(buffer); + } + /// Return the response to JavaScript as integer. pub fn returnInt(e: *Event, n: i64) void { c.webui_return_int(e, n); @@ -1153,6 +1188,12 @@ pub const Event = extern struct { c.webui_return_string(e, str.ptr); } + /// Format a string response into `buffer`, then return it to JavaScript. + pub fn returnFmt(e: *Event, buffer: []u8, comptime fmt: []const u8, args: anytype) !void { + const response = try std.fmt.bufPrintZ(buffer, fmt, args); + e.returnString(response); + } + /// Return the response to JavaScript as boolean. pub fn returnBool(e: *Event, b: bool) void { c.webui_return_bool(e, b); @@ -1242,12 +1283,24 @@ pub const Event = extern struct { return @ptrCast(ptr); } + /// Get an argument as a raw byte slice at a specific index. + pub fn getRawSliceAt(e: *Event, index: usize) ![]const u8 { + const size = try e.getSizeAt(index); + return e.getRawAt(index)[0..size]; + } + // Get the first argument raw buffer pub fn getRaw(e: *Event) [*]const u8 { const ptr = c.webui_get_string(e); return @ptrCast(ptr); } + /// Get the first argument as a raw byte slice. + pub fn getRawSlice(e: *Event) ![]const u8 { + const size = try e.getSize(); + return e.getRaw()[0..size]; + } + /// Get an argument as boolean at a specific index pub fn getBoolAt(e: *Event, index: usize) bool { return c.webui_get_bool_at(e, index);