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 throughRegistryBuilder::convert_with. - Emits the wrapper code per item —
on_function/on_struct/on_enum/on_conston thePrebindgentrait.
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:
flat::Flat::builderparses the declared sources into the model, andRegistry::builderstarts describing a binding over it.- The generator states that binding, then
RegistryBuilder::crossingshands over every crossing needing a conversion — inner types first, so each one can be built from those already done.convert_withanswers them andbuildnames any gap. - 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-ccrate): wire types are raw pointers and primitive C types; converters are thin transmutes;pre_stagesare usually empty (errors surface as return codes). - JNI / Kotlin back-end (the separate
prebindgen-jnicrate): wire types are JNI handles (jlong,JObject); converters marshal across the JVM boundary;pre_stagescarry fallible steps whoseErrarms throw JVM exceptions (the exception info lives in that back-end’sMetadata).
§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:
- Members & constants:
fun! - Conversions:
convert!,from!,try_from!,into!,try_into! - Boundary expansion:
expand_param!,expand_return!,fields!
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 (formerlyapi/lang/jnigen, now the separateprebindgen-jnicrate) because none of it is Kotlin/JNI-specific — it only referencesTypeKey,Origin<syn::Type>, and plainsyntypes.prebindgen-jni’s ownjni/decl.rskeeps the genuinely Kotlin-specific declarations (ptr_class!,enum_class!,sealed_class!,data_class!,constant!,package!) and re-exports these types from here, so the publicprebindgen_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-flatcrate — re-exported here so an adapter names one crate root for the whole pipeline. The prebindgen source language: one parser from captured records toElements. - 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-flatcrate — 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-upfold_shapecombinator. - types_
util - The flat model itself lives in the separate
prebindgen-flatcrate — re-exported here so an adapter names one crate root for the whole pipeline. Sharedsyn::Typeshape 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 incore::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
ConvertDecldirectly from a bare Rust type:convert!(Millis)isConvertDecl::new(<Millis as syn::Type>). Seeptr_class!for the parsing mechanics. - expand_
param - Build a
ExpandParamDecldirectly from a bare Rust type:expand_param!(KeyExpr)isExpandParamDecl::new(<KeyExpr as syn::Type>). Seeptr_class!for the parsing mechanics. - expand_
return - Build a
ExpandReturnDecldirectly from a bare Rust type:expand_return!(Sample)isExpandReturnDecl::new(<Sample as syn::Type>). Seeptr_class!for the parsing mechanics. - expr
- Build a
syn::Exprfrom an expression token:expr!(format!("{A}:{B}")). The initializer argument of an adapter’sConstDecl::expr— allowed only for constants, where the expression binds no arguments. - fields
- Build a
FieldsDeclfrom the ident of a value-form accessor —fields!(sample_to_struct)isFieldsDecl::new(prebindgen_registry::ident!(sample_to_struct)). The argument ofExpandReturnDecl::fields. - from
- Build a
ConvertSourceDeclfor an input conversion viacore::convert:.input(from!(i32))requiresi32: Into<T>. Chainwithto use a binding-local callable instead of the trait. - fun
- Build a
FunctionDeclfrom a bare function ident or a path: - ident
- Build a
syn::Identfrom a bare identifier token. Unlikesyn::parse_quote!, this always yields the concrete typesyn::Ident— there’s no external context needed to infer it — so it can be passed directly into a genericimpl 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 genericimpl Into<T>bound doesn’t give it anything to unify against. - into
- Build a
ConvertSourceDeclfor an output conversion viacore::convert:.output(into!(i32))requiresT: Into<i32>. Chainwithto use a binding-local callable instead of the trait. - path
- Build a
syn::Pathfrom a bare path token:path!(crate::conv::f). The callable argument ofFunctionDecl::new_localand of an adapter’sConstDecl::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-> Rettail is optional (unit). The signature argument ofFunctionDecl::sigfor a path-builtfun!. - try_
from - Fallible twin of
from!:.input(try_from!(i32))requiresi32: TryInto<T>; anErrroutes to the caller’s error handler. Withwith, the callable returnsResultand must state its error type viaerror. - try_
into - Fallible twin of
into!:.output(try_into!(i32))requiresT: TryInto<i32>; anErrroutes to the caller’s error handler. Withwith, the callable returnsResultand must state its error type viaerror. - ty
- Build a
syn::Typefrom a bare Rust type token:ty!(i32). The type argument of decl methods likeConvertSourceDecl::from_type— always yields the concretesyn::Type, so no inference context is needed (seeident!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. - Type
KeyParse Error - 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 underlyingsynparse error.
Enums§
- Element
- One member of the flat API.