Skip to content

The surface owns generation: no generator file, no wrapper crate - #11

Open
zmaril wants to merge 1 commit into
mainfrom
feat/surface-owns-generation
Open

The surface owns generation: no generator file, no wrapper crate#11
zmaril wants to merge 1 commit into
mainfrom
feat/surface-owns-generation

Conversation

@zmaril

@zmaril zmaril commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

You said annotating should feel like pub — on the original function, no
special files. Two things stood in the way, and both were jedem's.

No generator file

surface! now takes an optional bindings: directory and emits generation
itself:

jedem::surface! {
    name: "hello", version: "0.1.0",
    api: [Hello, fallible, ripeness],
    bindings: "bindings",
}
  • cargo testchecks the committed bindings
  • JEDEM_WRITE=1 cargo test — rewrites them
  • cargo jedem generate — the same, under a nicer name

Writing is opt-in, so an ordinary test run is a check and never a silent
rewrite — the shape every snapshot tool uses. That removes the last
hand-written file
: no generator bin, and no drift-guard test either, because
the emitted one is the guard.

The manifest-key bug jawohl hit

Generated manifests keyed their dependency on the Rust crate name, so a
package named jawohl-surface produced a manifest cargo could not resolve.
jawohl worked around it by renaming its package; jedem's own demo could never
catch it, because hello has no hyphen.

Manifests now key on the package name while generated code uses the crate
name — they are separate inputs, and conflating them only looked fine because
the demo had no hyphen.

Proven, not asserted

The demo's generator bin and drift-guard test are deleted, its binding
crates move under demo/hello/bindings/, and the guard is verified to fire:
changing one doc comment fails with

  .../python/src/generated.rs differs
  .../node/src/generated.rs differs
regenerate with:  JEDEM_WRITE=1 cargo test

Both host round-trips still pass.

Generated code is not formatted

rustfmt rewraps method chains exceeding its chain_width, which jedem cannot
predict, so cargo fmt --all made committed bindings differ from a fresh
generation. Formatting generated code is the wrong operation on it — you
regenerate it. A rustfmt.toml records the rule; CI formats only hand-written
crates.

40 tests, clippy clean.

Next, in jawohl: annotate complete_json, Syntax and Validation
directly, add the one-shot functions as real public API, and delete the surface
crate entirely. That needs jawohl to depend on jedem — see my note on the PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HsDxLrGdx6nPaXkVEWkNvS

The owner did not like the binding setup: annotating should feel like `pub`, on
the original function, with no special files. Two things stood in the way, and
both were jedem's.

A consumer had to write a generator bin. Now `surface!` takes an optional
`bindings:` directory and emits the generation itself, as a test:

    jedem::surface! {
        name: "hello", version: "0.1.0",
        api: [Hello, fallible, ripeness],
        bindings: "bindings",
    }

`cargo test` checks the committed bindings; `JEDEM_WRITE=1 cargo test` rewrites
them, and `cargo jedem generate` does the same thing under a nicer name. Writing
is opt-in so an ordinary test run is a check and never a silent rewrite -- the
shape every snapshot-testing tool uses. That removes the last hand-written file:
no bin, and no drift-guard test to write either, since the emitted one is the
guard.

The generated manifests keyed their dependency on the RUST CRATE name, so a
package whose name contains a hyphen produced a manifest cargo could not
resolve. jawohl hit this and worked around it by renaming its package; jedem's
own demo never could, because `hello` has no hyphen. Manifests now key on the
package name and generated code uses the crate name, which are separate inputs.

The demo is converted to prove both rather than assert them: its generator bin
and its drift-guard test are deleted, the binding crates move under
demo/hello/bindings/ where the surface points, and the guard is verified to fire
-- changing one doc comment fails the test with the regeneration command.

Also, generated code is no longer formatted. rustfmt rewraps method chains that
exceed its chain_width, which jedem cannot predict, so `cargo fmt --all` made
committed bindings differ from a fresh generation. Formatting generated code is
the wrong operation on it; a rustfmt.toml records the rule and CI formats only
the hand-written crates.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HsDxLrGdx6nPaXkVEWkNvS
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