pub trait Prebindgen {
type Metadata: Clone + Default;
// Required methods
fn on_function(
&self,
f: &Function,
registry: &Registry<Self::Metadata>,
emit: &Emit,
) -> TokenStream;
fn on_struct(
&self,
s: &Struct,
registry: &Registry<Self::Metadata>,
emit: &Emit,
) -> TokenStream;
fn on_variant(
&self,
v: &Variant,
registry: &Registry<Self::Metadata>,
emit: &Emit,
) -> TokenStream;
fn on_enum(
&self,
e: &Enum,
registry: &Registry<Self::Metadata>,
emit: &Emit,
) -> TokenStream;
// Provided methods
fn prerequisites(
&self,
_registry: &Registry<Self::Metadata>,
_emit: &Emit,
) -> Vec<Item> { ... }
fn post_process_item(
&self,
_item: &mut Item,
_registry: &Registry<Self::Metadata>,
_emit: &Emit,
) { ... }
fn validate(
&self,
_binding: &Building<'_, Self::Metadata>,
) -> Result<(), String> { ... }
fn validate_resolved(
&self,
_registry: &Registry<Self::Metadata>,
) -> Result<(), String> { ... }
fn source_module(&self) -> Option<&Path> { ... }
fn on_const(
&self,
c: &Constant,
_registry: &Registry<Self::Metadata>,
emit: &Emit,
) -> TokenStream { ... }
}Expand description
The single extension point of the pipeline: implement this trait once per
destination language (C/cbindgen, JNI/Kotlin, Swift, Python, …) to teach
the language-agnostic Registry how that language represents Rust types
on the wire and what wrapper code to emit.
The trait has no language-specific concepts of its own, and — since the
registry stopped asking it questions — one job left: per-item emission.
The file emitter calls on_function / on_struct / on_enum / on_const
to produce the per-item wrapper code, plus prerequisites and
post_process_item around them and the two validate hooks for
adapter invariants.
What used to be here and is not any more: which items to build, how
composites decompose, and the wire form of each type. A generator states the
first two into the builder (RegistryBuilder::export,
RegistryBuilder::decompose)
and answers the third by filling RegistryBuilder::crossings — so nothing in
core calls back to ask. Moving emission out too is what would delete this
trait entirely (prebindgen#251 phase E).
Anything language-specific the rest of the pipeline must carry — a JNI
adapter’s Kotlin class names and exception info, a C adapter’s header
names, etc. — rides in Self::Metadata, an opaque type the adapter
chooses. It is set in each ConverterImpl::metadata, propagated by the
resolver into TypeEntry::metadata, and read back by the adapter’s own
emitter. Adapters that need no extras leave it at the default ().
§The rule an adapter must obey
“Classify off kind, spell off the syntax”
tells an adapter where to get each fact. It is silent on the question
adapters actually face — what the destination language ends up seeing.
That one has its own answer:
Same
kind⇒ same destination-language type. The wire is the generator’s to choose, and may differ per spelling.
The weaker-sounding half is the important one. It is tempting to write “same
kind ⇒ same wire”, and that is false — prebindgen’s own adapters
violate it deliberately:
| Rust | kind | Kotlin type | wire |
|---|---|---|---|
&[Payload] | Ref(Slice) | List<Payload> | Long — a handle to a Rust-side Vec |
Vec<Box<Payload>> | Vec(Boxed) | List<Payload> | JObject — a Java List<Payload> |
Two wires, one surface. Choosing a wire is exactly the generator’s job, and
the destination-language wrapper absorbs the difference; a caller cannot
tell. What a caller can tell — and what
unwrapped exists to prevent — is the
type changing because the source spelled a Box.
The rule scopes to converted positions: those where a converter stands
between the Rust value and the destination and is therefore free to bridge.
It cannot apply to a layout mirror, where the destination type is
reinterpreted from the source struct’s bytes and is a layout fact rather
than a surface choice — there Box<T> (a pointer) genuinely is a different
destination type from T (inline), the spelling is load-bearing by
construction, and no erasure can apply. The C adapter’s repr_c_struct is
the one such position in-tree, and its own documentation carries that half.
Reusing a mirror’s spelling test in a converted position is how the rule gets broken (prebindgen#230, #292).
Required Associated Types§
Required Methods§
Sourcefn on_function(
&self,
f: &Function,
registry: &Registry<Self::Metadata>,
emit: &Emit,
) -> TokenStream
fn on_function( &self, f: &Function, registry: &Registry<Self::Metadata>, emit: &Emit, ) -> TokenStream
Wrap a #[prebindgen] fn into the destination-language wrapper
(e.g. JNI extern "C" fn).
Sourcefn on_struct(
&self,
s: &Struct,
registry: &Registry<Self::Metadata>,
emit: &Emit,
) -> TokenStream
fn on_struct( &self, s: &Struct, registry: &Registry<Self::Metadata>, emit: &Emit, ) -> TokenStream
Per-struct emission. Typically empty for languages that get everything they need from auto-generated converters.
Sourcefn on_variant(
&self,
v: &Variant,
registry: &Registry<Self::Metadata>,
emit: &Emit,
) -> TokenStream
fn on_variant( &self, v: &Variant, registry: &Registry<Self::Metadata>, emit: &Emit, ) -> TokenStream
Per-sum emission — an enum whose alternatives carry payloads.
Separate from Self::on_enum because the model separates them: the
two are numbered differently and consumed as different constructs. An
adapter with nothing to say about one shape returns an empty stream, as
both in-tree adapters do for both.
Provided Methods§
Sourcefn prerequisites(
&self,
_registry: &Registry<Self::Metadata>,
_emit: &Emit,
) -> Vec<Item>
fn prerequisites( &self, _registry: &Registry<Self::Metadata>, _emit: &Emit, ) -> Vec<Item>
Rust items the adapter’s emitted converters depend on (helper structs, type aliases, runtime-support code). Emitted at the top of the destination file, before all auto-generated converters.
Default: none. Wrapper adapters that compose a base adapter should
forward to or extend the base’s prerequisites(). The resolved
registry is supplied so prerequisites can be gated on what the
(feature-aware) scan actually contains — e.g. emitting a
per-opaque-handle item only for handles a scanned #[prebindgen]
fn references.
Sourcefn post_process_item(
&self,
_item: &mut Item,
_registry: &Registry<Self::Metadata>,
_emit: &Emit,
)
fn post_process_item( &self, _item: &mut Item, _registry: &Registry<Self::Metadata>, _emit: &Emit, )
Final post-processing pass applied to every emitted item right before write. Default: no-op.
Use this for cross-cutting transforms that would otherwise have to be remembered at every individual emit site — e.g. qualifying bare type references against a source module so the emitted converter bodies compile in the binding crate’s scope. Walks the entire AST, not just signatures, so type ascriptions and casts inside function bodies are covered.
Sourcefn validate(
&self,
_binding: &Building<'_, Self::Metadata>,
) -> Result<(), String>
fn validate( &self, _binding: &Building<'_, Self::Metadata>, ) -> Result<(), String>
Adapter-invariant checks that need registry signatures — the
earliest they can run (decl objects are built before any source is
read). Called by RegistryBuilder::validate_with right after the declaration
scan (so a missing fn has already hard-errored; validate sees only
indexed items) and before plan application. An Err aborts the
resolve as ScanError::AdapterInvariant with the message verbatim
— e.g. jnigen rejects a .fun() member whose target has no
receiver parameter of the class type.
Default: no checks.
Sourcefn validate_resolved(
&self,
_registry: &Registry<Self::Metadata>,
) -> Result<(), String>
fn validate_resolved( &self, _registry: &Registry<Self::Metadata>, ) -> Result<(), String>
Post-resolve validation boundary — the counterpart of
Self::validate that sees the fully resolved registry (converters,
plans, metadata). Every artifact writer calls it before writing
anything, so an invalid binding fails cleanly — with every problem
reported at once — instead of panicking midway after a sibling
artifact already reached disk. Deterministic over (self, registry);
it runs once per write call, which keeps artifact writes
order-independent.
Default: no checks.
Sourcefn source_module(&self) -> Option<&Path>
fn source_module(&self) -> Option<&Path>
Absolute path under which the source crate’s items are reachable
from the generated file (e.g. zenoh_flat), for adapters that
qualify emitted references against one. Drives the default
Self::on_const: with a source module available, a named const
re-emits as a path-alias to the source item instead of copying its
initializer tokens. Default: None.
Sourcefn on_const(
&self,
c: &Constant,
_registry: &Registry<Self::Metadata>,
emit: &Emit,
) -> TokenStream
fn on_const( &self, c: &Constant, _registry: &Registry<Self::Metadata>, emit: &Emit, ) -> TokenStream
Per-const emission. Default: a named const re-emits as a path-alias
when Self::source_module is available —
initializer tokens are never copied, so a const whose initializer
references source-crate internals stays valid in the generated file.
An adapter without a source module passes the const through verbatim.
A const reaching here is always named: prebindgen’s own injected feature
checks are Guards, not consts, so this
never has to recognise one.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".