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}