Skip to content

Add Zig 0.16 CLI port (spec parser + URL builder)#2

Merged
ThomAub merged 7 commits into
mainfrom
claude/zig-cli-wasm-implementation-ohqfo
Apr 21, 2026
Merged

Add Zig 0.16 CLI port (spec parser + URL builder)#2
ThomAub merged 7 commits into
mainfrom
claude/zig-cli-wasm-implementation-ohqfo

Conversation

@ThomAub
Copy link
Copy Markdown
Owner

@ThomAub ThomAub commented Apr 20, 2026

Summary

  • New zig/ tree with a Zig 0.16 port of the CLI's pure-logic surface: spec grammar parsing (src/spec.zig) and docs.rs URL construction (src/url.zig), driven from src/main.zig.
  • Scope is intentionally narrow so a follow-up WASM build compares like-for-like against a Rust WASM build of the same surface. Networking, zstd decode, rustdoc-JSON parsing and Markdown rendering stay in the Rust crate.
  • Unit tests mirror the Rust spec.rs and fetch.rs test cases.

Follow-ups

  • Wire wasm32-wasi (or wasm32-unknown-unknown) targets on both sides and compare .wasm sizes.

Test plan

  • cd zig && zig build test (requires Zig 0.16 — not installed in this sandbox, so not yet executed here)
  • cd zig && zig build run -- serde::de::Deserialize prints https://docs.rs/crate/serde/latest/json/57.zst
  • cd zig && zig build run -- tokio@1.52.1::sync::Mutex --target x86_64-unknown-linux-gnu prints the target-scoped URL

https://claude.ai/code/session_01Ei65b8fDC7sABmKnq1Wgne

ThomAub and others added 6 commits April 21, 2026 09:51
Narrow Zig parallel to the Rust ItemSpec::parse and fetch::build_url,
kept minimal so a later wasm target can be compared against a same-scope
Rust wasm build. Network, zstd, and rendering stay in the Rust crate.

https: //claude.ai/code/session_01Ei65b8fDC7sABmKnq1Wgne
Co-authored-by: Claude <noreply@anthropic.com>
Restructures zig/ into lib/ (Zig sources) + src/ (TypeScript worker),
matching mattzcarey/zigflare. build.zig now produces a wasm32-freestanding
ReleaseSmall artifact exporting alloc, free, and resolve_url; the native
CLI moves to `zig build cli` and goes through the same resolveUrl core so
both surfaces are in lockstep.

The worker at src/index.ts loads md_docrs.wasm, marshals the spec and
optional target triple across the WASM boundary, and returns the resolved
docs.rs rustdoc JSON URL. No network / zstd / rendering yet — keeps the
artifact directly comparable to an equivalent Rust wasm32 build.

https: //claude.ai/code/session_01Ei65b8fDC7sABmKnq1Wgne
Co-authored-by: Claude <noreply@anthropic.com>
Turns the root crate into a workspace and feature-gates the HTTP / server /
CLI surface (reqwest, zstd, tokio, axum, tower-http, tracing, clap) behind
the `http`, `server`, and `cli` features so the pure pipeline — spec,
resolve, render, cache — now compiles for `wasm32-unknown-unknown`.

Adds a new `rust-wasm` workspace member exposing the exact same C ABI as
`zig/lib/wasm.zig`:

  alloc(len) -> *u8
  free(ptr, len)
  resolve_url(spec, target, out, out_cap) -> u32
  render_markdown(json, spec, target, *len_out) -> *u8   (behind `render`)

The no_mangle exports are gated to `target_arch = "wasm32"` so host tests
don't shadow libc's free and cause infinite recursion during dealloc.

Current sizes for `--profile wasm-release`:
  * resolve_url only (`--no-default-features`):  35,939 bytes
  * full pipeline (+render_markdown):           414,560 bytes

The Zig worker at `zig/src/index.ts` works against either .wasm without
changes. Porting render_markdown to Zig is the follow-up; that's where
the JSON→Markdown comparison becomes apples-to-apples.

https: //claude.ai/code/session_01Ei65b8fDC7sABmKnq1Wgne
Co-authored-by: Claude <noreply@anthropic.com>
The previous fingerprint (0xb3f2a91c4d6e7058) was rejected by Zig 0.16's
stricter validator. Replace it with the value Zig derives from the package
name (.md_docrs_zig) so 'zig build' succeeds out of the box.

Co-authored-by: Claude <noreply@anthropic.com>
Zig 0.16 removed std.heap.GeneralPurposeAllocator and std.process.argsAlloc
in favour of main(init: std.process.Init) with an arena + Io handed in by
the startup shim. cli.zig now takes that parameter, returns an exit code
(!u8), and uses the new Io.File.Writer for stdout/stderr, so 'zig build cli'
and 'zig build run -- ...' both succeed against the shipped 0.16.0 stdlib.

Expand the native-CLI section of zig/README.md with concrete spec examples
and document the exit-code convention so the binary is actually usable,
not just buildable.

Co-authored-by: Claude <noreply@anthropic.com>
Adds a new workspace member at wasm/ (md-docrs-wasm-compare) that loads
each .wasm artifact inside an embedded wasmtime host, drives the shared
resolve_url ABI through a fixed spec list, and reports byte size, output
parity, and median / p95 per-call latency in one table. An optional
'wasmer' cargo feature swaps the runtime to wasmer under --runtime wasmer
so ABI cost and JIT cost can be separated.

wasm/build.sh builds zig (ReleaseSmall) + rust-minimal (no-default-features)
+ rust-full (render_markdown) and stages them as zig.wasm / rust-minimal.wasm
/ rust-full.wasm under wasm/artifacts/, which the harness picks up by
default via CARGO_MANIFEST_DIR. Missing artifacts are skipped rather than
erroring so partial runs still print something useful.

Parity confirmed against all three modules on the default spec list
(serde, tokio@1.52.1::sync::Mutex, anyhow::Error with target override,
rustdoc-types@0.57::Crate): byte-identical output from every artifact
under both runtimes.

Co-authored-by: Claude <noreply@anthropic.com>
@ThomAub ThomAub force-pushed the claude/zig-cli-wasm-implementation-ohqfo branch from 5cbf26e to 789a0c4 Compare April 21, 2026 07:56
@ThomAub ThomAub marked this pull request as ready for review April 21, 2026 09:32
@ThomAub ThomAub merged commit fa47ce3 into main Apr 21, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant