Skip to main content

prebindgen_registry/
prebindgen.rs

1//! `Prebindgen` — what a generator still hands the emitter.
2//!
3//! One method per `#[prebindgen]` item kind (`on_function`, `on_struct`,
4//! `on_enum`, `on_const`) returning the wrapper Rust tokens to emit, plus the
5//! items they depend on (`prerequisites`), a cross-cutting rewrite
6//! (`post_process_item`) and two invariant checks.
7//!
8//! **Conversion is not here.** A generator builds those itself, against the
9//! demand `RegistryBuilder::crossings` hands it, and gives them back through
10//! `RegistryBuilder::convert_with` — so there is no `on_input_type`, no deferral, and no
11//! fixed-point loop retrying until it converges.
12//!
13//! [`ConverterImpl::function`] is the **complete** Rust function for a
14//! converter — signature, body, attributes, lifetimes. The generator owns 100%
15//! of the shape. Callers read the name from `function.sig.ident` and the wire
16//! form from `destination`.
17
18use proc_macro2::TokenStream;
19
20use crate::{niches::Niches, registry::Registry};
21
22/// A shared predicate over an item name, as used by
23/// `Prebindgen`'s ignore hooks (bulk ignores keyed on a naming
24/// family rather than an exact ident).
25pub type NamePredicate = std::sync::Arc<dyn Fn(&str) -> bool + Send + Sync>;
26
27/// One link in a converter's [stage chain](`ConverterImpl::pre_stages`) —
28/// a value-inspecting step that sits between the rust value the
29/// `#[prebindgen]` fn yields/receives and the wire-facing
30/// [`ConverterImpl::function`].
31///
32/// Each stage is a fallible `In → Result<Out, Err>` function. The core
33/// pipeline only ever emits and de-duplicates [`Self::function`]; how a
34/// stage's `Err` arm is surfaced to the foreign side — throw an exception,
35/// return an error code, set `errno`, … — is entirely up to the
36/// destination-language adapter and is described by [`Self::metadata`].
37#[derive(Clone)]
38pub struct Stage<M = ()> {
39    /// Complete function definition for this stage. Same shape as
40    /// [`ConverterImpl::function`] but typed for this stage's own `In →
41    /// Out` and own error type.
42    pub function: syn::ItemFn,
43    /// Adapter-specific extras for this stage — same [`Metadata`] type as
44    /// the owning converter ([`ConverterImpl::metadata`]). The core never
45    /// inspects this; the adapter's emitter reads it to decide how the
46    /// stage's `Err` arm is surfaced (e.g. a JNI adapter stores the JVM
47    /// exception class and `throw_*` fn to call here; a C adapter might
48    /// store the error-code sentinel). Defaults to `()`.
49    ///
50    /// [`Metadata`]: Prebindgen::Metadata
51    pub metadata: M,
52}
53
54/// Result of resolving one converter — the wire (destination) type the rest
55/// of the registry sees, plus the complete generated function.
56///
57/// Invariant: `function.sig.ident` MUST be a deterministic function of the
58/// `(rust_type, destination)` pair so that callers of this converter — both
59/// other generated converters from the same adapter and any hand-written code
60/// that knows the convention — can compute or look up the name.
61#[derive(Clone)]
62pub struct ConverterImpl<M = ()> {
63    /// Wire/destination type. Other converters that ask "what's the wire
64    /// form of this rust type?" read this. The actual function may return
65    /// a wrapped form (e.g. an adapter's own `Result`-like envelope) — that
66    /// is the adapter's internal calling convention; `destination` is the
67    /// value the wire carries on success.
68    pub destination: syn::Type,
69    /// Complete function definition for the **wire-facing** stage. The
70    /// adapter owns the parameter list, return type, `unsafe`/`pub`
71    /// modifiers, lifetime parameters, and any attribute annotations.
72    /// For input direction this is the FIRST stage in execution order
73    /// (it takes the wire); for output direction this is the LAST stage
74    /// (it produces the wire).
75    pub function: syn::ItemFn,
76    /// **Rust-side** stages that compose with [`Self::function`] to form
77    /// the full conversion chain. Default empty — a 1-stage converter
78    /// is just `function`.
79    ///
80    /// Order is rust-side-first → function-side-last. Concretely:
81    /// * **Input** (wire → rust): chain runs `wire → function →
82    ///   pre_stages[0] → pre_stages[1] → … → pre_stages[N-1] → rust`.
83    /// * **Output** (rust → wire): chain runs `rust → pre_stages[N-1] →
84    ///   … → pre_stages[1] → pre_stages[0] → function → wire`.
85    ///
86    /// Each stage is fallible; how its `Err` arm is surfaced is adapter
87    /// specific and carried in [`Stage::metadata`].
88    pub pre_stages: Vec<Stage<M>>,
89    /// Bit-patterns the wire type can represent but this converter never
90    /// produces (output) and rejects (input). Wrapper handlers like
91    /// `Option<_>` consume one slot for their own discriminant and
92    /// re-export the rest — see [`Niches`] for the cascade model.
93    /// Default is empty (no niche optimisation).
94    pub niches: Niches,
95    /// Adapter-specific extras carried alongside the converter. Filled by
96    /// the same handler that produces `destination` / `function` /
97    /// `niches`, copied through into `TypeEntry::metadata` by the resolver,
98    /// and read by the adapter's language-side emitters. Set this where you
99    /// build the converter, not in a side channel.
100    pub metadata: M,
101    /// Inner types this converter composed from — the types whose
102    /// `input_entry`/`output_entry` the adapter looked up to build a wrapper
103    /// (`Option<X>` → `[X]`, `Result<T,E>` → `[T, E]`, `&T` → `[&T]`). Empty
104    /// for a terminal converter (scalar, opaque handle, string) and for
105    /// a callback's own converter (callback args are cross-direction — their
106    /// required-ness flows through the registry's type-graph edges, not here). The
107    /// resolver copies these into `TypeEntry::subs`, which `propagate_required`
108    /// walks to mark reachable types required.
109    ///
110    /// **Identities, not spellings.** They are looked up and walked, never
111    /// emitted — [`TypeEntry::subs`](crate::registry::TypeEntry::subs)
112    /// was already `Vec<TypeKey>` and the resolver keyed these on arrival, so
113    /// the spelling existed only to be converted. An adapter that composed the
114    /// inner type keys it (`TypeKey::from_type`); one that read it off the model
115    /// asks the reading (`TypeRef::key`), and names no escape to do it.
116    pub subs: Vec<crate::registry::TypeKey>,
117}
118
119/// The single extension point of the pipeline: implement this trait once per
120/// **destination language** (C/cbindgen, JNI/Kotlin, Swift, Python, …) to teach
121/// the language-agnostic [`Registry`] how that language represents Rust types
122/// on the wire and what wrapper code to emit.
123///
124/// The trait has no language-specific concepts of its own, and — since the
125/// registry stopped asking it questions — one job left: **per-item emission**.
126/// The file emitter calls `on_function` / `on_struct` / `on_enum` / `on_const`
127/// to produce the per-item wrapper code, plus `prerequisites` and
128/// `post_process_item` around them and the two `validate` hooks for
129/// adapter invariants.
130///
131/// What used to be here and is not any more: which items to build, how
132/// composites decompose, and the wire form of each type. A generator states the
133/// first two into the builder (`RegistryBuilder::export`,
134/// `RegistryBuilder::decompose`)
135/// and answers the third by filling `RegistryBuilder::crossings` — so nothing in
136/// core calls back to ask. Moving emission out too is what would delete this
137/// trait entirely (prebindgen#251 phase E).
138///
139/// Anything language-specific the rest of the pipeline must carry — a JNI
140/// adapter's Kotlin class names and exception info, a C adapter's header
141/// names, etc. — rides in [`Self::Metadata`], an opaque type the adapter
142/// chooses. It is set in each `ConverterImpl::metadata`, propagated by the
143/// resolver into `TypeEntry::metadata`, and read back by the adapter's own
144/// emitter. Adapters that need no extras leave it at the default `()`.
145///
146/// # The rule an adapter must obey
147///
148/// "Classify off [`kind`](crate::flat::TypeRef::kind), spell off the syntax"
149/// tells an adapter where to get each fact. It is silent on the question
150/// adapters actually face — what the **destination language** ends up seeing.
151/// That one has its own answer:
152///
153/// > **Same `kind` ⇒ same destination-language type.** The *wire* is the
154/// > generator's to choose, and may differ per spelling.
155///
156/// The weaker-sounding half is the important one. It is tempting to write "same
157/// `kind` ⇒ same wire", and that is **false** — prebindgen's own adapters
158/// violate it deliberately:
159///
160/// | Rust | `kind` | Kotlin type | wire |
161/// |---|---|---|---|
162/// | `&[Payload]` | `Ref(Slice)` | `List<Payload>` | `Long` — a handle to a Rust-side `Vec` |
163/// | `Vec<Box<Payload>>` | `Vec(Boxed)` | `List<Payload>` | `JObject` — a Java `List<Payload>` |
164///
165/// Two wires, one surface. Choosing a wire is exactly the generator's job, and
166/// the destination-language wrapper absorbs the difference; a caller cannot
167/// tell. What a caller *can* tell — and what
168/// [`unwrapped`](crate::flat::TypeRef::unwrapped) exists to prevent — is the
169/// **type** changing because the source spelled a `Box`.
170///
171/// The rule scopes to **converted** positions: those where a converter stands
172/// between the Rust value and the destination and is therefore free to bridge.
173/// It cannot apply to a **layout mirror**, where the destination type is
174/// reinterpreted from the source struct's bytes and is a *layout* fact rather
175/// than a surface choice — there `Box<T>` (a pointer) genuinely is a different
176/// destination type from `T` (inline), the spelling is load-bearing by
177/// construction, and no erasure can apply. The C adapter's `repr_c_struct` is
178/// the one such position in-tree, and its own documentation carries that half.
179///
180/// Reusing a mirror's spelling test in a converted position is how the rule
181/// gets broken (prebindgen#230, #292).
182pub trait Prebindgen {
183    /// Adapter-specific extras every resolved converter carries. The
184    /// resolver copies this from each `ConverterImpl` it accepts into
185    /// the matching `TypeEntry`, so emitter code reads metadata off
186    /// the registry rather than through a parallel side channel.
187    type Metadata: Clone + Default;
188
189    /// Rust items the adapter's emitted converters depend on (helper
190    /// structs, type aliases, runtime-support code). Emitted at the top
191    /// of the destination file, before all auto-generated converters.
192    ///
193    /// Default: none. Wrapper adapters that compose a base adapter should
194    /// forward to or extend the base's `prerequisites()`. The resolved
195    /// `registry` is supplied so prerequisites can be gated on what the
196    /// (feature-aware) scan actually contains — e.g. emitting a
197    /// per-opaque-handle item only for handles a scanned `#[prebindgen]`
198    /// fn references.
199    fn prerequisites(
200        &self,
201        _registry: &Registry<Self::Metadata>,
202        _emit: &prebindgen_flat::Emit,
203    ) -> Vec<syn::Item> {
204        Vec::new()
205    }
206
207    // ── Declaration queries ────────────────────────────────────────
208
209    /// Final post-processing pass applied to every emitted item right
210    /// before write. Default: no-op.
211    ///
212    /// Use this for cross-cutting transforms that would otherwise have
213    /// to be remembered at every individual emit site — e.g. qualifying
214    /// bare type references against a source module so the emitted
215    /// converter bodies compile in the binding crate's scope. Walks the
216    /// entire AST, not just signatures, so type ascriptions and casts
217    /// inside function bodies are covered.
218    fn post_process_item(
219        &self,
220        _item: &mut syn::Item,
221        _registry: &Registry<Self::Metadata>,
222        _emit: &prebindgen_flat::Emit,
223    ) {
224    }
225
226    /// Adapter-invariant checks that need registry **signatures** — the
227    /// earliest they can run (decl objects are built before any source is
228    /// read). Called by `RegistryBuilder::validate_with` right after the declaration
229    /// scan (so a missing fn has already hard-errored; validate sees only
230    /// indexed items) and before plan application. An `Err` aborts the
231    /// resolve as `ScanError::AdapterInvariant` with the message verbatim
232    /// — e.g. jnigen rejects a `.fun()` member whose target has no
233    /// receiver parameter of the class type.
234    ///
235    /// Default: no checks.
236    fn validate(
237        &self,
238        _binding: &crate::registry::Building<'_, Self::Metadata>,
239    ) -> Result<(), String> {
240        Ok(())
241    }
242
243    /// Post-**resolve** validation boundary — the counterpart of
244    /// [`Self::validate`] that sees the fully resolved registry (converters,
245    /// plans, metadata). Every artifact writer calls it before writing
246    /// anything, so an invalid binding fails cleanly — with every problem
247    /// reported at once — instead of panicking midway after a sibling
248    /// artifact already reached disk. Deterministic over `(self, registry)`;
249    /// it runs once per write call, which keeps artifact writes
250    /// order-independent.
251    ///
252    /// Default: no checks.
253    fn validate_resolved(&self, _registry: &Registry<Self::Metadata>) -> Result<(), String> {
254        Ok(())
255    }
256
257    /// Absolute path under which the source crate's items are reachable
258    /// from the generated file (e.g. `zenoh_flat`), for adapters that
259    /// qualify emitted references against one. Drives the default
260    /// [`Self::on_const`]: with a source module available, a named const
261    /// re-emits as a path-alias to the source item instead of copying its
262    /// initializer tokens. Default: `None`.
263    fn source_module(&self) -> Option<&syn::Path> {
264        None
265    }
266
267    // ── Item methods ───────────────────────────────────────────────
268    //
269    // Each takes the **element**, not the `syn` item it was parsed from.
270    //
271    // The element is the model's own node: its types are `TypeRef`s, already
272    // classified. An adapter handed one therefore cannot ask what a type means
273    // and be told "no reading" — the question a `&syn::ItemFn` forced it to ask
274    // the registry, and which answered wrongly for a type that never entered
275    // the pipeline (#275). What generated Rust must *spell* is still exactly
276    // available, through `spell()`: classify off `kind`, spell with `spell()`.
277
278    /// Wrap a `#[prebindgen]` fn into the destination-language wrapper
279    /// (e.g. JNI `extern "C"` fn).
280    fn on_function(
281        &self,
282        f: &prebindgen_flat::flat::Function,
283        registry: &Registry<Self::Metadata>,
284        emit: &prebindgen_flat::Emit,
285    ) -> TokenStream;
286
287    /// Per-struct emission. Typically empty for languages that get
288    /// everything they need from auto-generated converters.
289    fn on_struct(
290        &self,
291        s: &prebindgen_flat::flat::Struct,
292        registry: &Registry<Self::Metadata>,
293        emit: &prebindgen_flat::Emit,
294    ) -> TokenStream;
295
296    /// Per-sum emission — an `enum` whose alternatives carry payloads.
297    ///
298    /// Separate from [`Self::on_enum`] because the model separates them: the
299    /// two are numbered differently and consumed as different constructs. An
300    /// adapter with nothing to say about one shape returns an empty stream, as
301    /// both in-tree adapters do for both.
302    fn on_variant(
303        &self,
304        v: &prebindgen_flat::flat::Variant,
305        registry: &Registry<Self::Metadata>,
306        emit: &prebindgen_flat::Emit,
307    ) -> TokenStream;
308
309    /// Per-enum emission — the fieldless shape, a named set of integers.
310    fn on_enum(
311        &self,
312        e: &prebindgen_flat::flat::Enum,
313        registry: &Registry<Self::Metadata>,
314        emit: &prebindgen_flat::Emit,
315    ) -> TokenStream;
316
317    /// Per-const emission. Default: a named const re-emits as a path-alias
318    /// when [`Self::source_module`] is available —
319    /// initializer tokens are never copied, so a const whose initializer
320    /// references source-crate internals stays valid in the generated file.
321    /// An adapter without a source module passes the const through verbatim.
322    ///
323    /// A const reaching here is always named: prebindgen's own injected feature
324    /// checks are [`Guard`](prebindgen_flat::flat::Guard)s, not consts, so this
325    /// never has to recognise one.
326    fn on_const(
327        &self,
328        c: &prebindgen_flat::flat::Constant,
329        _registry: &Registry<Self::Metadata>,
330        emit: &prebindgen_flat::Emit,
331    ) -> TokenStream {
332        match self.source_module() {
333            Some(m) => emit.const_alias(c, m),
334            None => emit.const_verbatim(c),
335        }
336    }
337}