Idiomatic Zig bindings for corvid —
an embedded database with a typed C ABI — over the published release
artifacts: fetch.sh / fetch.ps1 download the pinned release,
sha256-verify it against the release's checksums.txt, and zig build
consumes the fetched corvid.h + platform cdylib offline. No Rust
toolchain, no vendored binaries.
Documentation: the corvid docs site is canonical — this binding has its own corvid-zig page, and the C ABI section documents every symbol the binding wraps (handles, ownership, errors, threading).
The binding has two layers:
src/corvid.zig— the idiomatic API:Db/Collection/Query/Pred/Valuewrapper structs withdeinit(),CORVID_ERRmapped to a Zig error set (corvid.Error), consumed-by-call arguments moved and rendered inert, borrowed views typedValueView(freeing a borrow is a compile error here — UB in C), the C fn-pointer callbacks (update,scan) wrapped as Zig closures whose errors ride the ABI's abort channel, and[]const u8/[]const f32slices everywhere.corvid.c— the raw@cImportofcorvid.h, exposed because the golden harness drives the ABI exactly the way the engine's own C harness does.
The golden-suite port is a statement-for-statement port of the engine's own C harness, replaying the 267-line fixture suite against the downloaded libcorvid (the fixtures are vendored from the pinned release, and fetch byte-compares them against the release's copies on every download).
Requirements: Zig 0.16.0 (the current stable line; see
docs/PLAN.md for the toolchain policy), and curl +
shasum/sha256sum (macOS/Linux) or PowerShell 5+ (Windows).
./fetch.sh # download + verify corvid v0.4.1 into deps/current/
zig build test # wrapper unit tests + the golden suite (267/267)
zig build run-quickstart # open → insert → kNN → print
zig build examples # build the whole six-program tourWindows (PowerShell): ./fetch.ps1, then the same zig build steps. For
a binary you run by hand from zig-out/bin, put deps/current on PATH
so the loader finds corvid.dll (the build's own run steps do this for
you).
A taste of the API (examples/quickstart.zig):
const corvid = @import("corvid");
var db = try corvid.Db.openMemory();
defer db.deinit();
var docs = try db.collection("docs");
defer docs.deinit();
var doc = corvid.Value.map();
defer doc.deinit();
var title = try corvid.Value.text("ada");
try doc.put("title", &title); // moves title into the map
var v = corvid.Value.vector(&.{ 1.0, 0.0 });
try doc.put("v", &v);
try docs.insert("p1", doc); // insert CLONES the value
var q = try docs.query();
defer q.deinit(); // no-op after run()
var rows = try q.vector("v", &.{ 1.0, 0.0 }, 3, .cosine).run();
defer rows.deinit();
while (rows.next()) |row| { // row.key / row.score / row.doc
const t = row.doc.mapGet("title").?.textRef().?;
// borrowed until the next next()/deinit — clone it to keep it
}The positional phrase search (new in the engine's v0.3.0):
var rows = try notes.phraseSearch("body", "embedded the database", 10);
defer rows.deinit();
// stop words collapse out of adjacency: matches "embedded database".
// Row order is BM25 relevance; k == 0 is an empty cursor, not an error.zig build test replays the engine's entire golden fixture suite — 267
executable lines across 8 files, including the v0.3.0 additions
(VMAP_KEYS/GET_KEYS map-key iteration, PHRASE/PHRASE_K0 direct
positional search) — against the downloaded libcorvid, with the same
discipline as the engine harness: every counted line must dispatch, first
failure names file:line + OP + expected-vs-got, every handle freed on its
creation path (the CI sanitizer leg builds the harness with ASan and
expects zero reports). Output protocol, one line per fixture:
SMOKE golden/queries.txt lines=46 executed=46
If the published artifacts ever disagree with the vendored fixtures or the header, this fails where the engine's own suite stayed green — that divergence is a finding for the engine repo, never patched around here.
Every construct of the engine's public surface (the radar-enforced list
the engine publishes as scripts/bindings/surface.tsv at each release
tag — 327 rows at this pin) is resolved in docs/SURFACE.tsv: the Zig
API exposing it plus the golden fixture line that proves it, or N/A +
reason where the v1 ABI deliberately does not expose it (FFI.md §9).
scripts/surface-gate.sh fails CI when a line is unresolved, a cell is
empty, or the N/A count drifts from the committed baseline — so an
engine pin bump that changes the surface lands in this gate, not in a
user's bug report.
The engine pin lives in one variable in the fetch scripts
(CORVID_VERSION=v0.4.1). Artifacts are always taken from that exact
tag's GitHub release and sha256-verified; deps/ is never committed.
Bumps are one-variable changes, gated by the golden suite.
Not published yet — consume via git for now. A zig package /
registry story is deliberately deferred until the wrapper surface has
ridden at least one engine bump cycle.
MIT — see LICENSE.