Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions src/tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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)
// =============================================================================
Expand All @@ -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));
Expand Down Expand Up @@ -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" {
Expand All @@ -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;
Expand Down
55 changes: 54 additions & 1 deletion src/webui.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -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(
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading