crates/dwarves-web-client/build.rs
// build.rs — compile this crate's OWN `proto/dwarf.proto` into a prost + tonic CLIENT.
//
// ── WHY A THIRD COPY, AND NOT `../dwarves-embed/proto` ─────────────────────────
//
// Until 2026-08-24 this file read the sibling's copy directly:
//
// let proto = "../dwarves-embed/proto/dwarf.proto";
//
// which is REUSE inside the checkout and a BROKEN CRATE on crates.io. `cargo package`
// only ever puts files from UNDER the package root into the tarball, so `../` resolves
// outside the extracted crate and simply is not there. MEASURED — `cargo package` on
// `dwarves-web-client 0.1.4` in a standalone workspace:
//
// thread 'main' panicked at build.rs:17:53:
// protox: compile dwarf.proto: file '../dwarves-embed/proto/dwarf.proto'
// is not in any include path
// error: failed to verify package tarball
//
// The `publish = true` flag came off on 2026-08-23 (4425148) and this is why the crate
// never actually went up: nothing about it is wrong INSIDE the workspace, and nothing
// about it works outside one. That is the whole shape of a publish defect — it can only
// be seen from where the consumer stands, and after an irreversible upload it is too
// late to see it at all.
//
// So the crate owns its proto, exactly as `dwarves` and `dwarves-embed` already own
// theirs. dwarf.proto now has THREE hand-maintained copies and the byte-for-byte
// agreement of this one with the embed copy is not a comment, it is
// `dwarves-embed/tests/proto_parity.rs` — protobuf decodes by NUMBER, so a field that
// drifts here does not error, it lands in the wrong slot on a live `dwarves-server`.
//
// Pure-Rust codegen (the nordisk zero-shell charter): `protox` (a pure-Rust .proto
// parser) produces the FileDescriptorSet, then `tonic-build` emits the client bindings
// from it — no `protoc` binary, no C. Mirrors `crates/dwarves-embed/build.rs`.
fn main() {
println!("cargo:rerun-if-changed=proto/dwarf.proto");
println!("cargo:rerun-if-changed=build.rs");
let fds =
protox::compile(["proto/dwarf.proto"], ["proto"]).expect("protox: compile dwarf.proto");
tonic_prost_build::configure()
.build_server(false)
.build_client(true)
// ── NO tonic `Channel` CONSTRUCTOR ────────────────────────────────────────
//
// `build_transport(true)` (the default) makes tonic-prost-build emit an
// UNGATED `impl DwarfClient<tonic::transport::Channel>` carrying the `connect`
// convenience constructor. This crate declares
// `tonic = { default-features = false, features = ["codegen"] }` — no
// `channel`, deliberately — because it is the BROWSER client: it dials through
// `tonic_web_wasm_client::Client` over `fetch`, and tonic's native transport
// (hyper + tokio sockets) neither compiles to wasm nor is ever wanted here.
//
// Inside the dwarves workspace that mismatch was INVISIBLE: `dwarves-embed`
// declares `tonic = { features = ["transport", "codegen"] }`, and cargo's
// feature UNIFICATION handed the same tonic build to this crate, so the
// generated `connect` always found its `Channel`. Standalone — i.e. as a
// consumer of the published crate sees it — it does not:
//
// error[E0433]: cannot find `transport` in `tonic`
// --> OUT_DIR/dwarf.v1.rs:177:29
// note: found an item that was configured out
// --> tonic-0.14.6/src/lib.rs:110:9 `#[cfg(any(feature = "server",
// feature = "channel"))] pub mod transport;`
// error: could not compile `dwarves-web-client` (lib) due to 4 previous errors
//
// MEASURED 2026-08-24 via `cargo package` in a standalone workspace. The fix is
// NOT to add tonic's `channel` feature — that would drag a native socket stack
// into the wasm bundle and break the wasm target instead. The constructor is
// simply not part of this client's surface, so it is not generated.
.build_transport(false)
.compile_fds(fds)
.expect("tonic-build: codegen");
}