Skip to main content

Crate prebindgen_registry

Crate prebindgen_registry 

Source
Expand description

§prebindgen-registry

Registry-based, language-agnostic converter pipeline for prebindgen. This crate turns a stream of #[prebindgen] items — read through Source and parsed by flat::Flat — into generated Rust FFI bindings plus a fully resolved table of type converters. It has no knowledge of any particular destination language — C, JNI/Kotlin, Swift, Python, etc. all plug in the same way.

It also re-exports the flat model (flat, shape, types_util) from the separate prebindgen-flat crate, so a language adapter names one crate root for the whole pipeline rather than reaching back into prebindgen-flat for half of it.

§The plug-in point

Write one generator per destination language. It does two things:

  • Says how the language represents Rust types on the wire — it builds a ConverterImpl (a generated converter fn plus its wire type) for each crossing the registry hands it, and gives them all back through RegistryBuilder::convert_with.
  • Emits the wrapper code per itemon_function / on_struct / on_enum / on_const on the Prebindgen trait.

Everything language-specific that must travel through the pipeline rides in the back-end’s chosen Metadata type (a JNI back-end’s Kotlin class names and exception info, a C back-end’s header names, …). It is set on each converter, propagated into the registry’s TypeEntry, and read back by the back-end’s own emitter — no side channels. Back-ends needing no extras leave it at the default ().

§Flow

A build script sees one type — the generator — and never names a Flat or a Registry:

let jni = JniGen::builder()
    .package(package!("io.zenoh"))
    .fun(fun!(session_open))
    .source(zenoh_flat::PREBINDGEN_OUT_DIR)
    .build()?;
jni.write_rust(&rust_dest)?;
jni.write_kotlin(&kotlin_root)?;

Inside build(), the generator does what it alone knows how to do:

  1. flat::Flat::builder parses the declared sources into the model, and Registry::builder starts describing a binding over it.
  2. The generator states that binding, then RegistryBuilder::crossings hands over every crossing needing a conversion — inner types first, so each one can be built from those already done. convert_with answers them and build names any gap.
  3. The resolved registry becomes a field of the built generator, whose write_* methods emit the artifacts — Rust wrappers, and whatever else that language needs (a C header, Kotlin sources, …).

§Universality, by example

The same machinery serves very different languages:

  • C / cbindgen back-end (the separate prebindgen-c crate): wire types are raw pointers and primitive C types; converters are thin transmutes; pre_stages are usually empty (errors surface as return codes).
  • JNI / Kotlin back-end (the separate prebindgen-jni crate): wire types are JNI handles (jlong, JObject); converters marshal across the JVM boundary; pre_stages carry fallible steps whose Err arms throw JVM exceptions (the exception info lives in that back-end’s Metadata).

§Macros

The declaration surface is built almost entirely from exported macros. This crate defines the language-neutral ones — the domain vocabulary shared by every adapter, plus the syntax helpers they’re built from:

Syntax helpers produce a bare syn node — Type / Path / Expr / Signature / Ident — to hand to a declaration method that requires one. They exist only to sidestep syn::parse_quote!’s type-inference ambiguity (E0283) in a generic argument position, not to express a domain concept: ty!, path!, expr!, sig!, ident!.

The JNI/Kotlin-specific declaration macros — package!, ptr_class!, data_class!, enum_class!, sealed_class!, variant!, constant! — construct a typed *Decl for the Kotlin surface and live in the separate prebindgen-jni crate, which hands the result to its JniGenBuilder.

Re-exports§

pub use self::decl::ConvertDecl;
pub use self::decl::ConvertSourceDecl;
pub use self::decl::ConvertSpec;
pub use self::decl::ExpandDecl;
pub use self::decl::ExpandParamDecl;
pub use self::decl::ExpandReturnDecl;
pub use self::decl::FieldsDecl;
pub use self::decl::FunctionDecl;
pub use self::decl::LocalField;
pub use self::decl::LocalVariant;
pub use self::diagnostics::warn_unclaimed;
pub use self::diagnostics::Claimed;
pub use self::domain::DomainScalar;
pub use self::domain::RepresentationDomain;
pub use self::domain::ScalarValue;
pub use self::niches::NicheSlot;
pub use self::niches::Niches;
pub use self::prebindgen::ConverterImpl;
pub use self::prebindgen::NamePredicate;
pub use self::prebindgen::Prebindgen;
pub use self::prebindgen::Stage;
pub use self::registry::Building;
pub use self::registry::Conversions;
pub use self::registry::Crossing;
pub use self::registry::Decompositions;
pub use self::registry::Direction;
pub use self::registry::DuplicateNameError;
pub use self::registry::NotExpressibleEntry;
pub use self::registry::Registry;
pub use self::registry::RegistryBuilder;
pub use self::registry::ScanError;
pub use self::registry::TypeEntry;
pub use self::registry::WriteRustError;

Modules§

decl
Language-neutral declaration vocabulary: the decl objects and constructor macros a build script uses to describe boundary expansion (expand_param! / expand_return!), free functions (fun!), and canonical single-value conversions (convert!). Moved down from the JNI adapter (formerly api/lang/jnigen, now the separate prebindgen-jni crate) because none of it is Kotlin/JNI-specific — it only references TypeKey, Origin<syn::Type>, and plain syn types. prebindgen-jni’s own jni/decl.rs keeps the genuinely Kotlin-specific declarations (ptr_class!, enum_class!, sealed_class!, data_class!, constant!, package!) and re-exports these types from here, so the public prebindgen_flat::* surface is unaffected by the split.
diagnostics
What a binding did not claim, reported to the build log.
domain
Valid subsets of scalar representations used by custom conversions.
expand
Constructor expansion — fold a value’s construction into the wire signature of the function that consumes it, so the foreign side builds the value and calls the function in a single FFI crossing.
flat
The flat model itself lives in the separate prebindgen-flat crate — re-exported here so an adapter names one crate root for the whole pipeline. The prebindgen source language: one parser from captured records to Elements.
niches
Niche optimisation for FFI-wire encodings.
prebindgen
Prebindgen — what a generator still hands the emitter.
registry
Which type conversions a binding needs, and whether it has them all.
shape
The flat model itself lives in the separate prebindgen-flat crate — re-exported here so an adapter names one crate root for the whole pipeline. Shape<N> — a leaf wrapped in an ordered stack of structural layers (Option, Vec), with a bottom-up fold_shape combinator.
types_util
The flat model itself lives in the separate prebindgen-flat crate — re-exported here so an adapter names one crate root for the whole pipeline. Shared syn::Type shape utilities — the Option/Vec/reference peelers and short-name helpers every pipeline stage needs. One definition here replaces the per-module copies that used to live in core::unfold, core::expand, and the jnigen adapter.
unfold
Output (data) expansion — the dual of constructor expansion (api/core/expand.rs). A function returning a rich type is decomposed by a deconstructor into a set of leaf values.
write
Rust file emission for the resolved Registry.

Macros§

convert
Build a ConvertDecl directly from a bare Rust type: convert!(Millis) is ConvertDecl::new(<Millis as syn::Type>). See ptr_class! for the parsing mechanics.
expand_param
Build a ExpandParamDecl directly from a bare Rust type: expand_param!(KeyExpr) is ExpandParamDecl::new(<KeyExpr as syn::Type>). See ptr_class! for the parsing mechanics.
expand_return
Build a ExpandReturnDecl directly from a bare Rust type: expand_return!(Sample) is ExpandReturnDecl::new(<Sample as syn::Type>). See ptr_class! for the parsing mechanics.
expr
Build a syn::Expr from an expression token: expr!(format!("{A}:{B}")). The initializer argument of an adapter’s ConstDecl::expr — allowed only for constants, where the expression binds no arguments.
fields
Build a FieldsDecl from the ident of a value-form accessorfields!(sample_to_struct) is FieldsDecl::new(prebindgen_registry::ident!(sample_to_struct)). The argument of ExpandReturnDecl::fields.
from
Build a ConvertSourceDecl for an input conversion via core::convert: .input(from!(i32)) requires i32: Into<T>. Chain with to use a binding-local callable instead of the trait.
fun
Build a FunctionDecl from a bare function ident or a path:
ident
Build a syn::Ident from a bare identifier token. Unlike syn::parse_quote!, this always yields the concrete type syn::Ident — there’s no external context needed to infer it — so it can be passed directly into a generic impl Into<T> parameter without hitting rustc’s “type annotations needed” ambiguity. syn::parse_quote!’s output type has to be pinned by a concrete parameter type to infer successfully; a generic impl Into<T> bound doesn’t give it anything to unify against.
into
Build a ConvertSourceDecl for an output conversion via core::convert: .output(into!(i32)) requires T: Into<i32>. Chain with to use a binding-local callable instead of the trait.
path
Build a syn::Path from a bare path token: path!(crate::conv::f). The callable argument of FunctionDecl::new_local and of an adapter’s ConstDecl::with.
sig
State a binding-local fn’s exact Rust signature, with named parameters (they become the foreign-side parameter names): sig!((s: &Summary, verbose: bool) -> String); the -> Ret tail is optional (unit). The signature argument of FunctionDecl::sig for a path-built fun!.
try_from
Fallible twin of from!: .input(try_from!(i32)) requires i32: TryInto<T>; an Err routes to the caller’s error handler. With with, the callable returns Result and must state its error type via error.
try_into
Fallible twin of into!: .output(try_into!(i32)) requires T: TryInto<i32>; an Err routes to the caller’s error handler. With with, the callable returns Result and must state its error type via error.
ty
Build a syn::Type from a bare Rust type token: ty!(i32). The type argument of decl methods like ConvertSourceDecl::from_type — always yields the concrete syn::Type, so no inference context is needed (see ident! for the E0283 background).

Structs§

Emit
The capability to render captured Rust syntax.
Flat
The flat API: every #[prebindgen] item from every ingested source, parsed, indexed by name, and with every type reference resolved.
TypeKey
The canonical type identity, which the source model owns — re-exported here because the registry’s tables are keyed by it. Canonical type-shape key: identity is the token string of the normalized type. Normalization is a closed rule set — group/paren unwrap, a crate::/self::/source-module path reduced to its final segment, and a prelude path read as the bare name the language knows it by (std::vec::Vec<Foo>Vec<Foo>) — and any spelling it does not cover is kept verbatim.
TypeKeyParseError
The canonical type identity, which the source model owns — re-exported here because the registry’s tables are keyed by it. Structured failure of TypeKey::parse: the offending input plus the underlying syn parse error.

Enums§

Element
One member of the flat API.