diff --git a/zig/build.zig b/zig/build.zig index 26240b8..2cd4e20 100644 --- a/zig/build.zig +++ b/zig/build.zig @@ -4,7 +4,7 @@ const builtin = @import("builtin"); pub fn build(b: *std.Build) void { comptime { const current_zig = builtin.zig_version; - const min_zig = std.SemanticVersion.parse("0.11.0-dev.3834+d98147414") catch unreachable; // std.builtin.Version -> std.SemanticVersion + const min_zig = std.SemanticVersion.parse("0.12.0-dev.64+b835fd90c") catch unreachable; // std.json.ArrayHashMap if (current_zig.order(min_zig) == .lt) { @compileError(std.fmt.comptimePrint("Your Zig version v{} does not meet the minimum build requirement of v{}", .{ current_zig, min_zig })); } diff --git a/zig/examples/basic.zig b/zig/examples/basic.zig index 67a7773..1cd1f62 100644 --- a/zig/examples/basic.zig +++ b/zig/examples/basic.zig @@ -40,9 +40,9 @@ pub fn main() !void { // var my_plugin = try Plugin.init(allocator, &context, wasm, &[_]Function{f}, true); defer my_plugin.deinit(); - var config = std.StringHashMap([]const u8).init(allocator); - defer config.deinit(); - try config.put("thing", "this is a really important thing"); + var config = std.json.ArrayHashMap([]const u8){}; + defer config.deinit(allocator); + try config.map.put(allocator, "thing", "this is a really important thing"); try my_plugin.setConfig(allocator, config); const input = "aeiouAEIOU____________________________________&smtms_y?" ** 1182; diff --git a/zig/src/manifest.zig b/zig/src/manifest.zig index 44fed72..a593248 100644 --- a/zig/src/manifest.zig +++ b/zig/src/manifest.zig @@ -13,16 +13,27 @@ pub const WasmUrl = struct { hash: ?[]const u8 = null, name: ?[]const u8 = null, method: ?[]const u8 = null, - headers: ?std.StringHashMap([]const u8) = null, + headers: ?std.json.ArrayHashMap([]const u8) = null, }; -pub const Wasm = union(enum) { wasm_data: WasmData, wasm_file: WasmFile, wasm_url: WasmUrl }; +pub const Wasm = union(enum) { + wasm_data: WasmData, + wasm_file: WasmFile, + wasm_url: WasmUrl, + pub fn jsonStringify(self: @This(), jws: anytype) !void { + switch (self) { + inline else => |value| { + try jws.write(value); + }, + } + } +}; pub const Manifest = struct { wasm: []const Wasm, memory: ?struct { max_pages: ?u32 } = null, - config: ?std.StringHashMap([]const u8) = null, + config: ?std.json.ArrayHashMap([]const u8) = null, allowed_hosts: ?[]const []const u8 = null, - allowed_paths: ?std.StringHashMap([]const u8) = null, + allowed_paths: ?std.json.ArrayHashMap([]const u8) = null, timeout: ?usize = null, }; diff --git a/zig/src/plugin.zig b/zig/src/plugin.zig index 636b54a..58333c1 100644 --- a/zig/src/plugin.zig +++ b/zig/src/plugin.zig @@ -4,7 +4,6 @@ const Manifest = @import("manifest.zig").Manifest; const Function = @import("function.zig"); const CancelHandle = @import("cancel_handle.zig"); const c = @import("ffi.zig"); -const utils = @import("utils.zig"); const Self = @This(); @@ -52,7 +51,7 @@ pub fn init(allocator: std.mem.Allocator, ctx: *Context, data: []const u8, funct /// Create a new plugin from the given manifest pub fn initFromManifest(allocator: std.mem.Allocator, ctx: *Context, manifest: Manifest, functions: []const Function, wasi: bool) !Self { - const json = try utils.stringifyAlloc(allocator, manifest); + const json = try std.json.stringifyAlloc(allocator, manifest, .{ .emit_null_optional_fields = false }); defer allocator.free(json); return init(allocator, ctx, json, functions, wasi); } @@ -67,7 +66,7 @@ pub fn create(allocator: std.mem.Allocator, data: []const u8, functions: []const /// Create a new plugin from the given manifest in its own context pub fn createFromManifest(allocator: std.mem.Allocator, manifest: Manifest, functions: []const Function, wasi: bool) !Self { - const json = try utils.stringifyAlloc(allocator, manifest); + const json = try std.json.stringifyAlloc(allocator, manifest, .{ .emit_null_optional_fields = false }); defer allocator.free(json); return create(allocator, json, functions, wasi); } @@ -128,15 +127,15 @@ pub fn update(self: *Self, data: []const u8, wasi: bool) !void { /// Update a plugin with the given manifest pub fn updateWithManifest(self: *Self, allocator: std.mem.Allocator, manifest: Manifest, wasi: bool) !void { - const json = try utils.stringifyAlloc(allocator, manifest); + const json = try std.json.stringifyAlloc(allocator, manifest, .{ .emit_null_optional_fields = false }); defer allocator.free(json); return self.update(json, wasi); } /// Set configuration values -pub fn setConfig(self: *Self, allocator: std.mem.Allocator, config: std.StringHashMap([]const u8)) !void { +pub fn setConfig(self: *Self, allocator: std.mem.Allocator, config: std.json.ArrayHashMap([]const u8)) !void { self.ctx.mutex.lock(); defer self.ctx.mutex.unlock(); - const config_json = try utils.stringifyAlloc(allocator, config); + const config_json = try std.json.stringifyAlloc(allocator, config, .{ .emit_null_optional_fields = false }); defer allocator.free(config_json); _ = c.extism_plugin_config(self.ctx.ctx, self.id, config_json.ptr, @as(u64, config_json.len)); } diff --git a/zig/src/utils.zig b/zig/src/utils.zig deleted file mode 100644 index c50cea9..0000000 --- a/zig/src/utils.zig +++ /dev/null @@ -1,162 +0,0 @@ -const std = @import("std"); -const json = std.json; - -pub fn stringifyAlloc(allocator: std.mem.Allocator, value: anytype) ![]const u8 { - var list = std.ArrayList(u8).init(allocator); - errdefer list.deinit(); - try stringify(value, .{ .emit_null_optional_fields = false }, list.writer()); - return list.toOwnedSlice(); -} - -fn stringify( - value: anytype, - options: json.StringifyOptions, - out_stream: anytype, -) @TypeOf(out_stream).Error!void { - const T = @TypeOf(value); - if (comptime std.mem.count(u8, @typeName(T), "hash_map.HashMap") > 0 and std.mem.count(u8, @typeName(T), "?") == 0) { - var it = value.iterator(); - try out_stream.writeByte('{'); - var i: usize = 0; - while (it.next()) |entry| { - i += 1; - const name = entry.key_ptr.*; - try json.encodeJsonString(name, options, out_stream); - if (i < value.count()) try out_stream.writeByte(':'); - const val = entry.value_ptr.*; - try json.encodeJsonString(val, options, out_stream); - } - try out_stream.writeByte('}'); - return; - } - - switch (@typeInfo(T)) { - .Float, .ComptimeFloat => { - return std.fmt.formatFloatScientific(value, std.fmt.FormatOptions{}, out_stream); - }, - .Int, .ComptimeInt => { - return std.fmt.formatIntValue(value, "", std.fmt.FormatOptions{}, out_stream); - }, - .Bool => { - return out_stream.writeAll(if (value) "true" else "false"); - }, - .Null => { - return out_stream.writeAll("null"); - }, - .Optional => { - if (value) |payload| { - return try stringify(payload, options, out_stream); - } else { - return try stringify(null, options, out_stream); - } - }, - .Enum => { - if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { - return value.jsonStringify(options, out_stream); - } - - @compileError("Unable to stringify enum '" ++ @typeName(T) ++ "'"); - }, - .Union => { - if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { - return value.jsonStringify(options, out_stream); - } - - const info = @typeInfo(T).Union; - if (info.tag_type) |UnionTagType| { - inline for (info.fields) |u_field| { - if (value == @field(UnionTagType, u_field.name)) { - return try stringify(@field(value, u_field.name), options, out_stream); - } - } - } else { - @compileError("Unable to stringify untagged union '" ++ @typeName(T) ++ "'"); - } - }, - .Struct => |S| { - if (comptime std.meta.trait.hasFn("jsonStringify")(T)) { - return value.jsonStringify(options, out_stream); - } - - try out_stream.writeByte('{'); - var field_output = false; - var child_options = options; - child_options.whitespace = .indent_2; - inline for (S.fields) |Field| { - // don't include void fields - if (Field.type == void) continue; - - var emit_field = true; - - // don't include optional fields that are null when emit_null_optional_fields is set to false - if (@typeInfo(Field.type) == .Optional) { - if (options.emit_null_optional_fields == false) { - if (@field(value, Field.name) == null) { - emit_field = false; - } - } - } - - if (emit_field) { - if (!field_output) { - field_output = true; - } else { - try out_stream.writeByte(','); - } - try json.encodeJsonString(Field.name, options, out_stream); - try out_stream.writeByte(':'); - if (child_options.whitespace != .minified) { - try out_stream.writeByte(' '); - } - try stringify(@field(value, Field.name), child_options, out_stream); - } - } - try out_stream.writeByte('}'); - return; - }, - .ErrorSet => return stringify(@as([]const u8, @errorName(value)), options, out_stream), - .Pointer => |ptr_info| switch (ptr_info.size) { - .One => switch (@typeInfo(ptr_info.child)) { - .Array => { - const Slice = []const std.meta.Elem(ptr_info.child); - return stringify(@as(Slice, value), options, out_stream); - }, - else => { - // TODO: avoid loops? - std.debug.print("a: {}", .{std.mem.indexOf(u8, @typeName(T), "HashMap") != null}); - // return stringify(value.*, options, out_stream); - }, - }, - // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972) - .Slice => { - if (ptr_info.child == u8 and std.unicode.utf8ValidateSlice(value)) { - try json.encodeJsonString(value, options, out_stream); - return; - } - - try out_stream.writeByte('['); - var child_options = options; - for (value, 0..) |x, i| { - if (i != 0) { - try out_stream.writeByte(','); - } - try stringify(x, child_options, out_stream); - } - try out_stream.writeByte(']'); - return; - }, - else => { - @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"); - }, - }, - .Array => return stringify(&value, options, out_stream), - .Vector => |info| { - const array: [info.len]info.child = value; - return stringify(&array, options, out_stream); - }, - else => { - @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"); - }, - } - unreachable; -}