mirror of
https://github.com/extism/extism.git
synced 2026-01-09 13:57:55 -05:00
refactor(zig)!: Simplify JSON stringifying (#414)
Uses `std.json.ArrayHashMap` for stringifying. # Breaking Changes * Plugin.setConfig's `config` parameter's type changed to `std.json.ArrayHashMap([]const u8)` * WasmUrl's `header` field's type changed to `std.json.ArrayHashMap([]const u8)` * Manifest's `config` and `allowed_path` fields' types changed to `std.json.ArrayHashMap([]const u8)`
This commit is contained in:
@@ -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 }));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user