Skip to main content

Zig

How to install and use the Extism Zig PDK

Installation

via gyro

gyro add --src github extism/zig-pdk

via zigmod

zigmod.yml
name: my-plugin
main: src/main.zig
license: None
description: None
root_dependencies:
- src: git https://github.com/extism/zig-pdk

via build.zig

First, clone the extism-pdk library into a local project directory:

mkdir -p libs
cd libs
git clone https://github.com/extism/zig-pdk

Then, update your build.zig:

build.zig
exe.addAnonymousModule("extism-pdk", .{ .source_file = .{ .path = "libs/zig-pdk/src/main.zig" } });
exe.rdynamic = true;

And import it into your program:

plugin.zig
const extism_pdk = @import("extism-pdk");
const plugin = extism_pdk.Plugin;
const http = extism_pdk.http;
// ...

Compiling to WebAssembly

Since WebAssembly is probably the only target for your Extism plug-in, add this to your build.zig:

build.zig
const target = b.standardTargetOptions(.{ 
.default_target = .{ .abi = .musl, .os_tag = .freestanding, .cpu_arch = .wasm32 },
});

Then simply run:

zig build

Example Usage

plugin.zig
const std = @import("std");
const extism_pdk = @import("extism-pdk");
const Plugin = extism_pdk.Plugin;
const http = extism_pdk.http;

pub fn main() void {}
const allocator = std.heap.wasm_allocator;

// define some type to write as output from the plugin back to the host
const Output = struct {
count: i32,
config: []const u8,
a: []const u8,
};

export fn count_vowels() i32 {
const plugin = Plugin.init(allocator);
plugin.log(.Debug, "plugin start");
const input = plugin.getInput() catch unreachable;
defer allocator.free(input);
plugin.log(.Debug, "plugin input");
var count: i32 = 0;
for (input) |char| {
switch (char) {
'A', 'I', 'E', 'O', 'U', 'a', 'e', 'i', 'o', 'u' => count += 1,
else => {},
}
}

// use persistent variables owned by a plugin instance (stored in-memory between function calls)
var var_a_optional = plugin.getVar("a") catch unreachable;
plugin.log(.Debug, "plugin var get");

if (var_a_optional == null) {
plugin.setVar("a", "this is var a");
plugin.log(.Debug, "plugin var set");
} else {
allocator.free(var_a_optional.?);
}
const var_a = plugin.getVar("a") catch unreachable orelse "";
defer allocator.free(var_a);

// access host-provided configuration (key/value)
const thing = plugin.getConfig("thing") catch unreachable orelse "<unset by host>";
plugin.log(.Debug, "plugin config get");

const data = Output{ .count = count, .config = thing, .a = var_a };
const output = std.json.stringifyAlloc(allocator, data, .{}) catch unreachable;
defer allocator.free(output);
plugin.log(.Debug, "plugin json encoding");

// write the plugin data back to the host
plugin.output(output);
plugin.log(.Debug, "plugin output");

return 0;
}

export fn make_http_request() i32 {
const plugin = Plugin.init(allocator);
// create an HTTP request via Extism built-in function (doesn't require WASI)
var req = http.HttpRequest.init(allocator, "GET", "https://jsonplaceholder.typicode.com/todos/1");
defer req.deinit();

// set headers on the request object
req.setHeader("some-name", "some-value") catch unreachable;
req.setHeader("another", "again") catch unreachable;

// make the request and get the response back
const res = plugin.request(req) catch unreachable;
defer res.deinit();

// `outputMemory` provides a zero-copy way to write plugin data back to the host
plugin.outputMemory(res.memory);

return 0;
}