Skip to main content

prebindgen_c/
builder.rs

1use super::*;
2
3impl CbindgenBuilder {
4    fn clear_current(&mut self) {
5        self.current = None;
6    }
7
8    /// Create an adapter with no declarations (emits an empty library).
9    pub fn new() -> Self {
10        Self::default()
11    }
12
13    /// Set the module path the original `#[prebindgen]` items live under
14    /// (e.g. `syn::parse_quote!(zenoh_flat)`). Root-level modifier: resets the
15    /// current declaration, so it can't be followed by `.base_name()`/`.error()`/etc.
16    pub fn source_module(mut self, p: syn::Path) -> Self {
17        self.source_module = Some(p);
18        self.clear_current();
19        self
20    }
21
22    /// Set the name of the universal memory-freeing function (a type-agnostic C
23    /// `free`) the generated layer exports for releasing `char*` data it hands to
24    /// C — string returns and `String` fields of data structs. Root-level
25    /// modifier: resets the current declaration. Required whenever the adapter
26    /// produces such string memory; otherwise that's a build error.
27    pub fn free_memory_function(mut self, name: impl Into<String>) -> Self {
28        self.free_fn = Some(name.into());
29        self.clear_current();
30        self
31    }
32
33    /// Set the **base** Rust-type mangler: maps a type's Rust short name (e.g.
34    /// `ZKeyExpr`) to a canonical token (e.g. `keyexpr`). Its output feeds
35    /// [`Self::mangle_type_name`], [`Self::mangle_destructor`] and
36    /// [`Self::mangle_callback`], so a one-off spelling fix (e.g. `KeyExpr` →
37    /// `keyexpr`) lives in a single place instead of a per-declaration
38    /// `.base_name()` exception. Root-level modifier (resets the current
39    /// declaration). The adapter ships no default — unset, the base defaults to the
40    /// `snake_case` of the Rust short name.
41    pub fn mangle_rust_type(mut self, f: impl Fn(&str) -> String + 'static) -> Self {
42        self.mangle_rust_type = Some(Box::new(f));
43        self.clear_current();
44        self
45    }
46
47    /// Set the type-name mangler: base (see [`Self::mangle_rust_type`]) → the C
48    /// type name emitted for a `opaque_ptr` / `data_struct` / `enum_type` (e.g.
49    /// `keyexpr` → `z_keyexpr_t`). The base can be overridden per declaration by
50    /// [`.base_name()`](Self::base_name). Root-level modifier.
51    pub fn mangle_type_name(mut self, f: impl Fn(&str) -> String + 'static) -> Self {
52        self.mangle_type_name = Some(Box::new(f));
53        self.clear_current();
54        self
55    }
56
57    /// Set the destructor mangler: base → an opaque handle's `_drop` symbol (e.g.
58    /// `keyexpr` → `z_keyexpr_drop`). Root-level modifier.
59    pub fn mangle_destructor(mut self, f: impl Fn(&str) -> String + 'static) -> Self {
60        self.mangle_destructor = Some(Box::new(f));
61        self.clear_current();
62        self
63    }
64
65    /// Set the "take" mangler: base → the public move symbol of a `value_opaque`
66    /// type used as a [`Self::takeable_param`] (e.g. `sample` → `z_sample_take`).
67    /// When unset, the take symbol defaults to `<destructor-base>_take`. Root-level
68    /// modifier.
69    pub fn mangle_take(mut self, f: impl Fn(&str) -> String + 'static) -> Self {
70        self.mangle_take = Some(Box::new(f));
71        self.clear_current();
72        self
73    }
74
75    /// Set the callback-struct mangler: the bases of a callback's argument types
76    /// → the closure struct's C name (e.g. `["sample"]` → `z_closure_sample_t`,
77    /// `[]` → `z_closure_drop_t`). A per-declaration [`.base_name()`](Self::base_name)
78    /// replaces the args' bases with a single explicit base. Root-level modifier.
79    pub fn mangle_callback(mut self, f: impl Fn(&[String]) -> String + 'static) -> Self {
80        self.mangle_callback = Some(Box::new(f));
81        self.clear_current();
82        self
83    }
84
85    /// Set the function mangler: a `#[prebindgen]` function's Rust ident → its
86    /// exported `#[no_mangle]` symbol (e.g. prefix `z_`). Functions are not types,
87    /// so this does not go through the base mangler; the ident can be overridden
88    /// per declaration by [`.base_name()`](Self::base_name). Root-level modifier.
89    pub fn mangle_function(mut self, f: impl Fn(&str) -> String + 'static) -> Self {
90        self.mangle_function = Some(Box::new(f));
91        self.clear_current();
92        self
93    }
94
95    /// Declare a `#[prebindgen]` function to convert into the C layer.
96    /// Every `#[prebindgen]` item captured in `dir` — see
97    /// `JniGenBuilder::source`.
98    pub fn source<P: AsRef<std::path::Path>>(mut self, dir: P) -> Self {
99        self.sources = std::mem::take(&mut self.sources).source(dir);
100        self
101    }
102
103    /// The same, for a dependency this crate **renames** in `Cargo.toml`.
104    pub fn source_named<P: AsRef<std::path::Path>>(
105        mut self,
106        dir: P,
107        crate_name: impl Into<String>,
108    ) -> Self {
109        self.sources = std::mem::take(&mut self.sources).source_named(dir, crate_name);
110        self
111    }
112
113    /// Add a captured item stream. Accumulates, so it mixes with
114    /// [`Self::source`].
115    pub fn items<I>(mut self, items: I) -> Self
116    where
117        I: IntoIterator<Item = (syn::Item, prebindgen::SourceLocation)>,
118    {
119        self.sources = std::mem::take(&mut self.sources).items(items);
120        self
121    }
122
123    pub fn function(mut self, ident: syn::Ident) -> Self {
124        assert!(
125            !self.ignored_functions.contains(&ident),
126            "Cbindgen::function cannot declare `{}` because it is already ignored",
127            ident
128        );
129        self.functions.insert(ident.clone(), FnCfg::default());
130        self.current = Some(CurrentDecl::Function(ident));
131        self
132    }
133
134    /// Declare a canonical scalar conversion shared with JniGenBuilder. A domain on
135    /// the [`ConvertDecl`] is validated in both directions; invalid scalar
136    /// values become by-value niches for `Option`/`Result`, with public C
137    /// constants derived from the conversion's naming base.
138    pub fn convert(mut self, decl: ConvertDecl) -> Self {
139        assert!(
140            decl.input_spec().is_some() || decl.output_spec().is_some(),
141            "Cbindgen::convert declares no input or output conversion"
142        );
143        let key = decl.key().clone();
144        assert!(
145            !self
146                .convert_decls
147                .iter()
148                .any(|existing| *existing.key() == key),
149            "Cbindgen::convert already declares {}",
150            key
151        );
152        self.convert_decls.push(decl);
153        self.current = Some(CurrentDecl::Convert(key));
154        self
155    }
156
157    /// Mark a `#[prebindgen]` function as intentionally ignored by this
158    /// adapter. Root-level modifier: suppresses the registry's
159    /// "skipping undeclared" warning for that function without scanning or
160    /// emitting it.
161    pub fn ignore_function(mut self, ident: syn::Ident) -> Self {
162        assert!(
163            !self.functions.contains_key(&ident),
164            "Cbindgen::ignore_function cannot ignore `{}` because it is already declared",
165            ident
166        );
167        self.ignored_functions.insert(ident);
168        self.clear_current();
169        self
170    }
171
172    /// Allow the most recently declared [`Self::function`] to `panic!` on an
173    /// internal error message. Required when a non-`Result` function has a
174    /// fallible input (otherwise that's a build error) — a null borrow, an
175    /// invalid `String`, or an out-of-range discriminant for a declared
176    /// [`Self::enum_type`].
177    pub fn panic(mut self) -> Self {
178        match &self.current {
179            Some(CurrentDecl::Function(ident)) => {
180                let ident = ident.clone();
181                self.functions
182                    .get_mut(&ident)
183                    .expect("function entry vanished")
184                    .panic = true;
185            }
186            other => panic!(
187                "Cbindgen::panic must be chained after a `function(...)` call, \
188                 not after {}",
189                describe_current(other)
190            ),
191        }
192        self
193    }
194
195    /// Declare a pointer-struct (opaque-handle) type — a `Box`-owned Rust value
196    /// the C side holds as `#[repr(C)] struct T { _0: *mut c_void }`. Its C
197    /// struct + `<name>_drop` destructor are generated. (Mirrors `JniExt`'s
198    /// `ptr_class`.)
199    pub fn opaque_ptr(mut self, ty: syn::Type) -> Self {
200        let key = TypeKey::from_type(&ty);
201        assert!(
202            !self.ignored_types.contains(&key),
203            "Cbindgen::opaque_ptr cannot declare `{}` because it is already ignored",
204            key
205        );
206        self.opaque.insert(key.clone(), TypeCfg::new(ty));
207        self.current = Some(CurrentDecl::Ptr(key));
208        self
209    }
210
211    /// Declare a by-value `#[repr(C)]` data struct (e.g. `Error`).
212    pub fn data_struct(mut self, ty: syn::Type) -> Self {
213        let key = TypeKey::from_type(&ty);
214        assert!(
215            !self.ignored_types.contains(&key),
216            "Cbindgen::data_struct cannot declare `{}` because it is already ignored",
217            key
218        );
219        self.data.insert(key.clone(), TypeCfg::new(ty));
220        self.current = Some(CurrentDecl::Data(key));
221        self
222    }
223
224    /// Declare an **inline-opaque by-value, plain-data** type: the Rust value
225    /// `rust_ty` is passed across the C ABI *by value* (no `Box`) by transmuting it
226    /// to/from `opaque_ty`, an opaque `#[repr(C, align(_))]` counterpart of
227    /// identical size+align (typically produced by a size/align probe generator and
228    /// defined elsewhere). Use this for types that hold **no external resource**
229    /// (typically `Copy` — e.g. a timestamp): consuming one simply moves it out,
230    /// leaving the source's bitwise duplicate harmlessly droppable, so **no
231    /// gravestone write-back and no `prebindgen_c_runtime::Gravestone` impl are needed** —
232    /// only the autogenerated `prebindgen_c_runtime::Transmute` glue (emitted here) plus a
233    /// fail-closed `const _` size+align equality assert. Contrast
234    /// [`Self::opaque_owned_struct`] for types owning external data.
235    pub fn opaque_data_struct(self, rust_ty: syn::Type, opaque_ty: syn::Type) -> Self {
236        self.declare_opaque(rust_ty, opaque_ty, OpaqueKind::Data)
237    }
238
239    /// Declare an **inline-opaque by-value, owns-external-data** type: like
240    /// [`Self::opaque_data_struct`], but for a Rust value that owns external resources
241    /// (refcounts / heap — e.g. a byte buffer, a sample). Passed *by value* (no
242    /// `Box`) via the `opaque_ty` transmute counterpart; the converters move values
243    /// via `prebindgen_c_runtime::Transmute` and write a **gravestone** back on consume
244    /// (safe drop-after-move), exposing an `Option<rust_ty>` null niche. The
245    /// consumer must implement `prebindgen_c_runtime::Gravestone` for `opaque_ty` — only
246    /// its *logic* (`rust_gravestone`).
247    pub fn opaque_owned_struct(self, rust_ty: syn::Type, opaque_ty: syn::Type) -> Self {
248        self.declare_opaque(rust_ty, opaque_ty, OpaqueKind::Owned)
249    }
250
251    /// Shared body of [`Self::opaque_data_struct`] / [`Self::opaque_owned_struct`].
252    pub(super) fn declare_opaque(
253        mut self,
254        rust_ty: syn::Type,
255        opaque_ty: syn::Type,
256        kind: OpaqueKind,
257    ) -> Self {
258        let method = match kind {
259            OpaqueKind::Data => "opaque_data_struct",
260            OpaqueKind::Owned => "opaque_owned_struct",
261        };
262        let key = TypeKey::from_type(&rust_ty);
263        assert!(
264            !self.ignored_types.contains(&key),
265            "Cbindgen::{method} cannot declare `{key}` because it is already ignored",
266        );
267        self.value_opaque.insert(
268            key.clone(),
269            ValueOpaqueCfg {
270                opaque: opaque_ty,
271                kind,
272                generate_mirror: false,
273                assume_c_field_validity: false,
274                cfg: TypeCfg::new(rust_ty),
275            },
276        );
277        self.current = Some(CurrentDecl::ValueOpaque(key));
278        self
279    }
280
281    /// Declare a **`#[repr(C)]`, FFI-safe value struct** crossed **by direct
282    /// reinterpret** (zero-copy) — the C struct's memory *is* the Rust struct's
283    /// memory. Unlike [`Self::data_struct`] (which copies each field, lowering a
284    /// `String` to `char*`), this passes the whole struct by value via
285    /// `prebindgen_c_runtime::Transmute` and a `&T` borrow / `impl Fn(&T)` callback as a
286    /// zero-copy `*const` pointer cast — the value-opaque machinery, but with an
287    /// **auto-generated visible-field** `#[repr(C)]` C mirror (so C reads the
288    /// fields directly) instead of an opaque blob.
289    ///
290    /// Every field must be FFI-safe: a primitive, a declared
291    /// [`Self::enum_type`], or an **opaque pointer** `Option<Box<T>>` / `Box<T>`
292    /// where `T` is a declared [`Self::opaque_ptr`] (rendered `*mut t_t`; this is
293    /// how a heap `String` rides along — `Option<Box<String>>` → `string_t *`). The
294    /// source type **must** be `#[repr(C)]`; a fail-closed `size_of`/`align_of`
295    /// assert against the generated mirror proves the reinterpret sound at compile
296    /// time. Call after the manglers are configured (the mirror name is resolved
297    /// through them). A `<base>_drop` is generated.
298    ///
299    /// **Owned-ness is inferred** from the fields: a struct with an opaque-pointer field
300    /// owns external resources, so a by-value consume cleans the moved-from slot (nulls
301    /// the owned pointers) to keep the caller's later `_drop` a no-op — no `.owned()`
302    /// modifier and no double-free footgun. A struct with only scalar/enum fields is
303    /// plain data (a by-value crossing is a bitwise copy with no write-back). The source
304    /// type needs `Default` **only** if it has a bare `Box<T>` field (whose gravestone
305    /// can't be a NULL pointer); `Option<Box<T>>` fields are nulled in place.
306    ///
307    /// # Why the spelling is load-bearing here
308    ///
309    /// This is the one position that is **exempt** from the rule stated on
310    /// [`prebindgen_registry::Prebindgen`] — *same `kind` ⇒ same
311    /// destination-language type* — and the exemption is structural rather than
312    /// a concession. A mirror is not converted; it is **reinterpreted from the
313    /// source struct's bytes**, so its field types are a *layout* fact. `Box<T>`
314    /// is a pointer and `T` is inline: to C they really are different types, and
315    /// the size/align assert above would reject a mirror that pretended
316    /// otherwise. So this path reads the wrapper the model erases —
317    /// [`kind`](prebindgen_registry::flat::TypeRef::kind) rather than
318    /// [`unwrapped`](prebindgen_registry::flat::TypeRef::unwrapped) — on
319    /// purpose. It is the one place the usual "classify off `kind`, spell off
320    /// the syntax" split inverts, and it inverts because the contract is layout
321    /// rather than surface.
322    ///
323    /// That is also why a wrapper the model erases must **not** be refused here
324    /// (prebindgen#230). `Option<Box<String>>` is how a source crate says "this
325    /// field is a nullable pointer" — a layout statement a zero-copy mirror is
326    /// entitled to read, not the source naming a C type. There is no competing
327    /// spelling to prefer: `Option<String>` is a 24-byte niche-optimised value
328    /// with no C representation at all, and declaring one is a hard error naming
329    /// the field. Rejecting the `Box` would leave a nullable-pointer field
330    /// inexpressible.
331    pub fn repr_c_struct(mut self, ty: syn::Type) -> Self {
332        let key = TypeKey::from_type(&ty);
333        assert!(
334            !self.ignored_types.contains(&key),
335            "Cbindgen::repr_c_struct cannot declare `{}` because it is already ignored",
336            key
337        );
338        let mirror = self.c_type_ident(&key);
339        self.value_opaque.insert(
340            key.clone(),
341            ValueOpaqueCfg {
342                opaque: syn::parse_quote!(#mirror),
343                kind: OpaqueKind::Data,
344                generate_mirror: true,
345                assume_c_field_validity: false,
346                cfg: TypeCfg::new(ty),
347            },
348        );
349        self.current = Some(CurrentDecl::ValueOpaque(key));
350        self
351    }
352
353    /// Accept a [`Self::repr_c_struct`] whose mirror has **restricted-validity**
354    /// fields, taking responsibility for their bytes.
355    ///
356    /// A `repr_c_struct` crosses IN by one whole-struct reinterpret, so there is
357    /// no per-field hook where a C-supplied byte could be normalised or checked
358    /// before the source struct exists. A field whose Rust type accepts only
359    /// *some* bit patterns — `bool` (`0`/`1`) or a declared [`Self::enum_type`]
360    /// (the declared discriminants) — is therefore undefined behaviour the
361    /// moment C writes anything else into the mirror and hands it back. The
362    /// generator rejects such a declaration by default (#170 instance 3, #158
363    /// instance 3); the real fix is a raw-wire lowering, which does not exist
364    /// yet.
365    ///
366    /// This modifier is the acknowledgement, not a fix: it says the C side of
367    /// this binding is trusted to write only in-domain bytes into those fields.
368    /// It exists so that the audit rejects **silently unsound new declarations**
369    /// without removing bindings that already ship. Chain it directly after the
370    /// [`Self::repr_c_struct`] it applies to; the panic message names every
371    /// field it would cover.
372    ///
373    /// Prefer, in order: move the field into a [`Self::data_struct`] (per-field
374    /// wires, so `bool` normalises), pass it as a separate scalar parameter, or
375    /// widen it to an integer the whole domain of which is valid.
376    pub fn assume_c_field_validity(mut self) -> Self {
377        match self.current.clone() {
378            Some(CurrentDecl::ValueOpaque(key)) => {
379                self.value_opaque
380                    .get_mut(&key)
381                    .expect("entry vanished")
382                    .assume_c_field_validity = true;
383            }
384            other => panic!(
385                "Cbindgen::assume_c_field_validity must follow a `repr_c_struct` declaration, \
386                 not {}",
387                describe_current(&other)
388            ),
389        }
390        self
391    }
392
393    /// Mark a `#[prebindgen]` type as intentionally ignored by this adapter.
394    /// Root-level modifier: suppresses the registry's "skipping undeclared"
395    /// warning for that type without scanning or emitting it.
396    pub fn ignore_type(mut self, ty: syn::Type) -> Self {
397        let key = TypeKey::from_type(&ty);
398        assert!(
399            !self.opaque.contains_key(&key)
400                && !self.data.contains_key(&key)
401                && !self.value_opaque.contains_key(&key)
402                && !self.enums.contains_key(&key)
403                && !self.tagged_unions.contains_key(&key),
404            "Cbindgen::ignore_type cannot ignore `{}` because it is already declared",
405            key
406        );
407        self.ignored_types.insert(key);
408        self.clear_current();
409        self
410    }
411
412    /// Set the **base name** token of the **current declaration** (universal
413    /// modifier): the per-declaration base fed to the name manglers, replacing the
414    /// auto-derived one. For a type it replaces the `mangle_rust_type` base (so
415    /// `mangle_type_name`/`mangle_destructor`/`mangle_take` all see it); for a
416    /// function it replaces the ident fed to `mangle_function`; for a callback it
417    /// is the sole base fed to `mangle_callback` (replacing the args' bases —
418    /// useful to disambiguate e.g. `&T` from `T` closures). E.g.
419    /// `.callback(...).base_name("sample_ref")` with a `|bases| "z_closure_{…}_t"`
420    /// mangler → `z_closure_sample_ref_t`. Panics if not chained directly after a
421    /// declaration.
422    pub fn base_name(mut self, base: impl Into<String>) -> Self {
423        let base = base.into();
424        match self.current.clone() {
425            Some(CurrentDecl::Ptr(key)) => {
426                self.opaque.get_mut(&key).expect("entry vanished").base = Some(base);
427            }
428            Some(CurrentDecl::Data(key)) => {
429                self.data.get_mut(&key).expect("entry vanished").base = Some(base);
430            }
431            Some(CurrentDecl::ValueOpaque(key)) => {
432                self.value_opaque
433                    .get_mut(&key)
434                    .expect("entry vanished")
435                    .cfg
436                    .base = Some(base);
437            }
438            Some(CurrentDecl::Enum(key)) => {
439                self.enums.get_mut(&key).expect("entry vanished").base = Some(base);
440            }
441            Some(CurrentDecl::TaggedUnion(key)) => {
442                self.tagged_unions
443                    .get_mut(&key)
444                    .expect("entry vanished")
445                    .base = Some(base);
446            }
447            Some(CurrentDecl::Callback(key)) => {
448                self.callbacks.get_mut(&key).expect("entry vanished").base = Some(base);
449            }
450            Some(CurrentDecl::Function(ident)) => {
451                self.functions.get_mut(&ident).expect("entry vanished").base = Some(base);
452            }
453            Some(CurrentDecl::Convert(key)) => {
454                self.convert_bases.insert(key, base);
455            }
456            None => panic!(
457                "Cbindgen::base_name must be chained directly after a declaration \
458                 (`opaque_ptr` / `data_struct` / `enum_type` / `tagged_union` / `callback` / \
459                 `function` / `convert`)"
460            ),
461        }
462        self
463    }
464
465    /// Mark the current declaration (which must be a [`Self::data_struct`]) as an
466    /// error type: it may appear as the `E` of a `Result<_, E>` return. The type
467    /// must implement `From<String>`. Panics if the current declaration is not a
468    /// data struct.
469    pub fn error(mut self) -> Self {
470        match &self.current {
471            Some(CurrentDecl::Data(key)) => {
472                self.error.insert(key.clone());
473            }
474            other => panic!(
475                "Cbindgen::error must be chained after a `data_struct(...)` call \
476                 (error types are marshalled by value), not after {}",
477                describe_current(other)
478            ),
479        }
480        self
481    }
482
483    /// Declare an **opaque error type** — one that appears as the `E` of a
484    /// `Result<_, E>` but is *not* a by-value [`Self::data_struct`] (e.g.
485    /// `ZError = Box<dyn Error + Send + Sync>`). Such an error is marshalled to C
486    /// as a `char*` message obtained by calling `message_fn(&err) -> String`
487    /// (e.g. `z_error_message`); the generated wrapper's error out-param becomes
488    /// `char **e`. The type must implement `From<String>` (so a fallible input's
489    /// internal message can be lifted into it). Root-level modifier (resets the
490    /// current declaration).
491    pub fn opaque_error(mut self, error_ty: syn::Type, message_fn: syn::Ident) -> Self {
492        let key = TypeKey::from_type(&error_ty);
493        self.error.insert(key.clone());
494        self.opaque_errors.insert(key, message_fn);
495        self.clear_current();
496        self
497    }
498
499    /// Declare a C-like (fieldless) enum type to convert. (Mirrors `JniExt`'s
500    /// `enum_class`.)
501    ///
502    /// Crossing **into** Rust, the caller's discriminant is validated before any
503    /// Rust enum is built — an out-of-range one is a fallible-input error, so a
504    /// non-`Result` function taking this enum by value needs [`Self::panic`].
505    /// See the module docs for why the wire is `MaybeUninit<mirror>`.
506    pub fn enum_type(mut self, ty: syn::Type) -> Self {
507        let key = TypeKey::from_type(&ty);
508        assert!(
509            !self.ignored_types.contains(&key),
510            "Cbindgen::enum_type cannot declare `{}` because it is already ignored",
511            key
512        );
513        self.enums.insert(key.clone(), TypeCfg::new(ty));
514        self.current = Some(CurrentDecl::Enum(key));
515        self
516    }
517
518    /// Declare a **data-carrying** enum: it crosses by value as a `#[repr(C)]`
519    /// enum with payload variants, which cbindgen renders as the idiomatic C
520    /// tagged union (a tag enum plus a `union` of the variant bodies). The
521    /// counterpart of [`Self::enum_type`], which is for the unit-variant-only
522    /// case a plain C `enum` can hold. (Mirrors `JniExt`'s `sealed_class`.)
523    ///
524    /// Each payload field crosses as its own wire, chosen by the same policy a
525    /// [`Self::repr_c_struct`] field uses, extended with `String` → `char *`:
526    /// a scalar passes through, a declared [`Self::enum_type`] becomes its C
527    /// enum, a `String` becomes a malloc'd `char *`, and an opaque pointer
528    /// `Option<Box<T>>` / `Box<T>` (with `T` a declared [`Self::opaque_ptr`])
529    /// becomes `*mut t_t`. Anything else is a generation error.
530    ///
531    /// **Ownership.** The union crosses by value, so when any variant's
532    /// payload wire owns memory (`char *`, an opaque pointer) a typed
533    /// `<base>_drop(t_t *)` is generated that frees the **active arm** —
534    /// consistent with the existing typed per-pointer drops. A union whose
535    /// payloads are all plain data needs no drop and gets none.
536    ///
537    /// **Validity.** Crossing **into** Rust, the tag a C caller supplied is
538    /// range-checked before any Rust enum is built — so, exactly like
539    /// [`Self::enum_type`], a union taken by value is a fallible input, and a
540    /// function taking one without a `Result` return needs [`Self::panic`].
541    /// A data struct carrying a union field inherits that fallibility. See the
542    /// module docs for the wire this uses and why.
543    pub fn tagged_union(mut self, ty: syn::Type) -> Self {
544        let key = TypeKey::from_type(&ty);
545        assert!(
546            !self.ignored_types.contains(&key),
547            "Cbindgen::tagged_union cannot declare `{}` because it is already ignored",
548            key
549        );
550        self.tagged_unions.insert(key.clone(), TypeCfg::new(ty));
551        self.current = Some(CurrentDecl::TaggedUnion(key));
552        self
553    }
554
555    /// Declare a callback signature so its `impl Fn(...)` parameters resolve and
556    /// a `#[repr(C)]` closure struct (`{ void *context; call; drop }`) is
557    /// emitted for it. `ty` must be `impl Fn(Args...) + Send + Sync + 'static`.
558    /// Identical signatures share one struct. Sets the declaration cursor, so a
559    /// following `.base_name("...")` sets the base fed to
560    /// [`mangle_callback`](Self::mangle_callback) (else the args' bases drive
561    /// the generated name).
562    pub fn callback(mut self, ty: syn::Type) -> Self {
563        let args = extract_fn_trait_args(&ty).unwrap_or_else(|| {
564            panic!(
565                "Cbindgen::callback expects `impl Fn(Args...) + Send + Sync + 'static`, got `{}`",
566                ty.to_token_stream()
567            )
568        });
569        let key: CallbackKey = args.iter().map(TypeKey::from_type).collect();
570        self.callbacks.insert(key.clone(), CbCfg::new(args));
571        self.current = Some(CurrentDecl::Callback(key));
572        self
573    }
574
575    /// Mark argument `idx` of the **current callback declaration** as a *takeable
576    /// owned pointer*: the C `call` receives `*mut z_x_t` (not by value); the
577    /// callee may take the value (`z_x_take` moves it out, leaving a gravestone) or
578    /// just read it; the trampoline drops it after the call (no-op if taken). The
579    /// arg type must be an inline-opaque type ([`Self::opaque_owned_struct`] /
580    /// [`Self::opaque_data_struct`]). Chain after `.callback(...)` (and any `.base_name(...)`).
581    pub fn takeable_param(mut self, idx: usize) -> Self {
582        match &self.current {
583            Some(CurrentDecl::Callback(key)) => {
584                let key = key.clone();
585                self.callbacks
586                    .get_mut(&key)
587                    .expect("entry vanished")
588                    .takeable
589                    .insert(idx);
590            }
591            other => panic!(
592                "Cbindgen::takeable_param must be chained after a `callback(...)` call, not after {}",
593                describe_current(other)
594            ),
595        }
596        self
597    }
598
599    // ── Internal helpers ───────────────────────────────────────────────
600
601    /// Fully-qualify a bare single-segment source type against
602    /// [`Self::source_module`] (e.g. `ZKeyExpr` → `zenoh_flat::ZKeyExpr`).
603    /// Anything already qualified, or with no `source_module` set, is returned
604    /// unchanged.
605    pub(super) fn src_ty(&self, ty: &syn::Type) -> syn::Type {
606        // Built-in scalar primitives (`f64`, `i32`, …) live in no source module;
607        // qualifying them would produce invalid paths like `zenoh_flat::f64` (hit by
608        // callback args, e.g. `impl Fn(f64)`). Leave them bare.
609        if is_scalar(ty) {
610            return ty.clone();
611        }
612        // Std `String` likewise lives in no source module — qualifying it would
613        // produce `zenoh_flat::String`. It can be declared `opaque_ptr` (a boxed
614        // pointer the C side holds as `string_t *`), so resolve it to the std path.
615        if is_string(ty) {
616            return syn::parse_quote!(::std::string::String);
617        }
618        if let (Some(m), syn::Type::Path(tp)) = (&self.source_module, ty) {
619            if tp.qself.is_none() && tp.path.leading_colon.is_none() && tp.path.segments.len() == 1
620            {
621                let mut path = m.clone();
622                path.segments.push(tp.path.segments[0].clone());
623                return syn::Type::Path(syn::TypePath { qself: None, path });
624            }
625        }
626        ty.clone()
627    }
628
629    /// [`Self::src_ty`] off the **identity** — the type peer of
630    /// [`Self::src_fn`], for the emitters that hold a declared type's key or a
631    /// declaration's own `Origin` rather than a node.
632    ///
633    /// A `TypeKey` is a normalized type, so re-parsing it is the reverse of
634    /// `from_type` and the qualification policy stays in one place: `String`
635    /// still resolves to `::std::string::String` (it can be declared
636    /// `opaque_ptr`), scalars are still left bare, and only a bare
637    /// single-segment path is prefixed.
638    pub(super) fn src_ty_of(&self, key: &TypeKey) -> syn::Type {
639        let spelled: syn::Type = syn::parse_str(key.as_str())
640            .expect("a `TypeKey` is a normalized `syn::Type`, so it re-parses");
641        self.src_ty(&spelled)
642    }
643
644    /// Path to a source function (e.g. `zenoh_flat::z_keyexpr_try_from`).
645    pub(super) fn src_fn(&self, ident: &syn::Ident) -> syn::Path {
646        match &self.source_module {
647            Some(m) => {
648                let mut p = m.clone();
649                p.segments.push(syn::PathSegment::from(ident.clone()));
650                p
651            }
652            None => syn::Path::from(ident.clone()),
653        }
654    }
655
656    /// If `ty` is `&[E]` (a shared slice borrow) whose element `E` is a declared
657    /// **inline-opaque by-value** type ([`Self::repr_c_struct`] /
658    /// [`Self::opaque_data_struct`] / [`Self::opaque_owned_struct`] — all in
659    /// `value_opaque`), return `E`. Such a type's C counterpart is layout-identical
660    /// to the Rust value (size+align asserted by a generated `const _`), so a
661    /// `*const counterpart` block reinterprets to `&[E]` zero-copy — exactly as the
662    /// single-`&E` input converter reinterprets one element. Scalar slices take the
663    /// separate [`scalar_slice_elem`](super::scalar_slice_elem) path; other element
664    /// kinds (e.g. `data_struct`, whose wire copies each field) are unsupported here.
665    pub(super) fn value_opaque_slice_elem(&self, ty: &syn::Type) -> Option<syn::Type> {
666        let syn::Type::Reference(r) = ty else {
667            return None;
668        };
669        if r.mutability.is_some() {
670            return None;
671        }
672        let syn::Type::Slice(s) = &*r.elem else {
673            return None;
674        };
675        let elem = (*s.elem).clone();
676        self.value_opaque
677            .contains_key(&TypeKey::from_type(&elem))
678            .then_some(elem)
679    }
680
681    /// For a `&[E]` callback-argument slice, return `(src_elem, c_elem_wire)`:
682    /// the fully-qualified Rust element type and the C element wire it crosses as
683    /// (a scalar crosses as itself; an inline-opaque `E` crosses as its layout-
684    /// identical counterpart, e.g. `payload_t`). Drives the two-component
685    /// `(*const c_elem_wire, size_t)` lowering of the closure `call` param in
686    /// `prereq_callback_structs` / `dispatch_fn_input`. `None` for non-slice args.
687    pub(super) fn callback_slice_elem_wire(
688        &self,
689        ty: &syn::Type,
690    ) -> Option<(syn::Type, syn::Type)> {
691        if let Some(elem) = self.value_opaque_slice_elem(ty) {
692            let wire = self
693                .value_opaque_ty(&elem)
694                .expect("value_opaque_slice_elem guaranteed a value_opaque element")
695                .clone();
696            return Some((self.src_ty(&elem), wire));
697        }
698        scalar_slice_elem(ty).map(|elem| (elem.clone(), elem))
699    }
700
701    /// [`Self::callback_slice_elem_wire`] off the classification, for the
702    /// resolver side — the declaration side keeps the node peer above, because
703    /// a `.callback(...)` argument is written by the build script and the model
704    /// may never have interned it.
705    pub(super) fn callback_slice_elem_wire_of(
706        &self,
707        ty: &TypeRef,
708    ) -> Option<(syn::Type, syn::Type)> {
709        let elem = super::r_shared_slice_elem(ty)?;
710        let key = elem.key();
711        if let Some(wire) = self.value_opaque_ty_of(&key) {
712            return Some((self.src_ty_of(&key), wire.clone()));
713        }
714        super::scalar_ty(elem).map(|s| (s.clone(), s))
715    }
716
717    /// Like [`Self::src_ty`], but recurses into reference and slice element types so
718    /// `&ZSample` becomes `&zenoh_flat::ZSample` and `&[Payload]` becomes
719    /// `&[perftest_flat::Payload]` (needed so a callback's `Fn(&[E])` closure type
720    /// names the qualified element).
721    /// [`Self::src_ty`], recursing into a borrow's and a slice's element — off
722    /// the classification. `&ZSample` becomes `&zenoh_flat::ZSample` and
723    /// `&[Payload]` becomes `&[perftest_flat::Payload]`, so a callback's
724    /// `Fn(&[E])` closure type names the qualified element.
725    ///
726    /// The two recursing forms are `TypeKind::Ref` and `TypeKind::Slice`, which
727    /// is what the `syn::Type::Reference` / `syn::Type::Slice` match this
728    /// replaces was reading — and the borrow's lifetime and mutability are on
729    /// the kind, so the rebuilt spelling says what the source said.
730    pub(super) fn src_ty_deep_of(&self, ty: &TypeRef) -> syn::Type {
731        match ty.kind() {
732            TypeKind::Ref {
733                lifetime,
734                mutable,
735                inner,
736            } => {
737                let inner = self.src_ty_deep_of(inner);
738                let lt = lifetime.as_ref().map(|l| quote!(#l)).unwrap_or_default();
739                let m = if *mutable { quote!(mut) } else { quote!() };
740                syn::parse_quote!(& #lt #m #inner)
741            }
742            TypeKind::Slice(elem) => {
743                let elem = self.src_ty_deep_of(elem);
744                syn::parse_quote!([#elem])
745            }
746            _ => self.src_ty_of(&ty.key()),
747        }
748    }
749
750    /// [`Self::in_name`] off the **identity**, for a caller holding a reading
751    /// rather than a node — which is every per-field site.
752    pub(super) fn in_name_of(key: &TypeKey) -> syn::Ident {
753        format_ident!("__cbg_in_{}", sanitize(key))
754    }
755
756    /// [`Self::out_name`] off the identity. See [`Self::in_name_of`].
757    pub(super) fn out_name_of(key: &TypeKey) -> syn::Ident {
758        format_ident!("__cbg_out_{}", sanitize(key))
759    }
760
761    /// Config of a declared type (across the opaque/data/enum maps), by key.
762    pub(super) fn type_cfg(&self, key: &TypeKey) -> Option<&TypeCfg> {
763        self.opaque
764            .get(key)
765            .or_else(|| self.data.get(key))
766            .or_else(|| self.value_opaque.get(key).map(|c| &c.cfg))
767            .or_else(|| self.enums.get(key))
768            .or_else(|| self.tagged_unions.get(key))
769    }
770
771    /// The opaque counterpart type of a declared inline-opaque type, if any.
772    pub(super) fn value_opaque_ty(&self, ty: &syn::Type) -> Option<&syn::Type> {
773        self.value_opaque_ty_of(&TypeKey::from_type(ty))
774    }
775
776    /// [`Self::value_opaque_ty`] off the **identity**, for a caller holding a
777    /// reading — which is the whole selector chain.
778    pub(super) fn value_opaque_ty_of(&self, key: &TypeKey) -> Option<&syn::Type> {
779        self.value_opaque.get(key).map(|c| &c.opaque)
780    }
781
782    /// [`Self::value_opaque_slice_elem`] off the classification.
783    pub(super) fn r_value_opaque_slice_elem<'t>(&self, t: &'t TypeRef) -> Option<&'t TypeRef> {
784        super::r_shared_slice_elem(t).filter(|e| self.value_opaque.contains_key(&e.key()))
785    }
786
787    /// Type keys used as a takeable callback parameter (any `.takeable_param(idx)`
788    /// across all declared callbacks). These value_opaque types get a public
789    /// `<base>_take(dst, src)` move function.
790    pub(super) fn takeable_type_keys(&self) -> HashSet<TypeKey> {
791        let mut s = HashSet::new();
792        for (key, cfg) in &self.callbacks {
793            for &idx in &cfg.takeable {
794                if let Some(tk) = key.get(idx) {
795                    s.insert(tk.clone());
796                }
797            }
798        }
799        s
800    }
801
802    /// Public "take" (move) symbol for a takeable value_opaque type:
803    /// [`Self::mangle_take`] over the base, else `<base>_take` (e.g.
804    /// `z_sample_take`). Symmetric with [`Self::destructor_symbol`].
805    pub(super) fn take_symbol(&self, key: &TypeKey) -> syn::Ident {
806        if let Some(f) = &self.mangle_take {
807            return format_ident!("{}", f(&self.rust_base(key)));
808        }
809        format_ident!("{}_take", self.rust_base(key))
810    }
811
812    /// Base token for a Rust type: [`Self::mangle_rust_type`] applied to the Rust
813    /// short name, or the short name verbatim when unset. Feeds the type-name,
814    /// destructor and callback manglers.
815    pub(super) fn rust_base(&self, key: &TypeKey) -> String {
816        if let Some(b) = self.type_cfg(key).and_then(|c| c.base.clone()) {
817            return b;
818        }
819        let short = type_short(key);
820        match &self.mangle_rust_type {
821            Some(f) => f(&short),
822            // No mangler: a C-like `snake_case` default (so destructors/take/type
823            // names read e.g. `sample_drop`, not `Sample_drop`).
824            None => snake_case(&short),
825        }
826    }
827
828    /// Emitted C type name of a declared type: [`Self::mangle_type_name`] over the
829    /// base, else the base (which is the `mangle_rust_type`/`.base_name` token).
830    pub(super) fn c_type_name(&self, key: &TypeKey) -> String {
831        let base = self.rust_base(key);
832        match &self.mangle_type_name {
833            Some(f) => f(&base),
834            None => base,
835        }
836    }
837
838    /// C type identifier (the `#[repr(C)]` struct/enum name + the wire type used
839    /// across converters and wrappers).
840    pub(super) fn c_type_ident(&self, key: &TypeKey) -> syn::Ident {
841        format_ident!("{}", self.c_type_name(key))
842    }
843
844    /// Destructor symbol of an opaque handle: [`Self::mangle_destructor`] over the
845    /// base, else `<base>_drop`.
846    pub(super) fn destructor_symbol(&self, key: &TypeKey) -> syn::Ident {
847        if let Some(f) = &self.mangle_destructor {
848            return format_ident!("{}", f(&self.rust_base(key)));
849        }
850        format_ident!("{}_drop", self.rust_base(key))
851    }
852
853    /// Emitted C type name of a callback's closure struct: [`Self::mangle_callback`]
854    /// over the bases — a `.base_name(...)` override (as the sole base) when set,
855    /// else the args' derived bases — or, with no mangler, a generic default
856    /// (`closure` for zero bases, `closure_<base0>_<base1>…` otherwise). The
857    /// adapter's own default carries no target-language naming convention.
858    pub(super) fn callback_c_name(&self, key: &CallbackKey) -> String {
859        let base_override = self.callbacks.get(key).and_then(|c| c.base.clone());
860        if let Some(f) = &self.mangle_callback {
861            // The override (when set) is the sole base; otherwise the args' bases.
862            let bases: Vec<String> = match &base_override {
863                Some(b) => vec![b.clone()],
864                None => key.iter().map(|k| self.rust_base(k)).collect(),
865            };
866            return f(&bases);
867        }
868        // No mangler: an explicit base is the name as-is; otherwise compose from
869        // the args' bases.
870        if let Some(b) = base_override {
871            return b;
872        }
873        if key.is_empty() {
874            "closure".to_string()
875        } else {
876            let parts: Vec<String> = key.iter().map(|k| self.rust_base(k)).collect();
877            format!("closure_{}", parts.join("_"))
878        }
879    }
880
881    /// C struct identifier for a callback's closure type (see
882    /// [`Self::callback_c_name`]).
883    pub(super) fn callback_c_ident(&self, key: &CallbackKey) -> syn::Ident {
884        format_ident!("{}", self.callback_c_name(key))
885    }
886}
887
888/// Rebuild the canonical `impl Fn(args...) + Send + Sync + 'static` type from an
889/// argument list (matching the source spelling so its [`TypeKey`] round-trips —
890/// see `core::resolve`'s reconstruction).
891pub(super) fn callback_fn_type(args: &[syn::Type]) -> syn::Type {
892    syn::parse_quote!(impl Fn(#(#args),*) + Send + Sync + 'static)
893}
894
895/// Human-readable description of the current declaration, for panic messages.
896fn describe_current(current: &Option<CurrentDecl>) -> String {
897    match current {
898        None => "no declaration".to_string(),
899        Some(CurrentDecl::Ptr(k)) => format!("opaque_ptr `{}`", k.as_str()),
900        Some(CurrentDecl::Data(k)) => format!("data_struct `{}`", k.as_str()),
901        Some(CurrentDecl::ValueOpaque(k)) => format!("value_opaque `{}`", k.as_str()),
902        Some(CurrentDecl::Enum(k)) => format!("enum_type `{}`", k.as_str()),
903        Some(CurrentDecl::TaggedUnion(k)) => format!("tagged_union `{}`", k.as_str()),
904        Some(CurrentDecl::Callback(k)) => {
905            let args: Vec<&str> = k.iter().map(|t| t.as_str()).collect();
906            format!("callback `impl Fn({})`", args.join(", "))
907        }
908        Some(CurrentDecl::Function(i)) => format!("function `{i}`"),
909        Some(CurrentDecl::Convert(k)) => format!("convert `{k}`"),
910    }
911}