prebindgen_registry/decl.rs
1//! Language-neutral declaration vocabulary: the decl objects and constructor
2//! macros a build script uses to describe boundary expansion (`expand_param!`
3//! / `expand_return!`), free functions (`fun!`), and canonical single-value
4//! conversions (`convert!`). Moved down from the JNI adapter (formerly
5//! `api/lang/jnigen`, now the separate `prebindgen-jni` crate) because none of
6//! it is Kotlin/JNI-specific — it only references [`TypeKey`],
7//! [`Origin<syn::Type>`], and plain `syn` types. `prebindgen-jni`'s own
8//! `jni/decl.rs` keeps the genuinely Kotlin-specific declarations
9//! (`ptr_class!`, `enum_class!`, `sealed_class!`, `data_class!`, `constant!`,
10//! `package!`) and re-exports these types from here, so the public
11//! `prebindgen_flat::*` surface is unaffected by the split.
12
13use prebindgen_flat::flat::{Origin, TypeKey};
14use quote::ToTokens;
15
16/// The origin of a type a **build script** wrote.
17///
18/// Real tokens, and deliberately no source position: `SourceLocation::default()`
19/// is the sanctioned placeless location for exactly this — a signature or type a
20/// build script authored was never in a captured file, and `has_position` already
21/// gates what a diagnostic prints for one.
22pub fn declared_origin(ty: syn::Type) -> Origin<syn::Type> {
23 Origin::new(ty, std::rc::Rc::new(prebindgen::SourceLocation::default()))
24}
25
26// ──────────────────────────────────────────────────────────────────────
27// Shared local accumulators (replayed into `Expansions`/`Deconstructors`
28// by the accept logic in `builder.rs` once a decl is handed to `Declarations`)
29// ──────────────────────────────────────────────────────────────────────
30
31/// One arm of an `expand_param!` `.variant*` list (type-level or per-fn).
32#[derive(Clone)]
33pub enum LocalVariant {
34 /// Build via this declared constructor member / constructor fn.
35 Ctor(syn::Ident),
36 /// Accept an already-built value directly.
37 SelfIdentity,
38}
39
40/// One arm of an `expand_return!` `.field*` list (type-level or per-fn). The name is stored raw (`None` = derive at replay time: for a
41/// class-level field, the class member's Kotlin name if the accessor is a
42/// declared member, else `snake_to_camel`; for a per-fn field,
43/// `snake_to_camel`).
44// large_enum_variant: a handful of fields exist per binding, held while
45// declarations replay — boxing the syn payloads would only complicate the
46// arms (same trade-off as `ConvertSourceKind`).
47#[allow(clippy::large_enum_variant)]
48#[derive(Clone)]
49pub enum LocalField {
50 /// Include the named accessor's value as a leaf/field, with an optional
51 /// explicit name override.
52 Named(syn::Ident, Option<String>),
53 /// Include the handle itself as a field.
54 SelfField,
55 /// Include a **custom, locally-defined** accessor's value: any fn the
56 /// binding crate defines, declared with the one binding-local vocabulary
57 /// (`fun!(crate::f).sig(sig!((v: &Self) -> Ret))`) — no `#[prebindgen]`
58 /// item behind it, so the full signature (receiver explicit) is stated.
59 /// `name_override` follows the uniform field-name precedence.
60 Local {
61 path: syn::Path,
62 sig: syn::Signature,
63 name_override: Option<String>,
64 },
65 /// Include **every field of the type's value form** — the struct returned
66 /// by the named accessor — each as its own field. Expands to the same
67 /// records the fields would produce if named one by one; see
68 /// [`ExpandReturnDecl::fields`].
69 Fields(FieldsDecl),
70}
71
72/// Build a [`FunctionDecl`] from a bare function ident or a path:
73///
74/// * `fun!(foo)` — a `#[prebindgen]` fn; its signature is read from the
75/// registry.
76/// * `fun!(crate::foo)` — a **binding-local** fn: any fn the binding crate
77/// defines, exported through the same machinery as a `#[prebindgen]` one.
78/// A path carries no signature to read, so chain
79/// [`.sig(sig!(…))`](crate::FunctionDecl::sig). The generated file
80/// calls it by the declared path (it compiles inside the binding crate,
81/// so `crate::`-rooted paths resolve).
82#[macro_export]
83macro_rules! fun {
84 ($name:ident) => {
85 $crate::FunctionDecl::new($crate::ident!($name))
86 };
87 ($path:path) => {
88 $crate::FunctionDecl::new_local($crate::__macro_support::parse_path(stringify!($path)))
89 };
90}
91
92/// State a binding-local fn's exact Rust signature, with **named parameters**
93/// (they become the foreign-side parameter names): `sig!((s: &Summary,
94/// verbose: bool) -> String)`; the `-> Ret` tail is optional (unit). The
95/// signature argument of [`FunctionDecl::sig`](crate::FunctionDecl::sig)
96/// for a path-built [`fun!`](crate::fun).
97#[macro_export]
98macro_rules! sig {
99 (($($params:tt)*) $(-> $ret:ty)?) => {
100 $crate::__macro_support::parse_signature(stringify!(($($params)*) $(-> $ret)?))
101 };
102}
103
104/// Build a `syn::Type` from a bare Rust type token: `ty!(i32)`. The type
105/// argument of decl methods like [`ConvertSourceDecl::from_type`] — always
106/// yields the concrete `syn::Type`, so no inference context is needed (see
107/// [`ident!`](crate::ident) for the E0283 background).
108#[macro_export]
109macro_rules! ty {
110 ($t:ty) => {
111 $crate::__macro_support::parse_type(stringify!($t))
112 };
113}
114
115/// Build a `syn::Path` from a bare path token: `path!(crate::conv::f)`. The
116/// callable argument of [`FunctionDecl::new_local`] and of an adapter's
117/// `ConstDecl::with`.
118#[macro_export]
119macro_rules! path {
120 ($p:path) => {
121 $crate::__macro_support::parse_path(stringify!($p))
122 };
123}
124
125/// Build a `syn::Expr` from an expression token: `expr!(format!("{A}:{B}"))`.
126/// The initializer argument of an adapter's `ConstDecl::expr` — allowed only
127/// for constants, where the expression binds no arguments.
128#[macro_export]
129macro_rules! expr {
130 ($e:expr) => {
131 $crate::__macro_support::parse_expr(stringify!($e))
132 };
133}
134
135/// Build a [`ConvertDecl`] directly from a bare Rust type:
136/// `convert!(Millis)` is `ConvertDecl::new(<Millis as syn::Type>)`.
137/// See `ptr_class!` for the parsing mechanics.
138#[macro_export]
139macro_rules! convert {
140 ($t:ty) => {
141 $crate::ConvertDecl::new($crate::__macro_support::parse_type(stringify!($t)))
142 };
143}
144
145/// Build a [`ExpandParamDecl`] directly from a bare Rust type:
146/// `expand_param!(KeyExpr)` is `ExpandParamDecl::new(<KeyExpr as syn::Type>)`.
147/// See `ptr_class!` for the parsing mechanics.
148#[macro_export]
149macro_rules! expand_param {
150 ($t:ty) => {
151 $crate::ExpandParamDecl::new($crate::__macro_support::parse_type(stringify!($t)))
152 };
153}
154
155/// Build a [`ConvertSourceDecl`](crate::ConvertSourceDecl) for an
156/// **input** conversion via `core::convert`: `.input(from!(i32))` requires
157/// `i32: Into<T>`. Chain `with` to
158/// use a binding-local callable instead of the trait.
159#[macro_export]
160macro_rules! from {
161 ($t:ty) => {
162 $crate::ConvertSourceDecl::from_type($crate::__macro_support::parse_type(stringify!($t)))
163 };
164}
165
166/// Fallible twin of [`from!`]: `.input(try_from!(i32))` requires
167/// `i32: TryInto<T>`; an `Err` routes to the caller's error handler. With
168/// `with`, the callable returns
169/// `Result` and must state its error type via
170/// `error`.
171#[macro_export]
172macro_rules! try_from {
173 ($t:ty) => {
174 $crate::ConvertSourceDecl::try_from_type($crate::__macro_support::parse_type(stringify!(
175 $t
176 )))
177 };
178}
179
180/// Build a [`ConvertSourceDecl`](crate::ConvertSourceDecl) for an
181/// **output** conversion via `core::convert`: `.output(into!(i32))` requires
182/// `T: Into<i32>`. Chain `with` to
183/// use a binding-local callable instead of the trait.
184#[macro_export]
185macro_rules! into {
186 ($t:ty) => {
187 $crate::ConvertSourceDecl::into_type($crate::__macro_support::parse_type(stringify!($t)))
188 };
189}
190
191/// Fallible twin of [`into!`]: `.output(try_into!(i32))` requires
192/// `T: TryInto<i32>`; an `Err` routes to the caller's error handler. With
193/// `with`, the callable returns
194/// `Result` and must state its error type via
195/// `error`.
196#[macro_export]
197macro_rules! try_into {
198 ($t:ty) => {
199 $crate::ConvertSourceDecl::try_into_type($crate::__macro_support::parse_type(stringify!(
200 $t
201 )))
202 };
203}
204
205/// Build a [`ExpandReturnDecl`] directly from a bare Rust type:
206/// `expand_return!(Sample)` is `ExpandReturnDecl::new(<Sample as syn::Type>)`.
207/// See `ptr_class!` for the parsing mechanics.
208#[macro_export]
209macro_rules! expand_return {
210 ($t:ty) => {
211 $crate::ExpandReturnDecl::new($crate::__macro_support::parse_type(stringify!($t)))
212 };
213}
214
215/// Build a [`FieldsDecl`] from the ident of a **value-form accessor** —
216/// `fields!(sample_to_struct)` is `FieldsDecl::new(prebindgen_registry::ident!(sample_to_struct))`.
217/// The argument of [`ExpandReturnDecl::fields`](crate::ExpandReturnDecl::fields).
218#[macro_export]
219macro_rules! fields {
220 ($name:ident) => {
221 $crate::FieldsDecl::new($crate::ident!($name))
222 };
223}
224
225// ──────────────────────────────────────────────────────────────────────
226// Boundary decls — how a declared type crosses the FFI boundary by default
227// ──────────────────────────────────────────────────────────────────────
228
229/// Declares a type's **default input boundary**: how a parameter of this type
230/// may be supplied, as a list of *variants* — "built from this constructor's
231/// ingredients, OR that one's, OR passed as an existing handle". Applies to
232/// every function with a parameter of the type; a single function opts out or
233/// narrows via [`FunctionDecl::expand_param`].
234///
235/// Build one with [`expand_param!`](crate::expand_param), add arms with
236/// [`variant`](Self::variant) / [`variant_self`](Self::variant_self), and hand
237/// it to the adapter's `expand` declaration.
238///
239/// **Generated shape** — at the wire tier this is a selector dispatch: with
240/// more than one arm the parameter crosses as a selector `Int` plus one
241/// nullable slot per arm (`keyExprSel: Int, keyExpr0: String?,
242/// keyExpr1: KeyExpr?`), and the raw call site passes `(0, "key", null)`-style
243/// tuples. That selector form is always emitted; the wrapper's generated KDoc
244/// shape-notes document the exact slots per function.
245///
246/// **Splittability (checked)** — a multi-variant declaration must be
247/// *splittable*: its arms must surface as **distinct JVM signatures**, so a
248/// function can request idiomatic typed **overloads** on top of the selector
249/// form (`f(key: String, …)` / `f(key: KeyExpr, …)`) via
250/// [`FunctionDecl::split_on_param`](crate::fun). This is verified up front —
251/// two arms with the same erased parameter types are a hard build error.
252/// [`.no_split()`](Self::no_split) suppresses that check for a variant set that
253/// will only ever be used as the selector form. The type-level declaration
254/// itself emits no overloads; emission is per-function via `.split_on_param`.
255/// The selector form always stays public, so consumers can also add their own
256/// same-named overloads by hand.
257///
258/// The type does **not** have to be declared in any package. A boundary decl
259/// on an undeclared type makes it **rust-side-only**: the value is always
260/// built from its ingredients at the boundary and never materializes in
261/// Kotlin — no class, no handle, nothing to `close()`. The one restriction is
262/// structural: [`variant_self`](Self::variant_self) hard-errors for such a
263/// type, since there is no Kotlin object to pass.
264///
265/// ```
266/// // A KeyExpr param accepts EITHER a String (built via keyexpr_new_try_from)
267/// // OR an existing KeyExpr handle:
268/// let _ = prebindgen_registry::expand_param!(KeyExpr)
269/// .variant(prebindgen_registry::fun!(keyexpr_new_try_from))
270/// .variant_self();
271/// ```
272#[derive(Clone)]
273pub struct ExpandParamDecl {
274 key: TypeKey,
275 /// The type this declaration was **written with** — the `X` the macro
276 /// received. Kept because the declaration is where it came from: recovering
277 /// it later *from* the key was reasoning backwards from an identity (#291).
278 rust_type: Origin<syn::Type>,
279 variants: Vec<LocalVariant>,
280 /// `.no_split()` — suppress the proactive splittability check for this
281 /// variant set (it will only ever be used as the selector form). See
282 /// [`Self::no_split`].
283 no_split: bool,
284}
285
286impl ExpandParamDecl {
287 pub fn new(rust_type: syn::Type) -> Self {
288 Self {
289 key: TypeKey::from_type(&rust_type),
290 rust_type: declared_origin(rust_type),
291 variants: Vec::new(),
292 no_split: false,
293 }
294 }
295
296 /// The type identity this declaration is registered under.
297 pub fn key(&self) -> &TypeKey {
298 &self.key
299 }
300
301 /// The type this declaration was written with, as originally parsed.
302 pub fn rust_type(&self) -> &Origin<syn::Type> {
303 &self.rust_type
304 }
305
306 /// The declared build-from / existing-handle arms, in declaration order.
307 /// `pub(crate)`, not `pub` — [`LocalVariant`] itself is `pub(crate)`
308 /// (a public fn cannot return a private type).
309 pub fn variants(&self) -> &[LocalVariant] {
310 &self.variants
311 }
312
313 /// Whether `.no_split()` was declared (suppresses the splittability
314 /// check). Named `is_no_split` rather than `no_split` — that name is
315 /// already the builder method that sets the flag ([`Self::no_split`]).
316 pub fn is_no_split(&self) -> bool {
317 self.no_split
318 }
319
320 /// Add a **build-from** arm: parameters of this type also carry the
321 /// named `#[prebindgen]` constructor's inputs on the wire, and Rust
322 /// builds the value in the same call. E.g. `keyexpr_new_try_from(&str)`
323 /// gives every function taking a `KeyExpr` a String-carrying arm —
324 /// as a selector + nullable slot at the wire tier (see the type-level
325 /// docs for the exact generated shape), not as a Kotlin overload.
326 ///
327 /// A variant arm only *names* the constructor: no Kotlin surface of its
328 /// own, so a decorated `fun!` (`.name()` / expand overrides) is a hard
329 /// error rather than a silent discard.
330 pub fn variant(mut self, ctor: FunctionDecl) -> Self {
331 assert!(
332 ctor.kotlin_name_override.is_none()
333 && ctor.param_expands.is_empty()
334 && ctor.return_expand.is_none(),
335 "expand_param!({}).variant(fun!({})): a variant arm only names the \
336 `#[prebindgen]` constructor — .name()/expand overrides don't apply",
337 self.key.as_str(),
338 ctor.rust_ident
339 );
340 assert!(
341 ctor.local.is_none(),
342 "expand_param!({}).variant(fun!(…::{f})): a variant arm only NAMES a fn — \
343 declare the binding-local fn via .fun/.method/.constructor/convert! first, \
344 then reference it here by ident: fun!({f})",
345 self.key.as_str(),
346 f = ctor.rust_ident
347 );
348 self.variants.push(LocalVariant::Ctor(ctor.rust_ident));
349 self
350 }
351
352 /// Add the **existing-handle** arm: also accept an already-built value.
353 /// On its own this is simply the default (a bare handle), so declaring it
354 /// alone changes nothing; it earns its place next to build variants.
355 pub fn variant_self(mut self) -> Self {
356 self.variants.push(LocalVariant::SelfIdentity);
357 self
358 }
359
360 /// **Suppress the splittability check.** A multi-variant expansion is
361 /// verified up front to be *splittable* (its arms surface as distinct JVM
362 /// signatures) so that [`FunctionDecl::split_on_param`](crate::fun) can emit
363 /// idiomatic typed overloads. `.no_split()` opts this variant set out of
364 /// that check — declare it when two arms genuinely share a JVM signature and
365 /// you only ever want the selector form (a function that then tries to
366 /// `.split_on_param` such a parameter gets the concrete ambiguity error).
367 ///
368 /// A no-op on a single-variant declaration (nothing to check).
369 pub fn no_split(mut self) -> Self {
370 self.no_split = true;
371 self
372 }
373}
374
375/// Declares a type's **default output boundary**: wherever the type is
376/// returned or handed to a callback, it is decomposed into this set of
377/// *fields*, all delivered in one FFI crossing — instead of an opaque handle
378/// the caller must then query field by field with more JNI calls. Applies to
379/// every function returning the type; a single function opts out or replaces
380/// the set via [`FunctionDecl::expand_return`].
381///
382/// Build one with [`expand_return!`](crate::expand_return), add fields with
383/// [`field`](Self::field) / [`field_self`](Self::field_self), and hand it to
384/// the adapter's `expand` declaration.
385///
386/// The type does **not** have to be declared in any package. A boundary decl
387/// on an undeclared type makes it **rust-side-only**: every returned /
388/// callback-delivered / `Result`-error value of it is decomposed into these
389/// fields and the value itself never reaches Kotlin. This is the natural
390/// shape for an error type consumed by the `onError` channel — no dead
391/// Kotlin class is emitted. Restrictions for such a type:
392/// [`field_self`](Self::field_self) hard-errors (there is no Kotlin object to
393/// deliver), and field names cannot inherit from class members (there are
394/// none) — use `.name(...)` on each field or accept the camel-cased default.
395///
396/// ```
397/// // A returned Sample crosses as { payload, kind } in one call:
398/// let _ = prebindgen_registry::expand_return!(Sample)
399/// .field(prebindgen_registry::fun!(sample_get_payload))
400/// .field(prebindgen_registry::fun!(sample_get_kind));
401/// ```
402
403#[derive(Clone)]
404pub struct ExpandReturnDecl {
405 key: TypeKey,
406 /// The type this declaration was **written with** — the `X` the macro
407 /// received. Kept because the declaration is where it came from: recovering
408 /// it later *from* the key was reasoning backwards from an identity (#291).
409 rust_type: Origin<syn::Type>,
410 fields: Vec<LocalField>,
411}
412
413impl ExpandReturnDecl {
414 pub fn new(rust_type: syn::Type) -> Self {
415 Self {
416 key: TypeKey::from_type(&rust_type),
417 rust_type: declared_origin(rust_type),
418 fields: Vec::new(),
419 }
420 }
421
422 /// The type identity this declaration is registered under.
423 pub fn key(&self) -> &TypeKey {
424 &self.key
425 }
426
427 /// The type this declaration was written with, as originally parsed.
428 pub fn rust_type(&self) -> &Origin<syn::Type> {
429 &self.rust_type
430 }
431
432 /// The declared field records, in declaration order. Named `field_list`
433 /// rather than `fields` — that name is already the builder method that
434 /// appends a value-form ([`Self::fields`]). `pub(crate)`, not `pub` —
435 /// [`LocalField`] itself is `pub(crate)` (a public fn cannot return a
436 /// private type).
437 pub fn field_list(&self) -> &[LocalField] {
438 &self.fields
439 }
440
441 /// Add one field — a reader whose value crosses as this leaf:
442 ///
443 /// * `fun!(f)` — a `#[prebindgen]` reader (`f(&Self) -> Field`), its
444 /// signature read from the registry.
445 /// * `fun!(crate::f).sig(sig!((v: &Self) -> Field))` — a **custom,
446 /// locally-defined** reader: any fn the binding crate defines, its
447 /// signature stated (the receiver explicit — it must take `&Self`).
448 /// One use among many: conditional delivery, an `Option<&Self>` return
449 /// becoming a nullable handle leaf that is null when the binding-side
450 /// predicate declines.
451 ///
452 /// The Kotlin field name is uniform for both: an explicit `.name(...)`
453 /// on the `fun!`; else the Kotlin name of the class member if the same
454 /// fn is also declared as a method on this type's class (so a getter
455 /// that is both a method and a field is named once); else the
456 /// camel-cased fn ident (a path's LAST segment).
457 ///
458 /// Only the accessor's name is used here: expand overrides on the `fun!`
459 /// are a hard error rather than a silent discard (the field's own
460 /// decomposition comes from ITS type's boundary decl, not from the
461 /// accessor).
462 pub fn field(mut self, accessor: FunctionDecl) -> Self {
463 self.reject_beside_consuming("field(..)");
464 assert!(
465 accessor.param_expands.is_empty() && accessor.return_expand.is_none(),
466 "expand_return!({}).field(fun!({})): expand overrides don't apply to a \
467 field accessor — only .name() is honored",
468 self.key.as_str(),
469 accessor.rust_ident
470 );
471 self.fields.push(match accessor.local {
472 None => LocalField::Named(accessor.rust_ident, accessor.kotlin_name_override),
473 Some((path, sig)) => {
474 let Some(sig) = sig else {
475 panic!(
476 "expand_return!({}).field(fun!({p})): a binding-local field states \
477 its accessor's signature — chain .sig(sig!((v: &{k}) -> Ret))",
478 self.key.as_str(),
479 p = quote::quote!(#path),
480 k = self.key.as_str()
481 );
482 };
483 LocalField::Local {
484 path,
485 sig,
486 name_override: accessor.kotlin_name_override,
487 }
488 }
489 });
490 self
491 }
492
493 /// Include the **handle itself** among the fields, so the consumer gets a
494 /// live, closeable object in addition to the read-out values (e.g. a
495 /// `Query` delivered with its fields *and* the handle it needs to reply).
496 /// Declare it **last**, after any field that decomposes a nested handle,
497 /// so the generated Rust moves the value only after those borrows.
498 pub fn field_self(mut self) -> Self {
499 self.reject_beside_consuming("field_self()");
500 self.fields.push(LocalField::SelfField);
501 self
502 }
503
504 /// The one rule [`Self::fields_self_into`] adds: it hands the value **itself**
505 /// over, so nothing else in the decl can still read it.
506 fn reject_beside_consuming(&self, what: &str) {
507 if let Some(f) = self.fields.iter().find_map(|f| match f {
508 LocalField::Fields(d) if d.consuming => Some(&d.func),
509 _ => None,
510 }) {
511 panic!(
512 "expand_return!({k}).fields_self_into(fields!({f})).{what}: `.fields_self_into(..)` hands \
513 the value ITSELF over as its fields, so nothing else can read it afterwards — \
514 it must be the decl's only record. Use `.fields(fields!(..))` with the \
515 borrowing form of the accessor if you need both.",
516 k = self.key.as_str(),
517 f = f,
518 );
519 }
520 }
521
522 /// Take the fields from the type's **value form** — a `#[prebindgen]`
523 /// accessor returning "this type's own accessors gathered into one struct"
524 /// — instead of restating them.
525 ///
526 /// `.fields(fields!(f))` is exactly `.field(...)` applied to each field of
527 /// that struct, so it has the same configurability (per-field overrides and
528 /// renames live on the [`FieldsDecl`]) and, crucially, the same
529 /// decomposition rule: **each field crosses by its own type's default
530 /// output boundary**. A field whose type has its own `expand_return!` is
531 /// decomposed by it (a `KeyExpr` field still crosses as its string, not as
532 /// a handle); a declared `data_class!` field expands into its fields; a
533 /// field behind `Option` / `Vec` stays one leaf. So swapping a hand-written
534 /// field list for `.fields(...)` keeps the boundary shape it already had —
535 /// what changes is that the list can no longer drift from the struct.
536 ///
537 /// ```
538 /// // Instead of restating SampleStruct's fields one by one:
539 /// let _ = prebindgen_registry::expand_return!(Sample)
540 /// .fields(prebindgen_registry::fields!(sample_to_struct));
541 /// ```
542 ///
543 /// The accessor **borrows** its receiver (`f(v: &Self) -> SelfStruct`):
544 /// the struct is built from a borrow, so each field is cloned into it and
545 /// the leaves clone again out of it, and the value survives. It therefore
546 /// mixes freely — `.fields(...).field_self()` delivers the value form's
547 /// fields *and* the live handle. At most one value form per decl.
548 ///
549 /// Where the value is delivered **owned** — a callback argument
550 /// (`impl Fn(Sample)`), an owned return — and nothing else needs it, use
551 /// [`fields_self_into`](Self::fields_self_into) instead: those clones are being paid
552 /// on a value that is about to be dropped.
553 pub fn fields(mut self, decl: FieldsDecl) -> Self {
554 self.reject_beside_consuming("fields(..)");
555 self.reject_second_value_form(&decl);
556 self.fields.push(LocalField::Fields(decl));
557 self
558 }
559
560 /// Like [`fields`](Self::fields), but the accessor **consumes** its
561 /// receiver (`f(v: Self) -> SelfStruct`): the value is moved in and each
562 /// field is moved *out* into its leaf. No clones at all.
563 ///
564 /// This is the same decision [`field_self`](Self::field_self) makes, one
565 /// step further: `.field_self()` hands the value over whole,
566 /// `.fields_self_into(...)` hands *the value itself* over as its parts, and
567 /// `.fields(...)` hands over a copy of its parts. Use it wherever the value
568 /// arrives owned and is not needed afterwards — the hot receive path this
569 /// whole declarator exists to make cheap.
570 ///
571 /// ```
572 /// let _ = prebindgen_registry::expand_return!(Sample)
573 /// .fields_self_into(prebindgen_registry::fields!(sample_into_struct));
574 /// ```
575 ///
576 /// Because it gives the value away it must be the decl's **only** record —
577 /// a `.field_self()` or a sibling `.field(...)` would read a value that is
578 /// gone — which is a declaration-time panic either way round. It may still
579 /// be reached through *another* value form: the parent's field is handed to
580 /// it by move, since a hoisted value form is an owned struct and its fields
581 /// are disjoint.
582 ///
583 /// The declarator and the accessor's signature must agree; naming a
584 /// `&Self` accessor here (or a by-value one on [`fields`](Self::fields)) is
585 /// an error, so the declared intent cannot drift from the function it
586 /// names. At a **borrowed** delivery position there is no value to give up,
587 /// so the emitter clones once up front and consumes the clone — the same
588 /// cost the borrowing form would have paid, which keeps one declaration
589 /// usable by both owned and `&T` returns of the type.
590 pub fn fields_self_into(mut self, decl: FieldsDecl) -> Self {
591 self.reject_second_value_form(&decl);
592 assert!(
593 self.fields.is_empty(),
594 "expand_return!({k}).fields_self_into(fields!({f})): `.fields_self_into(..)` hands the value \
595 ITSELF over as its fields, so it must be the decl's only record — the records \
596 already declared would read a value that is gone. Use `.fields(fields!(..))` with \
597 the borrowing form of the accessor if you need both.",
598 k = self.key.as_str(),
599 f = decl.func,
600 );
601 self.fields.push(LocalField::Fields(decl.consuming()));
602 self
603 }
604
605 fn reject_second_value_form(&self, decl: &FieldsDecl) {
606 assert!(
607 !self
608 .fields
609 .iter()
610 .any(|f| matches!(f, LocalField::Fields(_))),
611 "expand_return!({}): the decl already expands a value form (fields!({})) — \
612 one value form states the whole field set",
613 self.key.as_str(),
614 decl.func
615 );
616 }
617}
618
619/// A **value-form expansion**: the accessor whose returned struct supplies the
620/// fields, plus the per-field adjustments. Built with
621/// [`fields!`](crate::fields) and handed to
622/// [`ExpandReturnDecl::fields`].
623///
624/// Both adjusters key on the **Rust struct field name**, mirroring
625/// [`FunctionDecl::expand_param`]'s Rust-parameter-name key: an unknown field
626/// name or a repeated one is a hard error, so a field renamed upstream is
627/// caught rather than silently ignored.
628#[derive(Clone)]
629pub struct FieldsDecl {
630 func: syn::Ident,
631 overrides: Vec<(String, ExpandReturnDecl)>,
632 names: Vec<(String, String)>,
633 /// Set by [`ExpandReturnDecl::fields_self_into`] — the accessor consumes its
634 /// receiver. Declared rather than read off the signature, because giving
635 /// the value away is a boundary decision; the two are cross-checked when
636 /// the records are resolved.
637 consuming: bool,
638}
639
640impl FieldsDecl {
641 pub fn new(func: syn::Ident) -> Self {
642 Self {
643 func,
644 overrides: Vec::new(),
645 names: Vec::new(),
646 consuming: false,
647 }
648 }
649
650 pub(crate) fn consuming(mut self) -> Self {
651 self.consuming = true;
652 self
653 }
654
655 /// The value-form accessor's ident, as declared with [`fields!`](crate::fields).
656 pub fn func(&self) -> &syn::Ident {
657 &self.func
658 }
659
660 /// The per-field decomposition overrides, in declaration order.
661 pub fn overrides(&self) -> &[(String, ExpandReturnDecl)] {
662 &self.overrides
663 }
664
665 /// The per-field name overrides, in declaration order.
666 pub fn names(&self) -> &[(String, String)] {
667 &self.names
668 }
669
670 /// Whether the accessor consumes its receiver (set by
671 /// [`ExpandReturnDecl::fields_self_into`]). Named `is_consuming` rather
672 /// than `consuming` — that name is already the crate-internal builder
673 /// method that sets the flag.
674 pub fn is_consuming(&self) -> bool {
675 self.consuming
676 }
677
678 /// Replace **one** field's decomposition, with the same
679 /// [`ExpandReturnDecl`] a type-level default uses — so the complete-set
680 /// rule applies here too: the decl states that field's entire leaf set.
681 /// Use it where the field's type default is not what this boundary wants
682 /// (a lone `.field_self()` keeps the raw handle instead of decomposing it).
683 ///
684 /// An override declaring **no** records states an empty leaf set: the field
685 /// **does not cross**. That is the one way to drop a field a value form
686 /// carries, and it follows from the complete-set rule rather than adding a
687 /// rule — a boundary that wants none of a field's leaves says so the same
688 /// way it says it wants some of them. (A *generator-level*
689 /// `expand_return!(T)` with no records is still an error: a type has to
690 /// cross somehow.) Use it for a field the binding has no surface for —
691 /// diagnostics a consumer never reads, a type whose accessors would drag in
692 /// a subtree nothing asks for:
693 ///
694 /// ```ignore
695 /// prebindgen_registry::expand_return!(Sample).fields_self_into(
696 /// prebindgen_registry::fields!(sample_into_struct)
697 /// .field("timestamp_stack", prebindgen_registry::expand_return!(TimestampStack)),
698 /// );
699 /// ```
700 pub fn field(mut self, field: impl AsRef<str>, decl: ExpandReturnDecl) -> Self {
701 let field = field.as_ref().to_string();
702 assert!(
703 !self.overrides.iter().any(|(f, _)| *f == field),
704 "fields!({}).field(\"{}\", ...): field already has an override — declare its \
705 complete field set in ONE decl",
706 self.func,
707 field
708 );
709 self.overrides.push((field, decl));
710 self
711 }
712
713 /// Rename **one** field's leaf, overriding the name derived from the struct
714 /// field ident. The literal Kotlin name, like `fun!(f).name(...)`.
715 pub fn name(mut self, field: impl AsRef<str>, kotlin_name: impl Into<String>) -> Self {
716 let field = field.as_ref().to_string();
717 let kotlin_name = kotlin_name.into();
718 assert!(
719 !self.names.iter().any(|(f, _)| *f == field),
720 "fields!({}).name(\"{}\", ...): field is already renamed",
721 self.func,
722 field
723 );
724 // The derived names of inlined nested fields are joined with `"__"`, so
725 // an author name carrying one would forge a nesting that isn't there.
726 // (Core rejects it for a `.field()` name; here the name never reaches
727 // that check, so it is made at the point of declaration.)
728 assert!(
729 !kotlin_name.contains("__"),
730 "fields!({}).name(\"{}\", \"{}\"): `__` is the reserved chain separator \
731 and cannot appear in a leaf name",
732 self.func,
733 field,
734 kotlin_name,
735 );
736 self.names.push((field, kotlin_name));
737 self
738 }
739}
740
741/// Unifies the two boundary decls into one type so an adapter's `expand` can
742/// expose a single entry point — the boundary-decl peer of its class
743/// declarator.
744/// Deliberately **no** `impl From<syn::Type> for ExpandDecl` — a bare
745/// `syn::Type` alone doesn't say which direction it describes, so every
746/// declaration names its direction via the matching constructor macro:
747/// `.expand(prebindgen_registry::expand_param!(Summary)...)`,
748/// `.expand(prebindgen_registry::expand_return!(Sample)...)`.
749pub enum ExpandDecl {
750 Param(ExpandParamDecl),
751 Return(ExpandReturnDecl),
752}
753
754impl From<ExpandParamDecl> for ExpandDecl {
755 fn from(d: ExpandParamDecl) -> Self {
756 Self::Param(d)
757 }
758}
759impl From<ExpandReturnDecl> for ExpandDecl {
760 fn from(d: ExpandReturnDecl) -> Self {
761 Self::Return(d)
762 }
763}
764
765// ──────────────────────────────────────────────────────────────────────
766// Function decl
767// ──────────────────────────────────────────────────────────────────────
768
769/// Declares one `#[prebindgen]` function to export. The adapter either adds it
770/// to a package or attaches it to a class as a method or a factory.
771///
772/// Build it from a bare Rust name with [`fun!`](crate::fun) and chain
773/// [`name`](Self::name) to set its Kotlin name.
774/// [`expand_param`](Self::expand_param) / [`expand_return`](Self::expand_return)
775/// **override, for this one function**, the boundary defaults its
776/// parameter/return types declare at the generator level
777/// — using the very same decl objects, so the complete-set rule is identical
778/// at both scopes.
779pub struct FunctionDecl {
780 rust_ident: syn::Ident,
781 kotlin_name_override: Option<String>,
782 param_expands: Vec<(String, ExpandParamDecl)>,
783 return_expand: Option<ExpandReturnDecl>,
784 split_on_params: Vec<String>,
785 /// `fun!(crate::f)` — a **binding-local** fn: the declared path plus the
786 /// stated signature ([`sig`](Self::sig), required by acceptance time).
787 /// `None` = an ordinary `#[prebindgen]` registry fn.
788 local: Option<(syn::Path, Option<syn::Signature>)>,
789}
790
791impl FunctionDecl {
792 pub fn new(rust_ident: syn::Ident) -> Self {
793 Self {
794 rust_ident,
795 kotlin_name_override: None,
796 param_expands: Vec::new(),
797 return_expand: None,
798 split_on_params: Vec::new(),
799 local: None,
800 }
801 }
802
803 /// The Rust-side fn ident (the path's last segment for a binding-local fn).
804 pub fn rust_ident(&self) -> &syn::Ident {
805 &self.rust_ident
806 }
807
808 /// The explicit Kotlin-side name, if `.name(...)` was declared.
809 pub fn kotlin_name_override(&self) -> &Option<String> {
810 &self.kotlin_name_override
811 }
812
813 /// The per-parameter expand overrides declared with `.expand_param(...)`.
814 pub fn param_expands(&self) -> &[(String, ExpandParamDecl)] {
815 &self.param_expands
816 }
817
818 /// The return expand override declared with `.expand_return(...)`, if any.
819 pub fn return_expand(&self) -> &Option<ExpandReturnDecl> {
820 &self.return_expand
821 }
822
823 /// The parameters named with `.split_on_param(...)`, in declaration order.
824 pub fn split_on_params(&self) -> &[String] {
825 &self.split_on_params
826 }
827
828 /// The binding-local fn's declared path and stated signature, if this is
829 /// a `fun!(crate::f)` rather than an ordinary `#[prebindgen]` registry fn.
830 pub fn local(&self) -> &Option<(syn::Path, Option<syn::Signature>)> {
831 &self.local
832 }
833
834 /// Consume `self` into its raw fields, by value. Not a plain accessor —
835 /// the one call site (`accept_fn_expands` in the JNI builder) destructures
836 /// a whole `FunctionDecl` it owns to move each field (`Vec`s, the `local`
837 /// signature) onward without cloning, so a reference-returning accessor
838 /// won't do.
839 #[allow(clippy::type_complexity)]
840 pub fn into_parts(
841 self,
842 ) -> (
843 syn::Ident,
844 Option<String>,
845 Vec<(String, ExpandParamDecl)>,
846 Option<ExpandReturnDecl>,
847 Vec<String>,
848 Option<(syn::Path, Option<syn::Signature>)>,
849 ) {
850 (
851 self.rust_ident,
852 self.kotlin_name_override,
853 self.param_expands,
854 self.return_expand,
855 self.split_on_params,
856 self.local,
857 )
858 }
859
860 /// `fun!(crate::f)` — declare a **binding-local** fn by path. The fn
861 /// ident (the path's last segment) names it everywhere a registry fn's
862 /// ident would; chain [`sig`](Self::sig) to state its signature.
863 pub fn new_local(path: syn::Path) -> Self {
864 assert!(
865 path.segments.len() >= 2,
866 "fun!({}): a binding-local fn is called QUALIFIED from the generated file — \
867 give at least a `crate::`-rooted path (a bare ident declares a `#[prebindgen]` fn)",
868 quote::quote!(#path)
869 );
870 let ident = path.segments.last().expect("non-empty path").ident.clone();
871 Self {
872 local: Some((path, None)),
873 ..Self::new(ident)
874 }
875 }
876
877 /// State a binding-local fn's exact Rust signature (build it with
878 /// [`sig!`](crate::sig)) — a path carries no signature to read. The
879 /// parameter names become the foreign-side parameter names. Required for
880 /// a path-built [`fun!`](crate::fun); a hard error on a registry fn
881 /// (its signature is read from the registry).
882 pub fn sig(mut self, signature: syn::Signature) -> Self {
883 let Some((_, slot)) = &mut self.local else {
884 panic!(
885 "fun!({}).sig(...): a `#[prebindgen]` fn's signature is read from the \
886 registry — .sig() applies to path-built binding-local fns (fun!(crate::f))",
887 self.rust_ident
888 );
889 };
890 assert!(
891 slot.is_none(),
892 "fun!({}).sig(...): the signature is already stated",
893 self.rust_ident
894 );
895 *slot = Some(signature);
896 self
897 }
898
899 /// Set the Kotlin-side name. Default: the Rust name camel-cased
900 /// (`session_declare_publisher` → `sessionDeclarePublisher`).
901 pub fn name(mut self, kotlin_name: impl Into<String>) -> Self {
902 self.kotlin_name_override = Some(kotlin_name.into());
903 self
904 }
905
906 /// Override, for the named parameter of this function only, how that
907 /// parameter is supplied — with the same [`ExpandParamDecl`] a type-level
908 /// default uses, so the **complete-set rule** applies here too: the decl
909 /// states the entire variant set for this param (a lone `.variant_self()`
910 /// = "only a ready-made handle", replacing the type's build variants —
911 /// e.g. *un*-declaring a key expression needs the handle, not a string).
912 ///
913 /// `param` is the Rust parameter name; the decl's type is cross-checked
914 /// against that parameter's (peeled) type at generation time — an unknown
915 /// parameter or a type mismatch is a hard error. Call again with a
916 /// different `param` to override several parameters independently;
917 /// declaring the same parameter twice is a hard error.
918 pub fn expand_param(mut self, param: impl AsRef<str>, decl: ExpandParamDecl) -> Self {
919 let param = param.as_ref().to_string();
920 assert!(
921 !self.param_expands.iter().any(|(p, _)| *p == param),
922 "fun!({}).expand_param(\"{}\", ...): parameter already has an expand override — \
923 declare each parameter's complete variant set in ONE decl",
924 self.rust_ident,
925 param
926 );
927 self.param_expands.push((param, decl));
928 self
929 }
930
931 /// **Emit idiomatic typed Kotlin overloads for this parameter.** By default
932 /// a multi-variant expanded parameter crosses only as the selector tuple
933 /// (`expectedSel: Int, expected0: …, expected1: …`). `.split_on_param("p")`
934 /// additionally emits, alongside the selector wrapper, one typed overload
935 /// per variant of `p` — `f(count: Long, total: Double, …)` for a
936 /// `summary_new(count, total)` arm, `f(expected: Summary, …)` for
937 /// `variant_self()` — each delegating to the selector form.
938 ///
939 /// The parameter's variant set must be *splittable* (its arms surface as
940 /// distinct JVM signatures) — enforced up front on the
941 /// [`expand_param!`](crate::expand_param) declaration unless it opted out
942 /// with [`.no_split()`](ExpandParamDecl::no_split).
943 ///
944 /// Call again for **several** parameters: the generated overloads are then
945 /// the **cartesian product** of the named parameters' arms. That concrete
946 /// product must have no two combinations sharing a JVM signature — a hard
947 /// build error if it does.
948 ///
949 /// An `Option<…>` parameter splits through its **single-leaf** arms only
950 /// (nullable-arm rule): the overload keeps the arm's nullable type and
951 /// `null` selects absence — `f(encoding: Encoding?, …)` for a
952 /// `variant_self()` arm of an `Option<&Encoding>` parameter. Multi-leaf
953 /// arms stay selector-only; an optional parameter with no single-leaf arm
954 /// is a hard error.
955 ///
956 /// `param` is the Rust parameter name; it must be an expanded,
957 /// multi-variant parameter of this function (unknown / single-variant /
958 /// recursively-built ⇒ a hard error). Declaring the same parameter twice
959 /// is a hard error.
960 pub fn split_on_param(mut self, param: impl AsRef<str>) -> Self {
961 let param = param.as_ref().to_string();
962 assert!(
963 !self.split_on_params.contains(¶m),
964 "fun!({}).split_on_param(\"{}\"): parameter is already split",
965 self.rust_ident,
966 param
967 );
968 self.split_on_params.push(param);
969 self
970 }
971
972 /// Override this function's return decomposition — with the same
973 /// [`ExpandReturnDecl`] a type-level default uses, stating the complete
974 /// field set (a lone `.field_self()` = the raw whole value, which for a
975 /// borrowed `&T` / `Option<&T>` return crosses by cloning into a fresh
976 /// owned handle). The decl's type is cross-checked against the function's
977 /// (peeled) return type at generation time — a mismatch is a hard error.
978 /// At most one per function.
979 pub fn expand_return(mut self, decl: ExpandReturnDecl) -> Self {
980 assert!(
981 self.return_expand.is_none(),
982 "fun!({}).expand_return(...): the function already has a return expand override — \
983 declare the complete field set in ONE decl",
984 self.rust_ident
985 );
986 self.return_expand = Some(decl);
987 self
988 }
989}
990
991// ──────────────────────────────────────────────────────────────────────
992// Convert decl — the canonical single-value conversion for a type
993// ──────────────────────────────────────────────────────────────────────
994
995/// Declares a type's **canonical single-value conversion**: how one value of
996/// the type crosses the boundary wherever a single value is needed — as a
997/// parameter or return, inside `Option<_>` / `Vec<_>` / the `Result<T, E>`
998/// success position, as a `data_class` field. Each direction takes one
999/// [`ConvertSourceDecl`]:
1000///
1001/// ```rust,ignore
1002/// .convert(convert!(Millis)
1003/// .input(fun!(millis_from_long)) // fn(u64) -> Millis (wire → rust)
1004/// .output(fun!(millis_value)) // fn(&Millis) -> u64 (rust → wire)
1005/// .valid_range(0u64..=86_400_000)) // rejects invalid values; Option uses a niche
1006/// .convert(convert!(Celsius).input(from!(i32)).output(into!(i32)))
1007/// .convert(convert!(Label)
1008/// .input(try_from!(String).with(path!(crate::label_in)).error(ty!(String)))
1009/// .output(into!(String).with(path!(crate::label_out))))
1010/// ```
1011///
1012/// The foreign surface derives from the conversion's other-side type
1013/// (`u64` ⇒ Kotlin `ULong` / C `uint64_t`) — nothing is stated verbatim.
1014/// [`valid_range`](ConvertDecl::valid_range) and
1015/// [`valid_values`](ConvertDecl::valid_values) declare the legal subset of a
1016/// scalar representation. Generated converters validate the subset, and
1017/// adapters may reuse values outside it as allocation-free `Option`/`Result`
1018/// markers; [`exclude_values`](ConvertDecl::exclude_values) reserves holes in
1019/// an otherwise legal domain. A `try_` source's `Err`
1020/// routes to the caller's error handler. Conversion fns may live in the flat
1021/// crate or in a **helper crate** whose item stream is chained into the same
1022/// [`prebindgen_flat::Flat::builder`] parse; generated calls qualify each
1023/// function with its origin crate.
1024///
1025/// Distinct from the [`expand_param!`](crate::expand_param) /
1026/// [`expand_return!`](crate::expand_return) boundary decls: those reshape a
1027/// **function boundary** into multiple leaves (variants in / fields out),
1028/// while `convert!` defines the type's one-value form used everywhere else.
1029/// A type may declare both — expansion wins at the fn boundaries where it is
1030/// declared; the conversion serves every other position. The method names
1031/// differ deliberately: converters are direction-things ([`input`](ConvertDecl::input)
1032/// also serves callback returns, [`output`](ConvertDecl::output) also serves
1033/// callback arguments), while expansion decls are position-things.
1034/// One direction's conversion **source** — where the conversion code comes
1035/// from, the lowered form of a [`ConvertSourceDecl`].
1036// large_enum_variant: a handful of these exist per binding, held once in the
1037// builder — boxing the syn payloads would only complicate the decl arms.
1038#[allow(clippy::large_enum_variant)]
1039#[derive(Clone)]
1040pub enum ConvertSpec {
1041 /// A `#[prebindgen]` fn (flat or helper crate): the representable type
1042 /// and fallibility are read from its registry signature at lookup time.
1043 PrebindgenFn(syn::Ident),
1044 /// A `core::convert` trait impl; the representable type is stated
1045 /// explicitly (there is no signature to read). `fallible` selects
1046 /// `TryInto` (the associated `Error` routes to the caller's error
1047 /// handler) vs `Into`.
1048 Trait { repr: syn::Type, fallible: bool },
1049}
1050
1051impl ConvertSpec {
1052 /// One-line human description of the source kind (report use).
1053 pub fn describe(&self) -> String {
1054 match self {
1055 ConvertSpec::PrebindgenFn(f) => format!("`#[prebindgen]` fn `{f}`"),
1056 ConvertSpec::Trait {
1057 repr,
1058 fallible: false,
1059 } => format!("`Into` ⇄ `{}`", repr.to_token_stream()),
1060 ConvertSpec::Trait {
1061 repr,
1062 fallible: true,
1063 } => format!("`TryInto` ⇄ `{}`", repr.to_token_stream()),
1064 }
1065 }
1066}
1067
1068/// Which direction a [`ConvertSourceDecl`] was built for. The constructor
1069/// macro states it (`from!`/`try_from!` = into-Rust, `into!`/`try_into!` =
1070/// out-of-Rust) and the acceptor cross-checks it, so a chain like
1071/// `.output(from!(i32))` is a hard error instead of a silent misread.
1072#[derive(Clone, Copy, PartialEq)]
1073pub(crate) enum ConvertDirection {
1074 Input,
1075 Output,
1076}
1077
1078impl ConvertDirection {
1079 fn macros(self) -> &'static str {
1080 match self {
1081 ConvertDirection::Input => "from!/try_from!",
1082 ConvertDirection::Output => "into!/try_into!",
1083 }
1084 }
1085}
1086
1087/// One conversion source, accepted by [`ConvertDecl::input`] /
1088/// [`ConvertDecl::output`]. Built by [`fun!`](crate::fun) — a
1089/// `#[prebindgen]` conversion fn (bare ident, signature read from the
1090/// registry) or a **binding-local** one (`fun!(crate::f)` +
1091/// [`.sig(sig!(…))`](FunctionDecl::sig), the one vocabulary for locally
1092/// defined callables; a `Result<_, E>` return states the error channel) —
1093/// or by the direction-stating macros [`from!`](crate::from) /
1094/// [`try_from!`](crate::try_from) / [`into!`](crate::into) /
1095/// [`try_into!`](crate::try_into) (a `core::convert` **trait** conversion
1096/// with a stated representation type).
1097#[derive(Clone)]
1098pub struct ConvertSourceDecl {
1099 kind: ConvertSourceKind,
1100}
1101
1102// large_enum_variant: a handful of these exist per binding, held transiently
1103// while a decl is built — boxing the syn payloads would only complicate the
1104// arms (same trade-off as `ConvertSpec`).
1105#[allow(clippy::large_enum_variant)]
1106#[derive(Clone)]
1107pub(crate) enum ConvertSourceKind {
1108 /// `fun!(f)` / `fun!(crate::f).sig(…)` — a conversion fn; representable
1109 /// type and fallibility are read from its signature (registry, or the
1110 /// stated one carried in `local` and synthesized before scanning).
1111 Fun {
1112 ident: syn::Ident,
1113 local: Option<(syn::Path, syn::Signature)>,
1114 },
1115 /// `from!`/`try_from!`/`into!`/`try_into!` — a stated representation
1116 /// type, converted via the `core::convert` trait.
1117 Repr {
1118 direction: ConvertDirection,
1119 fallible: bool,
1120 ty: syn::Type,
1121 },
1122}
1123
1124impl ConvertSourceDecl {
1125 fn repr(direction: ConvertDirection, fallible: bool, ty: syn::Type) -> Self {
1126 Self {
1127 kind: ConvertSourceKind::Repr {
1128 direction,
1129 fallible,
1130 ty,
1131 },
1132 }
1133 }
1134 /// `from!(T)` — input via `T: Into<Self>`.
1135 pub fn from_type(ty: syn::Type) -> Self {
1136 Self::repr(ConvertDirection::Input, false, ty)
1137 }
1138 /// `try_from!(T)` — input via `T: TryInto<Self>`.
1139 pub fn try_from_type(ty: syn::Type) -> Self {
1140 Self::repr(ConvertDirection::Input, true, ty)
1141 }
1142 /// `into!(T)` — output via `Self: Into<T>`.
1143 pub fn into_type(ty: syn::Type) -> Self {
1144 Self::repr(ConvertDirection::Output, false, ty)
1145 }
1146 /// `try_into!(T)` — output via `Self: TryInto<T>`.
1147 pub fn try_into_type(ty: syn::Type) -> Self {
1148 Self::repr(ConvertDirection::Output, true, ty)
1149 }
1150}
1151
1152impl From<FunctionDecl> for ConvertSourceDecl {
1153 fn from(decl: FunctionDecl) -> Self {
1154 assert!(
1155 decl.kotlin_name_override.is_none()
1156 && decl.param_expands.is_empty()
1157 && decl.return_expand.is_none(),
1158 "fun!({}) as a conversion source: a conversion fn is never surfaced in \
1159 Kotlin — .name()/expand overrides don't apply",
1160 decl.rust_ident
1161 );
1162 let local = decl.local.map(|(path, sig)| {
1163 let Some(sig) = sig else {
1164 panic!(
1165 "fun!({p}) as a conversion source: a binding-local fn states its \
1166 signature — chain .sig(sig!((params) -> Ret))",
1167 p = quote::quote!(#path)
1168 );
1169 };
1170 (path, sig)
1171 });
1172 Self {
1173 kind: ConvertSourceKind::Fun {
1174 ident: decl.rust_ident,
1175 local,
1176 },
1177 }
1178 }
1179}
1180
1181#[derive(Clone)]
1182pub struct ConvertDecl {
1183 key: TypeKey,
1184 /// The type this declaration was **written with** — the `X` the macro
1185 /// received. Kept because the declaration is where it came from: recovering
1186 /// it later *from* the key was reasoning backwards from an identity (#291).
1187 rust_type: Origin<syn::Type>,
1188 input: Option<ConvertSpec>,
1189 output: Option<ConvertSpec>,
1190 domain: Option<crate::RepresentationDomain>,
1191 /// Binding-local fn sources declared on this convert (`fun!(crate::f)
1192 /// .sig(…)`): drained into [`Declarations::local_fns`] at acceptance so the
1193 /// synthesis pre-pass covers them.
1194 locals: Vec<(syn::Ident, syn::Path, syn::Signature)>,
1195}
1196
1197impl ConvertDecl {
1198 /// `: input …, output …` suffix for the report's conversions section.
1199 pub fn describe_sources(&self) -> String {
1200 let mut parts = Vec::new();
1201 if let Some(i) = &self.input {
1202 parts.push(format!("input {}", i.describe()));
1203 }
1204 if let Some(o) = &self.output {
1205 parts.push(format!("output {}", o.describe()));
1206 }
1207 if parts.is_empty() {
1208 String::new()
1209 } else {
1210 format!(": {}", parts.join(", "))
1211 }
1212 }
1213
1214 pub fn new(rust_type: syn::Type) -> Self {
1215 reject_builtin_convert_type(&TypeKey::from_type(&rust_type));
1216 Self {
1217 key: TypeKey::from_type(&rust_type),
1218 rust_type: declared_origin(rust_type),
1219 input: None,
1220 output: None,
1221 domain: None,
1222 locals: Vec::new(),
1223 }
1224 }
1225
1226 /// The type identity this declaration is registered under.
1227 pub fn key(&self) -> &TypeKey {
1228 &self.key
1229 }
1230
1231 /// The type this declaration was written with, as originally parsed.
1232 pub fn rust_type(&self) -> &Origin<syn::Type> {
1233 &self.rust_type
1234 }
1235
1236 /// The declared **into-Rust** conversion source, if any. Named
1237 /// `input_spec` rather than `input` — that name is already the builder
1238 /// method that declares it ([`Self::input`]).
1239 pub fn input_spec(&self) -> &Option<ConvertSpec> {
1240 &self.input
1241 }
1242
1243 /// The declared **out-of-Rust** conversion source, if any. Named
1244 /// `output_spec` rather than `output` — that name is already the builder
1245 /// method that declares it ([`Self::output`]).
1246 pub fn output_spec(&self) -> &Option<ConvertSpec> {
1247 &self.output
1248 }
1249
1250 /// The declared representation-domain restriction, if any.
1251 pub fn domain(&self) -> &Option<crate::RepresentationDomain> {
1252 &self.domain
1253 }
1254
1255 /// Binding-local fn sources declared on this convert, drained into the
1256 /// synthesis pre-pass at acceptance.
1257 pub fn locals(&self) -> &[(syn::Ident, syn::Path, syn::Signature)] {
1258 &self.locals
1259 }
1260
1261 /// Mutable access for draining binding-local fn sources into the
1262 /// synthesis pre-pass at acceptance (`Vec::append`).
1263 pub fn locals_mut(&mut self) -> &mut Vec<(syn::Ident, syn::Path, syn::Signature)> {
1264 &mut self.locals
1265 }
1266
1267 fn set_input(mut self, spec: ConvertSpec) -> Self {
1268 assert!(
1269 self.input.is_none(),
1270 "convert!({}): the input conversion is already declared — \
1271 declare each direction's conversion in ONE .input()/.output() call",
1272 self.key.as_str()
1273 );
1274 self.input = Some(spec);
1275 self
1276 }
1277
1278 fn set_output(mut self, spec: ConvertSpec) -> Self {
1279 assert!(
1280 self.output.is_none(),
1281 "convert!({}): the output conversion is already declared — \
1282 declare each direction's conversion in ONE .input()/.output() call",
1283 self.key.as_str()
1284 );
1285 self.output = Some(spec);
1286 self
1287 }
1288
1289 fn check_repr(&self, method: &str, repr: &syn::Type) {
1290 assert!(
1291 TypeKey::from_type(repr) != self.key,
1292 "convert!({k}).{method}: the representable type must differ from `{k}` itself",
1293 k = self.key.as_str()
1294 );
1295 }
1296
1297 /// Lower an accepted [`ConvertSourceDecl`] to the internal spec,
1298 /// cross-checking the source's stated direction against the acceptor. A
1299 /// binding-local fn source records its `(ident, path, sig)` in
1300 /// [`Self::locals`] for the synthesis pre-pass — after which it lowers
1301 /// exactly like a `#[prebindgen]` fn source.
1302 fn spec_of(
1303 &mut self,
1304 direction: ConvertDirection,
1305 method: &str,
1306 src: ConvertSourceDecl,
1307 ) -> ConvertSpec {
1308 match src.kind {
1309 ConvertSourceKind::Fun { ident, local } => {
1310 if let Some((path, sig)) = local {
1311 self.locals.push((ident.clone(), path, sig));
1312 }
1313 ConvertSpec::PrebindgenFn(ident)
1314 }
1315 ConvertSourceKind::Repr {
1316 direction: stated,
1317 fallible,
1318 ty,
1319 } => {
1320 assert!(
1321 stated == direction,
1322 "convert!({k}).{method}(...): the source was built with {got} — \
1323 an {method} conversion is built with {want}",
1324 k = self.key.as_str(),
1325 got = stated.macros(),
1326 want = direction.macros(),
1327 );
1328 self.check_repr(method, &ty);
1329 ConvertSpec::Trait { repr: ty, fallible }
1330 }
1331 }
1332 }
1333
1334 /// The **into-Rust** conversion (parameters, callback returns): how a
1335 /// value of this type is built from its representation. Accepts
1336 /// [`fun!`](crate::fun) (a `#[prebindgen]` `fn(U) -> T` /
1337 /// `fn(U) -> Result<T, E>`) or [`from!`](crate::from) /
1338 /// [`try_from!`](crate::try_from) (`Repr: Into<T>` / `TryInto`, or a
1339 /// binding-local callable via `.with(...)`).
1340 pub fn input(mut self, src: impl Into<ConvertSourceDecl>) -> Self {
1341 let spec = self.spec_of(ConvertDirection::Input, "input", src.into());
1342 self.set_input(spec)
1343 }
1344
1345 /// The **out-of-Rust** conversion (returns, callback arguments): how a
1346 /// value of this type is turned into its representation. Accepts
1347 /// [`fun!`](crate::fun) (a `#[prebindgen]` `fn(&T) -> U` / `fn(T) -> U`)
1348 /// or [`into!`](crate::into) / [`try_into!`](crate::try_into)
1349 /// (`T: Into<Repr>` / `TryInto`, or a binding-local callable via
1350 /// `.with(...)`).
1351 pub fn output(mut self, src: impl Into<ConvertSourceDecl>) -> Self {
1352 let spec = self.spec_of(ConvertDirection::Output, "output", src.into());
1353 self.set_output(spec)
1354 }
1355
1356 /// Restrict the scalar representation to a numeric range. Values outside
1357 /// the range are rejected and may be reused by wrappers such as `Option`.
1358 /// Floating-point ranges reject every NaN; use [`Self::valid_values`] when
1359 /// exact raw IEEE values (including a specific NaN payload) are intended.
1360 pub fn valid_range<T, R>(mut self, range: R) -> Self
1361 where
1362 T: crate::DomainScalar,
1363 R: ::core::ops::RangeBounds<T>,
1364 {
1365 assert!(
1366 self.domain.is_none(),
1367 "convert!({}): the representation domain is already declared",
1368 self.key.as_str()
1369 );
1370 self.domain = Some(crate::RepresentationDomain::range(range));
1371 self
1372 }
1373
1374 /// Restrict the scalar representation to a finite valid-value set. Float
1375 /// membership uses raw IEEE bits, so `0.0`, `-0.0`, and NaN payloads remain
1376 /// distinct.
1377 pub fn valid_values<T>(mut self, values: impl IntoIterator<Item = T>) -> Self
1378 where
1379 T: crate::DomainScalar,
1380 {
1381 assert!(
1382 self.domain.is_none(),
1383 "convert!({}): the representation domain is already declared",
1384 self.key.as_str()
1385 );
1386 self.domain = Some(crate::RepresentationDomain::values(values));
1387 self
1388 }
1389
1390 /// Remove finite values from the previously declared base domain. Float
1391 /// exclusions use raw IEEE bits.
1392 pub fn exclude_values<T>(mut self, values: impl IntoIterator<Item = T>) -> Self
1393 where
1394 T: crate::DomainScalar,
1395 {
1396 self.domain
1397 .as_mut()
1398 .unwrap_or_else(|| {
1399 panic!(
1400 "convert!({}): .exclude_values(...) requires .valid_range(...) \
1401 or .valid_values(...) first",
1402 self.key.as_str()
1403 )
1404 })
1405 .exclude(values);
1406 self
1407 }
1408}
1409
1410/// Rejects a `convert!` declaration on a Rust **builtin** type: builtins
1411/// already have their own converters, and the generated calls would try to
1412/// qualify the builtin with a crate path. Wrap the builtin in a source-crate
1413/// newtype (like `Millis(u64)`) instead.
1414fn reject_builtin_convert_type(key: &TypeKey) {
1415 const BUILTINS: &[&str] = &[
1416 "usize", "isize", "u8", "u16", "u32", "u64", "u128", "i8", "i16", "i32", "i64", "i128",
1417 "f32", "f64", "bool", "char", "str", "String",
1418 ];
1419 assert!(
1420 !BUILTINS.contains(&key.as_str()),
1421 "convert!({}): builtins already have converters — wrap the builtin in a newtype instead",
1422 key.as_str()
1423 );
1424}
1425
1426/// Bare-ident type `__JniErr` — the generated file's alias for the
1427/// `prebindgen-jni` crate's `JniBindingError` framework type. Built-in
1428/// converters use this as their `Result<…, _>` error type so their bodies'
1429/// `<__JniErr as From<String>>::from(...)` calls keep compiling. A
1430/// `Result<T, E>` return instead binds its own raw `E` (see
1431/// `JniGenBuilder::lookup_output`); the extern's `Err` arm funnels both to the
1432/// per-call `signal_error` sink via `E: Display`.
1433/// The origin-module prefix of a binding-local fn's declared path
1434/// (`crate::sub::f` → `"crate::sub"`). Paths are validated ≥2 segments at
1435/// decl time (`fun!` path arm / `FieldDecl::with`), so the prefix is
1436/// always non-empty.
1437pub fn local_path_prefix(path: &syn::Path) -> String {
1438 path.segments
1439 .iter()
1440 .take(path.segments.len() - 1)
1441 .map(|s| s.ident.to_string())
1442 .collect::<Vec<_>>()
1443 .join("::")
1444}