Skip to content

Commit

Permalink
chore: prep for release (#31)
Browse files Browse the repository at this point in the history
* chore: prep for release

* chore: use correct extism call command in docs
  • Loading branch information
nilslice authored May 22, 2024
1 parent 42b0b85 commit 67465e4
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 37 deletions.
108 changes: 72 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Extism Zig PDK


This library can be used to write [Extism Plug-ins](https://extism.org/docs/concepts/plug-in) in Zig.
This library can be used to write
[Extism Plug-ins](https://extism.org/docs/concepts/plug-in) in Zig.

## Install

Expand All @@ -13,10 +13,11 @@ cd my-plugin
zig init
```

Add the library as a dependency. Git ref should be the hash of the latest commit:
Add the library as a dependency. Git ref should be the hash of the latest
commit:

```sh
zig fetch --save https://github.com/extism/zig-pdk/archive/<git-ref-here>.tar.gz
zig fetch --save https://github.com/extism/zig-pdk/archive/refs/tags/v1.1.0.tar.gz
```

Change your `build.zig` so that it references `extism-pdk`:
Expand Down Expand Up @@ -49,7 +50,13 @@ pub fn build(b: *std.Build) void {

## Getting Started

The goal of writing an [Extism plug-in](https://extism.org/docs/concepts/plug-in) is to compile your Zig code to a Wasm module with exported functions that the host application can invoke. The first thing you should understand is creating an export. Let's write a simple program that exports a `greet` function which will take a name as a string and return a greeting string. Zig has excellent support for this through the `export` keyword:
The goal of writing an
[Extism plug-in](https://extism.org/docs/concepts/plug-in) is to compile your
Zig code to a Wasm module with exported functions that the host application can
invoke. The first thing you should understand is creating an export. Let's write
a simple program that exports a `greet` function which will take a name as a
string and return a greeting string. Zig has excellent support for this through
the `export` keyword:

```zig
const std = @import("std");
Expand All @@ -69,27 +76,31 @@ export fn greet() i32 {
}
```

> Note: if you started with the generated project files from `zig init`, you should delete `src/root.zig`
and any references to it if they are in your `build.zig` file.
> Note: if you started with the generated project files from `zig init`, you
> should delete `src/root.zig` and any references to it if they are in your
> `build.zig` file.
Then run:

```sh
zig build
```

This will put your compiled wasm in `zig-out/bin`.
We can now test it using the [Extism CLI](https://github.com/extism/cli)'s `run`
command:
This will put your compiled wasm in `zig-out/bin`. We can now test it using the
[Extism CLI](https://github.com/extism/cli)'s `call` command:

```bash
extism call ./zig-out/bin/my-plugin.wasm greet --input "Benjamin"
# => Hello, Benjamin!
```

> **Note**: We also have a web-based, plug-in tester called the [Extism Playground](https://playground.extism.org/)
> **Note**: We also have a web-based, plug-in tester called the
> [Extism Playground](https://playground.extism.org/)
### More Exports: Error Handling
Suppose want to re-write our greeting module to never greet Benjamins. We can use `Plugin.setError`:

Suppose want to re-write our greeting module to never greet Benjamins. We can
use `Plugin.setError`:

```zig
export fn greet() i32 {
Expand Down Expand Up @@ -123,7 +134,9 @@ echo $?

### Json

Extism export functions simply take bytes in and bytes out. Those can be whatever you want them to be. A common and simple way to get more complex types to and from the host is with json:
Extism export functions simply take bytes in and bytes out. Those can be
whatever you want them to be. A common and simple way to get more complex types
to and from the host is with json:

```zig
export fn add() i32 {
Expand All @@ -149,7 +162,7 @@ export fn add() i32 {
}
```

To use a json helper, you can accomplish the same as the above with:
To use a json helper, you can accomplish the same as the above with:

```zig
export fn add() i32 {
Expand Down Expand Up @@ -179,7 +192,9 @@ extism call ./zig-out/bin/my-plugin.wasm add --input='{"a": 20, "b": 21}'
## Configs

Configs are key-value pairs that can be passed in by the host when creating a
plug-in. These can be useful to statically configure the plug-in with some data that exists across every function call. Here is a trivial example using `Plugin.getConfig`:
plug-in. These can be useful to statically configure the plug-in with some data
that exists across every function call. Here is a trivial example using
`Plugin.getConfig`:

```zig
export fn greet() i32 {
Expand All @@ -195,7 +210,8 @@ export fn greet() i32 {
}
```

To test it, the [Extism CLI](https://github.com/extism/cli) has a `--config` option that lets you pass in `key=value` pairs:
To test it, the [Extism CLI](https://github.com/extism/cli) has a `--config`
option that lets you pass in `key=value` pairs:

```bash
extism call ./zig-out/bin/my-plugin.wasm greet --config user=Benjamin
Expand All @@ -206,7 +222,7 @@ extism call ./zig-out/bin/my-plugin.wasm greet --config user=Benjamin

Variables are another key-value mechanism but it's a mutable data store that
will persist across function calls. These variables will persist as long as the
host has loaded and not freed the plug-in.
host has loaded and not freed the plug-in.

```zig
export fn count() i32 {
Expand All @@ -226,7 +242,8 @@ export fn count() i32 {
}
```

To test it, the [Extism CLI](https://github.com/extism/cli) has a `--loop` option that lets you pass call the same function multiple times:
To test it, the [Extism CLI](https://github.com/extism/cli) has a `--loop`
option that lets you pass call the same function multiple times:

```sh
extism call ./zig-out/bin/my-plugin.wasm count --loop 3
Expand All @@ -235,12 +252,17 @@ extism call ./zig-out/bin/my-plugin.wasm count --loop 3
3
```

> **Note**: Use the untyped variants `Plugin.setVar(self: Plugin, key: []const u8, value: []const u8)` and `Plugin.getVar(self: Plugin, key: []const u8) !?[]u8` to handle your own types.
> **Note**: Use the untyped variants
> `Plugin.setVar(self: Plugin, key: []const u8, value: []const u8)` and
> `Plugin.getVar(self: Plugin, key: []const u8) !?[]u8` to handle your own
> types.
## Logging

Because Wasm modules by default do not have access to the system, printing to stdout won't work (unless you use WASI).
Extism provides a simple logging function that allows you to use the host application to log without having to give the plug-in permission to make syscalls.
Because Wasm modules by default do not have access to the system, printing to
stdout won't work (unless you use WASI). Extism provides a simple logging
function that allows you to use the host application to log without having to
give the plug-in permission to make syscalls.

```zig
export fn log_stuff() i32 {
Expand All @@ -265,11 +287,14 @@ extism call ./zig-out/bin/my-plugin.wasm log_stuff --log-level=debug
2023/11/22 14:00:26 An error log!
```

> *Note*: From the CLI you need to pass a level with `--log-level`. If you are running the plug-in in your own host using one of our SDKs, you need to make sure that you call `set_log_file` to `"stdout"` or some file location.
> _Note_: From the CLI you need to pass a level with `--log-level`. If you are
> running the plug-in in your own host using one of our SDKs, you need to make
> sure that you call `set_log_file` to `"stdout"` or some file location.
## HTTP

Sometimes it is useful to let a plug-in [make HTTP calls]. [see: Extism HTTP library](src/http.zig)
Sometimes it is useful to let a plug-in [make HTTP calls].
[see: Extism HTTP library](src/http.zig)

```zig
const http = extism_pdk.http;
Expand Down Expand Up @@ -324,7 +349,9 @@ export fn http_get() i32 {
}
```

By default, Extism modules cannot make HTTP requests unless you specify which hosts it can connect to. You can use `--allow-host` in the Extism CLI to set this:
By default, Extism modules cannot make HTTP requests unless you specify which
hosts it can connect to. You can use `--allow-host` in the Extism CLI to set
this:

```
extism call ./zig-out/bin/my-plugin.wasm http_get --allow-host='*.typicode.com'
Expand All @@ -333,23 +360,29 @@ extism call ./zig-out/bin/my-plugin.wasm http_get --allow-host='*.typicode.com'

## Imports (Host Functions)

Like any other code module, Wasm not only let's you export functions to the outside world, you can
import them too. Host Functions allow a plug-in to import functions defined in the host. For example,
if you host application is written in Python, it can pass a Python function down to your Zig plug-in
where you can invoke it.
Like any other code module, Wasm not only let's you export functions to the
outside world, you can import them too. Host Functions allow a plug-in to import
functions defined in the host. For example, if you host application is written
in Python, it can pass a Python function down to your Zig plug-in where you can
invoke it.

This topic can get fairly complicated and we have not yet fully abstracted the Wasm knowledge you need
to do this correctly. So we recommend reading our [concept doc on Host Functions](https://extism.org/docs/concepts/host-functions) before you get started.
This topic can get fairly complicated and we have not yet fully abstracted the
Wasm knowledge you need to do this correctly. So we recommend reading our
[concept doc on Host Functions](https://extism.org/docs/concepts/host-functions)
before you get started.

### A Simple Example

Host functions have a similar interface as exports. You just need to declare them as extern. You only declare the interface as it is the host's responsibility to provide the implementation:
Host functions have a similar interface as exports. You just need to declare
them as extern. You only declare the interface as it is the host's
responsibility to provide the implementation:

```zig
pub extern "extism:host/user" fn a_python_func(u64) u64;
```

We should be able to call this function as a normal Zig function. Note that we need to manually handle the pointer casting:
We should be able to call this function as a normal Zig function. Note that we
need to manually handle the pointer casting:

```zig
export fn hello_from_python() i32 {
Expand All @@ -375,8 +408,10 @@ export fn hello_from_python() i32 {

### Testing it out

We can't really test this from the Extism CLI as something must provide the implementation. So let's
write out the Python side here. Check out the [docs for Host SDKs](https://extism.org/docs/concepts/host-sdk) to implement a host function in a language of your choice.
We can't really test this from the Extism CLI as something must provide the
implementation. So let's write out the Python side here. Check out the
[docs for Host SDKs](https://extism.org/docs/concepts/host-sdk) to implement a
host function in a language of your choice.

```python
from extism import host_fn, Plugin
Expand All @@ -394,7 +429,7 @@ def a_python_func(input: str) -> str:
```

Now when we load the plug-in we pass the host function:

```python
manifest = {"wasm": [{"path": "/path/to/plugin.wasm"}]}
plugin = Plugin(manifest, functions=[a_python_func], wasi=True)
Expand All @@ -410,4 +445,5 @@ python3 app.py

### Reach Out!

Have a question or just want to drop in and say hi? [Hop on the Discord](https://extism.org/discord)!
Have a question or just want to drop in and say hi?
[Hop on the Discord](https://extism.org/discord)!
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.{
.name = "extism-pdk",
.version = "0.1.0",
.version = "1.1.0",
.paths = .{""},
}

0 comments on commit 67465e4

Please sign in to comment.