Skip to main content

prebindgen_registry/registry/
declare.rs

1//! Build a registry: say what the binding contains, then close it.
2//!
3//! Every declaring method here records; none derives. The builder is a passive
4//! recorder precisely so it never has to call back into a generator to find out
5//! what it is meant to produce — and it is a *separate type* from [`Registry`]
6//! so that "still being described" and "finished, and answerable" cannot be
7//! confused for one another.
8
9use std::collections::{HashMap, HashSet};
10
11use super::*;
12
13/// A registry under construction.
14///
15/// Chain the declarations, hand over the conversions, then [`build`](Self::build):
16///
17/// ```ignore
18/// let registry = Registry::builder(flat)?
19///     .export(&name)
20///     .decompose(decompositions)
21///     .convert_with(|crossing, built, emit| my_gen.convert(crossing, built, emit))?
22///     .build()?;
23/// ```
24///
25/// The result is read-only. Nothing can add a crossing to a `Registry`, which
26/// is what makes "every crossing has a conversion" a fact about the type rather
27/// than a phase you have to be careful about.
28pub struct RegistryBuilder<M> {
29    registry: Registry<M>,
30    /// Conversions handed over so far, applied at [`Self::build`].
31    built: HashMap<Crossing, TypeEntry<M>>,
32    /// The scan runs once, on demand: it needs every declaration, and
33    /// [`Self::crossings`] / [`Self::convert_with`] / [`Self::build`] each need
34    /// it to have run. `Some` holds the derived demand, in order.
35    order: Option<Vec<Crossing>>,
36}
37
38impl<M> Registry<M> {
39    /// Start describing a binding over this model.
40    ///
41    /// A `Flat` is what a registry projects, and reading captured prebindgen
42    /// output into one is [`FlatBuilder`](prebindgen_flat::flat::FlatBuilder)'s job
43    /// — so a build script says where items come from at the layer that owns
44    /// the question, and there is one such layer rather than two:
45    ///
46    /// ```
47    /// # prebindgen::Source::init_doctest_simulate();
48    /// use prebindgen_registry::{Flat, Registry};
49    ///
50    /// let flat = Flat::builder().source("source_ffi").build()?;
51    /// // Annotated only because nothing here resolves: in a build script `M` is
52    /// // fixed by the adapter passed to `resolve`, so no call site names it.
53    /// let registry: Registry<()> = Registry::builder(flat)?.build()?;
54    /// assert!(registry.flat().function("test_function").is_some());
55    /// # Ok::<_, Box<dyn std::error::Error>>(())
56    /// ```
57    ///
58    /// Several sources compose there too, including one this crate renames:
59    ///
60    /// ```ignore
61    /// let flat = Flat::builder()
62    ///     .source(flat_crate::PREBINDGEN_OUT_DIR)
63    ///     .source_named(helpers::PREBINDGEN_OUT_DIR, "helpers")
64    ///     .build()?;
65    /// ```
66    ///
67    /// **Fails on anything the language cannot express** — a `self` receiver, an
68    /// `async fn`, a generic binder, a type form outside the grammar, or a
69    /// reference to a type the flat API does not declare. All of them at once, so
70    /// a source crate that needs migrating sees one list instead of one rebuild
71    /// per item. This is independent of what any binding declares: an
72    /// inexpressible item is a hard error whether or not it is ever named.
73    pub fn builder(flat: prebindgen_flat::flat::Flat) -> Result<RegistryBuilder<M>, ScanError> {
74        let entries: Vec<NotExpressibleEntry> = flat
75            .unsupported()
76            .map(|u| NotExpressibleEntry {
77                name: u.name.clone(),
78                reason: u.error.to_string(),
79                location: (*u.origin.location).clone(),
80            })
81            .collect();
82        if !entries.is_empty() {
83            return Err(ScanError::NotExpressible { entries });
84        }
85
86        let mut registry = Registry::empty();
87        registry.flat = flat;
88        Ok(RegistryBuilder {
89            registry,
90            built: HashMap::new(),
91            order: None,
92        })
93    }
94}
95
96impl<M> RegistryBuilder<M> {
97    // ── configure: what this binding builds ───────────────────────────
98    //
99    // Pushed in by the generator before `resolve`. The registry never asks —
100    // it records, then derives the crossing set from what it was given.
101
102    /// An element this binding **exports**.
103    ///
104    /// The model says how to derive its crossings, so the caller does not: a
105    /// function's signature gives its parameters (in) and its return (out); a
106    /// const gives its value type (out). A name matching no element is an
107    /// error, reported with every other missing name at once by `resolve`
108    /// rather than here — a build script with three typos should learn all
109    /// three in one build.
110    pub fn export(mut self, name: &syn::Ident) -> Self {
111        self.registry.declared.functions.insert(name.clone());
112        self
113    }
114
115    /// A const this binding exports.
116    ///
117    /// Separate from [`Self::export`] only because *having a const mechanism at
118    /// all* is itself a fact: a binding that never calls this re-emits every
119    /// captured const verbatim, while one that calls it emits exactly what it
120    /// names. See [`Self::declares_consts`].
121    pub fn export_const(mut self, name: &syn::Ident) -> Self {
122        self.registry
123            .declared
124            .consts
125            .get_or_insert_with(HashSet::new)
126            .insert(name.clone());
127        self
128    }
129
130    /// Declare that this binding has a const mechanism, even if it exports no
131    /// consts. Without it every captured const is re-emitted verbatim.
132    pub fn declares_consts(mut self) -> Self {
133        self.registry
134            .declared
135            .consts
136            .get_or_insert_with(HashSet::new);
137        self
138    }
139
140    /// A type this binding **exports**: it crosses in both directions, and its
141    /// body — a struct's fields, an enum's payloads — is scanned too.
142    ///
143    /// Takes the **type the declaration was written with**, like its sibling
144    /// [`Self::cross`], and derives the key here. It used to take the key alone,
145    /// which meant the scan had to recover tokens *from* the key to intern the
146    /// type and to diagnose its spelling — reasoning backwards from an identity
147    /// to a thing that already existed. A build script wrote `ptr_class!(Foo)`;
148    /// this is that `Foo` (#291).
149    ///
150    /// Declaring the same type twice keeps the **first** spelling. That is what
151    /// the `HashSet` this replaced did with the identity, and what
152    /// `register_class` does with a reopened declarator: the two spellings agree
153    /// on identity by construction, so the tie-break only decides which
154    /// equivalent rendering the scan reads, and it should not depend on
155    /// declaration order.
156    pub fn export_type(mut self, ty: Origin<syn::Type>) -> Self {
157        self.registry.declared.types.entry(ty.key()).or_insert(ty);
158        self
159    }
160
161    /// A type that **crosses** in one direction without being exported.
162    ///
163    /// The escape hatch for a crossing no signature can yield: a re-exported
164    /// foreign type named by a class declaration, or the value type of a
165    /// constant the binding synthesizes. Direction is explicit because these
166    /// are genuinely one-sided — which is what stops an output-only crossing
167    /// from silently lacking its input twin, the asymmetry the old
168    /// `required_output_types` had.
169    pub fn cross(mut self, dir: Direction, ty: &syn::Type) -> Self {
170        self.registry.declared.crossings.push((dir, ty.clone()));
171        self
172    }
173
174    /// `from`'s conversion needs `on`'s to exist first.
175    ///
176    /// [`Self::crossings`] derives its order from the type structure, which
177    /// covers almost everything: an `Option<T>` visibly contains a `T`. It
178    /// cannot see a dependency the *declaration* creates — a `convert!` whose
179    /// body chains through a helper function's parameter type, say, where
180    /// nothing about the target type mentions the other side.
181    ///
182    /// State those here, and the order accounts for them. Getting it wrong is
183    /// not silent: the conversion that needed the missing one simply cannot be
184    /// built, and [`Self::build`] names it.
185    pub fn depends(mut self, from: Crossing, on: Crossing) -> Self {
186        self.registry.declared.edges.push((from, on));
187        self
188    }
189
190    /// A function this binding **references but never emits** — a helper whose
191    /// name appears in a declaration. Its absence is an error; its presence
192    /// emits nothing.
193    pub fn reference(mut self, name: &syn::Ident) -> Self {
194        self.registry.declared.helper_functions.insert(name.clone());
195        self
196    }
197
198    /// A function the **binding crate itself** defines, with the module path
199    /// generated calls should qualify it by.
200    ///
201    /// There is no `#[prebindgen]` item behind it, so this is the one input
202    /// that adds to the model rather than selecting from it: only the
203    /// signature is read, never the body. A name colliding with a captured
204    /// item is an error — the generated call would resolve the wrong function.
205    pub fn local_function(
206        mut self,
207        item_fn: syn::ItemFn,
208        origin: String,
209    ) -> Result<Self, ScanError> {
210        let ident = item_fn.sig.ident.clone();
211        // Written by hand in a build script, so the grammar is checked here or
212        // nowhere: a dropped `self` receiver would surface as an arity mismatch
213        // out of rustc on generated code, which is the wrong end of the pipeline
214        // to learn about a build.rs typo.
215        let lowered = self
216            .registry
217            .flat
218            .lower_signature(&item_fn)
219            .map_err(|error| ScanError::AdapterInvariant {
220                message: format!("binding-local fn `{ident}`: {error}"),
221            })?;
222        if self.registry.flat.element(&ident).is_some() {
223            return Err(ScanError::AdapterInvariant {
224                message: format!(
225                    "binding-local fn `{ident}` collides with a `#[prebindgen]` item — \
226                     the generated call would resolve the wrong fn; rename the \
227                     binding-local fn"
228                ),
229            });
230        }
231        self.registry.flat.add_local_function(lowered, origin);
232        Ok(self)
233    }
234
235    /// A function a decomposition reaches through rather than emits — excluded
236    /// from constructor composition, and the only functions a decomposer record
237    /// may name.
238    ///
239    /// Rides here until decompositions carry their own shape (step 2 of #251);
240    /// it is a property of the decomposition, not of the binding.
241    pub fn accessor(mut self, name: &syn::Ident) -> Self {
242        self.registry.declared.accessors.insert(name.clone());
243        self
244    }
245
246    /// The receiver type of a function emitted as a method. Same temporary
247    /// home as [`Self::accessor`].
248    pub fn method_receiver(mut self, name: &syn::Ident, receiver: TypeKey) -> Self {
249        self.registry
250            .declared
251            .method_receivers
252            .insert(name.clone(), receiver);
253        self
254    }
255
256    /// How this binding's composites cross **in pieces** instead of whole.
257    ///
258    /// Stated once, before [`Self::build`]. Replaces five separate callbacks
259    /// the registry used to make into the generator; see [`Decompositions`].
260    pub fn decompose(mut self, d: Decompositions) -> Self {
261        self.registry.declared.decompositions = d;
262        self
263    }
264}
265
266impl<M> RegistryBuilder<M> {
267    /// The model being described. Complete from the first call: everything that
268    /// adds to it ([`Self::local_function`]) is a declaration, not a derivation.
269    pub fn flat(&self) -> &prebindgen_flat::flat::Flat {
270        &self.registry.flat
271    }
272
273    /// Module paths of every ingested source, ingestion order.
274    ///
275    /// A model question, and the model is complete from the first call — so the
276    /// builder answers it exactly as the finished registry does.
277    pub fn all_source_modules(&self) -> Vec<syn::Path> {
278        self.registry.all_source_modules()
279    }
280
281    /// The origin crate's module path for an item — see
282    /// [`Registry::origin_module`].
283    pub fn origin_module(&self, ident: &syn::Ident) -> Option<syn::Path> {
284        self.registry.origin_module(ident)
285    }
286
287    /// The default module for references with no recorded origin — see
288    /// [`Registry::default_module`].
289    pub fn default_module(&self) -> Option<syn::Path> {
290        self.registry.default_module()
291    }
292
293    /// Every **named** item the model holds — see
294    /// [`Registry::named_item_idents`].
295    pub fn named_item_idents(&self) -> impl Iterator<Item = &syn::Ident> {
296        self.registry.named_item_idents()
297    }
298
299    /// Whether the source declares a type under this name — see
300    /// `Registry::declares_type`.
301    #[cfg(test)]
302    pub(crate) fn declares_type(&self, ident: &syn::Ident) -> bool {
303        self.registry.declares_type(ident)
304    }
305
306    /// Run the scan and apply the decompositions, once.
307    ///
308    /// Private and idempotent: three entry points need it to have happened, and
309    /// none of them should care whether it already did.
310    fn derive(&mut self) -> Result<&[Crossing], WriteRustError> {
311        if self.order.is_none() {
312            let mut declared = std::mem::take(&mut self.registry.declared);
313            let out = (|| {
314                self.registry.scan_declared_items(&declared)?;
315                self.registry.apply_adapter_plans(&mut declared)
316            })();
317            self.registry.declared = declared;
318            out?;
319            self.order = Some(self.registry.crossings());
320        }
321        Ok(self.order.as_deref().unwrap_or_default())
322    }
323
324    /// What a conversion — or a validation — is written against right now: the
325    /// model, the full crossing population, and whatever has been built so far.
326    fn view(&self) -> Building<'_, M> {
327        Building::new(
328            &self.registry,
329            &self.built,
330            self.order.as_deref().unwrap_or_default(),
331        )
332    }
333
334    /// Check this binding against a generator's own invariants, now that the
335    /// scan has read every declared signature.
336    ///
337    /// Earliest it can run: a missing declaration has already hard-errored, so
338    /// a check here sees only items that exist.
339    pub fn validate_with<E>(mut self, adapter: &E) -> Result<Self, WriteRustError>
340    where
341        E: Prebindgen<Metadata = M>,
342    {
343        self.derive()?;
344        adapter
345            .validate(&self.view())
346            .map_err(|message| ScanError::AdapterInvariant { message })?;
347        Ok(self)
348    }
349
350    /// Every crossing this binding needs a conversion for, **inner types
351    /// first** — see [`Self::crossings`] for what the order guarantees.
352    ///
353    /// Take this when you want to drive the loop yourself and hand the result
354    /// back through [`Self::conversions`]. [`Self::convert_with`] is the same
355    /// walk with the loop written for you.
356    pub fn crossings(&mut self) -> Result<Vec<Crossing>, WriteRustError> {
357        Ok(self.derive()?.to_vec())
358    }
359
360    /// Build a conversion for each crossing, in dependency order.
361    ///
362    /// `f` is called once per crossing with the conversions already built, so
363    /// by the time it sees `Option<Handle>` it can look up `Handle`. Returning
364    /// `None` records a gap — whether that gap matters is decided by
365    /// [`Self::build`], not here.
366    ///
367    /// This is a convenience over [`Self::crossings`] + [`Self::conversions`],
368    /// not a second mechanism: it is the same list, walked in the same order.
369    /// Nothing about it lets the registry choose when to call back — the
370    /// closure is yours, and the walk is finished before this returns.
371    pub fn convert_with<F>(mut self, mut f: F) -> Result<Self, WriteRustError>
372    where
373        F: FnMut(
374            &Crossing,
375            &Building<'_, M>,
376            &prebindgen_flat::Emit,
377        ) -> Option<crate::prebindgen::ConverterImpl<M>>,
378    {
379        // A converter IS generated Rust — `ConverterImpl::function` is a
380        // complete `syn::ItemFn` the adapter writes — so this closure is an
381        // emission callback and is handed the capability, exactly as the
382        // `on_*` ones are. See `prebindgen_flat::flat::emit`.
383        let emit = prebindgen_flat::Emit::new();
384        let order = self.derive()?.to_vec();
385        for crossing in &order {
386            let conv = f(crossing, &self.view(), &emit);
387            if let Some(c) = conv {
388                self.built
389                    .insert(crossing.clone(), TypeEntry::from_converter(c));
390            }
391        }
392        Ok(self)
393    }
394
395    /// Hand over conversions built elsewhere — the bulk peer of
396    /// [`Self::convert_with`], for a generator that walked
397    /// [`Self::crossings`] itself.
398    ///
399    /// Accumulates, so it composes with `convert_with` and with itself.
400    pub fn conversions(mut self, conversions: HashMap<Crossing, TypeEntry<M>>) -> Self {
401        self.built.extend(conversions);
402        self
403    }
404
405    /// The scanned registry, with no conversions applied and no completeness
406    /// check.
407    ///
408    /// Test-only, and deliberately so: it is the state between "described" and
409    /// "answerable", which is exactly what the split exists to keep out of
410    /// everyone else's hands.
411    #[cfg(test)]
412    pub(crate) fn scanned(mut self) -> Result<Registry<M>, ScanError> {
413        // Narrower than `build`'s error on purpose: the scan is the only phase
414        // this runs, so a test matching on `ScanError` says what it means.
415        match self.derive() {
416            Ok(_) => Ok(self.registry),
417            Err(WriteRustError::Scan(e)) => Err(e),
418            Err(other) => panic!("scanned(): unexpected non-scan failure: {other}"),
419        }
420    }
421
422    /// Close the binding: apply every conversion, check the set is complete,
423    /// and hand back a registry that can only be read.
424    ///
425    /// A crossing with no conversion is not itself a failure — the scan
426    /// over-approximates on purpose. What fails is a crossing *reachable from
427    /// an export* with none, and the error names every one at once.
428    pub fn build(mut self) -> Result<Registry<M>, WriteRustError> {
429        self.derive()?;
430        for ((dir, key), entry) in self.built {
431            if let Some(cell) = self.registry.type_table_mut(dir).get_mut(&key) {
432                cell.entry = Some(entry);
433            }
434        }
435        crate::resolve::check_complete(&self.registry)?;
436        Ok(self.registry)
437    }
438}
439
440/// A builder answers the same questions a finished registry does — with one
441/// difference that is the whole point of the split: [`conversion`] sees only
442/// what has been handed over *so far*.
443///
444/// That is what a generator writing a conversion needs (its inners, already
445/// built) and it is all it should be able to see. Everything else — the model,
446/// the decompositions — is complete from the moment it is declared.
447///
448/// [`conversion`]: Conversions::conversion
449impl<M> Conversions<M> for RegistryBuilder<M> {
450    fn reading(&self, key: &TypeKey) -> Option<prebindgen_flat::flat::TypeRef> {
451        self.registry.reading(key)
452    }
453    fn flat(&self) -> &prebindgen_flat::flat::Flat {
454        &self.registry.flat
455    }
456    fn conversion(
457        &self,
458        dir: Direction,
459        reading: &prebindgen_flat::flat::TypeRef,
460    ) -> Option<&TypeEntry<M>> {
461        self.built.get(&(dir, reading.key()))
462    }
463    fn crossing_keys(&self, dir: Direction) -> Vec<TypeKey> {
464        self.order
465            .as_deref()
466            .unwrap_or_default()
467            .iter()
468            .filter(|(d, _)| *d == dir)
469            .map(|(_, k)| k.clone())
470            .collect()
471    }
472    fn callback_arg_plan(&self, key: &TypeKey) -> Option<&crate::unfold::UnfoldPlan> {
473        self.registry.callback_arg_plans.get(key)
474    }
475    fn callback_arg_plans(&self) -> &HashMap<TypeKey, crate::unfold::UnfoldPlan> {
476        &self.registry.callback_arg_plans
477    }
478    fn unfold_plans(&self) -> &HashMap<syn::Ident, crate::unfold::UnfoldPlan> {
479        &self.registry.unfold_plans
480    }
481    fn error_plans(&self) -> &HashMap<syn::Ident, crate::unfold::UnfoldPlan> {
482        &self.registry.error_plans
483    }
484    fn decon_plans(&self) -> &HashMap<crate::unfold::DeconId, crate::unfold::DeconSpec> {
485        &self.registry.decon_plans
486    }
487}